i3bar workspace buttons protocol

This document explains the protocol in which i3bar expects input for configuring workspace buttons. This feature is available since i3 version 4.23.

The program defined by the workspace_command configuration option for i3bar can modify the workspace buttons displayed by i3bar. The command should constantly print in its standard output a stream of messages following the protocol defined in this page.

If you are looking for the status line protocol instead, see https://i3wm.org/docs/i3bar-protocol.html.

1. The protocol

Each message should be a newline-delimited JSON array. The array is in the same format as the GET_WORKSPACES ipc event, see https://i3wm.org/docs/ipc.html#_workspaces_reply.

As an example, this is the output of the i3-msg -t get_workspaces command:

[
  {
    "id": 94131549984064,
    "num": 1,
    "name": "1",
    "visible": false,
    "focused": false,
    "output": "HDMI-A-0",
    "urgent": false
  },
  {
    "id": 94131550477584,
    "num": 2,
    "name": "2",
    "visible": true,
    "focused": true,
    "output": "HDMI-A-0",
    "urgent": false
  },
  {
    "id": 94131550452704,
    "num": 3,
    "name": "3:some workspace",
    "visible": false,
    "focused": false,
    "output": "HDMI-A-0",
    "urgent": false
  }
]

Please note that this example was pretty printed for human consumption, with the "rect" field removed. Workspace button commands should output each array in one line.

Each element in the array represents a workspace. i3bar creates one workspace button for each element in the array. The order of these buttons is the same as the order of the elements in the array.

In general, we recommend subscribing to the workspace and output events, fetching the current workspace information with GET_WORKSPACES, modifying the JSON array in the response according to your needs and then printing it to the standard output. However, you are free to build a new message from the ground up.

1.1. Workspace objects in detail

The documentation of GET_WORKSPACES should be sufficient to understand the meaning of each property but here we provide extra notes for each property and its meaning with respect to i3bar.

All properties but name are optional.

id (integer)

If it is included it will be used to switch to that workspace when you click the corresponding button. If it’s not provided, the name will be used. You can use the id field to present workspaces under a modified name.

num (integer)

The only use of a workspace’s number is if the strip_workspace_numbers setting is enabled.

name (string)

The only required property. If an id is provided you can freely change the name as you wish, effectively renaming the buttons of i3bar.

visible (boolean)

Defaults to false if not included. focused takes precedence over it, however visible is important for more than one monitors.

focused (boolean)

Defaults to false if not included. Generally, exactly one of the workspaces should be focused. If not, no button will have the focused_workspace color.

urgent (boolean)

Defaults to false if not included.

rect (map)

Not used by i3bar but will be ignored.

output (string)

Defaults to the primary output if not included.

2. Examples

These example scripts require the jq utility to be installed but otherwise just use the standard i3-msg utility included with i3. However, you can write your own scripts in your preferred language, with the help of one of the pre-existing i3 libraries

2.1. Base configuration

bar {
  …
  workspace_command /path/to/your/script.sh
  …
}

2.2. Re-create the default behaviour of i3bar

Not very useful by itself but this will be the basic building block of all the following scripts. This one does not require jq.

#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
    # Initially print the current workspaces before we receive any events. This
    # avoids having an empty bar when starting up.
    i3-msg -t get_workspaces;
    # Then, while we receive events, update the workspace information.
    while read; do i3-msg -t get_workspaces; done;
}

2.3. Hide workspace named foo unless if it is focused.

#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
    i3-msg -t get_workspaces;
    while read; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c '[ .[] | select(.name != "foo" or .focused) ]'

Important! Make sure you use the --unbuffered flag with jq, otherwise you might not get the changes in real-time but whenever they are flushed, which might mean that you are getting an empty bar until enough events are written.

2.4. Show empty workspaces foo and bar on LVDS1 even if they do not exist at the moment.

#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
    i3-msg -t get_workspaces;
    while read; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c '
    def fake_ws(name): {
        name: name,
        output: "LVDS1",
    };
    . + [ fake_ws("foo"), fake_ws("bar") ] | unique_by(.name)
'

2.5. Sort workspaces in reverse alphanumeric order

#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
    i3-msg -t get_workspaces;
    while read; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c 'sort_by(.name) | reverse'

2.6. Append "foo" to the name of each workspace

#!/bin/sh
i3-msg -t subscribe -m '["workspace", "output"]' | {
    i3-msg -t get_workspaces;
    while read; do i3-msg -t get_workspaces; done;
} | jq --unbuffered -c '[ .[] | .name |= . + " foo" ]'