X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=docs%2Fipc;h=bcf8df1aa4abe5ba1c86fced66f689aba2fd05b4;hb=5295266cf293ad5829810006d370d21bf9ebb803;hp=1813e53a863e2a2561599cb62be0ee1324f4e138;hpb=82dc747396e5a6814c3bcd66f065c04e68253448;p=i3%2Fi3 diff --git a/docs/ipc b/docs/ipc index 1813e53a..bcf8df1a 100644 --- a/docs/ipc +++ b/docs/ipc @@ -1,7 +1,7 @@ IPC interface (interprocess communication) ========================================== Michael Stapelberg -October 2014 +September 2017 This document describes how to interface with i3 from a separate process. This is useful for example to remote-control i3 (to write test cases for example) or @@ -50,36 +50,23 @@ The magic string currently is "i3-ipc" and will only be changed when a change in the IPC API is done which breaks compatibility (we hope that we don’t need to do that). -Currently implemented message types are the following: - -COMMAND (0):: - The payload of the message is a command for i3 (like the commands you - can bind to keys in the configuration file) and will be executed - directly after receiving it. -GET_WORKSPACES (1):: - Gets the current workspaces. The reply will be a JSON-encoded list of - workspaces (see the reply section). -SUBSCRIBE (2):: - Subscribes your connection to certain events. See <> for a - description of this message and the concept of events. -GET_OUTPUTS (3):: - Gets the current outputs. The reply will be a JSON-encoded list of outputs - (see the reply section). -GET_TREE (4):: - Gets the layout tree. i3 uses a tree as data structure which includes - every container. The reply will be the JSON-encoded tree (see the reply - section). -GET_MARKS (5):: - Gets a list of marks (identifiers for containers to easily jump to them - later). The reply will be a JSON-encoded list of window marks (see - reply section). -GET_BAR_CONFIG (6):: - Gets the configuration (as JSON map) of the workspace bar with the - given ID. If no ID is provided, an array with all configured bar IDs is - returned instead. -GET_VERSION (7):: - Gets the version of i3. The reply will be a JSON-encoded dictionary - with the major, minor, patch and human-readable version. +.Currently implemented message types +[options="header",cols="^10%,^20%,^20%,^50%"] +|====================================================== +| Type (numeric) | Type (name) | Reply type | Purpose +| 0 | +RUN_COMMAND+ | <<_command_reply,COMMAND>> | Run the payload as an i3 command (like the commands you can bind to keys). +| 1 | +GET_WORKSPACES+ | <<_workspaces_reply,WORKSPACES>> | Get the list of current workspaces. +| 2 | +SUBSCRIBE+ | <<_subscribe_reply,SUBSCRIBE>> | Subscribe this IPC connection to the event types specified in the message payload. See <>. +| 3 | +GET_OUTPUTS+ | <<_outputs_reply,OUTPUTS>> | Get the list of current outputs. +| 4 | +GET_TREE+ | <<_tree_reply,TREE>> | Get the i3 layout tree. +| 5 | +GET_MARKS+ | <<_marks_reply,MARKS>> | Gets the names of all currently set marks. +| 6 | +GET_BAR_CONFIG+ | <<_bar_config_reply,BAR_CONFIG>> | Gets the specified bar configuration or the names of all bar configurations if payload is empty. +| 7 | +GET_VERSION+ | <<_version_reply,VERSION>> | Gets the i3 version. +| 8 | +GET_BINDING_MODES+ | <<_binding_modes_reply,BINDING_MODES>> | Gets the names of all currently configured binding modes. +| 9 | +GET_CONFIG+ | <<_config_reply,CONFIG>> | Returns the last loaded i3 config. +| 10 | +SEND_TICK+ | <<_tick_reply,TICK>> | Sends a tick event with the specified payload. +| 11 | +SYNC+ | <<_sync_reply,SYNC>> | Sends an i3 sync event with the specified random value to the specified window. +|====================================================== So, a typical message could look like this: -------------------------------------------------- @@ -122,7 +109,7 @@ payload. The following reply types are implemented: COMMAND (0):: - Confirmation/Error code for the COMMAND message. + Confirmation/Error code for the RUN_COMMAND message. WORKSPACES (1):: Reply to the GET_WORKSPACES message. SUBSCRIBE (2):: @@ -137,7 +124,14 @@ BAR_CONFIG (6):: Reply to the GET_BAR_CONFIG message. VERSION (7):: Reply to the GET_VERSION message. - +BINDING_MODES (8):: + Reply to the GET_BINDING_MODES message. +GET_CONFIG (9):: + Reply to the GET_CONFIG message. +TICK (10):: + Reply to the SEND_TICK message. + +[[_command_reply]] === COMMAND reply The reply consists of a list of serialized maps for each command that was @@ -149,6 +143,7 @@ human-readable error message in the property +error (string)+. [{ "success": true }] ------------------- +[[_workspaces_reply]] === WORKSPACES reply The reply consists of a serialized list of workspaces. Each workspace has the @@ -208,6 +203,7 @@ output (string):: ] ------------------- +[[_subscribe_reply]] === SUBSCRIBE reply The reply consists of a single serialized map. The only property is @@ -219,6 +215,7 @@ default) or whether a JSON parse error occurred. { "success": true } ------------------- +[[_outputs_reply]] === OUTPUTS reply The reply consists of a serialized list of outputs. Each output has the @@ -228,6 +225,8 @@ name (string):: The name of this output (as seen in +xrandr(1)+). Encoded in UTF-8. active (boolean):: Whether this output is currently active (has a valid mode). +primary (boolean):: + Whether this output is currently the primary output. current_workspace (string):: The name of the current workspace that is visible on this output. +null+ if the output is not active. @@ -258,11 +257,12 @@ rect (map):: "y": 0, "width": 1280, "height": 1024 - }, + } } ] ------------------- +[[_tree_reply]] === TREE reply The reply consists of a serialized tree. Each node in the tree (representing @@ -285,7 +285,7 @@ type (string):: Type of this container. Can be one of "root", "output", "con", "floating_con", "workspace" or "dockarea". border (string):: - Can be either "normal", "none" or "1pixel", dependending on the + Can be either "normal", "none" or "pixel", depending on the container’s border style. current_border_width (integer):: Number of pixels of the border width. @@ -328,10 +328,25 @@ window (integer):: This field is set to null for split containers or otherwise empty containers. This ID corresponds to what xwininfo(1) and other X11-related tools display (usually in hex). +window_properties (map):: + X11 window properties title, instance, class, window_role and transient_for. urgent (bool):: - Whether this container (window or workspace) has the urgency hint set. + Whether this container (window, split container, floating container or + workspace) has the urgency hint set, directly or indirectly. All parent + containers up until the workspace container will be marked urgent if they + have at least one urgent child. focused (bool):: Whether this container is currently focused. +focus (array of integer):: + List of child node IDs (see +nodes+, +floating_nodes+ and +id+) in focus + order. Traversing the tree by following the first entry in this array + will result in eventually reaching the one node with +focused+ set to + true. +nodes (array of node):: + The tiling (i.e. non-floating) child containers of this node. +floating_nodes (array of node):: + The floating child containers of this node. Only non-empty on nodes with + type +workspace+. Please note that in the following example, I have left out some keys/values which are not relevant for the type of the node. Otherwise, the example would @@ -386,7 +401,7 @@ JSON dump: "y": 0, "width": 1280, "height": 0 - }, + } }, { @@ -410,6 +425,12 @@ JSON dump: "width": 1280, "height": 782 }, + "window_properties": { + "class": "Evince", + "instance": "evince", + "title": "Properties", + "transient_for": 52428808 + }, "floating_nodes": [], "nodes": [ @@ -465,6 +486,7 @@ JSON dump: } ------------------------ +[[_marks_reply]] === MARKS reply The reply consists of a single array of strings for each container that has a @@ -473,6 +495,7 @@ The order of that array is undefined. If no window has a mark the response will be the empty array []. +[[_bar_config_reply]] === BAR_CONFIG reply This can be used by third-party workspace bars (especially i3bar, but others @@ -520,6 +543,14 @@ statusline:: Text color to be used for the statusline. separator:: Text color to be used for the separator. +focused_background:: + Background color of the bar on the currently focused monitor output. +focused_statusline:: + Text color to be used for the statusline on the currently focused + monitor output. +focused_separator:: + Text color to be used for the separator on the currently focused + monitor output. focused_workspace_text/focused_workspace_bg/focused_workspace_border:: Text/background/border color for a workspace button when the workspace has focus. @@ -564,6 +595,7 @@ binding_mode_text/binding_mode_bg/binding_mode_border:: } -------------- +[[_version_reply]] === VERSION reply The reply consists of a single JSON dictionary with the following keys: @@ -596,6 +628,53 @@ loaded_config_file_name (string):: } ------------------- +[[_binding_modes_reply]] +=== BINDING_MODES reply + +The reply consists of an array of all currently configured binding modes. + +*Example:* +--------------------- +["default", "resize"] +--------------------- + +[[_config_reply]] +=== CONFIG reply + +The config reply is a map which currently only contains the "config" member, +which is a string containing the config file as loaded by i3 most recently. + +*Example:* +------------------- +{ "config": "font pango:monospace 8\nbindsym Mod4+q exit\n" } +------------------- + +[[_tick_reply]] +=== TICK reply + +The reply is a map containing the "success" member. After the reply was +received, the tick event has been written to all IPC connections which subscribe +to tick events. UNIX sockets are usually buffered, but you can be certain that +once you receive the tick event you just triggered, you must have received all +events generated prior to the +SEND_TICK+ message (happened-before relation). + +*Example:* +------------------- +{ "success": true } +------------------- + +[[_sync_reply]] +=== SYNC reply + +The reply is a map containing the "success" member. After the reply was +received, the https://i3wm.org/docs/testsuite.html#i3_sync[i3 sync message] was +responded to. + +*Example:* +------------------- +{ "success": true } +------------------- + == Events [[events]] @@ -614,6 +693,14 @@ program does not want to cope which such kinds of race conditions (an event based library may not have a problem here), I suggest you create a separate connection to receive events. +If an event message needs to be sent and the socket is not writeable (write +returns EAGAIN, happens when the socket doesn't have enough buffer space for +writing new data) then i3 uses a queue system to store outgoing messages for +each client. This is combined with a timer: if the message queue for a client is +not empty and no data where successfully written in the past 10 seconds, the +connection is killed. Practically, this means that your client should try to +always read events from the socket to avoid having its connection closed. + === Subscribing to events By sending a message of type SUBSCRIBE with a JSON-encoded array as payload @@ -650,6 +737,12 @@ barconfig_update (4):: binding (5):: Sent when a configured command binding is triggered with the keyboard or mouse +shutdown (6):: + Sent when the ipc shuts down because of a restart or exit by user command +tick (7):: + Sent when the ipc client subscribes to the tick event (with +"first": + true+) or when any ipc client sends a SEND_TICK message (with +"first": + false+). *Example:* -------------------------------------------------------------------- @@ -673,9 +766,9 @@ if ($is_event) { This event consists of a single serialized map containing a property +change (string)+ which indicates the type of the change ("focus", "init", -"empty", "urgent"). A +current (object)+ property will be present with the -affected workspace whenever the type of event affects a workspace (otherwise, -it will be +null). +"empty", "urgent", "reload", "rename", "restored", "move"). A ++current (object)+ property will be present with the affected workspace +whenever the type of event affects a workspace (otherwise, it will be +null). When the change is "focus", an +old (object)+ property will be present with the previous workspace. When the first switch occurs (when i3 focuses the @@ -733,14 +826,15 @@ defines whether pango markup shall be used for displaying this mode. This event consists of a single serialized map containing a property +change (string)+ which indicates the type of the change -* +new+ - the window has become managed by i3 -* +close+ - the window has closed -* +focus+ - the window has received input focus -* +title+ - the window's title has changed -* +fullscreen_mode+ - the window has entered or exited fullscreen mode -* +move+ - the window has changed its position in the tree -* +floating+ - the window has transitioned to or from floating -* +urgent+ - the window has become urgent or lost its urgent status +* +new+ – the window has become managed by i3 +* +close+ – the window has closed +* +focus+ – the window has received input focus +* +title+ – the window's title has changed +* +fullscreen_mode+ – the window has entered or exited fullscreen mode +* +move+ – the window has changed its position in the tree +* +floating+ – the window has transitioned to or from floating +* +urgent+ – the window has become urgent or lost its urgent status +* +mark+ – a mark has been added to or removed from the window Additionally a +container (object)+ field will be present, which consists of the window's parent container. Be aware that for the "new" event, the @@ -769,7 +863,7 @@ same as a +GET_BAR_CONFIG+ reply for the bar with the given id. === binding event This event consists of a single serialized map reporting on the details of a -binding that ran a command because of user input. The +change (sring)+ field +binding that ran a command because of user input. The +change (string)+ field indicates what sort of binding event was triggered (right now it will always be +"run"+ but may be expanded in the future). @@ -807,6 +901,41 @@ input_type (string):: } --------------------------- +=== shutdown event + +This event is triggered when the connection to the ipc is about to shutdown +because of a user action such as a +restart+ or +exit+ command. The +change +(string)+ field indicates why the ipc is shutting down. It can be either ++"restart"+ or +"exit"+. + +*Example:* +--------------------------- +{ + "change": "restart" +} +--------------------------- + +=== tick event + +This event is triggered by a subscription to tick events or by a +SEND_TICK+ +message. + +*Example (upon subscription):* +-------------------------------------------------------------------------------- +{ + "first": true, + "payload": "" +} +-------------------------------------------------------------------------------- + +*Example (upon +SEND_TICK+ with a payload of +arbitrary string+):* +-------------------------------------------------------------------------------- +{ + "first": false, + "payload": "arbitrary string" +} +-------------------------------------------------------------------------------- + == See also (existing libraries) [[libraries]] @@ -818,8 +947,11 @@ know): C:: * i3 includes a headerfile +i3/ipc.h+ which provides you all constants. * https://github.com/acrisci/i3ipc-glib +C++:: + * https://github.com/drmgc/i3ipcpp Go:: - * https://github.com/proxypoke/i3ipc + * https://github.com/mdirkse/i3ipc-go + * https://github.com/i3/go-i3 JavaScript:: * https://github.com/acrisci/i3ipc-gjs Lua:: @@ -835,3 +967,68 @@ Ruby:: * https://github.com/badboy/i3-ipc (not maintained) Rust:: * https://github.com/tmerr/i3ipc-rs +OCaml:: + * https://github.com/Armael/ocaml-i3ipc + +== Appendix A: Detecting byte order in memory-safe languages + +Some programming languages such as Go don’t offer a way to serialize data in the +native byte order of the machine they’re running on without resorting to tricks +involving the +unsafe+ package. + +The following technique can be used (and will not be broken by changes to i3) to +detect the byte order i3 is using: + +1. The byte order dependent fields of an IPC message are message type and + payload length. + + * The message type +RUN_COMMAND+ (0) is the same in big and little endian, so + we can use it in either byte order to elicit a reply from i3. + + * The payload length 65536 + 256 (+0x00 01 01 00+) is the same in big and + little endian, and also small enough to not worry about memory allocations + of that size. We must use payloads of length 65536 + 256 in every message + we send, so that i3 will be able to read the entire message regardless of + the byte order it uses. + +2. Send a big endian encoded message of type +SUBSCRIBE+ (2) with payload `[]` + followed by 65536 + 256 - 2 +SPACE+ (ASCII 0x20) bytes. + + * If i3 is running in big endian, this message is treated as a noop, + resulting in a +SUBSCRIBE+ reply with payload `{"success":true}` + footnote:[A small payload is important: that way, we circumvent dealing + with UNIX domain socket buffer sizes, whose size depends on the + implementation/operating system. Exhausting such a buffer results in an i3 + deadlock unless you concurrently read and write, which — depending on the + programming language — makes the technique much more complicated.]. + + * If i3 is running in little endian, this message is read in its entirety due + to the byte order independent payload length, then + https://github.com/i3/i3/blob/d726d09d496577d1c337a4b97486f2c9fbc914f1/src/ipc.c#L1188[silently + discarded] due to the unknown message type. + +3. Send a byte order independent message, i.e. type +RUN_COMMAND+ (0) with + payload +nop byte order detection. padding:+, padded to 65536 + 256 bytes + with +a+ (ASCII 0x61) bytes. i3 will reply to this message with a reply of + type +COMMAND+ (0). + + * The human-readable prefix is in there to not confuse readers of the i3 log. + + * This messages serves as a synchronization primitive so that we know whether + i3 discarded the +SUBSCRIBE+ message or didn’t answer it yet. + +4. Receive a message header from i3, decoding the message type as big endian. + + * If the message’s reply type is +COMMAND+ (0), i3 is running in little + endian (because the +SUBSCRIBE+ message was discarded). Decode the message + payload length as little endian, receive the message payload. + + * If the message’s reply type is anything else, i3 is running in big endian + (because our big endian encoded +SUBSCRIBE+ message was answered). Decode + the message payload length in big endian, receive the message + payload. Then, receive the pending +COMMAND+ message reply in big endian. + +5. From here on out, send/receive all messages using the detected byte order. + +Find an example implementation of this technique in +https://github.com/i3/go-i3/blob/master/byteorder.go