• Main Page
  • Data Structures
  • Files
  • File List
  • Globals

src/manage.c

Go to the documentation of this file.
00001 /*
00002  * vim:ts=8:expandtab
00003  *
00004  * i3 - an improved dynamic tiling window manager
00005  *
00006  * © 2009-2010 Michael Stapelberg and contributors
00007  *
00008  * See file LICENSE for license information.
00009  *
00010  * src/manage.c: Contains all functions for initially managing new windows
00011  *               (or existing ones on restart).
00012  *
00013  */
00014 #include <stdlib.h>
00015 #include <string.h>
00016 #include <assert.h>
00017 
00018 #include <xcb/xcb.h>
00019 #include <xcb/xcb_icccm.h>
00020 
00021 #include "xcb.h"
00022 #include "data.h"
00023 #include "util.h"
00024 #include "i3.h"
00025 #include "table.h"
00026 #include "config.h"
00027 #include "handlers.h"
00028 #include "layout.h"
00029 #include "manage.h"
00030 #include "floating.h"
00031 #include "client.h"
00032 #include "workspace.h"
00033 #include "log.h"
00034 #include "ewmh.h"
00035 
00036 /*
00037  * Go through all existing windows (if the window manager is restarted) and manage them
00038  *
00039  */
00040 void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *prophs, xcb_window_t root) {
00041         xcb_query_tree_reply_t *reply;
00042         int i, len;
00043         xcb_window_t *children;
00044         xcb_get_window_attributes_cookie_t *cookies;
00045 
00046         /* Get the tree of windows whose parent is the root window (= all) */
00047         if ((reply = xcb_query_tree_reply(conn, xcb_query_tree(conn, root), 0)) == NULL)
00048                 return;
00049 
00050         len = xcb_query_tree_children_length(reply);
00051         cookies = smalloc(len * sizeof(*cookies));
00052 
00053         /* Request the window attributes for every window */
00054         children = xcb_query_tree_children(reply);
00055         for (i = 0; i < len; ++i)
00056                 cookies[i] = xcb_get_window_attributes(conn, children[i]);
00057 
00058         /* Call manage_window with the attributes for every window */
00059         for (i = 0; i < len; ++i)
00060                 manage_window(prophs, conn, children[i], cookies[i], true);
00061 
00062         free(reply);
00063         free(cookies);
00064 }
00065 
00066 /*
00067  * Restores the geometry of each window by reparenting it to the root window
00068  * at the position of its frame.
00069  *
00070  * This is to be called *only* before exiting/restarting i3 because of evil
00071  * side-effects which are to be expected when continuing to run i3.
00072  *
00073  */
00074 void restore_geometry(xcb_connection_t *conn) {
00075         Workspace *ws;
00076         Client *client;
00077         DLOG("Restoring geometry\n");
00078 
00079         TAILQ_FOREACH(ws, workspaces, workspaces)
00080                 SLIST_FOREACH(client, &(ws->focus_stack), focus_clients)
00081                         xcb_reparent_window(conn, client->child, root,
00082                                             client->rect.x, client->rect.y);
00083 
00084         /* Make sure our changes reach the X server, we restart/exit now */
00085         xcb_flush(conn);
00086 }
00087 
00088 /*
00089  * Do some sanity checks and then reparent the window.
00090  *
00091  */
00092 void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
00093                    xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
00094                    bool needs_to_be_mapped) {
00095         xcb_drawable_t d = { window };
00096         xcb_get_geometry_cookie_t geomc;
00097         xcb_get_geometry_reply_t *geom;
00098         xcb_get_window_attributes_reply_t *attr = 0;
00099 
00100         geomc = xcb_get_geometry(conn, d);
00101 
00102         /* Check if the window is mapped (it could be not mapped when intializing and
00103            calling manage_window() for every window) */
00104         if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
00105                 ELOG("Could not get attributes\n");
00106                 return;
00107         }
00108 
00109         if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE)
00110                 goto out;
00111 
00112         /* Don’t manage clients with the override_redirect flag */
00113         if (attr->override_redirect)
00114                 goto out;
00115 
00116         /* Check if the window is already managed */
00117         if (table_get(&by_child, window))
00118                 goto out;
00119 
00120         /* Get the initial geometry (position, size, …) */
00121         if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) == NULL)
00122                 goto out;
00123 
00124         /* Reparent the window and add it to our list of managed windows */
00125         reparent_window(conn, window, attr->visual, geom->root, geom->depth,
00126                         geom->x, geom->y, geom->width, geom->height,
00127                         geom->border_width);
00128 
00129         /* Generate callback events for every property we watch */
00130         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
00131         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
00132         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
00133         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_HINTS);
00134         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR);
00135         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]);
00136         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
00137 
00138         free(geom);
00139 out:
00140         free(attr);
00141         return;
00142 }
00143 
00144 /*
00145  * reparent_window() gets called when a new window was opened and becomes a child of the root
00146  * window, or it gets called by us when we manage the already existing windows at startup.
00147  *
00148  * Essentially, this is the point where we take over control.
00149  *
00150  */
00151 void reparent_window(xcb_connection_t *conn, xcb_window_t child,
00152                      xcb_visualid_t visual, xcb_window_t root, uint8_t depth,
00153                      int16_t x, int16_t y, uint16_t width, uint16_t height,
00154                      uint32_t border_width) {
00155 
00156         xcb_get_property_cookie_t wm_type_cookie, strut_cookie, state_cookie,
00157                                   utf8_title_cookie, title_cookie,
00158                                   class_cookie, leader_cookie;
00159         uint32_t mask = 0;
00160         uint32_t values[3];
00161         uint16_t original_height = height;
00162         bool map_frame = true;
00163 
00164         /* We are interested in property changes */
00165         mask = XCB_CW_EVENT_MASK;
00166         values[0] = CHILD_EVENT_MASK;
00167         xcb_change_window_attributes(conn, child, mask, values);
00168 
00169         /* Place requests for properties ASAP */
00170         wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
00171         strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
00172         state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
00173         utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128);
00174         leader_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[WM_CLIENT_LEADER], UINT32_MAX);
00175         title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128);
00176         class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
00177 
00178         Client *new = table_get(&by_child, child);
00179 
00180         /* Events for already managed windows should already be filtered in manage_window() */
00181         assert(new == NULL);
00182 
00183         LOG("Managing window 0x%08x\n", child);
00184         DLOG("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
00185         new = scalloc(sizeof(Client));
00186         new->force_reconfigure = true;
00187 
00188         /* Update the data structures */
00189         Client *old_focused = CUR_CELL->currently_focused;
00190 
00191         new->container = CUR_CELL;
00192         new->workspace = new->container->workspace;
00193 
00194         /* Minimum useful size for managed windows is 75x50 (primarily affects floating) */
00195         width = max(width, 75);
00196         height = max(height, 50);
00197 
00198         new->frame = xcb_generate_id(conn);
00199         new->child = child;
00200         new->rect.width = width;
00201         new->rect.height = height;
00202         new->width_increment = 1;
00203         new->height_increment = 1;
00204         new->border_width = border_width;
00205         /* Pre-initialize the values for floating */
00206         new->floating_rect.x = -1;
00207         new->floating_rect.width = width;
00208         new->floating_rect.height = height;
00209 
00210         if (config.default_border != NULL)
00211                 client_init_border(conn, new, config.default_border[1]);
00212 
00213         mask = 0;
00214 
00215         /* Don’t generate events for our new window, it should *not* be managed */
00216         mask |= XCB_CW_OVERRIDE_REDIRECT;
00217         values[0] = 1;
00218 
00219         /* We want to know when… */
00220         mask |= XCB_CW_EVENT_MASK;
00221         values[1] = FRAME_EVENT_MASK;
00222 
00223         i3Font *font = load_font(conn, config.font);
00224         width = min(width, c_ws->rect.x + c_ws->rect.width);
00225         height = min(height, c_ws->rect.y + c_ws->rect.height);
00226 
00227         Rect framerect = {x, y,
00228                           width + 2 + 2,                  /* 2 px border at each side */
00229                           height + 2 + 2 + font->height}; /* 2 px border plus font’s height */
00230 
00231         /* Yo dawg, I heard you like windows, so I create a window around your window… */
00232         new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, false, mask, values);
00233 
00234         /* Put the client inside the save set. Upon termination (whether killed or normal exit
00235            does not matter) of the window manager, these clients will be correctly reparented
00236            to their most closest living ancestor (= cleanup) */
00237         xcb_change_save_set(conn, XCB_SET_MODE_INSERT, child);
00238 
00239         /* Generate a graphics context for the titlebar */
00240         new->titlegc = xcb_generate_id(conn);
00241         xcb_create_gc(conn, new->titlegc, new->frame, 0, 0);
00242 
00243         /* Moves the original window into the new frame we've created for it */
00244         new->awaiting_useless_unmap = true;
00245         xcb_void_cookie_t cookie = xcb_reparent_window_checked(conn, child, new->frame, 0, font->height);
00246         if (xcb_request_check(conn, cookie) != NULL) {
00247                 DLOG("Could not reparent the window, aborting\n");
00248                 xcb_destroy_window(conn, new->frame);
00249                 free(new);
00250                 return;
00251         }
00252 
00253         /* Put our data structure (Client) into the table */
00254         table_put(&by_parent, new->frame, new);
00255         table_put(&by_child, child, new);
00256 
00257         /* We need to grab the mouse buttons for click to focus */
00258         xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
00259                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
00260                         1 /* left mouse button */,
00261                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
00262 
00263         xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
00264                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
00265                         3 /* right mouse button */,
00266                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
00267 
00268         /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
00269         xcb_atom_t *atom;
00270         xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
00271         if (preply != NULL && preply->value_len > 0 && (atom = xcb_get_property_value(preply))) {
00272                 for (int i = 0; i < xcb_get_property_value_length(preply); i++)
00273                         if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
00274                                 DLOG("Window is a dock.\n");
00275                                 Output *t_out = get_output_containing(x, y);
00276                                 if (t_out != c_ws->output) {
00277                                         DLOG("Dock client requested to be on output %s by geometry (%d, %d)\n",
00278                                                         t_out->name, x, y);
00279                                         new->workspace = t_out->current_workspace;
00280                                 }
00281                                 new->dock = true;
00282                                 new->borderless = true;
00283                                 new->titlebar_position = TITLEBAR_OFF;
00284                                 new->force_reconfigure = true;
00285                                 new->container = NULL;
00286                                 SLIST_INSERT_HEAD(&(t_out->dock_clients), new, dock_clients);
00287                                 /* If it’s a dock we can’t make it float, so we break */
00288                                 new->floating = FLOATING_AUTO_OFF;
00289                                 break;
00290                         } else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] ||
00291                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
00292                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_TOOLBAR] ||
00293                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_SPLASH]) {
00294                                 /* Set the dialog window to automatically floating, will be used below */
00295                                 new->floating = FLOATING_AUTO_ON;
00296                                 DLOG("dialog/utility/toolbar/splash window, automatically floating\n");
00297                         }
00298         }
00299 
00300         /* All clients which have a leader should be floating */
00301         if (!new->dock && !client_is_floating(new) && new->leader != 0) {
00302                 DLOG("Client has WM_CLIENT_LEADER hint set, setting floating\n");
00303                 new->floating = FLOATING_AUTO_ON;
00304         }
00305 
00306         if (new->workspace->auto_float) {
00307                 new->floating = FLOATING_AUTO_ON;
00308                 DLOG("workspace is in autofloat mode, setting floating\n");
00309         }
00310 
00311         if (new->dock) {
00312                 /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
00313                 uint32_t *strut;
00314                 preply = xcb_get_property_reply(conn, strut_cookie, NULL);
00315                 if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
00316                         /* We only use a subset of the provided values, namely the reserved space at the top/bottom
00317                            of the screen. This is because the only possibility for bars is at to be at the top/bottom
00318                            with maximum horizontal size.
00319                            TODO: bars at the top */
00320                         new->desired_height = strut[3];
00321                         if (new->desired_height == 0) {
00322                                 DLOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", original_height);
00323                                 new->desired_height = original_height;
00324                         }
00325                         DLOG("the client wants to be %d pixels high\n", new->desired_height);
00326                 } else {
00327                         DLOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", original_height);
00328                         new->desired_height = original_height;
00329                 }
00330         } else {
00331                 /* If it’s not a dock, we can check on which workspace we should put it. */
00332 
00333                 /* Firstly, we need to get the window’s class / title. We asked for the properties at the
00334                  * top of this function, get them now and pass them to our callback function for window class / title
00335                  * changes. It is important that the client was already inserted into the by_child table,
00336                  * because the callbacks won’t work otherwise. */
00337                 preply = xcb_get_property_reply(conn, utf8_title_cookie, NULL);
00338                 handle_windowname_change(NULL, conn, 0, new->child, atoms[_NET_WM_NAME], preply);
00339 
00340                 preply = xcb_get_property_reply(conn, title_cookie, NULL);
00341                 handle_windowname_change_legacy(NULL, conn, 0, new->child, WM_NAME, preply);
00342 
00343                 preply = xcb_get_property_reply(conn, class_cookie, NULL);
00344                 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
00345 
00346                 preply = xcb_get_property_reply(conn, leader_cookie, NULL);
00347                 handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply);
00348 
00349                 /* if WM_CLIENT_LEADER is set, we put the new window on the
00350                  * same window as its leader. This might be overwritten by
00351                  * assignments afterwards. */
00352                 if (new->leader != XCB_NONE) {
00353                         DLOG("client->leader is set (to 0x%08x)\n", new->leader);
00354                         Client *parent = table_get(&by_child, new->leader);
00355                         if (parent != NULL && parent->container != NULL) {
00356                                 Workspace *t_ws = parent->workspace;
00357                                 new->container = t_ws->table[parent->container->col][parent->container->row];
00358                                 new->workspace = t_ws;
00359                                 old_focused = new->container->currently_focused;
00360                                 map_frame = workspace_is_visible(t_ws);
00361                                 new->urgent = true;
00362                                 /* This is a little tricky: we cannot use
00363                                  * workspace_update_urgent_flag() because the
00364                                  * new window was not yet inserted into the
00365                                  * focus stack on t_ws. */
00366                                 t_ws->urgent = true;
00367                         } else {
00368                                 DLOG("parent is not usable\n");
00369                         }
00370                 }
00371 
00372                 struct Assignment *assign;
00373                 TAILQ_FOREACH(assign, &assignments, assignments) {
00374                         if (get_matching_client(conn, assign->windowclass_title, new) == NULL)
00375                                 continue;
00376 
00377                         if (assign->floating == ASSIGN_FLOATING_ONLY ||
00378                             assign->floating == ASSIGN_FLOATING) {
00379                                 new->floating = FLOATING_AUTO_ON;
00380                                 LOG("Assignment matches, putting client into floating mode\n");
00381                                 if (assign->floating == ASSIGN_FLOATING_ONLY)
00382                                         break;
00383                         }
00384 
00385                         LOG("Assignment \"%s\" matches, so putting it on workspace %d\n",
00386                             assign->windowclass_title, assign->workspace);
00387 
00388                         if (c_ws->output->current_workspace->num == (assign->workspace-1)) {
00389                                 DLOG("We are already there, no need to do anything\n");
00390                                 break;
00391                         }
00392 
00393                         DLOG("Changing container/workspace and unmapping the client\n");
00394                         Workspace *t_ws = workspace_get(assign->workspace-1);
00395                         workspace_initialize(t_ws, c_ws->output, false);
00396 
00397                         new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
00398                         new->workspace = t_ws;
00399                         old_focused = new->container->currently_focused;
00400 
00401                         map_frame = workspace_is_visible(t_ws);
00402                         break;
00403                 }
00404         }
00405 
00406         if (new->workspace->fullscreen_client != NULL) {
00407                 DLOG("Setting below fullscreen window\n");
00408 
00409                 /* If we are in fullscreen, we should place the window below
00410                  * the fullscreen window to not be annoying */
00411                 uint32_t values[] = {
00412                         new->workspace->fullscreen_client->frame,
00413                         XCB_STACK_MODE_BELOW
00414                 };
00415                 xcb_configure_window(conn, new->frame,
00416                                      XCB_CONFIG_WINDOW_SIBLING |
00417                                      XCB_CONFIG_WINDOW_STACK_MODE, values);
00418         }
00419 
00420         /* Insert into the currently active container, if it’s not a dock window */
00421         if (!new->dock && !client_is_floating(new)) {
00422                 /* Insert after the old active client, if existing. If it does not exist, the
00423                    container is empty and it does not matter, where we insert it */
00424                 if (old_focused != NULL && !old_focused->dock)
00425                         CIRCLEQ_INSERT_AFTER(&(new->container->clients), old_focused, new, clients);
00426                 else CIRCLEQ_INSERT_TAIL(&(new->container->clients), new, clients);
00427 
00428                 if (new->container->workspace->fullscreen_client != NULL)
00429                         SLIST_INSERT_AFTER(new->container->workspace->fullscreen_client, new, focus_clients);
00430                 else SLIST_INSERT_HEAD(&(new->container->workspace->focus_stack), new, focus_clients);
00431 
00432                 client_set_below_floating(conn, new);
00433         }
00434 
00435         if (client_is_floating(new)) {
00436                 SLIST_INSERT_HEAD(&(new->workspace->focus_stack), new, focus_clients);
00437 
00438                 /* Add the client to the list of floating clients for its workspace */
00439                 TAILQ_INSERT_TAIL(&(new->workspace->floating_clients), new, floating_clients);
00440 
00441                 new->container = NULL;
00442 
00443                 new->rect.width = new->floating_rect.width + 2 + 2;
00444                 new->rect.height = new->floating_rect.height + (font->height + 2 + 2) + 2;
00445 
00446                 /* Some clients (like GIMP’s color picker window) get mapped
00447                  * to (0, 0), so we push them to a reasonable position
00448                  * (centered over their leader) */
00449                 if (new->leader != 0 && x == 0 && y == 0) {
00450                         DLOG("Floating client wants to (0x0), moving it over its leader instead\n");
00451                         Client *leader = table_get(&by_child, new->leader);
00452                         if (leader == NULL) {
00453                                 DLOG("leader is NULL, centering it over current workspace\n");
00454 
00455                                 x = c_ws->rect.x + (c_ws->rect.width / 2) - (new->rect.width / 2);
00456                                 y = c_ws->rect.y + (c_ws->rect.height / 2) - (new->rect.height / 2);
00457                         } else {
00458                                 x = leader->rect.x + (leader->rect.width / 2) - (new->rect.width / 2);
00459                                 y = leader->rect.y + (leader->rect.height / 2) - (new->rect.height / 2);
00460                         }
00461                 }
00462                 new->floating_rect.x = new->rect.x = x;
00463                 new->floating_rect.y = new->rect.y = y;
00464                 DLOG("copying floating_rect from tiling (%d, %d) size (%d, %d)\n",
00465                                 new->floating_rect.x, new->floating_rect.y,
00466                                 new->floating_rect.width, new->floating_rect.height);
00467                 DLOG("outer rect (%d, %d) size (%d, %d)\n",
00468                                 new->rect.x, new->rect.y, new->rect.width, new->rect.height);
00469 
00470                 /* Make sure it is on top of the other windows */
00471                 xcb_raise_window(conn, new->frame);
00472                 reposition_client(conn, new);
00473                 resize_client(conn, new);
00474                 /* redecorate_window flushes */
00475                 redecorate_window(conn, new);
00476         }
00477 
00478         new->initialized = true;
00479 
00480         /* Check if the window already got the fullscreen hint set */
00481         xcb_atom_t *state;
00482         if ((preply = xcb_get_property_reply(conn, state_cookie, NULL)) != NULL &&
00483             (state = xcb_get_property_value(preply)) != NULL)
00484                 /* Check all set _NET_WM_STATEs */
00485                 for (int i = 0; i < xcb_get_property_value_length(preply); i++) {
00486                         if (state[i] != atoms[_NET_WM_STATE_FULLSCREEN])
00487                                 continue;
00488                         /* If the window got the fullscreen state, we just toggle fullscreen
00489                            and don’t event bother to redraw the layout – that would not change
00490                            anything anyways */
00491                         client_toggle_fullscreen(conn, new);
00492                         goto map;
00493                 }
00494 
00495         render_layout(conn);
00496 
00497 map:
00498         /* Map the window first to avoid flickering */
00499         xcb_map_window(conn, child);
00500         if (map_frame)
00501                 client_map(conn, new);
00502 
00503         if ((CUR_CELL->workspace->fullscreen_client == NULL || new->fullscreen) && !new->dock) {
00504                 /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
00505                 if ((new->workspace->fullscreen_client == NULL) || new->fullscreen) {
00506                         if (!client_is_floating(new)) {
00507                                 new->container->currently_focused = new;
00508                                 if (map_frame)
00509                                         render_container(conn, new->container);
00510                         }
00511                         if (new->container == CUR_CELL || client_is_floating(new)) {
00512                                 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
00513                                 ewmh_update_active_window(new->child);
00514                         }
00515                 }
00516         }
00517 
00518         xcb_flush(conn);
00519 }

Generated on Mon Aug 22 2011 for i3 by  doxygen 1.7.1