]> git.sur5r.net Git - i3/i3/blobdiff - src/manage.c
i3-nagbar: Set button inner-width to the width of the label
[i3/i3] / src / manage.c
index 9835aa2a9637a49ae2d1055caddf2cba2e442d00..892ac4dfb236e9c63827448227139a6d25489be3 100644 (file)
@@ -4,12 +4,15 @@
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
- * © 2009-2012 Michael Stapelberg and contributors (see also: LICENSE)
+ * © 2009-2013 Michael Stapelberg and contributors (see also: LICENSE)
  *
  * manage.c: Initially managing new windows (or existing ones on restart).
  *
  */
 #include "all.h"
+#include "yajl_utils.h"
+
+#include <yajl/yajl_gen.h>
 
 /*
  * Go through all existing windows (if the window manager is restarted) and manage them
@@ -64,8 +67,41 @@ void restore_geometry(void) {
                                 con->rect.x, con->rect.y);
         }
 
+    /* Strictly speaking, this line doesn’t really belong here, but since we
+     * are syncing, let’s un-register as a window manager first */
+    xcb_change_window_attributes(conn, root, XCB_CW_EVENT_MASK, (uint32_t[]){ XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT });
+
     /* Make sure our changes reach the X server, we restart/exit now */
-    xcb_flush(conn);
+    xcb_aux_sync(conn);
+}
+
+/*
+ * The following function sends a new window event, which consists
+ * of fields "change" and "container", the latter containing a dump
+ * of the window's container.
+ *
+ */
+static void ipc_send_window_new_event(Con *con) {
+    setlocale(LC_NUMERIC, "C");
+    yajl_gen gen = ygenalloc();
+
+    y(map_open);
+
+    ystr("change");
+    ystr("new");
+
+    ystr("container");
+    dump_node(gen, con, false);
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+    y(get_buf, &payload, &length);
+
+    ipc_send_event("window", I3_IPC_EVENT_WINDOW, (const char *)payload);
+    y(free);
+    setlocale(LC_NUMERIC, "");
 }
 
 /*
@@ -86,34 +122,30 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
 
 
     geomc = xcb_get_geometry(conn, d);
-#define FREE_GEOMETRY() do { \
-    if ((geom = xcb_get_geometry_reply(conn, geomc, 0)) != NULL) \
-        free(geom); \
-} while (0)
 
     /* Check if the window is mapped (it could be not mapped when intializing and
        calling manage_window() for every window) */
     if ((attr = xcb_get_window_attributes_reply(conn, cookie, 0)) == NULL) {
         DLOG("Could not get attributes\n");
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         return;
     }
 
     if (needs_to_be_mapped && attr->map_state != XCB_MAP_STATE_VIEWABLE) {
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         goto out;
     }
 
     /* Don’t manage clients with the override_redirect flag */
     if (attr->override_redirect) {
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         goto out;
     }
 
     /* Check if the window is already managed */
     if (con_by_window_id(window) != NULL) {
         DLOG("already managed (by con %p)\n", con_by_window_id(window));
-        FREE_GEOMETRY();
+        xcb_discard_reply(conn, geomc.sequence);
         goto out;
     }
 
@@ -188,7 +220,8 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL));
     window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL));
     window_update_role(cwindow, xcb_get_property_reply(conn, role_cookie, NULL), true);
-    window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL));
+    bool urgency_hint;
+    window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint);
 
     xcb_get_property_reply_t *startup_id_reply;
     startup_id_reply = xcb_get_property_reply(conn, startup_id_cookie, NULL);
@@ -289,11 +322,20 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     x_set_name(nc, name);
     free(name);
 
+    /* handle fullscreen containers */
     Con *ws = con_get_workspace(nc);
     Con *fs = (ws ? con_get_fullscreen_con(ws, CF_OUTPUT) : NULL);
     if (fs == NULL)
         fs = con_get_fullscreen_con(croot, CF_GLOBAL);
 
+    xcb_get_property_reply_t *state_reply = xcb_get_property_reply(conn, state_cookie, NULL);
+    if (xcb_reply_contains_atom(state_reply, A__NET_WM_STATE_FULLSCREEN)) {
+        fs = NULL;
+        con_toggle_fullscreen(nc, CF_OUTPUT);
+    }
+
+    FREE(state_reply);
+
     if (fs == NULL) {
         DLOG("Not in fullscreen mode, focusing\n");
         if (!cwindow->dock) {
@@ -349,10 +391,20 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
             con_toggle_fullscreen(fs, CF_OUTPUT);
         } else if (config.popup_during_fullscreen == PDF_SMART &&
                    fs != NULL &&
-                   fs->window != NULL &&
-                   fs->window->id == cwindow->transient_for) {
-            LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n");
-            con_focus(nc);
+                   fs->window != NULL) {
+            i3Window *transient_win = cwindow;
+            while (transient_win != NULL &&
+                   transient_win->transient_for != XCB_NONE) {
+                if (transient_win->transient_for == fs->window->id) {
+                    LOG("This floating window belongs to the fullscreen window (popup_during_fullscreen == smart)\n");
+                    con_focus(nc);
+                    break;
+                }
+                Con *next_transient = con_by_window_id(transient_win->transient_for);
+                if (next_transient == NULL)
+                    break;
+                transient_win = next_transient->window;
+            }
         }
     }
 
@@ -387,12 +439,6 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     xcb_change_window_attributes(conn, window, XCB_CW_EVENT_MASK, values);
     xcb_flush(conn);
 
-    reply = xcb_get_property_reply(conn, state_cookie, NULL);
-    if (xcb_reply_contains_atom(reply, A__NET_WM_STATE_FULLSCREEN))
-        con_toggle_fullscreen(nc, CF_OUTPUT);
-
-    FREE(reply);
-
     /* 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 (=
@@ -402,6 +448,10 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     /* Check if any assignments match */
     run_assignments(cwindow);
 
+    /* 'ws' may be invalid because of the assignments, e.g. when the user uses
+     * "move window to workspace 1", but had it assigned to workspace 2. */
+    ws = con_get_workspace(nc);
+
     /* If this window was put onto an invisible workspace (via assignments), we
      * render this workspace. It wouldn’t be rendered in our normal code path
      * because only the visible workspaces get rendered.
@@ -420,6 +470,15 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki
     }
     tree_render();
 
+    /* Send an event about window creation */
+    ipc_send_window_new_event(nc);
+
+    /* Windows might get managed with the urgency hint already set (Pidgin is
+     * known to do that), so check for that and handle the hint accordingly.
+     * This code needs to be in this part of manage_window() because the window
+     * needs to be on the final workspace first. */
+    con_set_urgency(nc, urgency_hint);
+
 geom_out:
     free(geom);
 out: