From 88b2abf5f208422ffc503c2c1d2a0de95d9ec4c5 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 8 Feb 2024 17:05:22 +0100 Subject: [PATCH 01/12] config: use format_str() instead of hand-rolled snprintf() --- sway/config.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/sway/config.c b/sway/config.c index d7b6b693..e91c0c8b 100644 --- a/sway/config.c +++ b/sway/config.c @@ -352,13 +352,7 @@ static char *config_path(const char *prefix, const char *config_folder) { if (!prefix || !prefix[0] || !config_folder || !config_folder[0]) { return NULL; } - - const char *filename = "config"; - - size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename); - char *path = calloc(size, sizeof(char)); - snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename); - return path; + return format_str("%s/%s/config", prefix, config_folder); } static char *get_config_path(void) { @@ -368,10 +362,7 @@ static char *get_config_path(void) { const char *config_home = getenv("XDG_CONFIG_HOME"); if ((config_home == NULL || config_home[0] == '\0') && home != NULL) { - size_t size_fallback = 1 + strlen(home) + strlen("/.config"); - config_home_fallback = calloc(size_fallback, sizeof(char)); - if (config_home_fallback != NULL) - snprintf(config_home_fallback, size_fallback, "%s/.config", home); + config_home_fallback = format_str("%s/.config", home); config_home = config_home_fallback; } From 1b5515400d0e10c8e1205b88cfc0e46ecb5faa96 Mon Sep 17 00:00:00 2001 From: Merlin Lex Date: Sun, 11 Feb 2024 12:20:15 +0100 Subject: [PATCH 02/12] ext-foreign-toplevel-list: Implement protocol References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4543 --- include/sway/server.h | 2 ++ include/sway/tree/view.h | 5 +++++ sway/desktop/xdg_shell.c | 1 + sway/server.c | 4 ++++ sway/tree/view.c | 42 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+) diff --git a/include/sway/server.h b/include/sway/server.h index adb62cda..7dbce0b7 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -116,6 +117,7 @@ struct sway_server { struct wl_listener output_power_manager_set_mode; struct wlr_input_method_manager_v2 *input_method; struct wlr_text_input_manager_v3 *text_input; + struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list; struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_manager; struct wlr_content_type_manager_v1 *content_type_manager_v1; struct wlr_data_control_manager_v1 *data_control_manager_v1; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 3e5a9bfe..5f6c2ead 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -84,6 +84,9 @@ struct sway_view { // transaction state. Updated on every commit. struct wlr_box geometry; + struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; + struct wl_listener ext_foreign_destroy; + struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; struct wl_listener foreign_fullscreen_request; @@ -284,6 +287,8 @@ struct sway_view *view_from_wlr_xwayland_surface( #endif struct sway_view *view_from_wlr_surface(struct wlr_surface *surface); +void view_update_app_id(struct sway_view *view); + /** * Re-read the view's title property and update any relevant title bars. * The force argument makes it recreate the title bars even if the title hasn't diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 7cdd97c8..d3f69a15 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -337,6 +337,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, set_app_id); struct sway_view *view = &xdg_shell_view->view; + view_update_app_id(view); view_execute_criteria(view); } diff --git a/sway/server.c b/sway/server.c index cc20e89d..8b8ccce9 100644 --- a/sway/server.c +++ b/sway/server.c @@ -60,6 +60,7 @@ #define SWAY_XDG_SHELL_VERSION 2 #define SWAY_LAYER_SHELL_VERSION 4 +#define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 bool allow_unsupported_gpu = false; @@ -93,6 +94,7 @@ static bool is_privileged(const struct wl_global *global) { global == server.output_manager_v1->global || global == server.output_power_manager_v1->global || global == server.input_method->global || + global == server.foreign_toplevel_list->global || global == server.foreign_toplevel_manager->global || global == server.data_control_manager_v1->global || global == server.screencopy_manager_v1->global || @@ -289,6 +291,8 @@ bool server_init(struct sway_server *server) { &server->output_power_manager_set_mode); server->input_method = wlr_input_method_manager_v2_create(server->wl_display); server->text_input = wlr_text_input_manager_v3_create(server->wl_display); + server->foreign_toplevel_list = + wlr_ext_foreign_toplevel_list_v1_create(server->wl_display, SWAY_FOREIGN_TOPLEVEL_LIST_VERSION); server->foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server->wl_display); diff --git a/sway/tree/view.c b/sway/tree/view.c index d6984178..aa87a0bb 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -582,6 +582,14 @@ static struct sway_workspace *select_workspace(struct sway_view *view) { return NULL; } +static void update_ext_foreign_toplevel(struct sway_view *view) { + struct wlr_ext_foreign_toplevel_handle_v1_state toplevel_state = { + .app_id = view_get_app_id(view), + .title = view_get_title(view), + }; + wlr_ext_foreign_toplevel_handle_v1_update_state(view->ext_foreign_toplevel, &toplevel_state); +} + static bool should_focus(struct sway_view *view) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *prev_con = seat_get_focused_container(seat); @@ -751,6 +759,15 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, } } + struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = { + .app_id = view_get_app_id(view), + .title = view_get_title(view), + }; + view->ext_foreign_toplevel = + wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); + wl_signal_add(&view->ext_foreign_toplevel->events.destroy, + &view->ext_foreign_destroy); + view->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); view->foreign_activate_request.notify = handle_foreign_activate_request; @@ -828,6 +845,10 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, input_manager_set_focus(&view->container->node); } + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); + } + const char *app_id; const char *class; if ((app_id = view_get_app_id(view)) != NULL) { @@ -847,6 +868,11 @@ void view_unmap(struct sway_view *view) { view->urgent_timer = NULL; } + if (view->ext_foreign_toplevel) { + wlr_ext_foreign_toplevel_handle_v1_destroy(view->ext_foreign_toplevel); + view->ext_foreign_toplevel = NULL; + } + if (view->foreign_toplevel) { wlr_foreign_toplevel_handle_v1_destroy(view->foreign_toplevel); view->foreign_toplevel = NULL; @@ -1014,6 +1040,18 @@ static size_t parse_title_format(struct sway_view *view, char *buffer) { return len; } +void view_update_app_id(struct sway_view *view) { + const char *app_id = view_get_app_id(view); + + if (view->foreign_toplevel && app_id) { + wlr_foreign_toplevel_handle_v1_set_app_id(view->foreign_toplevel, app_id); + } + + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); + } +} + void view_update_title(struct sway_view *view, bool force) { const char *title = view_get_title(view); @@ -1060,6 +1098,10 @@ void view_update_title(struct sway_view *view, bool force) { if (view->foreign_toplevel && title) { wlr_foreign_toplevel_handle_v1_set_title(view->foreign_toplevel, title); } + + if (view->ext_foreign_toplevel) { + update_ext_foreign_toplevel(view); + } } bool view_is_visible(struct sway_view *view) { From 1dc661af176d80db8b283e9c38fd127f71d33052 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 23 Jan 2024 10:10:42 -0500 Subject: [PATCH 03/12] layer_shell: Arrange popups even if exclusive zone doesn't change --- include/sway/desktop/transaction.h | 3 +++ sway/desktop/layer_shell.c | 2 ++ sway/desktop/transaction.c | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/include/sway/desktop/transaction.h b/include/sway/desktop/transaction.h index 17d41fa3..dd7edb7a 100644 --- a/include/sway/desktop/transaction.h +++ b/include/sway/desktop/transaction.h @@ -2,6 +2,7 @@ #define _SWAY_TRANSACTION_H #include #include +#include /** * Transactions enable us to perform atomic layout updates. @@ -58,4 +59,6 @@ bool transaction_notify_view_ready_by_serial(struct sway_view *view, bool transaction_notify_view_ready_by_geometry(struct sway_view *view, double x, double y, int width, int height); +void arrange_popups(struct wlr_scene_tree *popups); + #endif diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index c71abce7..9a35ef95 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -85,6 +85,8 @@ void arrange_layers(struct sway_output *output) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); output->usable_area = usable_area; arrange_output(output); + } else { + arrange_popups(root->layers.popup); } } diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index acc3e3f9..c1a988ca 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -606,9 +606,9 @@ static void arrange_output(struct sway_output *output, int width, int height) { } } -static void arrange_popup(struct wlr_scene_tree *popup) { +void arrange_popups(struct wlr_scene_tree *popups) { struct wlr_scene_node *node; - wl_list_for_each(node, &popup->children, link) { + wl_list_for_each(node, &popups->children, link) { struct sway_xdg_popup *popup = scene_descriptor_try_get(node, SWAY_SCENE_DESC_POPUP); @@ -679,7 +679,7 @@ static void arrange_root(struct sway_root *root) { } } - arrange_popup(root->layers.popup); + arrange_popups(root->layers.popup); } /** From 1846944f0454fec08ad91a04ab04e3b1ffcd1764 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 23 Jan 2024 10:17:26 -0500 Subject: [PATCH 04/12] xdg_shell: Extract struct for popup descriptor --- include/sway/tree/view.h | 7 +++++++ sway/desktop/transaction.c | 8 +++----- sway/desktop/xdg_shell.c | 5 ++++- sway/input/cursor.c | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 5f6c2ead..8f3626fd 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -177,6 +177,11 @@ struct sway_xwayland_unmanaged { }; #endif +struct sway_popup_desc { + struct wlr_scene_node *relative; + struct sway_view *view; +}; + struct sway_xdg_popup { struct sway_view *view; @@ -184,6 +189,8 @@ struct sway_xdg_popup { struct wlr_scene_tree *xdg_surface_tree; struct wlr_xdg_popup *wlr_xdg_popup; + struct sway_popup_desc desc; + struct wl_listener surface_commit; struct wl_listener new_popup; struct wl_listener destroy; diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index c1a988ca..e3196e3a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -609,17 +609,15 @@ static void arrange_output(struct sway_output *output, int width, int height) { void arrange_popups(struct wlr_scene_tree *popups) { struct wlr_scene_node *node; wl_list_for_each(node, &popups->children, link) { - struct sway_xdg_popup *popup = scene_descriptor_try_get(node, + struct sway_popup_desc *popup = scene_descriptor_try_get(node, SWAY_SCENE_DESC_POPUP); // the popup layer may have popups from layer_shell surfaces, in this // case those don't have a scene descriptor, so lets skip those here. if (popup) { - struct wlr_scene_tree *tree = popup->view->content_tree; - int lx, ly; - wlr_scene_node_coords(&tree->node, &lx, &ly); - wlr_scene_node_set_position(&popup->scene_tree->node, lx, ly); + wlr_scene_node_coords(popup->relative, &lx, &ly); + wlr_scene_node_set_position(node, lx, ly); } } } diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index d3f69a15..f4a25cf4 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -97,8 +97,11 @@ static struct sway_xdg_popup *popup_create(struct wlr_xdg_popup *wlr_popup, return NULL; } + popup->desc.relative = &view->content_tree->node; + popup->desc.view = view; + if (!scene_descriptor_assign(&popup->scene_tree->node, - SWAY_SCENE_DESC_POPUP, popup)) { + SWAY_SCENE_DESC_POPUP, &popup->desc)) { sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); wlr_scene_node_destroy(&popup->scene_tree->node); free(popup); diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 404c1eed..f25439cb 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -90,7 +90,7 @@ struct sway_node *node_at_coords( } if (!con) { - struct sway_xdg_popup *popup = + struct sway_popup_desc *popup = scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); if (popup) { con = popup->view->container; From 09c360d503eb7309de46af3745001df7dd911438 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 23 Jan 2024 10:17:37 -0500 Subject: [PATCH 05/12] layer_shell: Handle popups through popup descriptor We tried to synchronize layer shell popups with the parent layer shell on commits, but this is subtly wrong because we would only update the position for one layer shell that was committed, but not any other layer that might be affected. By moving handling to the scene descriptor we can iterate all popups and ensure they are synchronized. --- include/sway/layers.h | 5 ++++- sway/desktop/layer_shell.c | 14 ++++++++++---- sway/desktop/transaction.c | 10 +++------- sway/input/cursor.c | 2 +- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/include/sway/layers.h b/include/sway/layers.h index a7afb900..fd6384e0 100644 --- a/include/sway/layers.h +++ b/include/sway/layers.h @@ -3,6 +3,7 @@ #include #include #include +#include "sway/tree/view.h" struct sway_layer_surface { struct wl_listener map; @@ -14,10 +15,12 @@ struct sway_layer_surface { bool mapped; + struct wlr_scene_tree *popups; + struct sway_popup_desc desc; + struct sway_output *output; struct wlr_scene_layer_surface_v1 *scene; struct wlr_scene_tree *tree; - struct wlr_scene_tree *popups; struct wlr_layer_surface_v1 *layer_surface; }; diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 9a35ef95..769d3a86 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -122,6 +122,16 @@ static struct sway_layer_surface *sway_layer_surface_create( return NULL; } + surface->desc.relative = &scene->tree->node; + + if (!scene_descriptor_assign(&popups->node, + SWAY_SCENE_DESC_POPUP, &surface->desc)) { + sway_log(SWAY_ERROR, "Failed to allocate a popup scene descriptor"); + wlr_scene_node_destroy(&popups->node); + free(surface); + return NULL; + } + surface->tree = scene->tree; surface->scene = scene; surface->layer_surface = scene->layer_surface; @@ -224,10 +234,6 @@ static void handle_surface_commit(struct wl_listener *listener, void *data) { arrange_layers(surface->output); transaction_commit_dirty(); } - - int lx, ly; - wlr_scene_node_coords(&surface->scene->tree->node, &lx, &ly); - wlr_scene_node_set_position(&surface->popups->node, lx, ly); } static void handle_map(struct wl_listener *listener, void *data) { diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index e3196e3a..fd1c3d3a 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -612,13 +612,9 @@ void arrange_popups(struct wlr_scene_tree *popups) { struct sway_popup_desc *popup = scene_descriptor_try_get(node, SWAY_SCENE_DESC_POPUP); - // the popup layer may have popups from layer_shell surfaces, in this - // case those don't have a scene descriptor, so lets skip those here. - if (popup) { - int lx, ly; - wlr_scene_node_coords(popup->relative, &lx, &ly); - wlr_scene_node_set_position(node, lx, ly); - } + int lx, ly; + wlr_scene_node_coords(popup->relative, &lx, &ly); + wlr_scene_node_set_position(node, lx, ly); } } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index f25439cb..25fa603e 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -92,7 +92,7 @@ struct sway_node *node_at_coords( if (!con) { struct sway_popup_desc *popup = scene_descriptor_try_get(current, SWAY_SCENE_DESC_POPUP); - if (popup) { + if (popup && popup->view) { con = popup->view->container; } } From 7a2ff7ba810ced55297a1a2e420b0b1f4326ab72 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Mon, 12 Feb 2024 19:16:49 +0300 Subject: [PATCH 06/12] view: drop ext_foreign_destroy It's not used and causes a crash when a view is destroyed. --- include/sway/tree/view.h | 1 - sway/tree/view.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 8f3626fd..8bfda018 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -85,7 +85,6 @@ struct sway_view { struct wlr_box geometry; struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel; - struct wl_listener ext_foreign_destroy; struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; struct wl_listener foreign_activate_request; diff --git a/sway/tree/view.c b/sway/tree/view.c index aa87a0bb..a3ed0af1 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -765,8 +765,6 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, }; view->ext_foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create(server.foreign_toplevel_list, &foreign_toplevel_state); - wl_signal_add(&view->ext_foreign_toplevel->events.destroy, - &view->ext_foreign_destroy); view->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(server.foreign_toplevel_manager); From ca40663d4277ade2a7dfec0484db5b7881799ee4 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 15 Feb 2024 11:52:38 +0100 Subject: [PATCH 07/12] Fix build with wlroots DRM backend disabled The header is not installed by wlroots when the DRM backend is disabled. We don't need it here, so don't include it. Closes: https://github.com/swaywm/sway/issues/7943 --- include/sway/server.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/sway/server.h b/include/sway/server.h index 7dbce0b7..b047e20d 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From 541e6e260c2df0346b421cacfaf4ce5d204c49ba Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 15 Feb 2024 12:01:24 +0100 Subject: [PATCH 08/12] Drop unnecessary includes from sway/server.h --- include/sway/server.h | 17 ----------------- sway/desktop/layer_shell.c | 3 ++- sway/desktop/output.c | 2 ++ sway/input/cursor.c | 1 + sway/lock.c | 1 + sway/server.c | 8 ++++++++ sway/tree/container.c | 1 + sway/tree/view.c | 2 ++ sway/xdg_activation_v1.c | 1 + 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index b047e20d..b2eb2c36 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -2,23 +2,6 @@ #define _SWAY_SERVER_H #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "config.h" #include "list.h" #include "sway/desktop/idle_inhibit_v1.h" diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 769d3a86..f0134396 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -4,7 +4,9 @@ #include #include #include +#include #include +#include #include "log.h" #include "sway/scene_descriptor.h" #include "sway/desktop/transaction.h" @@ -16,7 +18,6 @@ #include "sway/server.h" #include "sway/tree/arrange.h" #include "sway/tree/workspace.h" -#include struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( struct wlr_surface *surface) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9c4baafd..81290ccb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 25fa603e..b00f81a9 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include diff --git a/sway/lock.c b/sway/lock.c index 8ad9c3f6..d267de4f 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include #include "log.h" #include "sway/input/cursor.h" #include "sway/input/keyboard.h" diff --git a/sway/server.c b/sway/server.c index 8b8ccce9..5b389f69 100644 --- a/sway/server.c +++ b/sway/server.c @@ -8,24 +8,32 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include +#include +#include #include #include #include #include #include +#include +#include #include +#include #include #include #include #include #include +#include #include #include #include diff --git a/sway/tree/container.c b/sway/tree/container.c index 30cb97ba..78448090 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/sway/tree/view.c b/sway/tree/view.c index a3ed0af1..5525bf63 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index c26ee19a..47270f73 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -1,4 +1,5 @@ #include +#include #include "sway/desktop/launcher.h" #include "sway/tree/view.h" #include "sway/tree/workspace.h" From f6d22f8e6886edfeca3ecbb695b02079e81ce360 Mon Sep 17 00:00:00 2001 From: Ronan Pigott Date: Wed, 7 Feb 2024 16:13:25 -0700 Subject: [PATCH 09/12] launcher: track the seat in the launcher ctx This is a more suitable place to track the requesting seat, since we are able to respond appropriately to destroy notifications. --- include/sway/desktop/launcher.h | 3 +++ sway/desktop/launcher.c | 13 ++++++++++++- sway/xdg_activation_v1.c | 21 ++++++++++++++++----- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h index b7716e82..ad465a9e 100644 --- a/include/sway/desktop/launcher.h +++ b/include/sway/desktop/launcher.h @@ -3,12 +3,15 @@ #include #include +#include "sway/input/seat.h" struct launcher_ctx { pid_t pid; char *fallback_name; struct wlr_xdg_activation_token_v1 *token; struct wl_listener token_destroy; + struct sway_seat *seat; + struct wl_listener seat_destroy; bool activated; diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 00a7e38a..4a4255d7 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -67,6 +67,9 @@ void launcher_ctx_destroy(struct launcher_ctx *ctx) { } wl_list_remove(&ctx->node_destroy.link); wl_list_remove(&ctx->token_destroy.link); + if (ctx->seat) { + wl_list_remove(&ctx->seat_destroy.link); + } wl_list_remove(&ctx->link); wlr_xdg_activation_token_v1_destroy(ctx->token); free(ctx->fallback_name); @@ -227,6 +230,12 @@ struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *tok return ctx; } +static void launch_ctx_handle_seat_destroy(struct wl_listener *listener, void *data) { + struct launcher_ctx *ctx = wl_container_of(listener, ctx, seat_destroy); + ctx->seat = NULL; + wl_list_remove(&ctx->seat_destroy.link); +} + // Creates a context with a new token for the internal launcher struct launcher_ctx *launcher_ctx_create_internal(void) { struct sway_seat *seat = input_manager_current_seat(); @@ -238,13 +247,15 @@ struct launcher_ctx *launcher_ctx_create_internal(void) { struct wlr_xdg_activation_token_v1 *token = wlr_xdg_activation_token_v1_create(server.xdg_activation_v1); - token->seat = seat->wlr_seat; struct launcher_ctx *ctx = launcher_ctx_create(token, &ws->node); if (!ctx) { wlr_xdg_activation_token_v1_destroy(token); return NULL; } + ctx->seat = seat; + ctx->seat_destroy.notify = launch_ctx_handle_seat_destroy; + wl_signal_add(&seat->wlr_seat->events.destroy, &ctx->seat_destroy); return ctx; } diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 47270f73..72c7fa4c 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -18,11 +18,15 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, return; } + struct launcher_ctx *ctx = event->token->data; + if (ctx == NULL) { + return; + } + if (!xdg_surface->surface->mapped) { // This is a startup notification. If we are tracking it, the data // field is a launcher_ctx. - struct launcher_ctx *ctx = event->token->data; - if (!ctx || ctx->activated) { + if (ctx->activated) { // This ctx has already been activated and cannot be used again // for a startup notification. It will be destroyed return; @@ -33,9 +37,16 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, return; } - struct wlr_seat *wlr_seat = event->token->seat; - struct sway_seat *seat = wlr_seat ? wlr_seat->data : NULL; - view_request_activate(view, seat); + // This is an activation request. If this context is internal we have ctx->seat. + struct sway_seat *seat = ctx->seat; + if (!seat) { + // Otherwise, use the seat indicated by the launcher client in set_serial + seat = ctx->token->seat ? ctx->token->seat->data : NULL; + } + + if (seat) { + view_request_activate(view, seat); + } } void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void *data) { From d19810eba8959f052d91fd6609cef6adf36b3951 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Tue, 26 Dec 2023 22:26:02 -0800 Subject: [PATCH 10/12] xdg-activation: distinguish activation and urgency requests Check if the app that requested a token has provided a valid input serial and a focused surface. Downgrade activation request to urgency otherwise. This is mostly in line with what other Wayland compositors decided to do, and offers a better security than the original logic. --- include/sway/desktop/launcher.h | 1 + include/sway/tree/view.h | 5 +++++ sway/desktop/launcher.c | 2 ++ sway/tree/view.c | 6 ++++++ sway/xdg_activation_v1.c | 5 ++++- 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/sway/desktop/launcher.h b/include/sway/desktop/launcher.h index ad465a9e..412068a9 100644 --- a/include/sway/desktop/launcher.h +++ b/include/sway/desktop/launcher.h @@ -14,6 +14,7 @@ struct launcher_ctx { struct wl_listener seat_destroy; bool activated; + bool had_focused_surface; struct sway_node *node; struct wl_listener node_destroy; diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 8bfda018..ef1a26b8 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -242,6 +242,11 @@ void view_set_activated(struct sway_view *view, bool activated); */ void view_request_activate(struct sway_view *view, struct sway_seat *seat); +/* + * Called when the view requests urgent state + */ +void view_request_urgent(struct sway_view *view); + /** * If possible, instructs the client to change their decoration mode. */ diff --git a/sway/desktop/launcher.c b/sway/desktop/launcher.c index 4a4255d7..0c02b997 100644 --- a/sway/desktop/launcher.c +++ b/sway/desktop/launcher.c @@ -216,6 +216,8 @@ struct launcher_ctx *launcher_ctx_create(struct wlr_xdg_activation_token_v1 *tok ctx->fallback_name = strdup(fallback_name); ctx->token = token; ctx->node = node; + // Having surface set means that the focus check in wlroots has passed + ctx->had_focused_surface = token->surface != NULL; ctx->node_destroy.notify = ctx_handle_node_destroy; wl_signal_add(&ctx->node->events.destroy, &ctx->node_destroy); diff --git a/sway/tree/view.c b/sway/tree/view.c index 5525bf63..4a0d8069 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -412,6 +412,12 @@ void view_request_activate(struct sway_view *view, struct sway_seat *seat) { transaction_commit_dirty(); } +void view_request_urgent(struct sway_view *view) { + if (config->focus_on_window_activation != FOWA_NONE) { + view_set_urgent(view, true); + } +} + void view_set_csd_from_server(struct sway_view *view, bool enabled) { sway_log(SWAY_DEBUG, "Telling view %p to set CSD to %i", view, enabled); if (view->xdg_decoration) { diff --git a/sway/xdg_activation_v1.c b/sway/xdg_activation_v1.c index 72c7fa4c..b7c80dd4 100644 --- a/sway/xdg_activation_v1.c +++ b/sway/xdg_activation_v1.c @@ -44,8 +44,11 @@ void xdg_activation_v1_handle_request_activate(struct wl_listener *listener, seat = ctx->token->seat ? ctx->token->seat->data : NULL; } - if (seat) { + if (seat && ctx->had_focused_surface) { view_request_activate(view, seat); + } else { + // The token is valid, but cannot be used to activate a window + view_request_urgent(view); } } From 7c11c463a3e7f821ed9f3c6de59e37358441952e Mon Sep 17 00:00:00 2001 From: Access Date: Tue, 20 Feb 2024 17:53:20 +0800 Subject: [PATCH 11/12] text_input: Implement input-method popups Co-authored-by: tadeokondrak --- include/sway/input/text_input.h | 3 + include/sway/input/text_input_popup.h | 20 +++ sway/desktop/layer_shell.c | 3 + sway/input/text_input.c | 228 +++++++++++++++++++++++++- 4 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 include/sway/input/text_input_popup.h diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 214e61d1..1993f928 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -21,18 +21,21 @@ struct sway_input_method_relay { struct sway_seat *seat; struct wl_list text_inputs; // sway_text_input::link + struct wl_list input_popups; // sway_input_popup::link struct wlr_input_method_v2 *input_method; // doesn't have to be present struct wl_listener text_input_new; struct wl_listener input_method_new; struct wl_listener input_method_commit; + struct wl_listener input_method_new_popup_surface; struct wl_listener input_method_grab_keyboard; struct wl_listener input_method_destroy; struct wl_listener input_method_keyboard_grab_destroy; }; + struct sway_text_input { struct sway_input_method_relay *relay; diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h new file mode 100644 index 00000000..e5f6ab8b --- /dev/null +++ b/include/sway/input/text_input_popup.h @@ -0,0 +1,20 @@ +#ifndef _SWAY_INPUT_TEXT_INPUT_POPUP_H +#define _SWAY_INPUT_TEXT_INPUT_POPUP_H + +#include "sway/tree/view.h" + +struct sway_input_popup { + struct sway_input_method_relay *relay; + + struct wlr_scene_tree *scene_tree; + struct sway_popup_desc desc; + struct wlr_input_popup_surface_v2 *popup_surface; + + struct wl_list link; + + struct wl_listener popup_destroy; + struct wl_listener popup_surface_commit; + + struct wl_listener focused_surface_unmap; +}; +#endif diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index f0134396..4b2584b6 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -137,6 +137,7 @@ static struct sway_layer_surface *sway_layer_surface_create( surface->scene = scene; surface->layer_surface = scene->layer_surface; surface->popups = popups; + surface->layer_surface->data = surface; return surface; } @@ -210,6 +211,8 @@ static void handle_node_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&layer->node_destroy.link); wl_list_remove(&layer->output_destroy.link); + layer->layer_surface->data = NULL; + free(layer); } diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 58911c2d..d80e34ac 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -2,7 +2,14 @@ #include #include "log.h" #include "sway/input/seat.h" +#include "sway/scene_descriptor.h" +#include "sway/tree/root.h" +#include "sway/tree/view.h" +#include "sway/output.h" #include "sway/input/text_input.h" +#include "sway/input/text_input_popup.h" +#include "sway/layers.h" +static void input_popup_update(struct sway_input_popup *popup); static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { @@ -102,6 +109,7 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { input_method_destroy); struct wlr_input_method_v2 *context = data; assert(context == relay->input_method); + wl_list_remove(&relay->input_method_new_popup_surface.link); relay->input_method = NULL; struct sway_text_input *text_input = relay_get_focused_text_input(relay); if (text_input) { @@ -133,6 +141,11 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, input->current.content_type.hint, input->current.content_type.purpose); } + struct sway_input_popup *popup; + wl_list_for_each(popup, &relay->input_popups, link) { + // send_text_input_rectangle is called in this function + input_popup_update(popup); + } wlr_input_method_v2_send_done(input_method); // TODO: pass intent, display popup size } @@ -255,6 +268,215 @@ static void relay_handle_text_input(struct wl_listener *listener, sway_text_input_create(relay, wlr_text_input); } +static void input_popup_update(struct sway_input_popup *popup) { + struct sway_text_input *text_input = + relay_get_focused_text_input(popup->relay); + + if (text_input == NULL || text_input->input->focused_surface == NULL) { + return; + } + + if (popup->scene_tree != NULL) { + wlr_scene_node_destroy(&popup->scene_tree->node); + popup->scene_tree = NULL; + } + if (popup->desc.relative != NULL) { + wlr_scene_node_destroy(popup->desc.relative); + popup->desc.relative = NULL; + } + popup->desc.view = NULL; + + if (!popup->popup_surface->surface->mapped) { + return; + } + + wlr_scene_node_destroy(&popup->scene_tree->node); + wlr_scene_node_destroy(popup->desc.relative); + popup->scene_tree = NULL; + + bool cursor_rect = text_input->input->current.features + & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + struct wlr_surface *focused_surface = text_input->input->focused_surface; + struct wlr_box cursor_area = text_input->input->current.cursor_rectangle; + + struct wlr_box output_box; + struct wlr_box parent; + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); + struct wlr_scene_tree *relative_parent; + + struct wlr_box geo = {0}; + + popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); + if (layer_surface != NULL) { + struct sway_layer_surface *layer = + layer_surface->data; + if (layer == NULL) { + return; + } + + relative_parent = layer->scene->tree; + struct wlr_output *output = layer->layer_surface->output; + wlr_output_layout_get_box(root->output_layout, output, &output_box); + int lx, ly; + wlr_scene_node_coords(&layer->tree->node, &lx, &ly); + parent.x = lx; + parent.y = ly; + popup->desc.view = NULL; + } else { + struct sway_view *view = view_from_wlr_surface(focused_surface); + relative_parent = view->scene_tree; + geo = view->geometry; + int lx, ly; + wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly); + struct wlr_output *output = wlr_output_layout_output_at(root->output_layout, + view->container->pending.content_x + view->geometry.x, + view->container->pending.content_y + view->geometry.y); + wlr_output_layout_get_box(root->output_layout, output, &output_box); + parent.x = lx; + parent.y = ly; + + parent.width = view->geometry.width; + parent.height = view->geometry.height; + popup->desc.view = view; + } + + struct wlr_scene_tree *relative = wlr_scene_tree_create(relative_parent); + + popup->desc.relative = &relative->node; + if (!scene_descriptor_assign(&popup->scene_tree->node, + SWAY_SCENE_DESC_POPUP, &popup->desc)) { + wlr_scene_node_destroy(&popup->scene_tree->node); + popup->scene_tree = NULL; + return; + } + + if (!cursor_rect) { + cursor_area.x = 0; + cursor_area.y = 0; + cursor_area.width = parent.width; + cursor_area.height = parent.height; + } + + int popup_width = popup->popup_surface->surface->current.width; + int popup_height = popup->popup_surface->surface->current.height; + int x1 = parent.x + cursor_area.x; + int x2 = parent.x + cursor_area.x + cursor_area.width; + int y1 = parent.y + cursor_area.y; + int y2 = parent.y + cursor_area.y + cursor_area.height; + int x = x1; + int y = y2; + + int available_right = output_box.x + output_box.width - x1; + int available_left = x2 - output_box.x; + if (available_right < popup_width && available_left > available_right) { + x = x2 - popup_width; + } + + int available_down = output_box.y + output_box.height - y2; + int available_up = y1 - output_box.y; + if (available_down < popup_height && available_up > available_down) { + y = y1 - popup_height; + } + + wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y); + if (cursor_rect) { + struct wlr_box box = { + .x = x1 - x, + .y = y1 - y, + .width = cursor_area.width, + .height = cursor_area.height, + }; + wlr_input_popup_surface_v2_send_text_input_rectangle( + popup->popup_surface, &box); + } + wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); +} + +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface) { + wl_list_remove(&popup->focused_surface_unmap.link); + + if (surface == NULL) { + wl_list_init(&popup->focused_surface_unmap.link); + input_popup_update(popup); + return; + } + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(surface); + if (layer_surface != NULL) { + wl_signal_add( + &layer_surface->surface->events.unmap, &popup->focused_surface_unmap); + input_popup_update(popup); + return; + } + + struct sway_view *view = view_from_wlr_surface(surface); + wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); +} + +static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_destroy); + wl_list_remove(&popup->focused_surface_unmap.link); + wl_list_remove(&popup->popup_surface_commit.link); + wl_list_remove(&popup->popup_destroy.link); + wl_list_remove(&popup->link); + + free(popup); +} + +static void handle_im_popup_surface_commit(struct wl_listener *listener, + void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_commit); + input_popup_update(popup); +} + +static void handle_im_focused_surface_unmap( + struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, focused_surface_unmap); + input_popup_update(popup); +} + +static void handle_im_new_popup_surface(struct wl_listener *listener, + void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + input_method_new_popup_surface); + struct sway_input_popup *popup = calloc(1, sizeof(*popup)); + popup->relay = relay; + popup->popup_surface = data; + popup->popup_surface->data = popup; + + wl_signal_add( + &popup->popup_surface->events.destroy, &popup->popup_destroy); + popup->popup_destroy.notify = handle_im_popup_destroy; + wl_signal_add(&popup->popup_surface->surface->events.commit, + &popup->popup_surface_commit); + popup->popup_surface_commit.notify = handle_im_popup_surface_commit; + wl_list_init(&popup->focused_surface_unmap.link); + popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; + + struct sway_text_input *text_input = relay_get_focused_text_input(relay); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } + + wl_list_insert(&relay->input_popups, &popup->link); +} + +static void text_input_send_enter(struct sway_text_input *text_input, + struct wlr_surface *surface) { + wlr_text_input_v3_send_enter(text_input->input, surface); + struct sway_input_popup *popup; + wl_list_for_each(popup, &text_input->relay->input_popups, link) { + input_popup_set_focus(popup, surface); + } +} + static void relay_handle_input_method(struct wl_listener *listener, void *data) { struct sway_input_method_relay *relay = wl_container_of(listener, relay, @@ -280,10 +502,13 @@ static void relay_handle_input_method(struct wl_listener *listener, wl_signal_add(&relay->input_method->events.destroy, &relay->input_method_destroy); relay->input_method_destroy.notify = handle_im_destroy; + wl_signal_add(&relay->input_method->events.new_popup_surface, + &relay->input_method_new_popup_surface); + relay->input_method_new_popup_surface.notify = handle_im_new_popup_surface; struct sway_text_input *text_input = relay_get_focusable_text_input(relay); if (text_input) { - wlr_text_input_v3_send_enter(text_input->input, + text_input_send_enter(text_input, text_input->pending_focused_surface); text_input_set_pending_focused_surface(text_input, NULL); } @@ -293,6 +518,7 @@ void sway_input_method_relay_init(struct sway_seat *seat, struct sway_input_method_relay *relay) { relay->seat = seat; wl_list_init(&relay->text_inputs); + wl_list_init(&relay->input_popups); relay->text_input_new.notify = relay_handle_text_input; wl_signal_add(&server.text_input->events.text_input, From d6150b6bb05ff9f59c0162fc2bab1709545fae7f Mon Sep 17 00:00:00 2001 From: llyyr Date: Tue, 20 Feb 2024 20:44:56 +0530 Subject: [PATCH 12/12] input/text_input: parent wlr_box may be uninitialized --- sway/input/text_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index d80e34ac..ad760a6f 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -300,7 +300,7 @@ static void input_popup_update(struct sway_input_popup *popup) { struct wlr_box cursor_area = text_input->input->current.cursor_rectangle; struct wlr_box output_box; - struct wlr_box parent; + struct wlr_box parent = {0}; struct wlr_layer_surface_v1 *layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); struct wlr_scene_tree *relative_parent;