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 +/tmp/i3-ipc.sock+.
+most languages. In the default configuration file, the ipc-socket gets created
+in +/tmp/i3-%u/ipc-socket.%p+ where +%u+ is your UNIX username and +%p+ is the
+PID of i3.
+
+All i3 utilities, like +i3-msg+ and +i3-input+ will read the +I3_SOCKET_PATH+
+X11 property, stored on the X11 root window.
== Establishing a connection
To send a message to i3, you have to format in the binary message format which
i3 expects. This format specifies a magic string in the beginning to ensure
-the integrity of messages (to prevent follow-up errors). Afterwards follows
-the length of the payload of the message as 32-bit integer and the type of
-the message as 32-bit integer (the integers are not converted, so they are
-in native byte order).
+the integrity of messages (to prevent follow-up errors). Following the magic
+string comes the length of the payload of the message as 32-bit integer, and
+the type of the message as 32-bit integer (the integers are not converted, so
+they are in native byte order).
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
SUBSCRIBE (2)::
Subscribes your connection to certain events. See <<events>> 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).
So, a typical message could look like this:
--------------------------------------------------
}
$sock->write(format_ipc_command("exit"));
-------------------------------------------------------------
+------------------------------------------------------------------------------
== Receiving replies from i3
-Replies of i3 usually consist of a simple string (the length of the string
+Replies from i3 usually consist of a simple string (the length of the string
is the message_length, so you can consider them length-prefixed) which in turn
contain the JSON serialization of a data structure. For example, the
GET_WORKSPACES message returns an array of workspaces (each workspace is a map
Reply to the GET_WORKSPACES message.
SUBSCRIBE (2)::
Confirmation/Error code for the SUBSCRIBE message.
+GET_OUTPUTS (3)::
+ Reply to the GET_OUTPUTS message.
+GET_TREE (4)::
+ Reply to the GET_TREE message.
+GET_MARKS (5)::
+ Reply to the GET_MARKS message.
=== COMMAND reply
focused (boolean)::
Whether this workspace currently has the focus (only one workspace
can have the focus at the same time).
+urgent (boolean)::
+ Whether a window on this workspace has the "urgent" flag set.
rect (map)::
The rectangle of this workspace (equals the rect of the output it
is on), consists of x, y, width, height.
"name": "1",
"visible": true,
"focused": true,
+ "urgent": false,
"rect": {
"x": 0,
"y": 0,
"name": "2",
"visible": false,
"focused": false,
+ "urgent": false,
"rect": {
"x": 0,
"y": 0,
{ "success": true }
-------------------
+=== GET_OUTPUTS reply
+
+The reply consists of a serialized list of outputs. Each output has the
+following properties:
+
+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).
+current_workspace (integer)::
+ The current workspace which 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.
+
+*Example:*
+-------------------
+[
+ {
+ "name": "LVDS1",
+ "active": true,
+ "current_workspace": 4,
+ "rect": {
+ "x": 0,
+ "y": 0,
+ "width": 1280,
+ "height": 800
+ }
+ },
+ {
+ "name": "VGA1",
+ "active": true,
+ "current_workspace": 1,
+ "rect": {
+ "x": 1280,
+ "y": 0,
+ "width": 1280,
+ "height": 1024
+ },
+ }
+]
+-------------------
+
+=== GET_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 all other containers, the content is not defined (yet).
+border (string)::
+ Can be either "normal", "none" or "1pixel", dependending on the
+ container’s border style.
+layout (string)::
+ Can be either "default", "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".
+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).
+geometry (map)::
+ The original geometry the window specified when i3 mapped it. Used when
+ switching a window to floating mode, for example.
+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
+ }
+ }
+
+ ]
+ }
+ ]
+ }
+ ]
+}
+
+
+=== GET_MARKS reply
+
+The reply consists of a single array of strings for each container that has a
+mark. The order of that array is undefined. If more than one container has the
+same mark, it will be represented multiple times in the reply (the array
+contents are not unique).
+
+If no window has a mark the response will be the empty array [].
+------------------------
+
+
== Events
[[events]]
To get informed when certain things happen in i3, clients can subscribe to
events. Events consist of a name (like "workspace") and an event reply type
(like I3_IPC_EVENT_WORKSPACE). The events sent by i3 are in the same format
-as replies to specific commands.
+as replies to specific commands. However, the highest bit of the message type
+is set to 1 to indicate that this is an event reply instead of a normal reply.
Caveat: As soon as you subscribe to an event, it is not guaranteed any longer
that the requests to i3 are processed in order. This means, the following
situation can happen: You send a GET_WORKSPACES request but you receive a
"workspace" event before receiving the reply to GET_WORKSPACES. If your
program does not want to cope which such kinds of race conditions (an
-event based library may not have a problem here), I advise to create a separate
-connection to receive events.
+event based library may not have a problem here), I suggest you create a
+separate connection to receive events.
=== Subscribing to events
payload: [ "workspace", "focus" ]
---------------------------------
+
=== Available events
-workspace::
+The numbers in parenthesis is the event type (keep in mind that you need to
+strip the highest bit first).
+
+workspace (0)::
Sent when the user switches to a different workspace, when a new
workspace is initialized or when a workspace is removed (because the
last client vanished).
+output (1)::
+ Sent when RandR issues a change notification (of either screens,
+ outputs, CRTCs or output properties).
+
+*Example:*
+--------------------------------------------------------------------
+# the appropriate 4 bytes read from the socket are stored in $input
+
+# unpack a 32-bit unsigned integer
+my $message_type = unpack("L", $input);
+
+# check if the highest bit is 1
+my $is_event = (($message_type >> 31) == 1);
+
+# use the other bits
+my $event_type = ($message_type & 0x7F);
+
+if ($is_event) {
+ say "Received event of type $event_type";
+}
+--------------------------------------------------------------------
=== workspace event
This event consists of a single serialized map containing a property
+change (string)+ which indicates the type of the change ("focus", "init",
-"empty").
+"empty", "urgent").
*Example:*
---------------------
{ "change": "focus" }
---------------------
+=== output event
+
+This event consists of a single serialized map containing a property
++change (string)+ which indicates the type of the change (currently only
+"unspecified").
+
+*Example:*
+---------------------------
+{ "change": "unspecified" }
+---------------------------
+
== See also
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
Perl::
http://search.cpan.org/search?query=AnyEvent::I3
+Python::
+ http://github.com/thepub/i3ipc