]> git.sur5r.net Git - i3/i3/blobdiff - src/x.c
replace remaining printf()s with D?LOG
[i3/i3] / src / x.c
diff --git a/src/x.c b/src/x.c
index f86559460ed213722fa70bbc1714d0e04636e13a..086dbc9be4dab5102a632d8ba28a167a3172fcce 100644 (file)
--- a/src/x.c
+++ b/src/x.c
 /* Stores the X11 window ID of the currently focused window */
 xcb_window_t focused_id = XCB_NONE;
 
-/* The bottom-to-top window stack of all windows which are managed by i3.
- * Used for x_get_window_stack(). */
-static xcb_window_t *btt_stack;
-static int btt_stack_num;
+/* Because 'focused_id' might be reset to force input focus (after click to
+ * raise), we separately keep track of the X11 window ID to be able to always
+ * tell whether the focused window actually changed. */
+static xcb_window_t last_focused = XCB_NONE;
 
 /* Stores coordinates to warp mouse pointer to if set */
 static Rect *warp_to;
@@ -36,7 +36,6 @@ typedef struct con_state {
     bool mapped;
     bool unmap_now;
     bool child_mapped;
-    bool above_all;
 
     /** The con for which this state is. */
     Con *con;
@@ -56,6 +55,7 @@ typedef struct con_state {
 
     CIRCLEQ_ENTRY(con_state) state;
     CIRCLEQ_ENTRY(con_state) old_state;
+    TAILQ_ENTRY(con_state) initial_mapping_order;
 } con_state;
 
 CIRCLEQ_HEAD(state_head, con_state) state_head =
@@ -64,6 +64,9 @@ CIRCLEQ_HEAD(state_head, con_state) state_head =
 CIRCLEQ_HEAD(old_state_head, con_state) old_state_head =
     CIRCLEQ_HEAD_INITIALIZER(old_state_head);
 
+TAILQ_HEAD(initial_mapping_head, con_state) initial_mapping_head =
+    TAILQ_HEAD_INITIALIZER(initial_mapping_head);
+
 /*
  * Returns the container state for the given frame. This function always
  * returns a container state (otherwise, there is a bug in the code and the
@@ -147,8 +150,10 @@ void x_con_init(Con *con, uint16_t depth) {
     state->id = con->frame;
     state->mapped = false;
     state->initial = true;
+    DLOG("Adding window 0x%08x to lists\n", state->id);
     CIRCLEQ_INSERT_HEAD(&state_head, state, state);
     CIRCLEQ_INSERT_HEAD(&old_state_head, state, old_state);
+    TAILQ_INSERT_TAIL(&initial_mapping_head, state, initial_mapping_order);
     DLOG("adding new state for window id 0x%08x\n", state->id);
 }
 
@@ -229,11 +234,12 @@ void x_con_kill(Con *con) {
     state = state_for_frame(con->frame);
     CIRCLEQ_REMOVE(&state_head, state, state);
     CIRCLEQ_REMOVE(&old_state_head, state, old_state);
+    TAILQ_REMOVE(&initial_mapping_head, state, initial_mapping_order);
     FREE(state->name);
     free(state);
 
     /* Invalidate focused_id to correctly focus new windows with the same ID */
-    focused_id = XCB_NONE;
+    focused_id = last_focused = XCB_NONE;
 }
 
 /*
@@ -668,9 +674,24 @@ void x_push_node(Con *con) {
         /* As the pixmap only depends on the size and not on the position, it
          * is enough to check if width/height have changed. Also, we don’t
          * create a pixmap at all when the window is actually not visible
-         * (height == 0). */
-        if ((state->rect.width != rect.width ||
-            state->rect.height != rect.height)) {
+         * (height == 0) or when it is not needed. */
+        bool has_rect_changed = (state->rect.width != rect.width || state->rect.height != rect.height);
+
+        /* The pixmap of a borderless leaf container will not be used except
+         * for the titlebar in a stack or tabs (issue #1013). */
+        bool is_pixmap_needed = (con->border_style != BS_NONE ||
+                !con_is_leaf(con) ||
+                con->parent->layout == L_STACKED ||
+                con->parent->layout == L_TABBED);
+
+        /* Check if the container has an unneeded pixmap left over from
+         * previously having a border or titlebar. */
+        if (!is_pixmap_needed && con->pixmap != XCB_NONE) {
+            xcb_free_pixmap(conn, con->pixmap);
+            con->pixmap = XCB_NONE;
+        }
+
+        if (has_rect_changed && is_pixmap_needed) {
             if (con->pixmap == 0) {
                 con->pixmap = xcb_generate_id(conn);
                 con->pm_gc = xcb_generate_id(conn);
@@ -732,10 +753,9 @@ void x_push_node(Con *con) {
     }
 
     /* Map if map state changed, also ensure that the child window
-     * is changed if we are mapped *and* in initial state (meaning the
-     * container was empty before, but now got a child). Unmaps are handled in
-     * x_push_node_unmaps(). */
-    if ((state->mapped != con->mapped || (con->mapped && state->initial)) &&
+     * is changed if we are mapped and there is a new, unmapped child window.
+     * Unmaps are handled in x_push_node_unmaps(). */
+    if ((state->mapped != con->mapped || (con->window != NULL && !state->child_mapped)) &&
         con->mapped) {
         xcb_void_cookie_t cookie;
 
@@ -835,6 +855,24 @@ static void x_push_node_unmaps(Con *con) {
         x_push_node_unmaps(current);
 }
 
+/*
+ * Returns true if the given container is currently attached to its parent.
+ *
+ * TODO: Remove once #1185 has been fixed
+ */
+static bool is_con_attached(Con *con) {
+    if (con->parent == NULL)
+        return false;
+
+    Con *current;
+    TAILQ_FOREACH(current, &(con->parent->nodes_head), nodes) {
+        if (current == con)
+            return true;
+    }
+
+    return false;
+}
+
 /*
  * Pushes all changes (state of each node, see x_push_node() and the window
  * stack) to X11.
@@ -870,19 +908,24 @@ void x_push_changes(Con *con) {
      * stack afterwards */
     int cnt = 0;
     CIRCLEQ_FOREACH_REVERSE(state, &state_head, state)
-        if (state->con && state->con->window)
+        if (con_has_managed_window(state->con))
             cnt++;
 
-    if (cnt != btt_stack_num) {
-        btt_stack = srealloc(btt_stack, sizeof(xcb_window_t) * cnt);
-        btt_stack_num = cnt;
+    /* The bottom-to-top window stack of all windows which are managed by i3.
+     * Used for x_get_window_stack(). */
+    static xcb_window_t *client_list_windows = NULL;
+    static int client_list_count = 0;
+
+    if (cnt != client_list_count) {
+        client_list_windows = srealloc(client_list_windows, sizeof(xcb_window_t) * cnt);
+        client_list_count = cnt;
     }
 
-    xcb_window_t *walk = btt_stack;
+    xcb_window_t *walk = client_list_windows;
 
     /* X11 correctly represents the stack if we push it from bottom to top */
     CIRCLEQ_FOREACH_REVERSE(state, &state_head, state) {
-        if (state->con && state->con->window)
+        if (con_has_managed_window(state->con))
             memcpy(walk++, &(state->con->window->id), sizeof(xcb_window_t));
 
         //DLOG("stack: 0x%08x\n", state->id);
@@ -900,17 +943,25 @@ void x_push_changes(Con *con) {
 
             xcb_configure_window(conn, prev->id, mask, values);
         }
-        if (state->above_all) {
-            DLOG("above all: 0x%08x\n", state->id);
-            xcb_configure_window(conn, state->id, XCB_CONFIG_WINDOW_STACK_MODE, (uint32_t[]){ XCB_STACK_MODE_ABOVE });
-        }
         state->initial = false;
     }
 
     /* If we re-stacked something (or a new window appeared), we need to update
-     * the _NET_CLIENT_LIST_STACKING hint */
-    if (stacking_changed)
-        ewmh_update_client_list_stacking(btt_stack, btt_stack_num);
+     * the _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING hints */
+    if (stacking_changed) {
+        DLOG("Client list changed (%i clients)\n", cnt);
+        ewmh_update_client_list_stacking(client_list_windows, client_list_count);
+
+        walk = client_list_windows;
+
+        /* reorder by initial mapping */
+        TAILQ_FOREACH(state, &initial_mapping_head, initial_mapping_order) {
+            if (con_has_managed_window(state->con))
+                *walk++ = state->con->window->id;
+        }
+
+        ewmh_update_client_list(client_list_windows, client_list_count);
+    }
 
     DLOG("PUSHING CHANGES\n");
     x_push_node(con);
@@ -955,17 +1006,18 @@ void x_push_changes(Con *con) {
             /* Invalidate focused_id to correctly focus new windows with the same ID */
             focused_id = XCB_NONE;
         } else {
-            bool set_focus = true;
             if (focused->window != NULL &&
-                focused->window->needs_take_focus) {
+                focused->window->needs_take_focus &&
+                focused->window->doesnt_accept_focus) {
                 DLOG("Updating focus by sending WM_TAKE_FOCUS to window 0x%08x (focused: %p / %s)\n",
                      to_focus, focused, focused->name);
-                send_take_focus(to_focus);
-                set_focus = !focused->window->doesnt_accept_focus;
-                DLOG("set_focus = %d\n", set_focus);
-            }
+                send_take_focus(to_focus, last_timestamp);
 
-            if (set_focus) {
+                ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE));
+
+                if (to_focus != last_focused && is_con_attached(focused))
+                   ipc_send_window_event("focus", focused);
+            } else {
                 DLOG("Updating focus (focused: %p / %s) to X11 window 0x%08x\n", focused, focused->name, to_focus);
                 /* We remove XCB_EVENT_MASK_FOCUS_CHANGE from the event mask to get
                  * no focus change events for our own focus changes. We only want
@@ -980,16 +1032,20 @@ void x_push_changes(Con *con) {
                     xcb_change_window_attributes(conn, focused->window->id, XCB_CW_EVENT_MASK, values);
                 }
 
-                ewmh_update_active_window(to_focus);
+                ewmh_update_active_window((con_has_managed_window(focused) ? focused->window->id : XCB_WINDOW_NONE));
+
+                if (to_focus != XCB_NONE && to_focus != last_focused && focused->window != NULL && is_con_attached(focused))
+                   ipc_send_window_event("focus", focused);
             }
 
-            focused_id = to_focus;
+            focused_id = last_focused = to_focus;
         }
     }
 
     if (focused_id == XCB_NONE) {
         DLOG("Still no window focused, better set focus to the root window\n");
         xcb_set_input_focus(conn, XCB_INPUT_FOCUS_POINTER_ROOT, root, XCB_CURRENT_TIME);
+        ewmh_update_active_window(XCB_WINDOW_NONE);
         focused_id = root;
     }
 
@@ -1029,18 +1085,12 @@ void x_push_changes(Con *con) {
  * Raises the specified container in the internal stack of X windows. The
  * next call to x_push_changes() will make the change visible in X11.
  *
- * If above_all is true, the X11 window will be raised to the top
- * of the stack. This should only be used for precisely one fullscreen
- * window per output.
- *
  */
-void x_raise_con(Con *con, bool above_all) {
+void x_raise_con(Con *con) {
     con_state *state;
     state = state_for_frame(con->frame);
     //DLOG("raising in new stack: %p / %s / %s / xid %08x\n", con, con->name, con->window ? con->window->name_json : "", state->id);
 
-    state->above_all = above_all;
-
     CIRCLEQ_REMOVE(&state_head, state, state);
     CIRCLEQ_INSERT_HEAD(&state_head, state, state);
 }
@@ -1063,6 +1113,16 @@ void x_set_name(Con *con, const char *name) {
     state->name = sstrdup(name);
 }
 
+/*
+ * Set up the I3_SHMLOG_PATH atom.
+ *
+ */
+void update_shmlog_atom() {
+    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root,
+            A_I3_SHMLOG_PATH, A_UTF8_STRING, 8,
+            strlen(shmlogname), shmlogname);
+}
+
 /*
  * Sets up i3 specific atoms (I3_SOCKET_PATH and I3_CONFIG_PATH)
  *
@@ -1075,8 +1135,7 @@ void x_set_i3_atoms(void) {
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
     xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_CONFIG_PATH, A_UTF8_STRING, 8,
                         strlen(current_configpath), current_configpath);
-    xcb_change_property(conn, XCB_PROP_MODE_REPLACE, root, A_I3_SHMLOG_PATH, A_UTF8_STRING, 8,
-                        strlen(shmlogname), shmlogname);
+    update_shmlog_atom();
 }
 
 /*
@@ -1086,7 +1145,9 @@ void x_set_i3_atoms(void) {
  */
 void x_set_warp_to(Rect *rect)
 {
-    warp_to = rect;
+    if (!config.disable_focus_follows_mouse &&
+        config.mouse_warping != POINTER_WARPING_NONE)
+        warp_to = rect;
 }
 
 /*