layout (like dwm, awesome, …) but provide mechanisms for you to easily create
the layout you need at the moment.
-=== The layout table
+=== The layout tree
-*********************************************************************************
-This section has not been updated for v4.0 yet, sorry! We wanted to release on
-time, but we will update this soon. Please talk to us on IRC if you need to
-know stuff *NOW* :).
-*********************************************************************************
-
-/////////////////////////////////////////////////////////////////////////////////
-To accomplish flexible layouts, we decided to simply use a table. The table
-grows and shrinks as you need it. Each cell holds a container which then holds
-windows (see picture below). You can use different layouts for each container
-(default layout and stacking layout).
+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 container, in default layout. The layout table has exactly one column,
-one row and therefore one cell. When you move one of the terminals to the
-right, the table needs to grow. It will be expanded to two columns and one row.
-This enables you to have different layouts for each container. The table then
-looks like this:
-
-[width="15%",cols="^,^"]
-|========
-| T1 | T2
-|========
-
-When moving terminal 2 to the bottom, the table will be expanded again.
-
-[width="15%",cols="^,^"]
-|========
-| T1 |
-| | T2
-|========
-
-You can really think of the layout table like a traditional HTML table, if
-you’ve ever designed one. Especially col- and rowspan work similarly. Below,
-you see an example of colspan=2 for the first container (which has T1 as
-window).
+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.
-[width="15%",cols="^asciidoc"]
-|========
-| T1
-|
-[cols="^,^",frame="none"]
-!========
-! T2 ! T3
-!========
-|========
-
-Furthermore, you can freely resize table cells.
-/////////////////////////////////////////////////////////////////////////////////
+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
+http://i3wm.org/docs/ipc.html#_get_tree_reply for documentation on that and an
+example.
== Files
Contains debugging functions to print unhandled X events.
src/ewmh.c::
-iFunctions to get/set certain EWMH properties easily.
+Functions to get/set certain EWMH properties easily.
src/floating.c::
Contains functions for floating mode (mostly resizing/dragging).
A workspace is identified by its name. Basically, you could think of
workspaces as different desks in your office, if you like the desktop
-methaphor. They just contain different sets of windows and are completely
+metaphor. They just contain different sets of windows and are completely
separate of each other. Other window managers also call this ``Virtual
desktops''.
whether this window is a dock (`_NET_WM_WINDOW_TYPE_DOCK`), like dzen2 for
example. Docks are handled differently, they don’t have decorations and are not
assigned to a specific container. Instead, they are positioned at the bottom
-of the screen. To get the height which needs to be reserved for the window,
-the `_NET_WM_STRUT_PARTIAL` property is used.
+or top of the screen (in the appropriate dock area containers). To get the
+height which needs to be reserved for the window, the `_NET_WM_STRUT_PARTIAL`
+property is used.
Furthermore, the list of assignments (to other workspaces, which may be on
other screens) is checked. If the window matches one of the user’s criteria,
=== Rendering the root container
-The i3 root container (+con->type == CT_ROOT+) represents the X11 root window.
+The i3 root container (`con->type == CT_ROOT`) represents the X11 root window.
It contains one child container for every output (like LVDS1, VGA1, …), which
is available on your computer.
=== Rendering an output
-Output containers (+con->layout == L_OUTPUT+) represent a hardware output like
+Output containers (`con->layout == L_OUTPUT`) represent a hardware output like
LVDS1, VGA1, etc. An output container has three children (at the moment): One
content container (having workspaces as children) and the top/bottom dock area
containers.
The rendering happens in the function +render_l_output()+ in the following
steps:
-1. Find the content container (+con->type == CT_CON+)
+1. Find the content container (`con->type == CT_CON`)
2. Get the currently visible workspace (+con_get_fullscreen_con(content,
CF_OUTPUT)+).
3. If there is a fullscreened window on that workspace, directly render it and
4. Sum up the space used by all the dock windows (they have a variable height
only).
5. Set the workspace rects (x/y/width/height) based on the position of the
- output (stored in +con->rect+) and the usable space
- (+con->rect.{width,height}+ without the space used for dock windows).
+ output (stored in `con->rect`) and the usable space
+ (`con->rect.{width,height}` without the space used for dock windows).
6. Recursively raise and render the output’s child containers (meaning dock
area containers and the content container).
=== Rendering a workspace or split container
From here on, there really is no difference anymore. All containers are of
-+con->type == CT_CON+ (whether workspace or split container) and some of them
-have a +con->window+, meaning they represent an actual window instead of a
+`con->type == CT_CON` (whether workspace or split container) and some of them
+have a `con->window`, meaning they represent an actual window instead of a
split container.
==== Default layout
In default layout, containers are placed horizontally or vertically next to
-each other (depending on the +con->orientation+). If a child is a leaf node (as
+each other (depending on the `con->orientation`). If a child is a leaf node (as
opposed to a split container) and has border style "normal", appropriate space
will be reserved for its window decoration.
== Pushing updates to X11 / Drawing
-TODO.
-
-/////////////////////////////////////////////////////////////////////////////////
+A big problem with i3 before version 4 was that we just sent requests to X11
+anywhere in the source code. This was bad because nobody could understand the
+entirety of our interaction with X11, it lead to subtle bugs and a lot of edge
+cases which we had to consider all over again.
+
+Therefore, since version 4, we have a single file, +src/x.c+, which is
+responsible for repeatedly transferring parts of our tree datastructure to X11.
+
++src/x.c+ consists of multiple parts:
+
+1. The state pushing: +x_push_changes()+, which calls +x_push_node()+.
+2. State modification functions: +x_con_init+, +x_reinit+,
+ +x_reparent_child+, +x_move_win+, +x_con_kill+, +x_raise_con+, +x_set_name+
+ and +x_set_warp_to+.
+3. Expose event handling (drawing decorations): +x_deco_recurse()+ and
+ +x_draw_decoration()+.
+
+=== Pushing state to X11
+
+In general, the function +x_push_changes+ should be called to push state
+changes. Only when the scope of the state change is clearly defined (for
+example only the title of a window) and its impact is known beforehand, one can
+optimize this and call +x_push_node+ on the appropriate con directly.
+
++x_push_changes+ works in the following steps:
+
+1. Clear the eventmask for all mapped windows. This leads to not getting
+ useless ConfigureNotify or EnterNotify events which are caused by our
+ requests. In general, we only want to handle user input.
+2. Stack windows above each other, in reverse stack order (starting with the
+ most obscured/bottom window). This is relevant for floating windows which
+ can overlap each other, but also for tiling windows in stacked or tabbed
+ containers. We also update the +_NET_CLIENT_LIST_STACKING+ hint which is
+ necessary for tab drag and drop in Chromium.
+3. +x_push_node+ will be called for the root container, recursively calling
+ itself for the container’s children. This function actually pushes the
+ state, see the next paragraph.
+4. If the pointer needs to be warped to a different position (for example when
+ changing focus to a differnt output), it will be warped now.
+5. The eventmask is restored for all mapped windows.
+6. Window decorations will be rendered by calling +x_deco_recurse+ on the root
+ container, which then recursively calls itself for the children.
+7. If the input focus needs to be changed (because the user focused a different
+ window), it will be updated now.
+8. +x_push_node_unmaps+ will be called for the root container. This function
+ only pushes UnmapWindow requests. Separating the state pushing is necessary
+ to handle fullscreen windows (and workspace switches) in a smooth fashion:
+ The newly visible windows should be visible before the old windows are
+ unmapped.
+
++x_push_node+ works in the following steps:
+
+1. Update the window’s +WM_NAME+, if changed (the +WM_NAME+ is set on i3
+ containers mainly for debugging purposes).
+2. Reparents a child window into the i3 container if the container was created
+ for a specific managed window.
+3. If the size/position of the i3 container changed (due to opening a new
+ window or switching layouts for example), the window will be reconfigured.
+ Also, the pixmap which is used to draw the window decoration/border on is
+ reconfigured (pixmaps are size-dependent).
+4. Size/position for the child window is adjusted.
+5. The i3 container is mapped if it should be visible and was not yet mapped.
+ When mapping, +WM_STATE+ is set to +WM_STATE_NORMAL+. Also, the eventmask of
+ the child window is updated and the i3 container’s contents are copied from
+ the pixmap.
+6. +x_push_node+ is called recursively for all children of the current
+ container.
+
++x_push_node_unmaps+ handles the remaining case of an i3 container being
+unmapped if it should not be visible anymore. +WM_STATE+ will be set to
++WM_STATE_WITHDRAWN+.
+
+
+=== Drawing window decorations/borders/backgrounds
+
++x_draw_decoration+ draws window decorations. It is run for every leaf
+container (representing an actual X11 window) and for every non-leaf container
+which is in a stacked/tabbed container (because stacked/tabbed containers
+display a window decoration for split containers, which at the moment just says
+"another container").
+
+Then, parameters are collected to be able to determine whether this decoration
+drawing is actually necessary or was already done. This saves a substantial
+number of redraws (depending on your workload, but far over 50%).
+
+Assuming that we need to draw this decoration, we start by filling the empty
+space around the child window (think of MPlayer with a specific aspect ratio)
+in the user-configured client background color.
+
+Afterwards, we draw the appropriate border (in case of border styles "normal"
+and "1pixel") and the top bar (in case of border style "normal").
+
+The last step is drawing the window title on the top bar.
-=== Common parts
-
-On the frame (the window which was created around the client’s window for the
-decorations), a black rectangle is drawn as a background for windows like
-MPlayer, which do not completely fit into the frame.
-
-=== Window decorations
-
-The window decorations consist of a rectangle in the appropriate color (depends
-on whether this window is the currently focused one, the last focused one in a
-not focused container or not focused at all) forming the background.
-Afterwards, two lighter lines are drawn and the last step is drawing the
-window’s title (see WM_NAME) onto it.
+/////////////////////////////////////////////////////////////////////////////////
-=== Resizing containers
+== Resizing containers
By clicking and dragging the border of a container, you can resize the whole
column (respectively row) which this container is in. This is necessary to keep
/////////////////////////////////////////////////////////////////////////////////
-== User commands / commandmode (src/cmdparse.{l,y})
-
-*********************************************************************************
-This section has not been updated for v4.0 yet, sorry! We wanted to release on
-time, but we will update this soon. Please talk to us on IRC if you need to
-know stuff *NOW* :).
-*********************************************************************************
-
-/////////////////////////////////////////////////////////////////////////////////
-
-
-Like in vim, you can control i3 using commands. They are intended to be a
-powerful alternative to lots of shortcuts, because they can be combined. There
-are a few special commands, which are the following:
-
-exec <command>::
-Starts the given command by passing it to `/bin/sh`.
-
-restart::
-Restarts i3 by executing `argv[0]` (the path with which you started i3) without
-forking.
-
-w::
-"With". This is used to select a bunch of windows. Currently, only selecting
-the whole container in which the window is in, is supported by specifying "w".
-
-f, s, d::
-Toggle fullscreen, stacking, default mode for the current window/container.
-
-The other commands are to be combined with a direction. The directions are h,
-j, k and l, like in vim (h = left, j = down, k = up, l = right). When you just
-specify the direction keys, i3 will move the focus in that direction. You can
-provide "m" or "s" before the direction to move a window respectively or snap.
-
-/////////////////////////////////////////////////////////////////////////////////
+== User commands (parser-specs/commands.spec)
+
+In the configuration file and when using i3 interactively (with +i3-msg+, for
+example), you use commands to make i3 do things, like focus a different window,
+set a window to fullscreen, and so on. An example command is +floating enable+,
+which enables floating mode for the currently focused window. See the
+appropriate section in the link:userguide.html[User’s Guide] for a reference of
+all commands.
+
+In earlier versions of i3, interpreting these commands was done using lex and
+yacc, but experience has shown that lex and yacc are not well suited for our
+command language. Therefore, starting from version 4.2, we use a custom parser
+for user commands (not yet for the configuration file).
+The input specification for this parser can be found in the file
++parser-specs/commands.spec+. Should you happen to use Vim as an editor, use
+:source parser-specs/highlighting.vim to get syntax highlighting for this file
+(highlighting files for other editors are welcome).
+
+.Excerpt from commands.spec
+-----------------------------------------------------------------------
+state INITIAL:
+ '[' -> call cmd_criteria_init(); CRITERIA
+ 'move' -> MOVE
+ 'exec' -> EXEC
+ 'workspace' -> WORKSPACE
+ 'exit' -> call cmd_exit()
+ 'restart' -> call cmd_restart()
+ 'reload' -> call cmd_reload()
+-----------------------------------------------------------------------
+
+The input specification is written in an extremely simple format. The
+specification is then converted into C code by the Perl script
+generate-commands-parser.pl (the output file names begin with GENERATED and the
+files are stored in the +include+ directory). The parser implementation
++src/commands_parser.c+ includes the generated C code at compile-time.
+
+The above excerpt from commands.spec illustrates nearly all features of our
+specification format: You describe different states and what can happen within
+each state. State names are all-caps; the state in the above excerpt is called
+INITIAL. A list of tokens and their actions (separated by an ASCII arrow)
+follows. In the excerpt, all tokens are literals, that is, simple text strings
+which will be compared with the input. An action is either the name of a state
+in which the parser will transition into, or the keyword 'call', followed by
+the name of a function (and optionally a state).
+
+=== Example: The WORKSPACE state
+
+Let’s have a look at the WORKSPACE state, which is a good example of all
+features. This is its definition:
+
+.WORKSPACE state (commands.spec)
+----------------------------------------------------------------
+# workspace next|prev|next_on_output|prev_on_output
+# workspace back_and_forth
+# workspace <name>
+state WORKSPACE:
+ direction = 'next_on_output', 'prev_on_output', 'next', 'prev'
+ -> call cmd_workspace($direction)
+ 'back_and_forth'
+ -> call cmd_workspace_back_and_forth()
+ workspace = string
+ -> call cmd_workspace_name($workspace)
+----------------------------------------------------------------
+
+As you can see from the commands, there are multiple different valid variants
+of the workspace command:
+
+workspace <direction>::
+ The word 'workspace' can be followed by any of the tokens 'next',
+ 'prev', 'next_on_output' or 'prev_on_output'. This command will
+ switch to the next or previous workspace (optionally on the same
+ output). +
+ There is one function called +cmd_workspace+, which is defined
+ in +src/commands.c+. It will handle this kind of command. To know which
+ direction was specified, the direction token is stored on the stack
+ with the name "direction", which is what the "direction = " means in
+ the beginning. +
+
+NOTE: Note that you can specify multiple literals in the same line. This has
+ exactly the same effect as if you specified `direction =
+ 'next_on_output' -> call cmd_workspace($direction)` and so forth. +
+
+NOTE: Also note that the order of literals is important here: If 'next' were
+ ordered before 'next_on_output', then 'next_on_output' would never
+ match.
+
+workspace back_and_forth::
+ This is a very simple case: When the literal 'back_and_forth' is found
+ in the input, the function +cmd_workspace_back_and_forth+ will be
+ called without parameters and the parser will return to the INITIAL
+ state (since no other state was specified).
+workspace <name>::
+ In this case, the workspace command is followed by an arbitrary string,
+ possibly in quotes, for example "workspace 3" or "workspace bleh". +
+ This is the first time that the token is actually not a literal (not in
+ single quotes), but just called string. Other possible tokens are word
+ (the same as string, but stops matching at a whitespace) and end
+ (matches the end of the input).
+
+=== Introducing a new command
+
+The following steps have to be taken in order to properly introduce a new
+command (or possibly extend an existing command):
+
+1. Define a function beginning with +cmd_+ in the file +src/commands.c+. Copy
+ the prototype of an existing function.
+2. After adding a comment on what the function does, copy the comment and
+ function definition to +include/commands.h+. Make the comment in the header
+ file use double asterisks to make doxygen pick it up.
+3. Write a test case (or extend an existing test case) for your feature, see
+ link:testsuite.html[i3 testsuite]. For now, it is sufficient to simply call
+ your command in all the various possible ways.
+4. Extend the parser specification in +parser-specs/commands.spec+. Run the
+ testsuite and see if your new function gets called with the appropriate
+ arguments for the appropriate input.
+5. Actually implement the feature.
+6. Document the feature in the link:userguide.html[User’s Guide].
== Moving containers
leads to code which looks like it works fine but which does not work under
certain conditions.
+* Forgetting to call `floating_fix_coordinates(con, old_rect, new_rect)` after
+ moving workspaces across outputs. Coordinates for floating containers are
+ not relative to workspace boundaries, so you must correct their coordinates
+ or those containers will show up in the wrong workspace or not at all.
+
== Using git / sending patches
For a short introduction into using git, see