]> git.sur5r.net Git - i3/i3/commitdiff
Bugfix: Eliminate race condition, fix dock windows
authorMichael Stapelberg <michael+x200@stapelberg.de>
Tue, 10 Mar 2009 19:56:25 +0000 (20:56 +0100)
committerMichael Stapelberg <michael+x200@stapelberg.de>
Tue, 10 Mar 2009 19:56:25 +0000 (20:56 +0100)
There was a race condition when mapping a window and not setting the event mask
before. Therefore, the ReparentNotify and (more important) the UnmapNotify generated
by reparenting were not received, thus leaving the awaiting_useless_unmap variable
of the client "true". To just make it work, in previous commits the DestroyNotify
handler was introduced. Fortunately, with fixing this race condition by first
setting the event mask and mapping the window afterwards, we can remove this handler.

As for the dock windows, there were quite some occurences were client->container
was used without checking if the client is inside a container at all.

Furthermore, the client’s strut containing the space to reserve at the screen edge
is now checked and the desired height is set to the window’s height if the strut
contains 0 or if no strut was specified at all.

include/data.h
include/handlers.h
src/handlers.c
src/layout.c
src/mainx.c
src/util.c

index 5ac2ccf119b073df14ba364ad9ba993ed85fbe3d..3ed605a612ea9c1cc5d196895448a451d7c67e2f 100644 (file)
@@ -204,6 +204,8 @@ struct Client {
 
         /* Backpointer. A client is inside a container */
         Container *container;
+        /* Because dock clients don’t have a container, we have this workspace-backpointer */
+        Workspace *workspace;
 
         /* x, y, width, height of the frame */
         Rect rect;
index e3f5d144748ef12577d916296b1d09baa1f70494..61396da818d9863b00f80cbadf0fc121fa236ffa 100644 (file)
@@ -18,7 +18,6 @@ int handle_button_press(void *ignored, xcb_connection_t *conn, xcb_button_press_
 int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_event_t *event);
 int handle_configure_event(void *prophs, xcb_connection_t *conn, xcb_configure_notify_event_t *event);
 int handle_configure_request(void *prophs, xcb_connection_t *conn, xcb_configure_request_event_t *event);
-int handle_destroy_notify_event(void *data, xcb_connection_t *conn, xcb_destroy_notify_event_t *event);
 int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_notify_event_t *event);
 int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
                                 xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop);
index 7475cd8f28eb8b6f2d362083a9b450311bf5486f..4c3024b580f07eeb4030b9ee4fd214194b685977 100644 (file)
@@ -158,7 +158,9 @@ int handle_enter_notify(void *ignored, xcb_connection_t *conn, xcb_enter_notify_
 
         /* Do plausibility checks: This event may be useless for us if it occurs on a window
            which is in a stacked container but not the focused one */
-        if (client->container->mode == MODE_STACK && client->container->currently_focused != client) {
+        if (client->container != NULL &&
+            client->container->mode == MODE_STACK &&
+            client->container->currently_focused != client) {
                 LOG("Plausibility check says: no\n");
                 return 1;
         }
@@ -410,6 +412,7 @@ int handle_map_request(void *prophs, xcb_connection_t *conn, xcb_map_request_eve
         wa.u.override_redirect = reply->override_redirect;
         LOG("window = 0x%08x, serial is %d.\n", event->window, event->sequence);
         add_ignore_event(event->sequence);
+
         manage_window(prophs, conn, event->window, wa);
         return 1;
 }
@@ -536,13 +539,19 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
                         }
         }
 
+        if (client->dock) {
+                LOG("Removing from dock clients\n");
+                SLIST_REMOVE(&(client->workspace->dock_clients), client, Client, dock_clients);
+        }
+
         LOG("child of 0x%08x.\n", client->frame);
         xcb_reparent_window(conn, client->child, root, 0, 0);
         xcb_destroy_window(conn, client->frame);
         xcb_flush(conn);
         table_remove(byParent, client->frame);
 
-        cleanup_table(conn, client->container->workspace);
+        if (client->container != NULL)
+                cleanup_table(conn, client->container->workspace);
 
         free(client);
 
@@ -551,20 +560,6 @@ int handle_unmap_notify_event(void *data, xcb_connection_t *conn, xcb_unmap_noti
         return 1;
 }
 
-/*
- * This handler makes sure that windows are closed correctly as we drop the first unmap event we
- * get because they are sometimes generated by remapping.
- *
- * FIXME: Rather than providing this event, I’d rather know why some applications need to have
- * unmap events ignored (like VLC’s open dialog, just open it two times) while others don’t (GIMP).
- * For now, just make it work.
- *
- */
-int handle_destroy_notify_event(void *data, xcb_connection_t *conn, xcb_destroy_notify_event_t *event) {
-        LOG("Destroynotify, faking unmapnotify\n");
-        return handle_unmap_notify_event(data, conn, (xcb_unmap_notify_event_t*)event);
-}
-
 /*
  * Called when a window changes its title
  *
@@ -608,6 +603,10 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state,
         if (old_name != NULL)
                 free(old_name);
 
+        /* If the client is a dock window, we don’t need to render anything */
+        if (client->dock)
+                return 1;
+
         if (client->container->mode == MODE_STACK)
                 render_container(conn, client->container);
         else decorate_window(conn, client, client->frame, client->titlegc, 0);
