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.
* 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.
*
*/
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);
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;
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;
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;
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;
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;
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);
}
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. */
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. */
/* 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
* 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. */
/* 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,
/* 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;
}
return false;
}
- return _con_move_to_con(con, target, false, true, false);
+ return _con_move_to_con(con, target, false, true, false, false);
}
/*
* 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);
}
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);
}
/*
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;
}
con->sticky = !con->sticky;
DLOG("New sticky status for con = %p is %i.\n", con, con->sticky);
+ output_push_sticky_windows();
}
tree_render();
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);
+ }
+ }
+ }
+}
/* 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
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;
}
}
}
/* 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) {
/* 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();
}
/*
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;