IPC interface (interprocess communication)
==========================================
-Michael Stapelberg <michael+i3@stapelberg.de>
-March 2010
+Michael Stapelberg <michael@i3wm.org>
+October 2014
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
The method of choice for IPC in our case is a unix socket because it has very
little overhead on both sides and is usually available without headaches in
-most languages. In the default configuration file, no ipc-socket path is
-specified and thus no socket is created. The standard path (which +i3-msg+ and
-+i3-input+ use) is +~/.i3/ipc.sock+.
+most languages. In the default configuration file, the ipc-socket gets created
+in +/tmp/i3-%u.XXXXXX/ipc-socket.%p+ where +%u+ is your UNIX username, +%p+ is
+the PID of i3 and XXXXXX is a string of random characters from the portable
+filename character set (see mkdtemp(3)). You can get the socketpath from i3 by
+calling +i3 --get-socketpath+.
+
+All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
+X11 property, stored on the X11 root window.
+
+[WARNING]
+.Use an existing library!
+There are existing libraries for many languages. You can have a look at
+<<libraries>> or search the web if your language of choice is not mentioned.
+Usually, it is not necessary to implement low-level communication with i3
+directly.
== Establishing a connection
-------------------------------------------------------------
use IO::Socket::UNIX;
-my $sock = IO::Socket::UNIX->new(Peer => '~/.i3/ipc.sock');
+chomp(my $path = qx(i3 --get-socketpath));
+my $sock = IO::Socket::UNIX->new(Peer => $path);
-------------------------------------------------------------
== Sending messages to i3
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. There is no reply to this message.
+ 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).
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.
+GET_BINDING_MODES (8)::
+ Gets a list of currently configured binding modes.
So, a typical message could look like this:
--------------------------------------------------
Or, as a hexdump:
------------------------------------------------------------------------------
00000000 69 33 2d 69 70 63 04 00 00 00 00 00 00 00 65 78 |i3-ipc........ex|
-00000010 69 74 0a |it.|
+00000010 69 74 |it|
------------------------------------------------------------------------------
To generate and send such a message, you could use the following code in Perl:
COMMAND (0)::
Confirmation/Error code for the COMMAND message.
-GET_WORKSPACES (1)::
+WORKSPACES (1)::
Reply to the GET_WORKSPACES message.
SUBSCRIBE (2)::
Confirmation/Error code for the SUBSCRIBE message.
-GET_OUTPUTS (3)::
+OUTPUTS (3)::
Reply to the GET_OUTPUTS message.
+TREE (4)::
+ Reply to the GET_TREE message.
+MARKS (5)::
+ Reply to the GET_MARKS message.
+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.
=== COMMAND reply
-The reply consists of a single serialized map. At the moment, the only
-property is +success (bool)+, but this will be expanded in future versions.
+The reply consists of a list of serialized maps for each command that was
+parsed. Each has the property +success (bool)+ and may also include a
+human-readable error message in the property +error (string)+.
*Example:*
-------------------
-{ "success": true }
+[{ "success": true }]
-------------------
-=== GET_WORKSPACES reply
+=== WORKSPACES reply
The reply consists of a serialized list of workspaces. Each workspace has the
following properties:
num (integer)::
The logical number of the workspace. Corresponds to the command
- to switch to this workspace.
+ to switch to this workspace. For named workspaces, this will be -1.
name (string)::
The name of this workspace (by default num+1), as changed by the
user. Encoded in UTF-8.
{ "success": true }
-------------------
-=== GET_OUTPUTS reply
+=== OUTPUTS reply
The reply consists of a serialized list of outputs. Each output has the
following properties:
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).
-current_workspace (integer)::
- The current workspace which is visible on this output. +null+ if the
- output is not active.
+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.
rect (map)::
The rectangle of this output (equals the rect of the output it
is on), consists of x, y, width, height.
{
"name": "LVDS1",
"active": true,
- "current_workspace": 4,
+ "current_workspace": "4",
"rect": {
"x": 0,
"y": 0,
{
"name": "VGA1",
"active": true,
- "current_workspace": 1,
+ "current_workspace": "1",
"rect": {
"x": 1280,
"y": 0,
"width": 1280,
"height": 1024
- },
+ }
}
]
-------------------
+=== TREE reply
+
+The reply consists of a serialized tree. Each node in the tree (representing
+one container) has at least the properties listed below. While the nodes might
+have more properties, please do not use any properties which are not documented
+here. They are not yet finalized and will probably change!
+
+id (integer)::
+ The internal ID (actually a C pointer value) of this container. Do not
+ make any assumptions about it. You can use it to (re-)identify and
+ address containers when talking to i3.
+name (string)::
+ The internal name of this container. For all containers which are part
+ of the tree structure down to the workspace contents, this is set to a
+ nice human-readable name of the container.
+ For containers that have an X11 window, the content is the title
+ (_NET_WM_NAME property) of that window.
+ For all other containers, the content is not defined (yet).
+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 "pixel", depending on the
+ container’s border style.
+current_border_width (integer)::
+ Number of pixels of the border width.
+layout (string)::
+ Can be either "splith", "splitv", "stacked", "tabbed", "dockarea" or
+ "output".
+ Other values might be possible in the future, should we add new
+ layouts.
+orientation (string)::
+ Can be either "none" (for non-split containers), "horizontal" or
+ "vertical".
+ THIS FIELD IS OBSOLETE. It is still present, but your code should not
+ use it. Instead, rely on the layout field.
+percent (float)::
+ The percentage which this container takes in its parent. A value of
+ +null+ means that the percent property does not make sense for this
+ container, for example for the root container.
+rect (map)::
+ The absolute display coordinates for this container. Display
+ coordinates means that when you have two 1600x1200 monitors on a single
+ X11 Display (the standard way), the coordinates of the first window on
+ the second monitor are +{ "x": 1600, "y": 0, "width": 1600, "height":
+ 1200 }+.
+window_rect (map)::
+ The coordinates of the *actual client window* inside its container.
+ These coordinates are relative to the container and do not include the
+ window decoration (which is actually rendered on the parent container).
+ So, when using the +default+ layout, you will have a 2 pixel border on
+ each side, making the window_rect +{ "x": 2, "y": 0, "width": 632,
+ "height": 366 }+ (for example).
+deco_rect (map)::
+ The coordinates of the *window decoration* inside its container. These
+ coordinates are relative to the container and do not include the actual
+ client window.
+geometry (map)::
+ The original geometry the window specified when i3 mapped it. Used when
+ switching a window to floating mode, for example.
+window (integer)::
+ The X11 window ID of the *actual client window* inside this container.
+ 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).
+urgent (bool)::
+ Whether this container (window or workspace) has the urgency hint set.
+focused (bool)::
+ Whether this container is currently focused.
+
+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
+be by far too long (it already is quite long, despite showing only 1 window and
+one dock window).
+
+It is useful to have an overview of the structure before taking a look at the
+JSON dump:
+
+* root
+** LVDS1
+*** topdock
+*** content
+**** workspace 1
+***** window 1
+*** bottomdock
+**** dock window 1
+** VGA1
+
+*Example:*
+-----------------------
+{
+ "id": 6875648,
+ "name": "root",
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "width": 1280,
+ "height": 800
+ },
+ "nodes": [
+
+ {
+ "id": 6878320,
+ "name": "LVDS1",
+ "layout": "output",
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "width": 1280,
+ "height": 800
+ },
+ "nodes": [
+
+ {
+ "id": 6878784,
+ "name": "topdock",
+ "layout": "dockarea",
+ "orientation": "vertical",
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "width": 1280,
+ "height": 0
+ }
+ },
+
+ {
+ "id": 6879344,
+ "name": "content",
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "width": 1280,
+ "height": 782
+ },
+ "nodes": [
+
+ {
+ "id": 6880464,
+ "name": "1",
+ "orientation": "horizontal",
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "width": 1280,
+ "height": 782
+ },
+ "floating_nodes": [],
+ "nodes": [
+
+ {
+ "id": 6929968,
+ "name": "#aa0000",
+ "border": "normal",
+ "percent": 1,
+ "rect": {
+ "x": 0,
+ "y": 18,
+ "width": 1280,
+ "height": 782
+ }
+ }
+
+ ]
+ }
+
+ ]
+ },
+
+ {
+ "id": 6880208,
+ "name": "bottomdock",
+ "layout": "dockarea",
+ "orientation": "vertical",
+ "rect": {
+ "x": 0,
+ "y": 782,
+ "width": 1280,
+ "height": 18
+ },
+ "nodes": [
+
+ {
+ "id": 6931312,
+ "name": "#00aa00",
+ "percent": 1,
+ "rect": {
+ "x": 0,
+ "y": 782,
+ "width": 1280,
+ "height": 18
+ }
+ }
+
+ ]
+ }
+ ]
+ }
+ ]
+}
+------------------------
+
+=== MARKS reply
+
+The reply consists of a single array of strings for each container that has a
+mark. A mark can only be set on one container, so the array is unique.
+The order of that array is undefined.
+
+If no window has a mark the response will be the empty array [].
+
+=== BAR_CONFIG reply
+
+This can be used by third-party workspace bars (especially i3bar, but others
+are free to implement compatible alternatives) to get the +bar+ block
+configuration from i3.
+
+Depending on the input, the reply is either:
+
+empty input::
+ An array of configured bar IDs
+Bar ID::
+ A JSON map containing the configuration for the specified bar.
+
+Each bar configuration has the following properties:
+
+id (string)::
+ The ID for this bar. Included in case you request multiple
+ configurations and want to differentiate the different replies.
+mode (string)::
+ Either +dock+ (the bar sets the dock window type) or +hide+ (the bar
+ does not show unless a specific key is pressed).
+position (string)::
+ Either +bottom+ or +top+ at the moment.
+status_command (string)::
+ Command which will be run to generate a statusline. Each line on stdout
+ of this command will be displayed in the bar. At the moment, no
+ formatting is supported.
+font (string)::
+ The font to use for text on the bar.
+workspace_buttons (boolean)::
+ Display workspace buttons or not? Defaults to true.
+binding_mode_indicator (boolean)::
+ Display the mode indicator or not? Defaults to true.
+verbose (boolean)::
+ Should the bar enable verbose output for debugging? Defaults to false.
+colors (map)::
+ Contains key/value pairs of colors. Each value is a color code in hex,
+ formatted #rrggbb (like in HTML).
+
+The following colors can be configured at the moment:
+
+background::
+ Background color of the bar.
+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.
+active_workspace_text/active_workspace_bg/active_workspace_border::
+ Text/background/border color for a workspace button when the workspace
+ is active (visible) on some output, but the focus is on another one.
+ You can only tell this apart from the focused workspace when you are
+ using multiple monitors.
+inactive_workspace_text/inactive_workspace_bg/inactive_workspace_border::
+ Text/background/border color for a workspace button when the workspace
+ does not have focus and is not active (visible) on any output. This
+ will be the case for most workspaces.
+urgent_workspace_text/urgent_workspace_bg/urgent_workspace_border::
+ Text/background/border color for workspaces which contain at least one
+ window with the urgency hint set.
+binding_mode_text/binding_mode_bg/binding_mode_border::
+ Text/background/border color for the binding mode indicator.
+
+
+*Example of configured bars:*
+--------------
+["bar-bxuqzf"]
+--------------
+
+*Example of bar configuration:*
+--------------
+{
+ "id": "bar-bxuqzf",
+ "mode": "dock",
+ "position": "bottom",
+ "status_command": "i3status",
+ "font": "-misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1",
+ "workspace_buttons": true,
+ "binding_mode_indicator": true,
+ "verbose": false,
+ "colors": {
+ "background": "#c0c0c0",
+ "statusline": "#00ff00",
+ "focused_workspace_text": "#ffffff",
+ "focused_workspace_bg": "#000000"
+ }
+}
+--------------
+
+=== VERSION reply
+
+The reply consists of a single JSON dictionary with the following keys:
+
+major (integer)::
+ The major version of i3, such as +4+.
+minor (integer)::
+ The minor version of i3, such as +2+. Changes in the IPC interface (new
+ features) will only occur with new minor (or major) releases. However,
+ bugfixes might be introduced in patch releases, too.
+patch (integer)::
+ The patch version of i3, such as +1+ (when the complete version is
+ +4.2.1+). For versions such as +4.2+, patch will be set to +0+.
+human_readable (string)::
+ A human-readable version of i3 containing the precise git version,
+ build date and branch name. When you need to display the i3 version to
+ your users, use the human-readable version whenever possible (since
+ this is what +i3 --version+ displays, too).
+loaded_config_file_name (string)::
+ The current config path.
+
+*Example:*
+-------------------
+{
+ "human_readable" : "4.2-169-gf80b877 (2012-08-05, branch \"next\")",
+ "loaded_config_file_name" : "/home/hwangcc23/.i3/config",
+ "minor" : 2,
+ "patch" : 0,
+ "major" : 4
+}
+-------------------
+
+=== BINDING_MODES reply
+
+The reply consists of an array of all currently configured binding modes.
+
+*Example:*
+---------------------
+["default", "resize"]
+---------------------
+
== Events
[[events]]
*Example:*
---------------------------------
type: SUBSCRIBE
-payload: [ "workspace", "focus" ]
+payload: [ "workspace", "output" ]
---------------------------------
output (1)::
Sent when RandR issues a change notification (of either screens,
outputs, CRTCs or output properties).
+mode (2)::
+ Sent whenever i3 changes its binding mode.
+window (3)::
+ Sent when a client's window is successfully reparented (that is when i3
+ has finished fitting it into a container), when a window received input
+ focus or when certain properties of the window have changed.
+barconfig_update (4)::
+ Sent when the hidden_state or mode field in the barconfig of any bar
+ instance was updated and when the config is reloaded.
+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
*Example:*
--------------------------------------------------------------------
This event consists of a single serialized map containing a property
+change (string)+ which indicates the type of the change ("focus", "init",
-"empty", "urgent").
+"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
+workspace visible at the beginning) there is no previous workspace, and the
++old+ property will be set to +null+. Also note that if the previous is empty
+it will get destroyed when switching, but will still be present in the "old"
+property.
*Example:*
---------------------
-{ "change": "focus" }
+{
+ "change": "focus",
+ "current": {
+ "id": 28489712,
+ "type": "workspace",
+ ...
+ }
+ "old": {
+ "id": 28489715,
+ "type": "workspace",
+ ...
+ }
+}
---------------------
=== output event
{ "change": "unspecified" }
---------------------------
-== See also
+=== mode event
+
+This event consists of a single serialized map containing a property
++change (string)+ which holds the name of current mode in use. The name
+is the same as specified in config when creating a mode. The default
+mode is simply named default. It contains a second property, +pango_markup+, which
+defines whether pango markup shall be used for displaying this mode.
+
+*Example:*
+---------------------------
+{
+ "change": "default",
+ "pango_markup": true
+}
+---------------------------
+
+=== window event
+
+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
+* +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
+container will hold the initial name of the newly reparented window (e.g.
+if you run urxvt with a shell that changes the title, you will still at
+this point get the window title as "urxvt").
+
+*Example:*
+---------------------------
+{
+ "change": "new",
+ "container": {
+ "id": 35569536,
+ "type": "con",
+ ...
+ }
+}
+---------------------------
+
+=== barconfig_update event
+
+This event consists of a single serialized map reporting on options from the
+barconfig of the specified bar_id that were updated in i3. This event is the
+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 (string)+ field
+indicates what sort of binding event was triggered (right now it will always be
++"run"+ but may be expanded in the future).
+
+The +binding (object)+ field contains details about the binding that was run:
+
+command (string)::
+ The i3 command that is configured to run for this binding.
+event_state_mask (array of strings)::
+ The group and modifier keys that were configured with this binding.
+input_code (integer)::
+ If the binding was configured with +bindcode+, this will be the key code
+ that was given for the binding. If the binding is a mouse binding, it will be
+ the number of the mouse button that was pressed. Otherwise it will be 0.
+symbol (string or null)::
+ If this is a keyboard binding that was configured with +bindsym+, this
+ field will contain the given symbol. Otherwise it will be +null+.
+input_type (string)::
+ This will be +"keyboard"+ or +"mouse"+ depending on whether or not this was
+ a keyboard or a mouse binding.
+
+*Example:*
+---------------------------
+{
+ "change": "run",
+ "binding": {
+ "command": "nop",
+ "event_state_mask": [
+ "shift",
+ "ctrl"
+ ],
+ "input_code": 0,
+ "symbol": "t",
+ "input_type": "keyboard"
+ }
+}
+---------------------------
+
+=== 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"
+}
+---------------------------
+
+== See also (existing libraries)
+
+[[libraries]]
For some languages, libraries are available (so you don’t have to implement
all this on your own). This list names some (if you wrote one, please let me
know):
C::
- i3 includes a headerfile +i3/ipc.h+ which provides you all constants.
- However, there is no library yet.
-Ruby::
- http://github.com/badboy/i3-ipc
+ * 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/mdirkse/i3ipc-go
+JavaScript::
+ * https://github.com/acrisci/i3ipc-gjs
+Lua::
+ * https://github.com/acrisci/i3ipc-lua
Perl::
- http://search.cpan.org/search?query=AnyEvent::I3
+ * https://metacpan.org/module/AnyEvent::I3
Python::
- http://github.com/thepub/i3ipc
+ * https://github.com/acrisci/i3ipc-python
+ * https://github.com/whitelynx/i3ipc (not maintained)
+ * https://github.com/ziberna/i3-py (not maintained)
+Ruby::
+ * https://github.com/veelenga/i3ipc-ruby
+ * https://github.com/badboy/i3-ipc (not maintained)
+Rust::
+ * https://github.com/tmerr/i3ipc-rs
+OCaml::
+ * https://github.com/Armael/ocaml-i3ipc