X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=docs%2Fhacking-howto;h=1377d87c8c4f50086df323e7775b7d715458738b;hb=6c112f2a9a5de4c3ef06d4cbce2bd7b22c0caa1d;hp=e7394f38ce4e61879a36448ebb94b43515eb2adb;hpb=2359c5049d0689d8dbd356e8b589395dae115aa3;p=i3%2Fi3 diff --git a/docs/hacking-howto b/docs/hacking-howto index e7394f38..1377d87c 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -160,7 +160,7 @@ src/debug.c:: 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). @@ -299,7 +299,7 @@ content (workspaces) of this output. 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''. @@ -406,8 +406,9 @@ 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 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, @@ -469,69 +470,218 @@ src/layout.c, function resize_client(). == Rendering (src/layout.c, render_layout() and render_container()) -********************************************************************************* -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* :). -********************************************************************************* - Rendering in i3 version 4 is the step which assigns the correct sizes for borders, decoration windows, child windows and the stacking order of all windows. In a separate step (+x_push_changes()+), these changes are pushed to X11. -///////////////////////////////////////////////////////////////////////////////// - - -There are several entry points to rendering: `render_layout()`, -`render_workspace()` and `render_container()`. The former one calls -`render_workspace()` for every screen, which in turn will call -`render_container()` for every container inside its layout table. 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_workspace()`, each -container gets its position (screen offset + offset in the table) and size -(container's width times colspan/rowspan). Then, `render_container()` is called, -which 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 do not 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. - -=== Tabbed mode - -Tabbed mode is like stack mode, except that the window decorations are drawn -in one single line at the top of the container. - -=== Window decorations +Keep in mind that all these properties (+rect+, +window_rect+ and +deco_rect+) +are temporary, meaning they will be overwritten by calling +render_con+. +Persistent position/size information is kept in +geometry+. + +The entry point for every rendering operation (except for the case of moving +floating windows around) currently is +tree_render()+ which will re-render +everything that’s necessary (for every output, only the currently displayed +workspace is rendered). This behavior is expected to change in the future, +since for a lot of updates, re-rendering everything is not actually necessary. +Focus was on getting it working correct, not getting it work very fast. + +What +tree_render()+ actually does is calling +render_con()+ on the root +container and then pushing the changes to X11. The following sections talk +about the different rendering steps, in the order of "top of the tree" (root +container) to the bottom. + +=== Rendering the root container + +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 the root will first render all tiling windows and then all floating +windows. This is necessary because a floating window can be positioned in such +a way that it is visible on two different outputs. Therefore, by first +rendering all the tiling windows (of all outputs), we make sure that floating +windows can never be obscured by tiling windows. + +Essentially, though, this code path will just call +render_con()+ for every +output and +x_raise_con(); render_con()+ for every floating window. + +In the special case of having a "global fullscreen" window (fullscreen mode +spanning all outputs), a shortcut is taken and +x_raise_con(); render_con()+ is +only called for the global fullscreen window. + +=== Rendering an output + +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`) +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 + return, thus ignoring the dock areas. +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). +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 +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 +opposed to a split container) and has border style "normal", appropriate space +will be reserved for its window decoration. + +==== Stacked layout + +In stacked layout, only the focused window is actually shown (this is achieved +by calling +x_raise_con()+ in reverse focus order at the end of +render_con()+). + +The available space for the focused window is the size of the container minus +the height of the window decoration for all windows inside this stacked +container. -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. +If border style is "1pixel" or "none", no window decoration height will be +reserved (or displayed later on), unless there is more than one window inside +the stacked container. + +==== Tabbed layout + +Tabbed layout works precisely like stacked layout, but the window decoration +position/size is different: They are placed next to each other on a single line +(fixed height). + +==== Dock area layout + +This is a special case. Users cannot chose the dock area layout, but it will be +set for the dock area containers. In the dockarea layout (at the moment!), +windows will be placed above each other. + +=== Rendering a window + +A window’s size and position will be determined in the following way: + +1. Subtract the border if border style is not "none" (but "normal" or "1pixel"). +2. Subtract the X11 border, if the window has an X11 border > 0. +3. Obey the aspect ratio of the window (think MPlayer). +4. Obey the height- and width-increments of the window (think terminal emulator + which can only be resized in one-line or one-character steps). + +== Pushing updates to X11 / Drawing + +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. -=== Fullscreen windows -For fullscreen windows, the `rect` (x, y, width, height) is not changed to -allow the client to easily go back to its previous position. Instead, -fullscreen windows are skipped when rendering. +///////////////////////////////////////////////////////////////////////////////// -=== 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