Make sure sticky windows pop to the front if they get sticky while not being on a visible workspace.
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.
This commit is contained in:
parent
23a1dadaae
commit
1c4c3f06fa
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
27
src/con.c
27
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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
35
src/output.c
35
src/output.c
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue