This document is intended to be the first thing you read before looking and/or touching i3’s source code. It should contain all important information to help you understand why things are like they are. If it does not mention something you find necessary, please do not hesitate to contact me.

WARNING!

This document is not 100% up to date. Specifically, everything up to and including [startup] has been updated recently. The rest might contain outdated information.

1. Building i3

You can build i3 like you build any other software package which uses The Meson Build system; see Quickstart Guide → Compiling a Meson project. In case you’re unfamiliar:

mkdir -p build
meson setup build
meson compile -C build

1.1. Build system features

  • ninja test runs the i3 testsuite. See docs/testsuite for details.

  • meson dist builds a release tarball and runs tests on the result.

  • meson -Ddocs=true -Dmans=true will enable the options to build docs and manpages. These options require additional dependencies that are normally not required for users who just want to build i3.

  • meson -Db_sanitize=address will enable the address sanitizer which is disabled by default. A summary of memory leaks will be printed on program exit. This can include false-positives. For other options of the b_sanitize flag see https://mesonbuild.com/Builtin-options.html.

2. Pull requests

Please talk to us before working on new features to see whether they will be accepted. A good way for this is to open an issue and asking for opinions on it. Even for accepted features, this can be a good way to refine an idea upfront. However, we don’t want to see certain features in i3, e.g., switching window focus in an Alt+Tab like way.

When working on bugfixes, please make sure you mention that you are working on it in the corresponding bug report at https://github.com/i3/i3/issues. In case there is no bug report yet, please create one.

After you are done, please submit your work for review as a pull request at https://github.com/i3/i3. In order to make your review go as fast as possible, you could have a look at previous reviews and see what the common mistakes are.

2.1. Which branch to use?

Work on i3 generally happens in two branches: “next” (default) and “stable”.

The contents of “stable” are always stable. That is, it contains the source code of the latest release, plus any bugfixes that were applied since that release.

New features are only found in the “next” branch. Always use this branch when writing new code (both bugfixes and features).

3. Window Managers

A window manager is not necessarily needed to run X, but it is usually used in combination with X to facilitate some things. The window manager’s job is to take care of the placement of windows, to provide the user with some mechanisms to change the position/size of windows and to communicate with clients to a certain extent (for example handle fullscreen requests of clients such as MPlayer).

There are no different contexts in which X11 clients run, so a window manager is just another client, like all other X11 applications. However, it handles some events which normal clients usually don’t handle.

In the case of i3, the tasks (and order of them) are the following:

  1. Grab the key bindings (events will be sent upon keypress/keyrelease)

  2. Iterate through all existing windows (if the window manager is not started as the first client of X) and manage them (reparent them, create window decorations, etc.)

  3. When new windows are created, manage them

  4. Handle the client’s _WM_STATE property, but only _WM_STATE_FULLSCREEN and _NET_WM_STATE_DEMANDS_ATTENTION

  5. Handle the client’s WM_NAME property

  6. Handle the client’s size hints to display them proportionally

  7. Handle the client’s urgency hint

  8. Handle enter notifications (focus follows mouse)

  9. Handle button (as in mouse buttons) presses for focus/raise on click

  10. Handle expose events to re-draw own windows such as decorations

  11. React to the user’s commands: Change focus, Move windows, Switch workspaces, Change the layout mode of a container (default/stacking/tabbed), start a new application, restart the window manager

In the following chapters, each of these tasks and their implementation details will be discussed.

3.1. Tiling window managers

Traditionally, there are two approaches to managing windows: The most common one nowadays is stacking (or floating, using i3’s terminology), which means the user can freely move/resize the windows, potentially overlapping them. The other approach is called tiling, which means that the window manager distributes windows to use as much space as possible while not overlapping each other.

The idea behind tiling is that you should not need to waste your time moving/resizing windows while you usually want to get some work done. After all, most users sooner or later tend to lay out their windows in a way which corresponds to tiling or stacking mode in i3. Therefore, why not let i3 do this for you? Certainly, it’s faster than you could ever do it.

The problem with most tiling window managers is that they are too inflexible. In my opinion, a window manager is just another tool, and similar to vim which can edit all kinds of text files (like source code, HTML, …) and is not limited to a specific file type, a window manager should not limit itself to a certain layout (like dwm, awesome, …) but provide mechanisms for you to easily create the layout you need at the moment.

3.2. The layout tree

The data structure which i3 uses to keep track of your windows is a tree. Every node in the tree is a container (type Con). Some containers represent actual windows (every container with a window != NULL), some represent split containers and a few have special purposes: they represent workspaces, outputs (like VGA1, LVDS1, …) or the X11 root window.

So, when you open a terminal and immediately open another one, they reside in the same split container, which uses the default layout. In case of an empty workspace, the split container we are talking about is the workspace.

To get an impression of how different layouts are represented, just play around and look at the data structures — they are exposed as a JSON hash. See https://i3wm.org/docs/ipc.html#_tree_reply for documentation on that and an example.

4. Files

i3’s source code is in the src folder while header files reside in include. Other tools such as i3bar and i3-nagbar have their own folders. i3 and its tools share an internal library called “libi3” which also has its own folder.

The following list gives an overview of the codebase, explaining the functionality of the most important, core source code files. Other files in the tree that are not mentioned here implement specific functionalities: for example, src/scratchpad.c is obviously about the scratchpad functionality.

include/data.h

Contains data definitions used by nearly all files.

include/*.h

Contains forward definitions for all public functions, as well as doxygen-compatible comments (so if you want to get a bit more of the big picture, either browse all header files or use doxygen if you prefer that).

src/config_directives.c
src/commands.c

Contain the definitions for all high-level config and command directives. These are excellent places to start with a top-to-bottom approach to understand specific i3 behavior. For example, if you want to investigate a bug that happens for the move to mark command, you can use gdb to pause in cmd_move_con_to_mark and then work your way from there, stepping into lower-level functions.

src/con.c

Contains all functions which deal with containers directly (creating containers, searching containers, getting specific properties from containers, …). Contains abstractions and auxiliary functions necessary to work with the container structure which is used in almost all parts of the codebase.

src/tree.c

Contains functions which deal with the tree abstraction. However, be aware that src/con.c also contains functions that heavily interact with the tree structure. Some functions that are included in str/tree.c are those that handle opening and closing containers in the tree, finding the container that should be focused next and flattening the tree. See also src/move.c for other move-specific functions that interact with the tree, which were moved into their own file because they are so long.

src/workspace.c

Contains functions which deal with workspaces. Includes code that creates new workspaces, shows existing ones and deals with workspace assignments.

src/handlers.c

Contains all handlers for all kinds of X events (new window title, new hints, unmapping, key presses, button presses, …). This is a very important file to understand how i3 interacts with changes to its environment.

src/command_parser.c
src/config_parser.c

Contain a hand-written parser to parse commands and configuration (commands are what you bind on keys and what you can send to i3 using the IPC interface, like move left or workspace 4). src/config.c is responsible for calling the configuration parser.

src/click.c
src/resize.c

Contain functions which handle mouse button clicks (right mouse button clicks initiate resizing and thus are relatively complex).

src/manage.c

Looks at existing or new windows and decides whether to manage them. If so, it reparents the window and inserts it into our data structures.

src/match.c

A "match" is a data structure which acts like a mask or expression to match certain windows or not. For example, when using commands, you can specify a command like this: [title="Firefox"] kill. The title member of the match data structure will then be filled and i3 will check each window using match_matches_window() to find the windows affected by this command.

src/randr.c

The RandR API is used to get (and re-query) the configured outputs (monitors, …). Legacy Xinerama support resides in src/xinerama.c.

src/render.c

Renders the tree data structure by assigning coordinates to every node. These values will later be pushed to X11 in src/x.c.

src/sighandler.c

Handles SIGSEGV, SIGABRT and SIGFPE by showing a dialog that i3 crashed. You can choose to let it dump core and restart i3 in-place (either trying to preserve layout or forget about it).

src/window.c

Handlers to update X11 window properties like WM_CLASS, _NET_WM_NAME, CLIENT_LEADER, etc.

include/.xmacro.

A file cont