]> git.sur5r.net Git - i3/i3/commitdiff
Make sure sticky windows pop to the front if they get sticky while not being on a... 1856/head
authorIngo Bürk <ingo.buerk@tngtech.com>
Wed, 26 Aug 2015 19:06:53 +0000 (21:06 +0200)
committerIngo Bürk <ingo.buerk@tngtech.com>
Sun, 13 Sep 2015 18:40:20 +0000 (20:40 +0200)
This commit also reworks the way focusing sticky windows is prevented by not focusing them temporarily at all, but preventing the focus in the first place.

include/con.h
include/output.h
src/commands.c
src/con.c
src/floating.c
src/handlers.c
src/output.c
src/scratchpad.c
src/workspace.c
testcases/t/251-sticky.t

index c0a3a46bee13d0c631c37736d0f8c6bd9ba108b3..cf55978dd337f2fbcd7398c4544503f21aa42dee 100644 (file)
@@ -218,10 +218,14 @@ void con_disable_fullscreen(Con *con);
  * The dont_warp flag disables pointer warping and will be set when this
  * function is called while dragging a floating window.
  *
+ * If ignore_focus is set, the container will be moved without modifying focus
+ * at all.
+ *
  * TODO: is there a better place for this function?
  *
  */
-void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp);
+void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates,
+                           bool dont_warp, bool ignore_focus);
 
 /**
  * Moves the given container to the given mark.
index e0125c06f74540d7c6ef96e4d655dd52a139ecc9..38e2689add05eb529dce2465850f93bc490bd751 100644 (file)
@@ -21,3 +21,10 @@ Con *output_get_content(Con *output);
  *
  */
 Output *get_output_from_string(Output *current_output, const char *output_str);
+
+/**
+ * Iterates over all outputs and pushes sticky windows to the currently visible
+ * workspace on that output.
+ *
+ */
+void output_push_sticky_windows(void);
index c8164da60e918422565198a94356ab415a9e07d8..8189604722745a37810b21deac63fc27b1003dd4 100644 (file)
@@ -461,7 +461,7 @@ void cmd_move_con_to_workspace(I3_CMD, char *which) {
 
     TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
-        con_move_to_workspace(current->con, ws, true, false);
+        con_move_to_workspace(current->con, ws, true, false, false);
     }
 
     cmd_output->needs_tree_render = true;
@@ -488,7 +488,7 @@ void cmd_move_con_to_workspace_back_and_forth(I3_CMD) {
 
     TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
-        con_move_to_workspace(current->con, ws, true, false);
+        con_move_to_workspace(current->con, ws, true, false, false);
     }
 
     cmd_output->needs_tree_render = true;
@@ -532,7 +532,7 @@ void cmd_move_con_to_workspace_name(I3_CMD, char *name) {
 
     TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
-        con_move_to_workspace(current->con, ws, true, false);
+        con_move_to_workspace(current->con, ws, true, false, false);
     }
 
     cmd_output->needs_tree_render = true;
@@ -583,7 +583,7 @@ void cmd_move_con_to_workspace_number(I3_CMD, char *which) {
 
     TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
-        con_move_to_workspace(current->con, workspace, true, false);
+        con_move_to_workspace(current->con, workspace, true, false, false);
     }
 
     cmd_output->needs_tree_render = true;
@@ -1223,7 +1223,7 @@ void cmd_move_con_to_output(I3_CMD, char *name) {
 
     TAILQ_FOREACH(current, &owindows, owindows) {
         DLOG("matching: %p / %s\n", current->con, current->con->name);
-        con_move_to_workspace(current->con, ws, true, false);
+        con_move_to_workspace(current->con, ws, true, false, false);
     }
 
     cmd_output->needs_tree_render = true;
@@ -1597,6 +1597,11 @@ void cmd_sticky(I3_CMD, char *action) {
         ewmh_update_sticky(current->con->window->id, sticky);
     }
 
+    /* A window we made sticky might not be on a visible workspace right now, so we need to make
+     * sure it gets pushed to the front now. */
+    output_push_sticky_windows();
+
+    cmd_output->needs_tree_render = true;
     ysuccess(true);
 }
 
index 24d563a59c90620d1342403de0b0ddbdbf114f9c..7a8ccd5830f9a0ad519c85b9498f7c937fadecca 100644 (file)
--- a/src/con.c
+++ b/src/con.c
@@ -741,7 +741,7 @@ void con_disable_fullscreen(Con *con) {
     con_set_fullscreen_mode(con, CF_NONE);
 }
 
-static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fix_coordinates, bool dont_warp) {
+static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fix_coordinates, bool dont_warp, bool ignore_focus) {
     Con *orig_target = target;
 
     /* Prevent moving if this would violate the fullscreen focus restrictions. */
@@ -763,7 +763,7 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
         Con *child;
         while (!TAILQ_EMPTY(&(source_ws->floating_head))) {
             child = TAILQ_FIRST(&(source_ws->floating_head));
-            con_move_to_workspace(child, target_ws, true, true);
+            con_move_to_workspace(child, target_ws, true, true, false);
         }
 
         /* If there are no non-floating children, ignore the workspace. */
@@ -823,7 +823,7 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
         /* If moving to a visible workspace, call show so it can be considered
          * focused. Must do before attaching because workspace_show checks to see
          * if focused container is in its area. */
-        if (workspace_is_visible(target_ws)) {
+        if (!ignore_focus && workspace_is_visible(target_ws)) {
             workspace_show(target_ws);
 
             /* Don’t warp if told so (when dragging floating windows with the
@@ -858,8 +858,9 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
      * workspace, that is, don’t move focus away if the target workspace is
      * invisible.
      * We don’t focus the con for i3 pseudo workspaces like __i3_scratch and
-     * we don’t focus when there is a fullscreen con on that workspace. */
-    if (!con_is_internal(target_ws) && !fullscreen) {
+     * we don’t focus when there is a fullscreen con on that workspace. We
+     * also don't do it if the caller requested to ignore focus. */
+    if (!ignore_focus && !con_is_internal(target_ws) && !fullscreen) {
         /* We need to save the focused workspace on the output in case the
          * new workspace is hidden and it's necessary to immediately switch
          * back to the originally-focused workspace. */
@@ -877,11 +878,12 @@ static bool _con_move_to_con(Con *con, Con *target, bool behind_focused, bool fi
     /* Descend focus stack in case focus_next is a workspace which can
      * occur if we move to the same workspace.  Also show current workspace
      * to ensure it is focused. */
-    workspace_show(current_ws);
+    if (!ignore_focus)
+        workspace_show(current_ws);
 
     /* Set focus only if con was on current workspace before moving.
      * Otherwise we would give focus to some window on different workspace. */
-    if (source_ws == current_ws)
+    if (!ignore_focus && source_ws == current_ws)
         con_focus(con_descend_focused(focus_next));
 
     /* 8. If anything within the container is associated with a startup sequence,
@@ -942,7 +944,7 @@ bool con_move_to_mark(Con *con, const char *mark) {
     /* For floating target containers, we just send the window to the same workspace. */
     if (con_is_floating(target)) {
         DLOG("target container is floating, moving container to target's workspace.\n");
-        con_move_to_workspace(con, con_get_workspace(target), true, false);
+        con_move_to_workspace(con, con_get_workspace(target), true, false, false);
         return true;
     }
 
@@ -959,7 +961,7 @@ bool con_move_to_mark(Con *con, const char *mark) {
         return false;
     }
 
-    return _con_move_to_con(con, target, false, true, false);
+    return _con_move_to_con(con, target, false, true, false, false);
 }
 
 /*
@@ -976,10 +978,13 @@ bool con_move_to_mark(Con *con, const char *mark) {
  * The dont_warp flag disables pointer warping and will be set when this
  * function is called while dragging a floating window.
  *
+ * If ignore_focus is set, the container will be moved without modifying focus
+ * at all.
+ *
  * TODO: is there a better place for this function?
  *
  */
-void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp) {
+void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool dont_warp, bool ignore_focus) {
     assert(workspace->type == CT_WORKSPACE);
 
     Con *source_ws = con_get_workspace(con);
@@ -989,7 +994,7 @@ void con_move_to_workspace(Con *con, Con *workspace, bool fix_coordinates, bool
     }
 
     Con *target = con_descend_focused(workspace);
-    _con_move_to_con(con, target, true, fix_coordinates, dont_warp);
+    _con_move_to_con(con, target, true, fix_coordinates, dont_warp, ignore_focus);
 }
 
 /*
index 0a510f67fa3de0ba8dc6df8ed4e9cf77050552d2..a9da2c708d18d4fdbe97f188d0e0b862027547d8 100644 (file)
@@ -412,7 +412,7 @@ bool floating_maybe_reassign_ws(Con *con) {
     Con *content = output_get_content(output->con);
     Con *ws = TAILQ_FIRST(&(content->focus_head));
     DLOG("Moving con %p / %s to workspace %p / %s\n", con, con->name, ws, ws->name);
-    con_move_to_workspace(con, ws, false, true);
+    con_move_to_workspace(con, ws, false, true, false);
     con_focus(con_descend_focused(con));
     return true;
 }
index 4b01cb5ec68c3b7fb61de29306b5e54dc3d6c6d2..d0b663740148db666b2cf7b64b62eaf7eb3aa381 100644 (file)
@@ -736,6 +736,7 @@ static void handle_client_message(xcb_client_message_event_t *event) {
                 con->sticky = !con->sticky;
 
             DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
+            output_push_sticky_windows();
         }
 
         tree_render();
index ec5d5f4791643af05a96df7238a819f72a3dc571..e29ab7469123eb954c1388eb3e5cdd50bf2ec57f 100644 (file)
@@ -46,3 +46,38 @@ Output *get_output_from_string(Output *current_output, const char *output_str) {
 
     return output;
 }
+
+/*
+ * Iterates over all outputs and pushes sticky windows to the currently visible
+ * workspace on that output.
+ *
+ */
+void output_push_sticky_windows(void) {
+    Con *output;
+    TAILQ_FOREACH(output, &(croot->nodes_head), nodes) {
+        Con *workspace, *visible_ws = NULL;
+        GREP_FIRST(visible_ws, output_get_content(output), workspace_is_visible(child));
+
+        /* We use this loop instead of TAILQ_FOREACH to avoid problems if the
+         * sticky window was the last window on that workspace as moving it in
+         * this case will close the workspace. */
+        for (workspace = TAILQ_FIRST(&(output_get_content(output)->nodes_head));
+             workspace != TAILQ_END(&(output_get_content(output)->nodes_head));) {
+            Con *current_ws = workspace;
+            workspace = TAILQ_NEXT(workspace, nodes);
+
+            /* Since moving the windows actually removes them from the list of
+             * floating windows on this workspace, here too we need to use
+             * another loop than TAILQ_FOREACH. */
+            Con *child;
+            for (child = TAILQ_FIRST(&(current_ws->floating_head));
+                 child != TAILQ_END(&(current_ws->floating_head));) {
+                Con *current = child;
+                child = TAILQ_NEXT(child, floating_windows);
+
+                if (con_is_sticky(current))
+                    con_move_to_workspace(current, visible_ws, true, false, true);
+            }
+        }
+    }
+}
index 06a7cc739452c2f04869ae49a8cdf38ce5521167..6d83a558ccdc738e42941588536b45806ff82714 100644 (file)
@@ -60,7 +60,7 @@ void scratchpad_move(Con *con) {
 
     /* 2: Send the window to the __i3_scratch workspace, mainting its
      * coordinates and not warping the pointer. */
-    con_move_to_workspace(con, __i3_scratch, true, true);
+    con_move_to_workspace(con, __i3_scratch, true, true, false);
 
     /* 3: If this is the first time this window is used as a scratchpad, we set
      * the scratchpad_state to SCRATCHPAD_FRESH. The window will then be
@@ -142,7 +142,7 @@ void scratchpad_show(Con *con) {
             floating->scratchpad_state != SCRATCHPAD_NONE) {
             DLOG("Found a visible scratchpad window on another workspace,\n");
             DLOG("moving it to this workspace: con = %p\n", walk_con);
-            con_move_to_workspace(walk_con, focused_ws, true, false);
+            con_move_to_workspace(walk_con, focused_ws, true, false, false);
             return;
         }
     }
@@ -189,7 +189,7 @@ void scratchpad_show(Con *con) {
     }
 
     /* 1: Move the window from __i3_scratch to the current workspace. */
-    con_move_to_workspace(con, active, true, false);
+    con_move_to_workspace(con, active, true, false, false);
 
     /* 2: Adjust the size if this window was not adjusted yet. */
     if (con->scratchpad_state == SCRATCHPAD_FRESH) {
index 2b2c3cbf0c83c5f1422e52222457f5bc4c7b8841..04053e9091c6dcf6fc3a8454c302c684e144ca45 100644 (file)
@@ -451,44 +451,8 @@ static void _workspace_show(Con *workspace) {
     /* Update the EWMH hints */
     ewmh_update_current_desktop();
 
-    /* Floating containers which are sticky need to be moved to the new workspace,
-     * but only on the same output. */
-    if (current != NULL && old_output == new_output) {
-        Con *prev = focused;
-        Con *child;
-
-        /* We can't simply iterate over the floating containers since moving a
-         * sticky container to the target workspace will modify that list.
-         * Instead, we first count the number of sticky containers, then memorize
-         * all of them and finally loop over that list to move them to the new
-         * workspace. */
-        int num_sticky = 0;
-        TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
-            if (con_is_sticky(child))
-                num_sticky++;
-        }
-
-        Con *sticky_cons[num_sticky];
-        int ctr = 0;
-        TAILQ_FOREACH(child, &(current->floating_head), floating_windows) {
-            if (con_is_sticky(child))
-                sticky_cons[ctr++] = child;
-        }
-
-        for (int i = 0; i < num_sticky; i++) {
-            con_move_to_workspace(sticky_cons[i], workspace, true, false);
-
-            /* We want sticky containers to be at the end of the focus head. */
-            TAILQ_REMOVE(&(workspace->focus_head), sticky_cons[i], focused);
-            TAILQ_INSERT_TAIL(&(workspace->focus_head), sticky_cons[i], focused);
-        }
-
-        /* Focus the correct container since moving the sticky containers
-         * changed the focus. However, if no container was focused before,
-         * we can leave the focus at the sticky container. */
-        if (prev != croot)
-            con_focus(prev);
-    }
+    /* Push any sticky windows to the now visible workspace. */
+    output_push_sticky_windows();
 }
 
 /*
index ada8c9ac2fc33efb2c831fc5e92c9529232e57aa..6729556dd5e1196d8f3b17d8a3d1b2d7744fc6cc 100644 (file)
@@ -77,6 +77,20 @@ cmd 'workspace ' . $ws;
 is(get_focused($ws), $focused, 'the tiling container has focus');
 cmd '[class="findme"] kill';
 
+###############################################################################
+# 5: Given a floating container on a non-visible workspace, when the window
+#    is made sticky, then the window immediately jumps to the currently
+#    visible workspace.
+###############################################################################
+fresh_workspace;
+open_floating_window(wm_class => 'findme');
+cmd 'mark sticky';
+$ws = fresh_workspace;
+cmd '[con_mark=sticky] sticky enable';
+
+is(@{get_ws($ws)->{floating_nodes}}, 1, 'the sticky window jumps to the front');
+cmd '[class="findme"] kill';
+
 ###############################################################################
 
 done_testing;