]> git.sur5r.net Git - i3/i3/blobdiff - src/manage.c
Implement the urgency hint for windows/workspaces
[i3/i3] / src / manage.c
index 9ddcab42e51dbedad3d900cc96ca6b04e7ba2260..f4eecc966be9353dfa325537acb14620a14269fd 100644 (file)
@@ -29,6 +29,7 @@
 #include "manage.h"
 #include "floating.h"
 #include "client.h"
+#include "workspace.h"
 
 /*
  * Go through all existing windows (if the window manager is restarted) and manage them
@@ -67,7 +68,6 @@ void manage_existing_windows(xcb_connection_t *conn, xcb_property_handlers_t *pr
 void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
                    xcb_window_t window, xcb_get_window_attributes_cookie_t cookie,
                    bool needs_to_be_mapped) {
-        LOG("managing window.\n");
         xcb_drawable_t d = { window };
         xcb_get_geometry_cookie_t geomc;
         xcb_get_geometry_reply_t *geom;
@@ -82,16 +82,12 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
                 return;
         }
 
-        if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
-                LOG("Window not mapped, not managing\n");
+        if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE)
                 goto out;
-        }
 
         /* Don’t manage clients with the override_redirect flag */
-        if (attr->override_redirect) {
-                LOG("override_redirect set, not managing\n");
+        if (attr->override_redirect)
                 goto out;
-        }
 
         /* Check if the window is already managed */
         if (table_get(&by_child, window))
@@ -109,7 +105,9 @@ void manage_window(xcb_property_handlers_t *prophs, xcb_connection_t *conn,
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_CLASS);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NAME);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_NORMAL_HINTS);
+        xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_HINTS);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, WM_TRANSIENT_FOR);
+        xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[WM_CLIENT_LEADER]);
         xcb_property_changed(prophs, XCB_PROPERTY_NEW_VALUE, window, atoms[_NET_WM_NAME]);
 
         free(geom);
@@ -130,10 +128,12 @@ 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, state_cookie,
-                                  utf8_title_cookie, title_cookie, class_cookie;
+                                  utf8_title_cookie, title_cookie,
+                                  class_cookie, leader_cookie;
         uint32_t mask = 0;
         uint32_t values[3];
         uint16_t original_height = height;
+        bool map_frame = true;
 
         /* We are interested in property changes */
         mask = XCB_CW_EVENT_MASK;
@@ -145,15 +145,16 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         strut_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STRUT_PARTIAL], UINT32_MAX);
         state_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_STATE], UINT32_MAX);
         utf8_title_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[_NET_WM_NAME], 128);
+        leader_cookie = xcb_get_any_property_unchecked(conn, false, child, atoms[WM_CLIENT_LEADER], UINT32_MAX);
         title_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_NAME, 128);
-        class_cookie  = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
+        class_cookie = xcb_get_any_property_unchecked(conn, false, child, WM_CLASS, 128);
 
         Client *new = table_get(&by_child, child);
 
         /* Events for already managed windows should already be filtered in manage_window() */
         assert(new == NULL);
 
-        LOG("reparenting new client\n");
+        LOG("Reparenting window 0x%08x\n", child);
         LOG("x = %d, y = %d, width = %d, height = %d\n", x, y, width, height);
         new = calloc(sizeof(Client), 1);
         new->force_reconfigure = true;
@@ -172,6 +173,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         new->child = child;
         new->rect.width = width;
         new->rect.height = height;
+        new->width_increment = 1;
+        new->height_increment = 1;
         /* Pre-initialize the values for floating */
         new->floating_rect.x = -1;
         new->floating_rect.width = width;
@@ -187,8 +190,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         mask |= XCB_CW_EVENT_MASK;
         values[1] = FRAME_EVENT_MASK;
 
-        LOG("Reparenting 0x%08x under 0x%08x.\n", child, new->frame);
-
         i3Font *font = load_font(conn, config.font);
         width = min(width, c_ws->rect.x + c_ws->rect.width);
         height = min(height, c_ws->rect.y + c_ws->rect.height);
@@ -200,11 +201,6 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
         /* Yo dawg, I heard you like windows, so I create a window around your window… */
         new->frame = create_window(conn, framerect, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_CURSOR_LEFT_PTR, false, mask, values);
 
-        /* Set WM_STATE_NORMAL because GTK applications don’t want to drag & drop if we don’t.
-         * Also, xprop(1) needs that to work. */
-        long data[] = { XCB_WM_STATE_NORMAL, XCB_NONE };
-        xcb_change_property(conn, XCB_PROP_MODE_REPLACE, new->child, atoms[WM_STATE], atoms[WM_STATE], 32, 2, data);
-
         /* Put the client inside the save set. Upon termination (whether killed or normal exit
            does not matter) of the window manager, these clients will be correctly reparented
            to their most closest living ancestor (= cleanup) */
@@ -236,7 +232,8 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
 
         xcb_grab_button(conn, false, child, XCB_EVENT_MASK_BUTTON_PRESS,
                         XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC, root, XCB_NONE,
-                        1 /* left mouse button */, XCB_MOD_MASK_1);
+                        3 /* right mouse button */,
+                        XCB_BUTTON_MASK_ANY /* don’t filter for any modifiers */);
 
         /* Get _NET_WM_WINDOW_TYPE (to see if it’s a dock) */
         xcb_atom_t *atom;
@@ -246,11 +243,13 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
                                 LOG("Window is a dock.\n");
                                 new->dock = true;
