From 298ccb539c5f9d538e49364f45e57ff0d88df717 Mon Sep 17 00:00:00 2001 From: Jonathan Buch Date: Tue, 25 Sep 2018 10:22:50 +0200 Subject: [PATCH 1/6] Add configuration for raising containers on focus * New configuration option: raise_floating (From the discussion on https://github.com/i3/i3/issues/2990) * By default, it still raises the window on focus, otherwise it will raise the window on click. --- include/sway/commands.h | 1 + include/sway/config.h | 1 + sway/commands.c | 1 + sway/commands/raise_floating.c | 14 ++++++++++++++ sway/config.c | 1 + sway/input/seat.c | 15 ++++++++++++++- sway/meson.build | 1 + 7 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 sway/commands/raise_floating.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 64f707f4b..f7fafb966 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -125,6 +125,7 @@ sway_cmd cmd_floating_modifier; sway_cmd cmd_floating_scroll; sway_cmd cmd_focus; sway_cmd cmd_focus_follows_mouse; +sway_cmd cmd_raise_floating; sway_cmd cmd_focus_on_window_activation; sway_cmd cmd_focus_wrapping; sway_cmd cmd_font; diff --git a/include/sway/config.h b/include/sway/config.h index 98a18b76a..02ace979c 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -359,6 +359,7 @@ struct sway_config { // Flags bool focus_follows_mouse; + bool raise_floating; bool mouse_warping; enum focus_wrapping_mode focus_wrapping; bool active; diff --git a/sway/commands.c b/sway/commands.c index 72db8ab9d..5dd27f7ec 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -107,6 +107,7 @@ static struct cmd_handler handlers[] = { { "new_window", cmd_default_border }, { "no_focus", cmd_no_focus }, { "output", cmd_output }, + { "raise_floating", cmd_raise_floating }, { "seat", cmd_seat }, { "set", cmd_set }, { "show_marks", cmd_show_marks }, diff --git a/sway/commands/raise_floating.c b/sway/commands/raise_floating.c new file mode 100644 index 000000000..930299a12 --- /dev/null +++ b/sway/commands/raise_floating.c @@ -0,0 +1,14 @@ +#include +#include +#include "sway/commands.h" +#include "util.h" + +struct cmd_results *cmd_raise_floating(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "raise_floating", EXPECTED_EQUAL_TO, 1))) { + return error; + } + config->raise_floating = + parse_boolean(argv[0], config->raise_floating); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} diff --git a/sway/config.c b/sway/config.c index 048b57de4..b56c4f71d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -221,6 +221,7 @@ static void config_defaults(struct sway_config *config) { // Flags config->focus_follows_mouse = true; + config->raise_floating = true; config->mouse_warping = true; config->focus_wrapping = WRAP_YES; config->validating = false; diff --git a/sway/input/seat.c b/sway/input/seat.c index 69bee47ec..c747eafc1 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -734,7 +734,7 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, // If we've focused a floating container, bring it to the front. // We do this by putting it at the end of the floating list. - if (container) { + if (container && config->raise_floating) { struct sway_container *floater = container; while (floater->parent) { floater = floater->parent; @@ -1017,6 +1017,19 @@ void seat_begin_down(struct sway_seat *seat, struct sway_container *con, seat->op_ref_con_lx = sx; seat->op_ref_con_ly = sy; seat->op_moved = false; + + // If we've focused a floating container, bring it to the front. + // We do this by putting it at the end of the floating list. + if (con && !config->raise_floating) { + struct sway_container *floater = con; + while (floater->parent) { + floater = floater->parent; + } + if (container_is_floating(floater)) { + list_move_to_end(floater->workspace->floating, floater); + node_set_dirty(&floater->workspace->node); + } + } } void seat_begin_move_floating(struct sway_seat *seat, diff --git a/sway/meson.build b/sway/meson.build index 00ebcb40d..8ab288695 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -48,6 +48,7 @@ sway_sources = files( 'commands/floating_modifier.c', 'commands/focus.c', 'commands/focus_follows_mouse.c', + 'commands/raise_floating.c', 'commands/focus_on_window_activation.c', 'commands/focus_wrapping.c', 'commands/font.c', From ec713125c6593b3671dc54e41b90333b42783dff Mon Sep 17 00:00:00 2001 From: Jonathan Buch Date: Tue, 25 Sep 2018 10:29:58 +0200 Subject: [PATCH 2/6] Simplify raising a container in seat * Factor out raising a floating window into s separate function to enable reuse. --- sway/input/seat.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index c747eafc1..15c56a43e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -606,6 +606,18 @@ static int handle_urgent_timeout(void *data) { return 0; } +static void container_raise_floating(struct sway_container *con) { + // Bring container to front by putting it at the end of the floating list. + struct sway_container *floater = con; + while (floater->parent) { + floater = floater->parent; + } + if (container_is_floating(floater)) { + list_move_to_end(floater->workspace->floating, floater); + node_set_dirty(&floater->workspace->node); + } +} + void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, bool warp, bool notify) { if (seat->focused_layer) { @@ -733,16 +745,8 @@ void seat_set_focus_warp(struct sway_seat *seat, struct sway_node *node, } // If we've focused a floating container, bring it to the front. - // We do this by putting it at the end of the floating list. if (container && config->raise_floating) { - struct sway_container *floater = container; - while (floater->parent) { - floater = floater->parent; - } - if (container_is_floating(floater)) { - list_move_to_end(floater->workspace->floating, floater); - node_set_dirty(&floater->workspace->node); - } + container_raise_floating(container); } if (last_focus) { @@ -1018,17 +1022,9 @@ void seat_begin_down(struct sway_seat *seat, struct sway_container *con, seat->op_ref_con_ly = sy; seat->op_moved = false; - // If we've focused a floating container, bring it to the front. - // We do this by putting it at the end of the floating list. + // In case the container was not raised by gaining focus, raise on click if (con && !config->raise_floating) { - struct sway_container *floater = con; - while (floater->parent) { - floater = floater->parent; - } - if (container_is_floating(floater)) { - list_move_to_end(floater->workspace->floating, floater); - node_set_dirty(&floater->workspace->node); - } + container_raise_floating(con); } } From 8bec0c90c71439027cec6d35438e3660196e5584 Mon Sep 17 00:00:00 2001 From: Jonathan Buch Date: Tue, 25 Sep 2018 14:57:47 +0200 Subject: [PATCH 3/6] Add manpage documentatioon for raise_floating --- sway/sway.5.scd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 3202d5176..3fda6cefc 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -441,6 +441,11 @@ The default colors are: devices. A list of input device names may be obtained via *swaymsg -t get\_inputs*. +*raise\_floating* yes|no + Controls the behaviour of floating windows. A _yes_ (the default) will + raise windows on gaining focus. A _no_ will only raise floating windows + by clicking anywhere in the window. + *seat* For details on seat subcommands, see *sway-input*(5). From 7727d54faf2939e30f82da562de83dbcda1749db Mon Sep 17 00:00:00 2001 From: Jonathan Buch Date: Thu, 27 Sep 2018 18:32:55 +0200 Subject: [PATCH 4/6] Fix focusing topmost floating windows Re-focus on the container on which the cursor hovers over. A special case is, if there are menus or other subsurfaces open in the focused container. It will prefer the focused container as long as there are subsurfaces. This commit starts caching the previous node as well as the previous x/y cursor position. Re-calculating the previous focused node by looking at the current state of the cursor position does not work, if the environment changes. --- include/sway/input/cursor.h | 1 + sway/input/cursor.c | 10 +++++----- sway/tree/container.c | 7 +++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/include/sway/input/cursor.h b/include/sway/input/cursor.h index 7ec451203..4d47ab429 100644 --- a/include/sway/input/cursor.h +++ b/include/sway/input/cursor.h @@ -10,6 +10,7 @@ struct sway_cursor { struct wlr_cursor *cursor; struct { double x, y; + struct sway_node *node; } previous; struct wlr_xcursor_manager *xcursor_manager; diff --git a/sway/input/cursor.c b/sway/input/cursor.c index afad6f6f0..3c62acb92 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -567,15 +567,15 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec, struct wlr_surface *surface = NULL; double sx, sy; - // Find the node beneath the pointer's previous position - struct sway_node *prev_node = node_at_coords(seat, - cursor->previous.x, cursor->previous.y, &surface, &sx, &sy); + struct sway_node *prev_node = cursor->previous.node; + struct sway_node *node = node_at_coords(seat, + cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); + // Update the stored previous position cursor->previous.x = cursor->cursor->x; cursor->previous.y = cursor->cursor->y; + cursor->previous.node = node; - struct sway_node *node = node_at_coords(seat, - cursor->cursor->x, cursor->cursor->y, &surface, &sx, &sy); if (node && config->focus_follows_mouse && allow_refocusing) { struct sway_node *focus = seat_get_focus(seat); if (focus && node->type == N_WORKSPACE) { diff --git a/sway/tree/container.c b/sway/tree/container.c index a069b1775..f9ddf3d65 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -369,6 +369,13 @@ struct sway_container *container_at(struct sway_workspace *workspace, } // If focused is floating, focused view's non-popups if (focus && focus->view && is_floating) { + // only switch to unfocused container if focused container has no menus open + bool has_subsurfaces = wl_list_length(&focus->view->surface->subsurfaces) > 0; + c = floating_container_at(lx, ly, surface, sx, sy); + if (!has_subsurfaces && c && c->view && *surface && c != focus) { + return c; + } + surface_at_view(focus, lx, ly, surface, sx, sy); if (*surface) { return focus; From 7e978d7a4c521a98b9f44274cd65791b06266ed3 Mon Sep 17 00:00:00 2001 From: Jonathan Buch Date: Wed, 3 Oct 2018 11:05:30 +0200 Subject: [PATCH 5/6] Use "raycasting" for determining focus for floating windows Floating containers and their surfaces are ordered in "raised last". This is used to detect the topmost surface and thus the focus. --- sway/tree/container.c | 50 ++++++++++--------------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index f9ddf3d65..f069a9e96 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -151,10 +151,10 @@ struct sway_container *container_find_child(struct sway_container *container, return NULL; } -static void surface_at_view(struct sway_container *con, double lx, double ly, +static struct sway_container *surface_at_view(struct sway_container *con, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { if (!sway_assert(con->view, "Expected a view")) { - return; + return NULL; } struct sway_view *view = con->view; double view_sx = lx - view->x + view->geometry.x; @@ -184,7 +184,9 @@ static void surface_at_view(struct sway_container *con, double lx, double ly, *sx = _sx; *sy = _sy; *surface = _surface; + return con; } + return NULL; } /** @@ -354,46 +356,16 @@ static bool surface_is_popup(struct wlr_surface *surface) { struct sway_container *container_at(struct sway_workspace *workspace, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - struct sway_container *c; - // Focused view's popups - struct sway_seat *seat = input_manager_current_seat(input_manager); - struct sway_container *focus = seat_get_focused_container(seat); - bool is_floating = focus && container_is_floating_or_child(focus); - // Focused view's popups - if (focus && focus->view) { - surface_at_view(focus, lx, ly, surface, sx, sy); - if (*surface && surface_is_popup(*surface)) { - return focus; - } - *surface = NULL; - } - // If focused is floating, focused view's non-popups - if (focus && focus->view && is_floating) { - // only switch to unfocused container if focused container has no menus open - bool has_subsurfaces = wl_list_length(&focus->view->surface->subsurfaces) > 0; - c = floating_container_at(lx, ly, surface, sx, sy); - if (!has_subsurfaces && c && c->view && *surface && c != focus) { + struct sway_container *c = NULL; + + // First cast a ray to handle floating windows + for (int i = workspace->floating->length - 1; i >= 0; --i) { + struct sway_container *cn = workspace->floating->items[i]; + if (cn->view && (c = surface_at_view(cn, lx, ly, surface, sx, sy))) { return c; } + } - surface_at_view(focus, lx, ly, surface, sx, sy); - if (*surface) { - return focus; - } - *surface = NULL; - } - // Floating (non-focused) - if ((c = floating_container_at(lx, ly, surface, sx, sy))) { - return c; - } - // If focused is tiling, focused view's non-popups - if (focus && focus->view && !is_floating) { - surface_at_view(focus, lx, ly, surface, sx, sy); - if (*surface) { - return focus; - } - *surface = NULL; - } // Tiling (non-focused) if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { return c; From 426c33f4dc2515867a0d3b04cb865d5cad091d10 Mon Sep 17 00:00:00 2001 From: Jonathan Buch Date: Wed, 3 Oct 2018 11:26:46 +0200 Subject: [PATCH 6/6] Reenable popup-handling for determining focus This reenables the popup-handling code before the floating-window focus change. --- sway/tree/container.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index f069a9e96..9db7aed1b 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -356,16 +356,33 @@ static bool surface_is_popup(struct wlr_surface *surface) { struct sway_container *container_at(struct sway_workspace *workspace, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { - struct sway_container *c = NULL; + struct sway_container *c; - // First cast a ray to handle floating windows + // Focused view's popups + struct sway_seat *seat = input_manager_current_seat(input_manager); + struct sway_container *focus = seat_get_focused_container(seat); + bool is_floating = focus && container_is_floating_or_child(focus); + // Focused view's popups + if (focus && focus->view) { + c = surface_at_view(focus, lx, ly, surface, sx, sy); + if (c && surface_is_popup(*surface)) { + return c; + } + *surface = NULL; + } + // Cast a ray to handle floating windows for (int i = workspace->floating->length - 1; i >= 0; --i) { struct sway_container *cn = workspace->floating->items[i]; if (cn->view && (c = surface_at_view(cn, lx, ly, surface, sx, sy))) { return c; } } - + // If focused is tiling, focused view's non-popups + if (focus && focus->view && !is_floating) { + if ((c = surface_at_view(focus, lx, ly, surface, sx, sy))) { + return c; + } + } // Tiling (non-focused) if ((c = tiling_container_at(&workspace->node, lx, ly, surface, sx, sy))) { return c;