@@ -649,6 +648,11 @@ int handle_expose_event(void *data, xcb_connection_t *conn, xcb_expose_event_t *
         }
 
         LOG("got client %s\n", client->name);
+        if (client->dock) {
+                LOG("this is a dock\n");
+                return 1;
+        }
+
         if (client->container->mode != MODE_STACK)
                 decorate_window(conn, client, client->frame, client->titlegc, 0);
         else {
index 17c99a6a03dd65c1f72dfaf74e604274bf26121f..68db98bdb58fd71501bf024042de1277080292d4 100644 (file)
@@ -179,7 +179,7 @@ void decorate_window(xcb_connection_t *conn, Client *client, xcb_drawable_t draw
  *
  */
 static void reposition_client(xcb_connection_t *conn, Client *client) {
-        LOG("frame needs to be pushed to %dx%d\n", client->rect.x, client->rect.y);
+        LOG("frame 0x%08x needs to be pushed to %dx%d\n", client->frame, client->rect.x, client->rect.y);
         /* Note: We can use a pointer to client->x like an array of uint32_ts
            because it is followed by client->y by definition */
         xcb_configure_window(conn, client->frame, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, &(client->rect.x));
@@ -192,7 +192,7 @@ static void reposition_client(xcb_connection_t *conn, Client *client) {
 static void resize_client(xcb_connection_t *conn, Client *client) {
         i3Font *font = load_font(conn, config.font);
 
-        LOG("resizing client to %d x %d\n", client->rect.width, client->rect.height);
+        LOG("resizing client 0x%08x to %d x %d\n", client->frame, client->rect.width, client->rect.height);
         xcb_configure_window(conn, client->frame,
                         XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT,
                         &(client->rect.width));
@@ -205,7 +205,7 @@ static void resize_client(xcb_connection_t *conn, Client *client) {
                         XCB_CONFIG_WINDOW_WIDTH |
                         XCB_CONFIG_WINDOW_HEIGHT;
         Rect *rect = &(client->child_rect);
-        switch (client->container->mode) {
+        switch ((client->container != NULL ? client->container->mode : MODE_DEFAULT)) {
                 case MODE_STACK:
                         rect->x = 2;
                         rect->y = 0;
@@ -397,6 +397,7 @@ void render_container(xcb_connection_t *conn, Container *container) {
 static void render_bars(xcb_connection_t *conn, Workspace *r_ws, int width, int *height) {
         Client *client;
         SLIST_FOREACH(client, &(r_ws->dock_clients), dock_clients) {
+                LOG("client is at %d, should be at %d\n", client->rect.y, *height);
                 if (client->force_reconfigure |
                     update_if_necessary(&(client->rect.x), 0) |
                     update_if_necessary(&(client->rect.y), *height))
index 4d96a8c4435194fd32cb19d94d9017c032b65a45..61d2bb547ad72c5b51de723722d189d391d54e9d 100644 (file)
@@ -126,14 +126,23 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                 int16_t x, int16_t y, uint16_t width, uint16_t height) {
 
         xcb_get_property_cookie_t wm_type_cookie, strut_cookie;
+        uint32_t mask = 0;
+        uint32_t values[3];
 
-        /* Place requests for properties ASAP */
-        wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
-        strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
+        /* We are interested in property changes */
+        mask = XCB_CW_EVENT_MASK;
+        values[0] =     XCB_EVENT_MASK_PROPERTY_CHANGE |
+                        XCB_EVENT_MASK_STRUCTURE_NOTIFY |
+                        XCB_EVENT_MASK_ENTER_WINDOW;
+        xcb_change_window_attributes(conn, child, mask, values);
 
         /* Map the window first to avoid flickering */
         xcb_map_window(conn, child);
 
+        /* Place requests for properties ASAP */
+        wm_type_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_WINDOW_TYPE], UINT32_MAX);
+        strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
+
         Client *new = table_get(byChild, child);
 
         /* Events for already managed windows should already be filtered in manage_window() */
@@ -142,19 +151,20 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         LOG("reparenting new client\n");
         new = calloc(sizeof(Client), 1);
         new->force_reconfigure = true;
-        uint32_t mask = 0;
-        uint32_t values[3];
 
         /* Update the data structures */
         Client *old_focused = CUR_CELL->currently_focused;
 
         new->container = CUR_CELL;
+        new->workspace = new->container->workspace;
 
         new->frame = xcb_generate_id(conn);
         new->child = child;
         new->rect.width = width;
         new->rect.height = height;
 
+        mask = 0;
+
         /* Don’t generate events for our new window, it should *not* be managed */
         mask |= XCB_CW_OVERRIDE_REDIRECT;
         values[0] = 1;
@@ -199,19 +209,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                 return;
         }
 
-        /* We are interested in property changes */
-        mask = XCB_CW_EVENT_MASK;
-        values[0] =     XCB_EVENT_MASK_PROPERTY_CHANGE |
-                        XCB_EVENT_MASK_STRUCTURE_NOTIFY |
-                        XCB_EVENT_MASK_ENTER_WINDOW;
-        cookie = xcb_change_window_attributes_checked(conn, child, mask, values);
-        if (xcb_request_check(conn, cookie) != NULL) {
-                LOG("Could not change window attributes, aborting\n");
-                xcb_destroy_window(conn, new->frame);
-                free(new);
-                return;
-        }
-
         /* Put our data structure (Client) into the table */
         table_put(byParent, new->frame, new);
         table_put(byChild, child, new);
@@ -222,16 +219,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         1 /* left mouse button */,
                         XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
 
-        /* Focus the new window if we’re not in fullscreen mode */
-        if (CUR_CELL->workspace->fullscreen_client == NULL) {
-                CUR_CELL->currently_focused = new;
-                xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
-        } else {
-                /* If we are in fullscreen, we should lower the window to not be annoying */
-                uint32_t values[] = { XCB_STACK_MODE_BELOW };
-                xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
-        }
-
         /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
         xcb_atom_t *atom;
         xcb_get_property_reply_t *preply = xcb_get_property_reply(conn, wm_type_cookie, NULL);
@@ -247,23 +234,44 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         }
         }
 
-        /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
-        uint32_t *strut;
-        preply = xcb_get_property_reply(conn, strut_cookie, NULL);
-        if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
-                /* We only use a subset of the provided values, namely the reserved space at the top/bottom
-                   of the screen. This is because the only possibility for bars is at to be at the top/bottom
-                   with maximum horizontal size.
-                   TODO: bars at the top */
-                new->desired_height = strut[3];
-                LOG("the client wants to be %d pixels height\n", new->desired_height);
+        if (new->dock) {
+                /* Get _NET_WM_STRUT_PARTIAL to determine the client’s requested height */
+                uint32_t *strut;
+                preply = xcb_get_property_reply(conn, strut_cookie, NULL);
+                if (preply != NULL && preply->value_len > 0 && (strut = xcb_get_property_value(preply))) {
+                        /* We only use a subset of the provided values, namely the reserved space at the top/bottom
+                           of the screen. This is because the only possibility for bars is at to be at the top/bottom
+                           with maximum horizontal size.
+                           TODO: bars at the top */
+                        new->desired_height = strut[3];
+                        if (new->desired_height == 0) {
+                                LOG("Client wanted to be 0 pixels high, using the window's height (%d)\n", height);
+                                new->desired_height = height;
+                        }
+                        LOG("the client wants to be %d pixels high\n", new->desired_height);
+                } else {
+                        LOG("The client didn't specify space to reserve at the screen edge, using its height (%d)\n", height);
+                        new->desired_height = height;
+                }
+        }
+
+        /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
+        if (CUR_CELL->workspace->fullscreen_client == NULL) {
+                if (!new->dock) {
+                        CUR_CELL->currently_focused = new;
+                        xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
+                }
+        } else {
+                /* If we are in fullscreen, we should lower the window to not be annoying */
+                uint32_t values[] = { XCB_STACK_MODE_BELOW };
+                xcb_configure_window(conn, new->frame, XCB_CONFIG_WINDOW_STACK_MODE, values);
         }
 
         /* Insert into the currently active container, if it’s not a dock window */
         if (!new->dock) {
                 /* Insert after the old active client, if existing. If it does not exist, the
                    container is empty and it does not matter, where we insert it */
-                if (old_focused != NULL)
+                if (old_focused != NULL && !old_focused->dock)
                         CIRCLEQ_INSERT_AFTER(&(CUR_CELL->clients), old_focused, new, clients);
                 else CIRCLEQ_INSERT_TAIL(&(CUR_CELL->clients), new, clients);
 
@@ -408,10 +416,6 @@ int main(int argc, char *argv[], char *env[]) {
            it any longer. Usually, the client destroys the window shortly afterwards. */
         xcb_event_set_unmap_notify_handler(&evenths, handle_unmap_notify_event, NULL);
 
-        /* Destroy notify is a step further than unmap notify. We handle it to make sure
-           that windows whose unmap notify was falsely ignored will get deleted properly */
-        xcb_event_set_destroy_notify_handler(&evenths, handle_destroy_notify_event, NULL);
-
         /* Configure notify = window’s configuration (geometry, stacking, …). We only need
            it to set up ignore the following enter_notify events */
         xcb_event_set_configure_notify_handler(&evenths, handle_configure_event, NULL);
index c79aa3d1bdba3a5ad32227a0046c74e4ef0862a4..2ca1e1ca1ab82f63d397eeedc7b3dfc2d77b5e9b 100644 (file)
@@ -233,7 +233,7 @@ void set_focus(xcb_connection_t *conn, Client *client) {
 
         /* If we’re in stacking mode, this renders the container to update changes in the title
            bars and to raise the focused client */
-        if ((old_client != NULL) && (old_client != client))
+        if ((old_client != NULL) && (old_client != client) && !old_client->dock)
                 redecorate_window(conn, old_client);
 
         SLIST_REMOVE(&(client->container->workspace->focus_stack), client, Client, focus_clients);