+                                new->borderless = true;
                                 new->titlebar_position = TITLEBAR_OFF;
                                 new->force_reconfigure = true;
                                 new->container = NULL;
                                 SLIST_INSERT_HEAD(&(c_ws->screen->dock_clients), new, dock_clients);
                                 /* If it’s a dock we can’t make it float, so we break */
+                                new->floating = FLOATING_AUTO_OFF;
                                 break;
                         } else if (atom[i] == atoms[_NET_WM_WINDOW_TYPE_DIALOG] ||
                                    atom[i] == atoms[_NET_WM_WINDOW_TYPE_UTILITY] ||
@@ -262,6 +261,12 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                         }
         }
 
+        /* All clients which have a leader should be floating */
+        if (!new->dock && !client_is_floating(new) && new->leader != 0) {
+                LOG("Client has WM_CLIENT_LEADER hint set, setting floating\n");
+                new->floating = FLOATING_AUTO_ON;
+        }
+
         if (new->workspace->auto_float) {
                 new->floating = FLOATING_AUTO_ON;
                 LOG("workspace is in autofloat mode, setting floating\n");
@@ -302,7 +307,9 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                 preply = xcb_get_property_reply(conn, class_cookie, NULL);
                 handle_windowclass_change(NULL, conn, 0, new->child, WM_CLASS, preply);
 
-                LOG("DEBUG: should have all infos now\n");
+                preply = xcb_get_property_reply(conn, leader_cookie, NULL);
+                handle_clientleader_change(NULL, conn, 0, new->child, atoms[WM_CLIENT_LEADER], preply);
+
                 struct Assignment *assign;
                 TAILQ_FOREACH(assign, &assignments, assignments) {
                         if (get_matching_client(conn, assign->windowclass_title, new) == NULL)
@@ -324,20 +331,15 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                                 break;
                         }
 
-                        LOG("Changin container/workspace and unmapping the client\n");
+                        LOG("Changing container/workspace and unmapping the client\n");
                         Workspace *t_ws = &(workspaces[assign->workspace-1]);
-                        if (t_ws->screen == NULL) {
-                                LOG("initializing new workspace, setting num to %d\n", assign->workspace);
-                                t_ws->screen = c_ws->screen;
-                                /* Copy the dimensions from the virtual screen */
-                                memcpy(&(t_ws->rect), &(t_ws->screen->rect), sizeof(Rect));
-                        }
+                        workspace_initialize(t_ws, c_ws->screen);
 
                         new->container = t_ws->table[t_ws->current_col][t_ws->current_row];
                         new->workspace = t_ws;
                         old_focused = new->container->currently_focused;
 
-                        xcb_unmap_window(conn, new->frame);
+                        map_frame = workspace_is_visible(t_ws);
                         break;
                 }
         }
@@ -373,10 +375,27 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
 
                 new->container = NULL;
 
-                new->floating_rect.x = new->rect.x = x;
-                new->floating_rect.y = new->rect.y = y;
                 new->rect.width = new->floating_rect.width + 2 + 2;
                 new->rect.height = new->floating_rect.height + (font->height + 2 + 2) + 2;
+
+                /* Some clients (like GIMP’s color picker window) get mapped
+                 * to (0, 0), so we push them to a reasonable position
+                 * (centered over their leader) */
+                if (new->leader != 0 && x == 0 && y == 0) {
+                        LOG("Floating client wants to (0x0), moving it over its leader instead\n");
+                        Client *leader = table_get(&by_child, new->leader);
+                        if (leader == NULL) {
+                                LOG("leader is NULL, centering it over current workspace\n");
+
+                                x = c_ws->rect.x + (c_ws->rect.width / 2) - (new->rect.width / 2);
+                                y = c_ws->rect.y + (c_ws->rect.height / 2) - (new->rect.height / 2);
+                        } else {
+                                x = leader->rect.x + (leader->rect.width / 2) - (new->rect.width / 2);
+                                y = leader->rect.y + (leader->rect.height / 2) - (new->rect.height / 2);
+                        }
+                }
+                new->floating_rect.x = new->rect.x = x;
+                new->floating_rect.y = new->rect.y = y;
                 LOG("copying floating_rect from tiling (%d, %d) size (%d, %d)\n",
                                 new->floating_rect.x, new->floating_rect.y,
                                 new->floating_rect.width, new->floating_rect.height);
@@ -405,20 +424,23 @@ void reparent_window(xcb_connection_t *conn, xcb_window_t child,
                            and don’t event bother to redraw the layout – that would not change
                            anything anyways */
                         client_toggle_fullscreen(conn, new);
-                        return;
+                        goto map;
                 }
 
         render_layout(conn);
 
+map:
         /* Map the window first to avoid flickering */
-        xcb_map_window(conn, new->frame);
         xcb_map_window(conn, child);
-        if (CUR_CELL->workspace->fullscreen_client == NULL && !new->dock) {
+        if (map_frame)
+                client_map(conn, new);
+
+        if ((CUR_CELL->workspace->fullscreen_client == NULL || new->fullscreen) && !new->dock) {
                 /* Focus the new window if we’re not in fullscreen mode and if it is not a dock window */
-                if (new->container->workspace->fullscreen_client == NULL) {
+                if ((new->workspace->fullscreen_client == NULL) || new->fullscreen) {
                         if (!client_is_floating(new))
                                 new->container->currently_focused = new;
-                        if (new->container == CUR_CELL)
+                        if (new->container == CUR_CELL || client_is_floating(new))
                                 xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, new->child, XCB_CURRENT_TIME);
                 }
         }