From: Michael Stapelberg Date: Wed, 23 Nov 2011 21:54:03 +0000 (+0000) Subject: hacking-howto: document X11 pushing/drawing X-Git-Tag: 4.2~224^2 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=f91f6c52e97fb0c8dfa6a7f85a35462e6d97ce6d;p=i3%2Fi3 hacking-howto: document X11 pushing/drawing --- diff --git a/docs/hacking-howto b/docs/hacking-howto index 8b8642b4..e4d8934c 100644 --- a/docs/hacking-howto +++ b/docs/hacking-howto @@ -583,26 +583,104 @@ A window’s size and position will be determined in the following way: == 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