]> git.sur5r.net Git - i3/i3/commitdiff
Add how to hack guide, first version
authorMichael Stapelberg <michael+x200@stapelberg.de>
Fri, 6 Mar 2009 23:50:18 +0000 (00:50 +0100)
committerMichael Stapelberg <michael+x200@stapelberg.de>
Fri, 6 Mar 2009 23:50:18 +0000 (00:50 +0100)
docs/hacking-howto [new file with mode: 0644]

diff --git a/docs/hacking-howto b/docs/hacking-howto
new file mode 100644 (file)
index 0000000..d2ba44f
--- /dev/null
@@ -0,0 +1,275 @@
+Hacking i3: How To
+==================
+Michael Stapelberg <michael+i3@stapelberg.de>
+March 2009
+
+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.
+
+== Window Managers
+
+A window manager is not necessarily needed to run X, but it is usually used in combination
+to facilitate some things. The window manager's job is to take care of the placement of
+windows, to provide the user 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:
+
+. Grab the key bindings (events will be sent upon keypress/keyrelease)
+. 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)
+. When new windows are created, manage them
+. Handle the client’s _WM_STATE property, but only the _WM_STATE_FULLSCREEN
+. Handle the client’s WM_NAME property
+. Handle the client’s size hints to display them proportionally
+. Handle enter notifications (focus follows mouse)
+. Handle button (as in mouse buttons) presses for focus/raise on click
+. Handle expose events to re-draw own windows such as decorations
+. React to the user’s commands: Change focus, Move windows, Switch workspaces,
+Change the layout mode of a container (default/stacking), Start a new application,
+Restart the window manager
+
+In the following chapters, each of these tasks and their implementation details will be discussed.
+
+== Files
+
+include/data.h::
+Contains data definitions used by nearly all files. You really need to read this first.
+
+include/*.h::
+Contains forward definitions for all public functions.
+
+src/commands.c::
+Parsing commands
+
+src/config.c::
+Parses the configuration file
+
+src/debug.c::
+Contains debugging functions to print unhandled X events
+
+src/handlers.c::
+Contains all handlers for all kind of X events
+
+src/layout.c::
+Renders your layout (screens, workspaces, containers)
+
+src/mainx.c::
+Initializes the window manager
+
+src/table.c::
+Manages the most important internal data structure, the design table.
+
+src/util.c::
+Contains useful functions which are not really dependant on anything.
+
+src/xcb.c::
+Contains wrappers to use xcb more easily.
+
+src/xinerama.c::
+(Re-)initializes the available screens and converts them to virtual screens (see below).
+
+== Data structures
+
+See include/data.h for documented data structures.
+
+=== Virtual screens
+
+A virtual screen (type i3Screen) is generated from the connected screens obtained
+through Xinerama. The difference to the raw Xinerama monitors as seen when using xrandr(1)
+is that it falls back to the lowest common resolution of the logical screens.
+
+For example, if your notebook has 1280x800 and you connect a video projector with
+1024x768, set up in clone mode (xrandr --output VGA --mode 1024x768 --same-as LVDS),
+i3 will have one virtual screen.
+
+However, if you configure it using xrandr --output VGA --mode 1024x768 --right-of LVDS,
+i3 will generate two virtual screens. For each virtual screen, a new workspace will be
+assigned. New workspaces are created on the screen you are currently on.
+
+== List/queue macros
+
+i3 makes heavy use of the list macros defined in BSD operating systems. To ensure
+that the operating system on which i3 is compiled has all the awaited features,
+i3 comes with include/queue.h. On BSD systems, you can use man queue(3). On Linux,
+you have to use google.
+
+The lists used are SLISTs (single linked lists) and CIRCLEQ (circular queues).
+Usually, only forward traversal is necessary, so an SLIST works fine. However,
+for the windows inside a container, a CIRCLEQ is necessary to go from the currently
+selected window to the window above/below.
+
+== Naming conventions
+
+There is a row of standard variables used in many events. The following names should be
+chosen for those:
+
+ * "conn" is the xcb_connection_t
+ * "event" is the event of the particular type
+ * "container" names a container
+ * "client" names a client, for example when using a CIRCLEQ_FOREACH
+
+== Startup (src/mainx.c)
+
+ * Establish the xcb connection
+ * Check for XKB extension on the separate X connection
+ * Check for Xinerama screens
+ * Grab the keycodes for which bindings exist
+ * Manage all existing windows
+ * Enter the event loop
+
+== Keybindings
+
+=== Grabbing the bindings
+
+Grabbing the bindings is quite straight-forward. You pass X your combination of modifiers and
+the keycode you want to grab and whether you want to grab them actively or passively. Most
+bindings (everything except for bindings using Mode_switch) are grabbed passively, that is,
+just the window manager gets the event and cannot replay it.
+
+We need to grab bindings that use Mode_switch actively because of a bug in X. When the window
+manager receives the keypress/keyrelease event for an actively grabbed keycode, it has to decide
+what to do with this event: It can either replay it so that other applications get it or it
+can prevent other applications from receiving it.
+
+So, why do we need to grab keycodes actively? Because X does not set the state-property of
+keypress/keyrelease events properly. The Mode_switch bit is not set and we need to get it
+using XkbGetState. This means we cannot pass X our combination of modifiers containing Mode_switch
+when grabbing the key and therefore need to grab the keycode itself without any modiffiers.
+This means, if you bind Mode_switch + keycode 38 ("a"), i3 will grab keycode 38 ("a") and
+check on each press of "a" if the Mode_switch bit is set using XKB. If yes, it will handle
+the event, if not, it will replay the event.
+
+=== Handling a keypress
+
+As mentioned in "Grabbing the bindings", upon a keypress event, i3 first gets the correct state.
+
+Then, it looks through all bindings and gets the one which matches the received event.
+
+The bound command is parsed directly in command mode.
+
+== Manage windows (src/mainx.c, manage_window() and reparent_window())
+
+manage_window() does some checks to decide whether the window should be managed at all:
+
+ * Windows have to be mapped, that is, visible on screen
+ * The override_redirect must not be set. Windows with override_redirect shall not be
+   managed by a window manager
+
+Afterwards, i3 gets the intial geometry and reparents the window if it wasn’t already
+managed.
+
+Reparenting means that for each window which is reparented, a new window, slightly larger
+than the original one, is created. The original window is then reparented to the bigger one
+(called "frame").
+
+After reparenting, the window type (_NET_WM_WINDOW_TYPE) is checked to see 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 needsd
+to be reserved for the window, the _NET_WM_STRUT_PARTIAL property is used.
+
+== What happens when an application is started?
+
+i3 does not care for applications. All it notices is when new windows are mapped (see
+src/handlers.c, handle_map_notify_event()). The window is then reparented (see section
+"Manage windows").
+
+After reparenting the window, render_layout() is called which renders the internal
+layout table. The window was placed in the currently focused container and
+therefore the new window and the old windows (if any) need te be moved/resized
+so that the currently active layout (default mode/stacking mode) is rendered
+correctly. To move/resize windows, a window is "configured" in X11-speak.
+
+Some applications, such as MPlayer obivously assume the window manager is stupid
+and therefore configure their windows by themselves. This generates an event called
+configurenotify. i3 handles these events and pushes the window back to its position/size.
+
+== _NET_WM_STATE
+
+Only the _NET_WM_STATE_FULLSCREEN atom is handled. It calls toggle_fullscreen() for the
+specific client which just configures the client to use the whole screen on which it
+currently is. Also, it is set as fullscreen_client for the i3Screen.
+
+== WM_NAME
+
+When the WM_NAME property of a window changes, its decoration (containing the title)
+is re-rendered.
+
+== Size hints
+
+== Rendering
+
+There are two entry points to rendering: render_layout() and render_container(). The
+former one renders all virtual screens, the currently active workspace of each virtual
+screen and all containers (inside the table cells) of these workspaces using
+render_container(). Therefore, if you need to render only a single container, for
+example because a window was removed, added or changed its title, you should directly
+call render_container().
+
+Rendering consists of two steps: In the first one, in render_layout(), each container
+gets its position (screen offset + offset in the table) and size (container's width
+times colspan/rowspan). Then, render_container() is called:
+
+render_container() then takes different approaches, depending on the mode the container
+is in.
+
+=== 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 don’t completely
+fit into the frame.
+
+=== Default mode
+
+Each clients gets the container’s width and an equal amount of height.
+
+=== Stack mode
+
+In stack mode, a window containing the decorations of all windows inside the container
+is placed at the top. The currently focused window is then given the whole remaining
+space.
+
+=== Window decorations
+
+The window decorations consist of a rectangle in the appropriate color (depends on whether
+this window is the currently focused one or 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
+
+By clicking and dragging the border of a container, you can resize it freely.
+
+TODO
+
+== User commands / commandmode (src/commands.c)
+
+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::
+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.