From 3629a832e5b3a73ccd4e42eca79943af121866da Mon Sep 17 00:00:00 2001 From: llyyr Date: Sat, 11 Jan 2025 18:49:20 +0530 Subject: [PATCH 01/61] layer_shell: cleanup new_popup listener when destroying node --- sway/desktop/layer_shell.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 333c09b4..05faa465 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -256,6 +256,7 @@ static void handle_node_destroy(struct wl_listener *listener, void *data) { wl_list_remove(&layer->unmap.link); wl_list_remove(&layer->surface_commit.link); wl_list_remove(&layer->node_destroy.link); + wl_list_remove(&layer->new_popup.link); wl_list_remove(&layer->output_destroy.link); layer->layer_surface->data = NULL; From e3f0ba4cd9ca709cac115ade54958885614d889c Mon Sep 17 00:00:00 2001 From: Jim Date: Sat, 11 Jan 2025 16:36:07 +0100 Subject: [PATCH 02/61] Increase max default buffer size to 1 MiB Increasing the max default buffer size prevents clients from crashing when they need more than 4096 bytes. This can happen when the GUI thread of the application is blocked, especially when moving your mouse over it with high mouse sensitivity. --- sway/server.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/server.c b/sway/server.c index e091d946..cbc0d259 100644 --- a/sway/server.c +++ b/sway/server.c @@ -222,6 +222,7 @@ bool server_init(struct sway_server *server) { server->wl_event_loop = wl_display_get_event_loop(server->wl_display); wl_display_set_global_filter(server->wl_display, filter_global, NULL); + wl_display_set_default_max_buffer_size(server->wl_display, 1024 * 1024); root = root_create(server->wl_display); From cff16d32f9435592d0fe1f5794213e50341d9bed Mon Sep 17 00:00:00 2001 From: Jacob McNamee Date: Thu, 26 Dec 2024 21:38:05 -0800 Subject: [PATCH 03/61] tree/view: add getters for sandbox properties --- include/sway/tree/view.h | 6 ++++++ sway/tree/view.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index f6032221..9f084eeb 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -221,6 +221,12 @@ const char *view_get_window_role(struct sway_view *view); uint32_t view_get_window_type(struct sway_view *view); +const char *view_get_sandbox_engine(struct sway_view *view); + +const char *view_get_sandbox_app_id(struct sway_view *view); + +const char *view_get_sandbox_instance_id(struct sway_view *view); + const char *view_get_shell(struct sway_view *view); void view_get_constraints(struct sway_view *view, double *min_width, diff --git a/sway/tree/view.c b/sway/tree/view.c index 492095b9..33161cc5 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -154,6 +155,34 @@ uint32_t view_get_window_type(struct sway_view *view) { return 0; } +static const struct wlr_security_context_v1_state *security_context_from_view( + struct sway_view *view) { + const struct wl_client *client = + wl_resource_get_client(view->surface->resource); + const struct wlr_security_context_v1_state *security_context = + wlr_security_context_manager_v1_lookup_client( + server.security_context_manager_v1, client); + return security_context; +} + +const char *view_get_sandbox_engine(struct sway_view *view) { + const struct wlr_security_context_v1_state *security_context = + security_context_from_view(view); + return security_context ? security_context->sandbox_engine : NULL; +} + +const char *view_get_sandbox_app_id(struct sway_view *view) { + const struct wlr_security_context_v1_state *security_context = + security_context_from_view(view); + return security_context ? security_context->app_id : NULL; +} + +const char *view_get_sandbox_instance_id(struct sway_view *view) { + const struct wlr_security_context_v1_state *security_context = + security_context_from_view(view); + return security_context ? security_context->instance_id : NULL; +} + const char *view_get_shell(struct sway_view *view) { switch(view->type) { case SWAY_VIEW_XDG_SHELL: From 60f06fc4f1590e1213893177ebf00ea780178562 Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Mon, 26 Sep 2022 18:59:46 +0000 Subject: [PATCH 04/61] ipc-json: add sandbox properties to view JSON --- sway/ipc-json.c | 12 ++++++++++++ sway/sway-ipc.7.scd | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index fc1df2ac..142512eb 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -602,6 +602,18 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "inhibit_idle", json_object_new_boolean(view_inhibit_idle(c->view))); + const char *sandbox_engine = view_get_sandbox_engine(c->view); + json_object_object_add(object, "sandbox_engine", + sandbox_engine ? json_object_new_string(sandbox_engine) : NULL); + + const char *sandbox_app_id = view_get_sandbox_app_id(c->view); + json_object_object_add(object, "sandbox_app_id", + sandbox_app_id ? json_object_new_string(sandbox_app_id) : NULL); + + const char *sandbox_instance_id = view_get_sandbox_instance_id(c->view); + json_object_object_add(object, "sandbox_instance_id", + sandbox_instance_id ? json_object_new_string(sandbox_instance_id) : NULL); + json_object *idle_inhibitors = json_object_new_object(); struct sway_idle_inhibitor_v1 *user_inhibitor = diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index fe6bb92e..f39e3518 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -403,6 +403,16 @@ node and will have the following properties: : (Only views) An object containing the state of the _application_ and _user_ idle inhibitors. _application_ can be _enabled_ or _none_. _user_ can be _focus_, _fullscreen_, _open_, _visible_ or _none_. +|- sandbox_engine +: string +: (Only views) The associated sandbox engine (or _null_) +|- sandbox_app_id +: string +: (Only views) The app ID provided by the associated sandbox engine (or _null_) +|- sandbox_instance_id +: string +: (Only views) The instance ID provided by the associated sandbox engine (or + _null_) |- window : integer : (Only xwayland views) The X11 window ID for the xwayland view From 3ab1f0ca3d4f7701c7219f9401f5f372bedc244b Mon Sep 17 00:00:00 2001 From: Puck Meerburg Date: Sun, 7 Aug 2022 10:12:51 +0000 Subject: [PATCH 05/61] criteria: add sandbox properties --- include/sway/criteria.h | 3 ++ sway/criteria.c | 86 ++++++++++++++++++++++++++++++++++++++++- sway/sway.5.scd | 16 ++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) diff --git a/include/sway/criteria.h b/include/sway/criteria.h index ae546821..8ba8c998 100644 --- a/include/sway/criteria.h +++ b/include/sway/criteria.h @@ -53,6 +53,9 @@ struct criteria { char urgent; // 'l' for latest or 'o' for oldest struct pattern *workspace; pid_t pid; + struct pattern *sandbox_engine; + struct pattern *sandbox_app_id; + struct pattern *sandbox_instance_id; }; bool criteria_is_empty(struct criteria *criteria); diff --git a/sway/criteria.c b/sway/criteria.c index 2b7290c0..0aefa008 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -34,7 +34,10 @@ bool criteria_is_empty(struct criteria *criteria) { && !criteria->tiling && !criteria->urgent && !criteria->workspace - && !criteria->pid; + && !criteria->pid + && !criteria->sandbox_engine + && !criteria->sandbox_app_id + && !criteria->sandbox_instance_id; } // The error pointer is used for parsing functions, and saves having to pass it @@ -98,6 +101,9 @@ void criteria_destroy(struct criteria *criteria) { #endif pattern_destroy(criteria->con_mark); pattern_destroy(criteria->workspace); + pattern_destroy(criteria->sandbox_engine); + pattern_destroy(criteria->sandbox_app_id); + pattern_destroy(criteria->sandbox_instance_id); free(criteria->target); free(criteria->cmdlist); free(criteria->raw); @@ -248,6 +254,66 @@ static bool criteria_matches_view(struct criteria *criteria, } } + if (criteria->sandbox_engine) { + const char *sandbox_engine = view_get_sandbox_engine(view); + if (!sandbox_engine) { + return false; + } + + switch (criteria->sandbox_engine->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(sandbox_engine, view_get_sandbox_engine(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(sandbox_engine, criteria->sandbox_engine->regex) < 0) { + return false; + } + break; + } + } + + if (criteria->sandbox_app_id) { + const char *sandbox_app_id = view_get_sandbox_app_id(view); + if (!sandbox_app_id) { + return false; + } + + switch (criteria->sandbox_app_id->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(sandbox_app_id, view_get_sandbox_app_id(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(sandbox_app_id, criteria->sandbox_app_id->regex) < 0) { + return false; + } + break; + } + } + + if (criteria->sandbox_instance_id) { + const char *sandbox_instance_id = view_get_sandbox_instance_id(view); + if (!sandbox_instance_id) { + return false; + } + + switch (criteria->sandbox_instance_id->match_type) { + case PATTERN_FOCUSED: + if (focused && lenient_strcmp(sandbox_instance_id, view_get_sandbox_instance_id(focused))) { + return false; + } + break; + case PATTERN_PCRE2: + if (regex_cmp(sandbox_instance_id, criteria->sandbox_instance_id->regex) < 0) { + return false; + } + break; + } + } + if (!criteria_matches_container(criteria, view->container)) { return false; } @@ -475,6 +541,9 @@ enum criteria_token { T_URGENT, T_WORKSPACE, T_PID, + T_SANDBOX_ENGINE, + T_SANDBOX_APP_ID, + T_SANDBOX_INSTANCE_ID, T_INVALID, }; @@ -514,6 +583,12 @@ static enum criteria_token token_from_name(char *name) { return T_FLOATING; } else if (strcmp(name, "pid") == 0) { return T_PID; + } else if (strcmp(name, "sandbox_engine") == 0) { + return T_SANDBOX_ENGINE; + } else if (strcmp(name, "sandbox_app_id") == 0) { + return T_SANDBOX_APP_ID; + } else if (strcmp(name, "sandbox_instance_id") == 0) { + return T_SANDBOX_INSTANCE_ID; } return T_INVALID; } @@ -617,6 +692,15 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { error = strdup("The value for 'pid' should be numeric"); } break; + case T_SANDBOX_ENGINE: + pattern_create(&criteria->sandbox_engine, value); + break; + case T_SANDBOX_APP_ID: + pattern_create(&criteria->sandbox_app_id, value); + break; + case T_SANDBOX_INSTANCE_ID: + pattern_create(&criteria->sandbox_instance_id, value); + break; case T_INVALID: break; } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 5e4df0dc..4def2cbb 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -1058,6 +1058,22 @@ The following attributes may be matched with: expression. If the value is \_\_focused\_\_, then all the views on the currently focused workspace matches. +*sandbox_engine* + Compare against the associated sandbox engine. Can be a regular expression. + If the value is \_\_focused\_\_, then the sandbox engine must be the same as + that of the currently focused window. + +*sandbox_app_id* + Compare against the app ID provided by the associated sandbox engine. Can be + a regular expression. If the value is \_\_focused\_\_, then the sandbox app + ID must be the same as that of the currently focused window. + +*sandbox_instance_id* + Compare against the instance ID provided by the associated sandbox engine. + Can be a regular expression. If the value is \_\_focused\_\_, then the + sandbox instance ID must be the same as that of the currently focused + window. + # SEE ALSO *sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) From f177d0544108d2fbf567ee0db646efc99e72d47f Mon Sep 17 00:00:00 2001 From: Jacob McNamee Date: Fri, 27 Dec 2024 00:03:06 -0800 Subject: [PATCH 06/61] tree/container: support sandbox properties in title format --- sway/sway.5.scd | 29 +++++++++++++++++++++++------ sway/tree/container.c | 9 +++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 4def2cbb..11468d77 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -372,12 +372,29 @@ set|plus|minus|toggle *title_format* Sets the format of window titles. The following placeholders may be used: - %title - The title supplied by the window ++ - %app_id - The wayland app ID (applicable to wayland windows only) ++ - %class - The X11 classname (applicable to xwayland windows only) ++ - %instance - The X11 instance (applicable to xwayland windows only) ++ - %shell - The protocol the window is using (typically xwayland or - xdg_shell) + *%title* + The title supplied by the window + + *%app_id* + The wayland app ID (applicable to wayland windows only) + + *%class* + The X11 classname (applicable to xwayland windows only) + + *%instance* + The X11 instance (applicable to xwayland windows only) + + *%shell* + The protocol the window is using (typically xwayland or xdg_shell) + + *%sandbox_engine* + The associated sandbox engine + + *%sandbox_app_id* + The app ID provided by the associated sandbox engine + + *%sandbox_instance_id* + The instance ID provided by the associated sandbox engine This command is typically used with *for_window* criteria. For example: diff --git a/sway/tree/container.c b/sway/tree/container.c index 0288beac..6ff4036f 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -716,6 +716,15 @@ size_t parse_title_format(struct sway_container *container, char *buffer) { } else if (has_prefix(next, "%shell")) { len += append_prop(buffer, view_get_shell(container->view)); format += strlen("%shell"); + } else if (has_prefix(next, "%sandbox_engine")) { + len += append_prop(buffer, view_get_sandbox_engine(container->view)); + format += strlen("%sandbox_engine"); + } else if (has_prefix(next, "%sandbox_app_id")) { + len += append_prop(buffer, view_get_sandbox_app_id(container->view)); + format += strlen("%sandbox_app_id"); + } else if (has_prefix(next, "%sandbox_instance_id")) { + len += append_prop(buffer, view_get_sandbox_instance_id(container->view)); + format += strlen("%sandbox_instance_id"); } else { lenient_strcat(buffer, "%"); ++format; From 0b08dce08cbcf515103d8a7fd8c390ed04c93428 Mon Sep 17 00:00:00 2001 From: Jacob McNamee Date: Sat, 4 Jan 2025 00:03:14 -0800 Subject: [PATCH 07/61] swaymsg: pretty-print sandbox properties --- swaymsg/main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/swaymsg/main.c b/swaymsg/main.c index 5c57171e..0ef6eb8a 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -330,6 +330,9 @@ static void pretty_print_tree(json_object *obj, int indent) { const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); int x11_id = json_object_get_int(json_object_object_get(obj, "window")); + const char *sandbox_engine = json_object_get_string(json_object_object_get(obj, "sandbox_engine")); + const char *sandbox_app_id = json_object_get_string(json_object_object_get(obj, "sandbox_app_id")); + const char *sandbox_instance_id = json_object_get_string(json_object_object_get(obj, "sandbox_instance_id")); printf(" (%s, pid: %d", shell, pid); if (app_id != NULL) { @@ -344,6 +347,15 @@ static void pretty_print_tree(json_object *obj, int indent) { if (x11_id != 0) { printf(", X11 window: 0x%X", x11_id); } + if (sandbox_engine != NULL) { + printf(", sandbox_engine: \"%s\"", sandbox_engine); + } + if (sandbox_app_id != NULL) { + printf(", sandbox_app_id: \"%s\"", sandbox_app_id); + } + if (sandbox_instance_id != NULL) { + printf(", sandbox_instance_id: \"%s\"", sandbox_instance_id); + } printf(")"); } From 30c858423dcdfb82d768a5025b5f00c19b250322 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 16 Jan 2025 19:11:36 +0530 Subject: [PATCH 08/61] config/output: don't leak background_fallback --- sway/config/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/config/output.c b/sway/config/output.c index 27708476..9fb3a12a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1018,6 +1018,7 @@ void free_output_config(struct output_config *oc) { free(oc->name); free(oc->background); free(oc->background_option); + free(oc->background_fallback); wlr_color_transform_unref(oc->color_transform); free(oc); } From 8acb0482da68af69d52ab168f9e30e2464b9c7a3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 12 Feb 2024 21:13:35 +0100 Subject: [PATCH 09/61] Add ext-image-copy-capture-v1 and ext-image-capture-source-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4545 --- include/sway/server.h | 1 + protocols/meson.build | 9 ++++++--- sway/server.c | 5 +++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index ccf4a9cc..7b76eb90 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -106,6 +106,7 @@ struct sway_server { struct wlr_content_type_manager_v1 *content_type_manager_v1; struct wlr_data_control_manager_v1 *data_control_manager_v1; struct wlr_screencopy_manager_v1 *screencopy_manager_v1; + struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_manager_v1; struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; struct wlr_security_context_manager_v1 *security_context_manager_v1; diff --git a/protocols/meson.build b/protocols/meson.build index d96f8757..4d24e707 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -9,12 +9,15 @@ wayland_scanner = find_program( protocols = [ wl_protocol_dir / 'stable/tablet/tablet-v2.xml', wl_protocol_dir / 'stable/xdg-shell/xdg-shell.xml', - wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', - wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', - wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', wl_protocol_dir / 'staging/content-type/content-type-v1.xml', wl_protocol_dir / 'staging/cursor-shape/cursor-shape-v1.xml', + wl_protocol_dir / 'staging/ext-foreign-toplevel-list/ext-foreign-toplevel-list-v1.xml', + wl_protocol_dir / 'staging/ext-image-capture-source/ext-image-capture-source-v1.xml', + wl_protocol_dir / 'staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml', wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', + wl_protocol_dir / 'unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml', + wl_protocol_dir / 'unstable/pointer-constraints/pointer-constraints-unstable-v1.xml', + wl_protocol_dir / 'unstable/xdg-output/xdg-output-unstable-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-output-power-management-unstable-v1.xml', diff --git a/sway/server.c b/sway/server.c index cbc0d259..c7fc2a6d 100644 --- a/sway/server.c +++ b/sway/server.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -108,6 +110,7 @@ static bool is_privileged(const struct wl_global *global) { global == server.foreign_toplevel_manager->global || global == server.data_control_manager_v1->global || global == server.screencopy_manager_v1->global || + global == server.ext_image_copy_capture_manager_v1->global || global == server.export_dmabuf_manager_v1->global || global == server.security_context_manager_v1->global || global == server.gamma_control_manager_v1->global || @@ -371,6 +374,8 @@ bool server_init(struct sway_server *server) { server->export_dmabuf_manager_v1 = wlr_export_dmabuf_manager_v1_create(server->wl_display); server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); + server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); + wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); wlr_viewporter_create(server->wl_display); From 3ff60987f3a1f8707e0d095a1dd822fda7aa1fe0 Mon Sep 17 00:00:00 2001 From: Bill Li Date: Mon, 27 Jan 2025 07:31:19 +0800 Subject: [PATCH 10/61] Drop wlr_matrix.h include from sway/desktop/output.c wlr_matrix is now private API. Fixes #8549 --- sway/desktop/output.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 607eca2c..d1facdea 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From d093c2e3583e785cb308bec74c08a61e8808faf6 Mon Sep 17 00:00:00 2001 From: Attila Fidan Date: Mon, 27 Jan 2025 05:13:24 +0000 Subject: [PATCH 11/61] input/cursor: remove tool_proximity listener in destroy --- sway/input/cursor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index e8c45118..1fd57ec4 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1047,6 +1047,7 @@ void sway_cursor_destroy(struct sway_cursor *cursor) { wl_list_remove(&cursor->touch_frame.link); wl_list_remove(&cursor->tool_axis.link); wl_list_remove(&cursor->tool_tip.link); + wl_list_remove(&cursor->tool_proximity.link); wl_list_remove(&cursor->tool_button.link); wl_list_remove(&cursor->request_set_cursor.link); From 851b8c6fb624a8056df16fa9db2b3dc05021e519 Mon Sep 17 00:00:00 2001 From: Dan Baterisna Date: Fri, 7 Feb 2025 00:18:50 +0800 Subject: [PATCH 12/61] man: Document bar mode toggle command Functionality for switching swaybar's current mode between hidden and docked currently exists, but is absent from the relevant manpage. --- sway/sway-bar.5.scd | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/sway-bar.5.scd b/sway/sway-bar.5.scd index 42e59d57..a1733aa2 100644 --- a/sway/sway-bar.5.scd +++ b/sway/sway-bar.5.scd @@ -143,6 +143,12 @@ runtime. This setting also applies to the current binding mode indicator. +The following commands may only be used at runtime. + +*mode* toggle [] + Toggles the current mode between _hide_ and _dock_. Any other mode + is treated as _hide_. + ## TRAY Swaybar provides a system tray where third-party applications may place icons. From 4852087e6166062d33372bd2869fce4033232e08 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Mon, 3 Feb 2025 13:13:48 -0500 Subject: [PATCH 13/61] output/background: fix config ignoring fallback color currently, the output background command handler prematurely returns with an error if the background file cannot be accessed. It should only error if user did not provide fallback color. closes #8556 Changes - Introduce variables to avoid uneccessary writing on output members - Log a debug message when fallback is being used over inaccessible file - Always parse the background color and swaynag warn if it is incorrect - when updating output member variables, free previous values - add cleanup label and goto it if `strdup` fails - Move output->member initializations to before parsing fallback, Also free and init output->background as well --- sway/commands/output/background.c | 90 +++++++++++++++---------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/sway/commands/output/background.c b/sway/commands/output/background.c index 55bd7671..fe162162 100644 --- a/sway/commands/output/background.c +++ b/sway/commands/output/background.c @@ -3,10 +3,8 @@ #include #include #include -#include #include "sway/commands.h" #include "sway/config.h" -#include "sway/swaynag.h" #include "log.h" #include "stringop.h" @@ -42,14 +40,14 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } struct output_config *output = config->handler_context.output_config; - + char *src = NULL; if (strcasecmp(argv[1], "solid_color") == 0) { if (!validate_color(argv[0])) { return cmd_results_new(CMD_INVALID, "Colors should be of the form #RRGGBB"); } - output->background = strdup(argv[0]); - output->background_option = strdup("solid_color"); + if (!(output->background = strdup(argv[0]))) goto cleanup; + if (!(output->background_option = strdup("solid_color"))) goto cleanup; output->background_fallback = NULL; argc -= 2; argv += 2; } else { @@ -77,37 +75,25 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing background file"); } - char *src = join_args(argv, j); + if (!(src = join_args(argv, j))) goto cleanup; if (!expand_path(&src)) { struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, "Invalid syntax (%s)", src); free(src); return cmd_res; } - if (!src) { - sway_log(SWAY_ERROR, "Failed to allocate expanded path"); - return cmd_results_new(CMD_FAILURE, "Unable to allocate resource"); - } if (config->reading && *src != '/') { // src file is inside configuration dir char *conf = strdup(config->current_config_path); - if (!conf) { - sway_log(SWAY_ERROR, "Failed to duplicate string"); - free(src); - return cmd_results_new(CMD_FAILURE, - "Unable to allocate resources"); - } + if (!conf) goto cleanup; char *conf_path = dirname(conf); char *real_src = malloc(strlen(conf_path) + strlen(src) + 2); if (!real_src) { - free(src); free(conf); - sway_log(SWAY_ERROR, "Unable to allocate memory"); - return cmd_results_new(CMD_FAILURE, - "Unable to allocate resources"); + goto cleanup; } snprintf(real_src, strlen(conf_path) + strlen(src) + 2, "%s/%s", conf_path, src); @@ -117,40 +103,48 @@ struct cmd_results *output_cmd_background(int argc, char **argv) { } bool can_access = access(src, F_OK) != -1; + argc -= j + 1; argv += j + 1; + free(output->background_option); + free(output->background_fallback); + free(output->background); + output->background = output->background_option = output->background_fallback = NULL; + char *fallback = NULL; + + if (argc && *argv[0] == '#') { + if (validate_color(argv[0])) { + if (!(fallback = strdup(argv[0]))) goto cleanup; + output->background_fallback = fallback; + } else { + sway_log(SWAY_ERROR, "fallback '%s' should be of the form #RRGGBB", argv[0]); + config_add_swaynag_warning("fallback '%s' should be of the form #RRGGBB\n", argv[0]); + } + argc--; argv++; + } + if (!can_access) { - sway_log_errno(SWAY_ERROR, "Unable to access background file '%s'", - src); - config_add_swaynag_warning("Unable to access background file '%s'", - src); - struct cmd_results *result = cmd_results_new(CMD_FAILURE, - "unable to access background file '%s'", src); - free(src); - return result; + if (!fallback) { + sway_log(SWAY_ERROR, "Unable to access background file '%s' " + "and no valid fallback provided", src); + struct cmd_results *res = cmd_results_new(CMD_FAILURE, "Unable to access " + "background file '%s' and no valid fallback provided", src); + free(src); + return res; + } + sway_log(SWAY_DEBUG, "Cannot access file '%s', using fallback '%s'", src, fallback); + output->background = fallback; + if (!(output->background_option = strdup("solid_color"))) goto cleanup; + output->background_fallback = NULL; } else { output->background = src; - output->background_option = strdup(mode); - } - argc -= j + 1; argv += j + 1; - - output->background_fallback = NULL; - if (argc && *argv[0] == '#') { - if (!validate_color(argv[0])) { - return cmd_results_new(CMD_INVALID, - "fallback color should be of the form #RRGGBB"); - } - - output->background_fallback = strdup(argv[0]); - argc--; argv++; - - if (!can_access) { - output->background = output->background_fallback; - output->background_option = strdup("solid_color"); - output->background_fallback = NULL; - } + if (!(output->background_option = strdup(mode))) goto cleanup; } } - config->handler_context.leftovers.argc = argc; config->handler_context.leftovers.argv = argv; return NULL; + +cleanup: + free(src); + sway_log(SWAY_ERROR, "Failed to allocate resources"); + return cmd_results_new(CMD_FAILURE, "Unable to allocate resources"); } From c1031d84655d2658dfeb8f4b93f099adee0494be Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Sun, 16 Feb 2025 18:37:25 +0100 Subject: [PATCH 14/61] sway/ipc-json: add ext-foreign-toplevel-handle identifier to get_tree ipc output Fixes #8291 --- sway/ipc-json.c | 5 +++++ sway/sway-ipc.7.scd | 3 +++ swaymsg/main.c | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 142512eb..d3adedd4 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "config.h" #include "log.h" @@ -577,6 +578,10 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "app_id", app_id ? json_object_new_string(app_id) : NULL); + json_object_object_add(object, "foreign_toplevel_identifier", + c->view->ext_foreign_toplevel ? + json_object_new_string(c->view->ext_foreign_toplevel->identifier) : NULL); + bool visible = view_is_visible(c->view); json_object_object_add(object, "visible", json_object_new_boolean(visible)); diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index f39e3518..ab85cf66 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -389,6 +389,9 @@ node and will have the following properties: |- pid : integer : (Only views) The PID of the application that owns the view +|- foreign_toplevel_identifier +: string +: (Only views) The ext-foreign-toplevel-list-v1 toplevel identifier of this node. |- visible : boolean : (Only views) Whether the node is visible diff --git a/swaymsg/main.c b/swaymsg/main.c index 0ef6eb8a..7534ea6d 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -330,6 +330,7 @@ static void pretty_print_tree(json_object *obj, int indent) { const char *instance = json_object_get_string(json_object_object_get(window_props_obj, "instance")); const char *class = json_object_get_string(json_object_object_get(window_props_obj, "class")); int x11_id = json_object_get_int(json_object_object_get(obj, "window")); + const char *foreign_toplevel_id = json_object_get_string(json_object_object_get(obj, "foreign_toplevel_identifier")); const char *sandbox_engine = json_object_get_string(json_object_object_get(obj, "sandbox_engine")); const char *sandbox_app_id = json_object_get_string(json_object_object_get(obj, "sandbox_app_id")); const char *sandbox_instance_id = json_object_get_string(json_object_object_get(obj, "sandbox_instance_id")); @@ -347,6 +348,9 @@ static void pretty_print_tree(json_object *obj, int indent) { if (x11_id != 0) { printf(", X11 window: 0x%X", x11_id); } + if (foreign_toplevel_id != NULL) { + printf(", foreign_toplevel_id: \"%s\"", foreign_toplevel_id); + } if (sandbox_engine != NULL) { printf(", sandbox_engine: \"%s\"", sandbox_engine); } From 10e50e6bf9b63b205c141f97a5709fd4d405542f Mon Sep 17 00:00:00 2001 From: Mark Stosberg Date: Sun, 16 Feb 2025 16:04:17 -0500 Subject: [PATCH 15/61] docs: use "window" instead of "view" throughout. "view" is an internal term, while the commonly understood user-facing term is "window" Ref: #7323 --- sway/sway-ipc.7.scd | 64 +++++++++++++++++++++---------------------- sway/sway.5.scd | 66 ++++++++++++++++++++++----------------------- 2 files changed, 65 insertions(+), 65 deletions(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index ab85cf66..833db0ef 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -138,7 +138,7 @@ has the following properties: : Whether the workspace is currently focused by the default seat (_seat0_) |- urgent : boolean -: Whether a view on the workspace has the urgent flag set +: Whether a window on the workspace has the urgent flag set |- rect : object : The bounds of the workspace. It consists of _x_, _y_, _width_, and _height_ @@ -374,7 +374,7 @@ node and will have the following properties: that can be used as an aid in submitting reproduction steps for bug reports |- fullscreen_mode : integer -: (Only containers and views) The fullscreen mode of the node. 0 means none, 1 means +: (Only containers and windows) The fullscreen mode of the node. 0 means none, 1 means full workspace, and 2 means global fullscreen |- floating : string @@ -384,45 +384,45 @@ node and will have the following properties: : Whether the window is in the scratchpad. Can be either "none" or "fresh" |- app_id : string -: (Only views) For an xdg-shell view, the name of the application, if set. +: (Only windows) For an xdg-shell window, the name of the application, if set. Otherwise, _null_ |- pid : integer -: (Only views) The PID of the application that owns the view +: (Only windows) The PID of the application that owns the window |- foreign_toplevel_identifier : string -: (Only views) The ext-foreign-toplevel-list-v1 toplevel identifier of this node. +: (Only windows) The ext-foreign-toplevel-list-v1 toplevel identifier of this node. |- visible : boolean -: (Only views) Whether the node is visible +: (Only windows) Whether the node is visible |- shell : string -: (Only views) The shell of the view, such as _xdg\_shell_ or _xwayland_ +: (Only windows) The shell of the window, such as _xdg\_shell_ or _xwayland_ |- inhibit_idle : boolean -: (Only views) Whether the view is inhibiting the idle state +: (Only windows) Whether the window is inhibiting the idle state |- idle_inhibitors : object -: (Only views) An object containing the state of the _application_ and _user_ idle inhibitors. +: (Only windows) An object containing the state of the _application_ and _user_ idle inhibitors. _application_ can be _enabled_ or _none_. _user_ can be _focus_, _fullscreen_, _open_, _visible_ or _none_. |- sandbox_engine : string -: (Only views) The associated sandbox engine (or _null_) +: (Only windows) The associated sandbox engine (or _null_) |- sandbox_app_id : string -: (Only views) The app ID provided by the associated sandbox engine (or _null_) +: (Only windows) The app ID provided by the associated sandbox engine (or _null_) |- sandbox_instance_id : string -: (Only views) The instance ID provided by the associated sandbox engine (or +: (Only windows) The instance ID provided by the associated sandbox engine (or _null_) |- window : integer -: (Only xwayland views) The X11 window ID for the xwayland view +: (Only xwayland windows) The X11 window ID for the xwayland window |- window_properties : object -: (Only xwayland views) An object containing the _title_, _class_, _instance_, - _window\_role_, _window\_type_, and _transient\_for_ for the view +: (Only xwayland windows) An object containing the _title_, _class_, _instance_, + _window\_role_, _window\_type_, and _transient\_for_ for the window *Example Reply:* @@ -927,13 +927,13 @@ containing the _#RRGGBBAA_ representation of the color: that are not visible |- urgent_workspace_text : The color to use for the text of the workspace buttons for workspaces that - contain an urgent view + contain an urgent window |- urgent_workspace_bg : The color to use for the background of the workspace buttons for workspaces - that contain an urgent view + that contain an urgent window |- urgent_workspace_border : The color to use for the border of the workspace buttons for workspaces that - contain an urgent view + contain an urgent window |- binding_mode_text : The color to use for the text of the binding mode indicator |- binding_mode_bg @@ -1480,7 +1480,7 @@ available: : Sent whenever the binding mode changes |- 0x80000003 : window -: Sent whenever an event involving a view occurs such as being reparented, +: Sent whenever an event involving a window occurs such as being reparented, focused, or closed |- 0x80000004 : barconfig_update @@ -1536,8 +1536,8 @@ The following change types are currently available: |- rename : The workspace was renamed |- urgent -: A view on the workspace has had their urgency hint set or all urgency hints - for views on the workspace have been cleared +: A window on the workspace has had their urgency hint set or all urgency hints + for windows on the workspace have been cleared |- reload : The configuration file has been reloaded @@ -1635,7 +1635,7 @@ with the following properties: ## 0x80000003. WINDOW -Sent whenever a change involving a view occurs. The event consists of a single +Sent whenever a change involving a window occurs. The event consists of a single object with the following properties: [- *PROPERTY* @@ -1646,30 +1646,30 @@ object with the following properties: :[ The type of change that occurred. See below for more information |- container : object -: An object representing the view effected +: An object representing the window effected The following change types are currently available: [- *TYPE* :- *DESCRIPTION* |- new -:[ The view was created +:[ The window was created |- close -: The view was closed +: The window was closed |- focus -: The view was focused +: The window was focused |- title -: The view's title has changed +: The window's title has changed |- fullscreen_mode -: The view's fullscreen mode has changed +: The window's fullscreen mode has changed |- move -: The view has been reparented in the tree +: The window has been reparented in the tree |- floating -: The view has become floating or is no longer floating +: The window has become floating or is no longer floating |- urgent -: The view's urgency hint has changed status +: The window's urgency hint has changed status |- mark -: A mark has been added or removed from the view +: A mark has been added or removed from the window *Example Event:* diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 11468d77..f580b2af 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -117,7 +117,7 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). Exit sway and end your Wayland session. *floating* enable|disable|toggle - Make focused view floating, non-floating, or the opposite of what it is now. + Make focused window floating, non-floating, or the opposite of what it is now. *focus* Moves focus to the container that matches the specified criteria. @@ -152,9 +152,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). Moves focus between the floating and tiled layers. *fullscreen* [enable|disable|toggle] [global] - Makes focused view fullscreen, non-fullscreen, or the opposite of what it + Makes focused window fullscreen, non-fullscreen, or the opposite of what it is now. If no argument is given, it does the same as _toggle_. If _global_ - is specified, the view will be fullscreen across all outputs. + is specified, the window will be fullscreen across all outputs. *gaps* inner|outer|horizontal|vertical|top|right|bottom|left all|current set|plus|minus|toggle @@ -164,16 +164,16 @@ set|plus|minus|toggle _vertical_. *inhibit_idle* focus|fullscreen|open|none|visible - Set/unset an idle inhibitor for the view. _focus_ will inhibit idle when - the view is focused by any seat. _fullscreen_ will inhibit idle when the + Set/unset an idle inhibitor for the window. _focus_ will inhibit idle when + the window is focused by any seat. _fullscreen_ will inhibit idle when the view is fullscreen (or a descendant of a fullscreen container) and is - visible. _open_ will inhibit idle until the view is closed (or the - inhibitor is unset/changed). _visible_ will inhibit idle when the view is + visible. _open_ will inhibit idle until the window is closed (or the + inhibitor is unset/changed). _visible_ will inhibit idle when the window is visible on any output. _none_ will remove any existing idle inhibitor for - the view. + the window. This can also be used with criteria to set an idle inhibitor for any - existing view or with _for_window_ to set idle inhibitors for future views. + existing window or with _for_window_ to set idle inhibitors for future windows. *layout* default|splith|splitv|stacking|tabbed Sets the layout mode of the focused container. @@ -331,12 +331,12 @@ set|plus|minus|toggle *shortcuts_inhibitor* enable|disable Enables or disables the ability of clients to inhibit keyboard - shortcuts for a view. This is primarily useful for virtualization and - remote desktop software. It affects either the currently focused view - or a set of views selected by criteria. Subcommand _disable_ - additionally deactivates any active inhibitors for the given view(s). + shortcuts for a window. This is primarily useful for virtualization and + remote desktop software. It affects either the currently focused window + or a set of windows selected by criteria. Subcommand _disable_ + additionally deactivates any active inhibitors for the given window(s). Criteria are particularly useful with the *for_window* command to - configure a class of views differently from the per-seat defaults + configure a class of windows differently from the per-seat defaults established by the *seat* subcommand of the same name. See *sway-input*(5) for more ways to affect inhibitors. @@ -364,7 +364,7 @@ set|plus|minus|toggle Swaps the position, geometry, and fullscreen status of two containers. The first container can be selected either by criteria or focus. The second container can be selected by _id_, _con_id_, or _mark_. _id_ can only be - used with xwayland views. If the first container has focus, it will retain + used with xwayland windows. If the first container has focus, it will retain focus unless it is moved to a different workspace or the second container becomes fullscreen on the same workspace as the first container. In either of those cases, the second container will gain focus. @@ -409,14 +409,14 @@ The following commands may be used either in the configuration file or at runtime. *assign* [→] [workspace] [number] - Assigns views matching _criteria_ (see *CRITERIA* for details) to + Assigns windows matching _criteria_ (see *CRITERIA* for details) to _workspace_. The → (U+2192) is optional and cosmetic. This command is equivalent to: for_window move container to workspace *assign* [→] output left|right|up|down| - Assigns views matching _criteria_ (see *CRITERIA* for details) to the + Assigns windows matching _criteria_ (see *CRITERIA* for details) to the specified output. The → (U+2192) is optional and cosmetic. This command is equivalent to: @@ -599,10 +599,10 @@ runtime. The window that has focus. *client.focused_inactive* - The most recently focused view within a container which is not focused. + The most recently focused window within a container which is not focused. *client.focused_tab_title* - A view that has focused descendant container. + A window that has focused descendant container. Tab or stack container title that is the parent of the focused container but is not directly focused. Defaults to focused_inactive if not specified and does not use the indicator and child_border colors. @@ -611,10 +611,10 @@ runtime. Ignored (present for i3 compatibility). *client.unfocused* - A view that does not have focus. + A window that does not have focus. *client.urgent* - A view with an urgency hint. *Note*: Native Wayland windows do not + A window with an urgency hint. *Note*: Native Wayland windows do not support urgency. Urgency only works for Xwayland windows. The meaning of each color is: @@ -629,12 +629,12 @@ runtime. The text color of the title bar. _indicator_ - The color used to indicate where a new view will open. In a tiled - container, this would paint the right border of the current view if a - new view would be opened to the right. + The color used to indicate where a new window will open. In a tiled + container, this would paint the right border of the current window if a + new window would be opened to the right. _child_border_ - The border around the view itself. + The border around the window itself. The default colors are: @@ -772,7 +772,7 @@ The default colors are: *gaps* inner|outer|horizontal|vertical|top|right|bottom|left Sets default _amount_ pixels of _inner_ or _outer_ gap, where the inner - affects spacing around each view and outer affects the spacing around each + affects spacing around each window and outer affects the spacing around each workspace. Outer gaps are in addition to inner gaps. To reduce or remove outer gaps, outer gaps can be set to a negative value. _outer_ gaps can also be specified per side with _top_, _right_, _bottom_, and _left_ or @@ -844,9 +844,9 @@ The default colors are: A list of output names may be obtained via *swaymsg -t get_outputs*. *popup_during_fullscreen* smart|ignore|leave_fullscreen - Determines what to do when a fullscreen view opens a dialog. + Determines what to do when a fullscreen window opens a dialog. If _smart_ (the default), the dialog will be displayed. If _ignore_, the - dialog will not be rendered. If _leave_fullscreen_, the view will exit + dialog will not be rendered. If _leave_fullscreen_, the window will exit fullscreen mode and the dialog will be rendered. *primary_selection* enabled|disabled @@ -976,14 +976,14 @@ A criteria is a string in the form of, for example: ``` The string contains one or more (space separated) attribute/value pairs. They -are used by some commands to choose which views to execute actions on. All +are used by some commands to choose which windows to execute actions on. All attributes must match for the criteria to match. Criteria is retained across commands separated by a *,*, but will be reset (and allow for new criteria, if desired) for commands separated by a *;*. Criteria may be used with either the *for_window* or *assign* commands to -specify operations to perform on new views. A criteria may also be used to -perform specific commands (ones that normally act upon one window) on all views +specify operations to perform on new windows. A criteria may also be used to +perform specific commands (ones that normally act upon one window) on all windows that match that criteria. For example: Focus on a window with the mark "IRC": @@ -1071,8 +1071,8 @@ The following attributes may be matched with: applications and requires XWayland. *workspace* - Compare against the workspace name for this view. Can be a regular - expression. If the value is \_\_focused\_\_, then all the views on the + Compare against the workspace name for this window. Can be a regular + expression. If the value is \_\_focused\_\_, then all the windows on the currently focused workspace matches. *sandbox_engine* From 38005bd85461d1e65295aacbf7f4d00e54295032 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 18 Feb 2025 12:38:44 -0500 Subject: [PATCH 16/61] output: Expose output_configure_scene to header --- include/sway/output.h | 3 +++ sway/desktop/output.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/include/sway/output.h b/include/sway/output.h index 4584ea45..f6354e0e 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -91,6 +91,9 @@ struct sway_output *output_from_wlr_output(struct wlr_output *output); struct sway_output *output_get_in_direction(struct sway_output *reference, enum wlr_direction direction); +void output_configure_scene(struct sway_output *output, + struct wlr_scene_node *node, float opacity); + void output_add_workspace(struct sway_output *output, struct sway_workspace *workspace); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d1facdea..d6a18252 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -202,7 +202,7 @@ static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, } } -static void output_configure_scene(struct sway_output *output, +void output_configure_scene(struct sway_output *output, struct wlr_scene_node *node, float opacity) { if (!node->enabled) { return; From 0da0d37f3dcbf66befb198fcd221d8345b9814b5 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 18 Feb 2025 12:39:18 -0500 Subject: [PATCH 17/61] output: Allow configuring scene without an output --- sway/desktop/output.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index d6a18252..e2419317 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -229,7 +229,9 @@ void output_configure_scene(struct sway_output *output, // hack: don't call the scene setter because that will damage all outputs // We don't want to damage outputs that aren't our current output that // we're configuring - buffer->filter_mode = get_scale_filter(output, buffer); + if (output) { + buffer->filter_mode = get_scale_filter(output, buffer); + } wlr_scene_buffer_set_opacity(buffer, opacity); } else if (node->type == WLR_SCENE_NODE_TREE) { From 7fab75a7a6d9b2cccfec7741cc59c705b3c40f15 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 18 Feb 2025 12:41:22 -0500 Subject: [PATCH 18/61] commands/opacity: Call output_configure_scene on updated container Calling container_update() wasn't enough: If there is no visible window decorations (title bar, borders) container_update would basically no-op and the scene wouldn't repaint with the update alpha. By also calling output_configure_scene() we force a call to wlr_scene_buffer_set_opacity() thus ensuring we update the scene. Closes: #8580 --- sway/commands/opacity.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/commands/opacity.c b/sway/commands/opacity.c index 610cecc6..de058c6b 100644 --- a/sway/commands/opacity.c +++ b/sway/commands/opacity.c @@ -2,7 +2,8 @@ #include #include #include "sway/commands.h" -#include "sway/tree/view.h" +#include "sway/tree/container.h" +#include "sway/output.h" #include "log.h" struct cmd_results *cmd_opacity(int argc, char **argv) { @@ -37,6 +38,7 @@ struct cmd_results *cmd_opacity(int argc, char **argv) { } con->alpha = val; + output_configure_scene(NULL, &con->scene_tree->node, 1); container_update(con); return cmd_results_new(CMD_SUCCESS, NULL); From 8a60f30423813f9c0938e1605939db71310c7b50 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 25 Feb 2025 10:52:52 +0100 Subject: [PATCH 19/61] sway_text_node: Apply max_width when rendering max_width was applied to the source box, but not to the cairo surface. The cairo surface would therefore take on arbitrarily large dimensions according to the required dimensions to fit the text input, which if large enough would cause failures during output rendering and leave a black hole in the titlebar. --- sway/sway_text_node.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 7c781355..4bc8d996 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -87,7 +87,7 @@ static void render_backing_buffer(struct text_buffer *buffer) { } float scale = buffer->scale; - int width = ceil(buffer->props.width * scale); + int width = ceil(get_text_width(&buffer->props) * scale); int height = ceil(buffer->props.height * scale); float *color = (float *)&buffer->props.color; float *background = (float *)&buffer->props.background; @@ -153,7 +153,7 @@ static void render_backing_buffer(struct text_buffer *buffer) { pixman_region32_init(&opaque); if (background[3] == 1) { pixman_region32_union_rect(&opaque, &opaque, 0, 0, - buffer->props.width, buffer->props.height); + get_text_width(&buffer->props), buffer->props.height); } wlr_scene_buffer_set_opaque_region(buffer->buffer_node, &opaque); pixman_region32_fini(&opaque); From 962e1e70a60e9f39d2fdb6fa1810017682fd1f7b Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 25 Feb 2025 13:44:54 +0100 Subject: [PATCH 20/61] sway_text_node: Remove use of source box The source box is always set to the full buffer dimensions, making it ineffective. Remove it. --- sway/sway_text_node.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/sway/sway_text_node.c b/sway/sway_text_node.c index 4bc8d996..89ece91e 100644 --- a/sway/sway_text_node.c +++ b/sway/sway_text_node.c @@ -64,18 +64,6 @@ static int get_text_width(struct sway_text_node *props) { return MAX(width, 0); } -static void update_source_box(struct text_buffer *buffer) { - struct sway_text_node *props = &buffer->props; - struct wlr_fbox source_box = { - .x = 0, - .y = 0, - .width = ceil(get_text_width(props) * buffer->scale), - .height = ceil(props->height * buffer->scale), - }; - - wlr_scene_buffer_set_source_box(buffer->buffer_node, &source_box); -} - static void render_backing_buffer(struct text_buffer *buffer) { if (!buffer->visible) { return; @@ -147,7 +135,6 @@ static void render_backing_buffer(struct text_buffer *buffer) { wlr_scene_buffer_set_buffer(buffer->buffer_node, &cairo_buffer->base); wlr_buffer_drop(&cairo_buffer->base); - update_source_box(buffer); pixman_region32_t opaque; pixman_region32_init(&opaque); @@ -300,7 +287,6 @@ void sway_text_node_set_max_width(struct sway_text_node *node, int max_width) { buffer->props.max_width = max_width; wlr_scene_buffer_set_dest_size(buffer->buffer_node, get_text_width(&buffer->props), buffer->props.height); - update_source_box(buffer); render_backing_buffer(buffer); } From e3d9cc2aa5f1c298fd956b64e5e20f50aaac72fe Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 11 Feb 2025 12:41:59 +0100 Subject: [PATCH 21/61] Rework fork/exec strategy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cmd_exec_process is used whenever sway is meant to execute a child process on behalf of the user, and had a lot of complexity. In order to avoid having to wait on the user's process, a double-fork was used, which in turn required us to wait on the outer process. In order to track the child PID for launcher purposes, a pipe was used to transmit the PID back to sway. This resulted in sway blocking for 5-6 ms per exec on my system, which is quite significant. The error handling was also quite lacking - the read loop did not handle errors at all for example. Instead, teach sway to handle SIGCHLD and do away with the double-fork. This in turn allows us to get rid of the pipe as we can record the child's PID directly. This reduces the time we block to just 1.5 ms on my system. We'd be able to get down to just 150 µs if we could use posix_spawn(3), but posix_spawn(3) cannot reset NOFILE. clone(2) or vfork(2) would be alternatives, but that presents portability issues. This change is replicated for swaybar, swaybg and swaynag handling, which had similar albeit less complicated implementations. --- sway/commands/exec_always.c | 75 ++++++++++--------------------------- sway/config/bar.c | 39 +++++++------------ sway/config/output.c | 40 +++++++------------- sway/main.c | 8 ++++ sway/swaynag.c | 51 ++++++++++--------------- 5 files changed, 73 insertions(+), 140 deletions(-) diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 8bc1048c..14566fc4 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -25,16 +25,6 @@ struct cmd_results *cmd_exec_validate(int argc, char **argv) { return error; } -static void export_xdga_token(struct launcher_ctx *ctx) { - const char *token = launcher_ctx_get_token_name(ctx); - setenv("XDG_ACTIVATION_TOKEN", token, 1); -} - -static void export_startup_id(struct launcher_ctx *ctx) { - const char *token = launcher_ctx_get_token_name(ctx); - setenv("DESKTOP_STARTUP_ID", token, 1); -} - struct cmd_results *cmd_exec_process(int argc, char **argv) { struct cmd_results *error = NULL; char *cmd = NULL; @@ -56,67 +46,42 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { sway_log(SWAY_DEBUG, "Executing %s", cmd); - int fd[2]; - if (pipe(fd) != 0) { - sway_log(SWAY_ERROR, "Unable to create pipe for fork"); - } - - pid_t pid, child; struct launcher_ctx *ctx = launcher_ctx_create_internal(); + // Fork process - if ((pid = fork()) == 0) { - // Fork child process again + pid_t child = fork(); + if (child == 0) { restore_nofile_limit(); setsid(); sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); signal(SIGPIPE, SIG_DFL); - close(fd[0]); - if ((child = fork()) == 0) { - close(fd[1]); - if (ctx) { - export_xdga_token(ctx); + + if (ctx) { + const char *token = launcher_ctx_get_token_name(ctx); + setenv("XDG_ACTIVATION_TOKEN", token, 1); + if (!no_startup_id) { + setenv("DESKTOP_STARTUP_ID", token, 1); } - if (ctx && !no_startup_id) { - export_startup_id(ctx); - } - execlp("sh", "sh", "-c", cmd, (void *)NULL); - sway_log_errno(SWAY_ERROR, "execlp failed"); - _exit(1); } - ssize_t s = 0; - while ((size_t)s < sizeof(pid_t)) { - s += write(fd[1], ((uint8_t *)&child) + s, sizeof(pid_t) - s); - } - close(fd[1]); + + execlp("sh", "sh", "-c", cmd, (void*)NULL); + sway_log_errno(SWAY_ERROR, "execve failed"); _exit(0); // Close child process - } else if (pid < 0) { + } else if (child < 0) { + launcher_ctx_destroy(ctx); free(cmd); - close(fd[0]); - close(fd[1]); return cmd_results_new(CMD_FAILURE, "fork() failed"); } - free(cmd); - close(fd[1]); // close write - ssize_t s = 0; - while ((size_t)s < sizeof(pid_t)) { - s += read(fd[0], ((uint8_t *)&child) + s, sizeof(pid_t) - s); - } - close(fd[0]); - // cleanup child process - waitpid(pid, NULL, 0); - if (child > 0) { - sway_log(SWAY_DEBUG, "Child process created with pid %d", child); - if (ctx != NULL) { - sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); - ctx->pid = child; - } - } else { - launcher_ctx_destroy(ctx); - return cmd_results_new(CMD_FAILURE, "Second fork() failed"); + + sway_log(SWAY_DEBUG, "Child process created with pid %d", child); + if (ctx != NULL) { + sway_log(SWAY_DEBUG, "Recording workspace for process %d", child); + ctx->pid = child; } + free(cmd); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/config/bar.c b/sway/config/bar.c index ecefb61a..f7eddfbb 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -220,29 +220,21 @@ static void invoke_swaybar(struct bar_config *bar) { signal(SIGPIPE, SIG_DFL); restore_nofile_limit(); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_set_cloexec(sockets[1], false)) { - _exit(EXIT_FAILURE); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - // run custom swaybar - char *const cmd[] = { - bar->swaybar_command ? bar->swaybar_command : "swaybar", - "-b", bar->id, NULL}; - execvp(cmd[0], cmd); + if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } - _exit(EXIT_SUCCESS); + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + // run custom swaybar + char *const cmd[] = { + bar->swaybar_command ? bar->swaybar_command : "swaybar", + "-b", bar->id, NULL}; + execvp(cmd[0], cmd); + _exit(EXIT_FAILURE); } if (close(sockets[1]) != 0) { @@ -250,11 +242,6 @@ static void invoke_swaybar(struct bar_config *bar) { return; } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return; - } - sway_log(SWAY_DEBUG, "Spawned swaybar %s", bar->id); } diff --git a/sway/config/output.c b/sway/config/output.c index 9fb3a12a..4d4a47b4 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1061,41 +1061,27 @@ static bool _spawn_swaybg(char **command) { return false; } else if (pid == 0) { restore_nofile_limit(); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_set_cloexec(sockets[1], false)) { - _exit(EXIT_FAILURE); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - execvp(command[0], command); - sway_log_errno(SWAY_ERROR, "failed to execute '%s' " - "(background configuration probably not applied)", - command[0]); + if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } - _exit(EXIT_SUCCESS); + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + execvp(command[0], command); + sway_log_errno(SWAY_ERROR, "failed to execute '%s' " + "(background configuration probably not applied)", + command[0]); + _exit(EXIT_FAILURE); } if (close(sockets[1]) != 0) { sway_log_errno(SWAY_ERROR, "close failed"); return false; } - int fork_status = 0; - if (waitpid(pid, &fork_status, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - - return WIFEXITED(fork_status) && WEXITSTATUS(fork_status) == EXIT_SUCCESS; + return true; } bool spawn_swaybg(void) { diff --git a/sway/main.c b/sway/main.c index accffc71..0cc7623d 100644 --- a/sway/main.c +++ b/sway/main.c @@ -48,6 +48,13 @@ void sig_handler(int signal) { sway_terminate(EXIT_SUCCESS); } +void sigchld_handler(int signal) { + pid_t pid; + do { + pid = waitpid(-1, NULL, WNOHANG); + } while (pid > 0); +} + void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -325,6 +332,7 @@ int main(int argc, char **argv) { // handle SIGTERM signals signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); + signal(SIGCHLD, sigchld_handler); // prevent ipc from crashing sway signal(SIGPIPE, SIG_IGN); diff --git a/sway/swaynag.c b/sway/swaynag.c index bc5e23ea..f0a31218 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -64,35 +64,27 @@ bool swaynag_spawn(const char *swaynag_command, goto failed; } else if (pid == 0) { restore_nofile_limit(); - - pid = fork(); - if (pid < 0) { - sway_log_errno(SWAY_ERROR, "fork failed"); - _exit(EXIT_FAILURE); - } else if (pid == 0) { - if (!sway_set_cloexec(sockets[1], false)) { - _exit(EXIT_FAILURE); - } - - if (swaynag->detailed) { - close(swaynag->fd[1]); - dup2(swaynag->fd[0], STDIN_FILENO); - close(swaynag->fd[0]); - } - - char wayland_socket_str[16]; - snprintf(wayland_socket_str, sizeof(wayland_socket_str), - "%d", sockets[1]); - setenv("WAYLAND_SOCKET", wayland_socket_str, true); - - size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; - char *cmd = malloc(length); - snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); - execlp("sh", "sh", "-c", cmd, NULL); - sway_log_errno(SWAY_ERROR, "execlp failed"); + if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } - _exit(EXIT_SUCCESS); + + if (swaynag->detailed) { + close(swaynag->fd[1]); + dup2(swaynag->fd[0], STDIN_FILENO); + close(swaynag->fd[0]); + } + + char wayland_socket_str[16]; + snprintf(wayland_socket_str, sizeof(wayland_socket_str), + "%d", sockets[1]); + setenv("WAYLAND_SOCKET", wayland_socket_str, true); + + size_t length = strlen(swaynag_command) + strlen(swaynag->args) + 2; + char *cmd = malloc(length); + snprintf(cmd, length, "%s %s", swaynag_command, swaynag->args); + execlp("sh", "sh", "-c", cmd, NULL); + sway_log_errno(SWAY_ERROR, "execlp failed"); + _exit(EXIT_FAILURE); } if (swaynag->detailed) { @@ -107,11 +99,6 @@ bool swaynag_spawn(const char *swaynag_command, return false; } - if (waitpid(pid, NULL, 0) < 0) { - sway_log_errno(SWAY_ERROR, "waitpid failed"); - return false; - } - return true; failed: From 048e304b8a54fc31b53b716538971c0c2315d4c2 Mon Sep 17 00:00:00 2001 From: Chris Perl Date: Fri, 7 Mar 2025 10:55:52 -0500 Subject: [PATCH 22/61] Remove constraint that con->view != NULL to use __focused__ criteria To use something like: [con_id=__focused__] mark --add --toggle foo The container must currently have a view. However, it is possible to focus parent containers that do not have a view. For example, via: focus parent Since containers without views can be the focused (meaning the container is marked "focused": true in the output of: swaymsg -t get_tree), it seems reasonable that a view is not required to target a container via __focused__. --- sway/criteria.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/criteria.c b/sway/criteria.c index 0aefa008..29f73f69 100644 --- a/sway/criteria.c +++ b/sway/criteria.c @@ -630,8 +630,7 @@ static bool parse_token(struct criteria *criteria, char *name, char *value) { if (strcmp(value, "__focused__") == 0) { struct sway_seat *seat = input_manager_current_seat(); struct sway_container *focus = seat_get_focused_container(seat); - struct sway_view *view = focus ? focus->view : NULL; - criteria->con_id = view ? view->container->node.id : 0; + criteria->con_id = focus ? focus->node.id : 0; } else { criteria->con_id = strtoul(value, &endptr, 10); if (*endptr != 0) { From 9dcccf784bf89e54e5a6f65f72c116943f3b30ea Mon Sep 17 00:00:00 2001 From: nilninull Date: Sun, 4 Feb 2024 17:20:45 +0900 Subject: [PATCH 23/61] Add the DesktopNames key to the sway.desktop session file According to https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html, to set the value of XDG_CURRENT_DESKTOP, a DesktopNames key is required in the session file. And the value of XDG_CURRENT_DESKTOP is used to run xdg-desktop-portal*. If this value is not set, xdg-desktop-portal-wlr will not run at login. --- sway.desktop | 1 + 1 file changed, 1 insertion(+) diff --git a/sway.desktop b/sway.desktop index 420db5aa..cc7fcee7 100644 --- a/sway.desktop +++ b/sway.desktop @@ -3,3 +3,4 @@ Name=Sway Comment=An i3-compatible Wayland compositor Exec=sway Type=Application +DesktopNames=sway;wlroots From 5d7b9a8320f8999059f287734c1df76289b01a27 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 9 Mar 2025 02:08:43 +0530 Subject: [PATCH 24/61] sway/server: create ext-data-control manager --- include/sway/server.h | 3 ++- sway/server.c | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 7b76eb90..95c9c45b 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -104,7 +104,8 @@ struct sway_server { 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; + struct wlr_data_control_manager_v1 *wlr_data_control_manager_v1; + struct wlr_ext_data_control_manager_v1 *ext_data_control_manager_v1; struct wlr_screencopy_manager_v1 *screencopy_manager_v1; struct wlr_ext_image_copy_capture_manager_v1 *ext_image_copy_capture_manager_v1; struct wlr_export_dmabuf_manager_v1 *export_dmabuf_manager_v1; diff --git a/sway/server.c b/sway/server.c index c7fc2a6d..5de6648f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -108,7 +109,8 @@ static bool is_privileged(const struct wl_global *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.wlr_data_control_manager_v1->global || + global == server.ext_data_control_manager_v1->global || global == server.screencopy_manager_v1->global || global == server.ext_image_copy_capture_manager_v1->global || global == server.export_dmabuf_manager_v1->global || @@ -376,7 +378,8 @@ bool server_init(struct sway_server *server) { server->screencopy_manager_v1 = wlr_screencopy_manager_v1_create(server->wl_display); server->ext_image_copy_capture_manager_v1 = wlr_ext_image_copy_capture_manager_v1_create(server->wl_display, 1); wlr_ext_output_image_capture_source_manager_v1_create(server->wl_display, 1); - server->data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + server->wlr_data_control_manager_v1 = wlr_data_control_manager_v1_create(server->wl_display); + server->ext_data_control_manager_v1 = wlr_ext_data_control_manager_v1_create(server->wl_display, 1); server->security_context_manager_v1 = wlr_security_context_manager_v1_create(server->wl_display); wlr_viewporter_create(server->wl_display); wlr_single_pixel_buffer_manager_v1_create(server->wl_display); From 8238e5242bdbbc4c3b7cba0651c620a89b872a27 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 9 Mar 2025 12:07:11 +0100 Subject: [PATCH 25/61] Use SIG_IGN for SIGCHLD instead of our own handler The behavior of handlers registered with signal(3p) is not well-defined for signals delivered more than once, as laid out in the man page. We should replace our use of signal with sigaction, but for SIGCHLD specifically we can also just skip the signals altogether by setting the handler to SIG_IGN which causes child reaping to not be required. Fixes: https://github.com/swaywm/sway/pull/8567 --- include/sway/server.h | 1 + sway/commands/exec_always.c | 5 +---- sway/config/bar.c | 7 +------ sway/main.c | 19 +++++++++++-------- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 95c9c45b..feb516c5 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -163,6 +163,7 @@ bool server_start(struct sway_server *server); void server_run(struct sway_server *server); void restore_nofile_limit(void); +void restore_signals(void); void handle_new_output(struct wl_listener *listener, void *data); diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 14566fc4..8f02bbdc 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -52,11 +52,8 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { pid_t child = fork(); if (child == 0) { restore_nofile_limit(); + restore_signals(); setsid(); - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGPIPE, SIG_DFL); if (ctx) { const char *token = launcher_ctx_get_token_name(ctx); diff --git a/sway/config/bar.c b/sway/config/bar.c index f7eddfbb..6cace0da 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -213,13 +213,8 @@ static void invoke_swaybar(struct bar_config *bar) { sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); return; } else if (pid == 0) { - // Remove the SIGUSR1 handler that wlroots adds for xwayland - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGPIPE, SIG_DFL); - restore_nofile_limit(); + restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } diff --git a/sway/main.c b/sway/main.c index 0cc7623d..cabdd3aa 100644 --- a/sway/main.c +++ b/sway/main.c @@ -48,13 +48,6 @@ void sig_handler(int signal) { sway_terminate(EXIT_SUCCESS); } -void sigchld_handler(int signal) { - pid_t pid; - do { - pid = waitpid(-1, NULL, WNOHANG); - } while (pid > 0); -} - void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -159,6 +152,14 @@ void restore_nofile_limit(void) { } } +void restore_signals(void) { + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +} + void enable_debug_flag(const char *flag) { if (strcmp(flag, "noatomic") == 0) { debug.noatomic = true; @@ -332,7 +333,9 @@ int main(int argc, char **argv) { // handle SIGTERM signals signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); - signal(SIGCHLD, sigchld_handler); + + // avoid need to reap children + signal(SIGCHLD, SIG_IGN); // prevent ipc from crashing sway signal(SIGPIPE, SIG_IGN); From 61cc08cf3c49b0a5785b50c070ef3c33f1bbacab Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 9 Mar 2025 12:17:42 +0100 Subject: [PATCH 26/61] config/output: Reset everything before swaybg exec swaybar and the exec command reset signal masks, signal handlers and NOFILE limit before exec, but swaybg was missing all that. Reset it for swaybg as well. --- sway/config/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/config/output.c b/sway/config/output.c index 4d4a47b4..b8a613cc 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1061,6 +1061,7 @@ static bool _spawn_swaybg(char **command) { return false; } else if (pid == 0) { restore_nofile_limit(); + restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } From 2f5b3c099965b72f4c14110fb42afc1a9d537029 Mon Sep 17 00:00:00 2001 From: melvinm1 <108258563+melvinm1@users.noreply.github.com> Date: Sun, 16 Mar 2025 00:29:15 +0100 Subject: [PATCH 27/61] Fix output repositioning in global fullscreen Call wlr_scene_output_set_position when in global fullscreen to correctly set output positions when repositioning outputs (using swaymsg output or similar). --- sway/desktop/transaction.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 50a597a6..52c03b18 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -652,6 +652,8 @@ static void arrange_root(struct sway_root *root) { struct sway_output *output = root->outputs->items[i]; struct sway_workspace *ws = output->current.active_workspace; + wlr_scene_output_set_position(output->scene_output, output->lx, output->ly); + if (ws) { arrange_workspace_floating(ws); } From 3a49409dae9b8d579a4b92370ff5ba4a48cb5156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Piwo=C5=84ski?= Date: Wed, 12 Mar 2025 11:56:52 +0000 Subject: [PATCH 28/61] sway/commands: Return error if container is not in scratchpad --- sway/commands/scratchpad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/commands/scratchpad.c b/sway/commands/scratchpad.c index c995f2f0..8a63740c 100644 --- a/sway/commands/scratchpad.c +++ b/sway/commands/scratchpad.c @@ -118,10 +118,10 @@ struct cmd_results *cmd_scratchpad(int argc, char **argv) { // If using criteria, this command is executed for every container which // matches the criteria. If this container isn't in the scratchpad, - // we'll just silently return a success. The same is true if the + // we'll return an error. The same is true if the // overridden node is not a container. if (!con || !con->scratchpad) { - return cmd_results_new(CMD_SUCCESS, NULL); + return cmd_results_new(CMD_INVALID, "Container is not in scratchpad."); } scratchpad_toggle_container(con); } else { From 30434b2beb0c621015452775011426da8d5e4705 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 19 Mar 2025 00:15:01 +0100 Subject: [PATCH 29/61] desktop/output: Skip repaint if wlr_output is disabled When the repaint timer fires, we check if the sway_output is disabled, and if so, skip the output commit after having reset frame_pending. The sway_output enable flag is only updated if the output is disabled and removed from the layout, not if the power is disabled for e.g. idle. This can lead to situations where a commit is attempted on a disabled output, which will lead to an attempted and failed primary swapchain allocation. Use the wlr_output.enabled state to check if the output is active. --- sway/desktop/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index e2419317..aec1d0a6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -264,7 +264,7 @@ static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; output->wlr_output->frame_pending = false; - if (!output->enabled) { + if (!output->wlr_output->enabled) { return 0; } From d148560f50ce81bd5ca0e0f0d52c65c21f8b751d Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Thu, 20 Mar 2025 21:58:21 +0900 Subject: [PATCH 30/61] text_input: Fix ime panic in ext-session-lock in the origin text_input.c, we only check the sway_view and layershell, but now we have the third shell named sessionlock, so we need to modify both text_input.c and view.c to handle the new type of shell --- sway/input/text_input.c | 25 +++++++++++++++++++++++++ sway/tree/view.c | 4 ++++ 2 files changed, 29 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index e1672467..a46f833c 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -10,6 +10,7 @@ #include "sway/input/text_input_popup.h" #include "sway/layers.h" #include "sway/server.h" +#include static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { @@ -385,6 +386,8 @@ static void input_popup_set_focus(struct sway_input_popup *popup, struct wlr_layer_surface_v1 *layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(surface); + struct wlr_session_lock_surface_v1 *lock_surface = + wlr_session_lock_surface_v1_try_from_wlr_surface(surface); struct wlr_scene_tree *relative_parent; if (layer_surface) { @@ -404,8 +407,30 @@ static void input_popup_set_focus(struct sway_input_popup *popup, // surface. Layer surfaces get destroyed as part of the output being // destroyed, thus also trickling down to popups. popup->fixed_output = layer->layer_surface->output; + } else if (lock_surface) { + wl_signal_add(&lock_surface->surface->events.unmap, + &popup->focused_surface_unmap); + + struct sway_layer_surface *lock = lock_surface->data; + if (lock == NULL) { + return; + } + + relative_parent = lock->scene->tree; + popup->desc.view = NULL; + + // we don't need to add an event here to NULL out this field because + // this field will only be initialized if the popup is part of a layer + // surface. Layer surfaces get destroyed as part of the output being + // destroyed, thus also trickling down to popups. + popup->fixed_output = lock->layer_surface->output; } else { struct sway_view *view = view_from_wlr_surface(surface); + // In the future there may be other shells been added, so we also need to check here. + if (view == NULL) { + sway_log(SWAY_DEBUG, "Unsupported IME focus surface"); + return; + } wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); relative_parent = view->scene_tree; popup->desc.view = view; diff --git a/sway/tree/view.c b/sway/tree/view.c index 33161cc5..a5617fb4 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -12,6 +12,7 @@ #include #include #include +#include #if WLR_HAS_XWAYLAND #include #endif @@ -1010,6 +1011,9 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { return NULL; } + if (wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface) != NULL) { + return NULL; + } const char *role = wlr_surface->role ? wlr_surface->role->name : NULL; sway_log(SWAY_DEBUG, "Surface of unknown type (role %s): %p", From 4b185a0fe0031455d5ceab1eda2b9d9ffe0c81de Mon Sep 17 00:00:00 2001 From: Paul Riou Date: Thu, 20 Mar 2025 20:01:36 +0000 Subject: [PATCH 31/61] stringop: fix has_prefix() arg order in config parsing has_prefix() expects the prefix to be the 2nd argument, not the first. The config parsing was broken when using `--input-device=`. Introduced by: 0c60d1581f7b12ae472c786b7dfe27a1c6ec9a47 "Use has_prefix() instead of strncmp() throughout" --- sway/commands/bind.c | 2 +- sway/commands/gesture.c | 2 +- swaybar/ipc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 32d4f891..15373d5a 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -367,7 +367,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, } } else if (strcmp("--exclude-titlebar", argv[0]) == 0) { exclude_titlebar = true; - } else if (has_prefix("--input-device=", argv[0])) { + } else if (has_prefix(argv[0], "--input-device=")) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); strip_quotes(binding->input); diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c index e63b7ac6..8dff29a3 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c @@ -121,7 +121,7 @@ static struct cmd_results *cmd_bind_or_unbind_gesture(int argc, char **argv, boo binding->flags |= BINDING_EXACT; } else if (strcmp("--no-warn", argv[0]) == 0) { warn = false; - } else if (has_prefix("--input-device=", argv[0])) { + } else if (has_prefix(argv[0], "--input-device=")) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); } else { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index f651f035..68d8dd32 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -46,7 +46,7 @@ void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { char *parse_font(const char *font) { char *new_font = NULL; - if (has_prefix("pango:", font)) { + if (has_prefix(font, "pango:")) { font += strlen("pango:"); } new_font = strdup(font); From c2d6aff64c1e265c8f1d95b780b54193defae18a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 12 Mar 2025 16:35:41 +0100 Subject: [PATCH 32/61] Avoid crashing on too many containers If far too many containers are created, they can become so small that their size calculations come out negative, leading to crashes on asserts. Instead, set a lower bound for sizes and disable the container entirely if it goes below it, giving whatever space it used to the last container. The splits are not recalculated, so currently the effect is that if all containers have the same width fraction, they keep getting narrower until at some point they all round to zero and the last container will be given all the available space. A better behavior would have been if the additional container did not contribute to size and fraction calculations at all, but it's an extreme edge-case, anything is better than crashing, and this is easier to implement. --- sway/desktop/transaction.c | 27 ++++++++++++++++++--------- sway/tree/arrange.c | 18 +++++++++++++++--- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 52c03b18..16ce8e2e 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -312,9 +312,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - if (activated) { - arrange_container(child, width, height - title_bar_height, - title_bar_height == 0, 0); + height -= title_bar_height; + if (activated && width > 0 && height > 0) { + arrange_container(child, width, height, title_bar_height == 0, 0); } else { disable_container(child); } @@ -341,9 +341,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - if (activated) { - arrange_container(child, width, height - title_height, - title_bar_height == 0, 0); + height -= title_bar_height; + if (activated && width > 0 && height > 0) { + arrange_container(child, width, height, title_bar_height == 0, 0); } else { disable_container(child); } @@ -359,8 +359,12 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_enabled(&child->border.tree->node, true); wlr_scene_node_set_position(&child->scene_tree->node, 0, off); wlr_scene_node_reparent(&child->scene_tree->node, content); - arrange_container(child, width, cheight, true, gaps); - off += cheight + gaps; + if (width > 0 && cheight > 0) { + arrange_container(child, width, cheight, true, gaps); + off += cheight + gaps; + } else { + disable_container(child); + } } } else if (layout == L_HORIZ) { int off = 0; @@ -372,7 +376,12 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, off, 0); wlr_scene_node_reparent(&child->scene_tree->node, content); arrange_container(child, cwidth, height, true, gaps); - off += cwidth + gaps; + if (cwidth > 0 && height > 0) { + arrange_container(child, cwidth, height, true, gaps); + off += cwidth + gaps; + } else { + disable_container(child); + } } } else { sway_assert(false, "unreachable"); diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 2b95a6dc..faf54d02 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -29,7 +29,7 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { } } - // Calculate each height fraction + // Calculate each width fraction double total_width_fraction = 0; for (int i = 0; i < children->length; ++i) { struct sway_container *child = children->items[i]; @@ -82,12 +82,18 @@ static void apply_horiz_layout(list_t *children, struct wlr_box *parent) { child->pending.y = parent->y; child->pending.width = round(child->width_fraction * child_total_width); child->pending.height = parent->height; - child_x += child->pending.width + inner_gap; // Make last child use remaining width of parent if (i == children->length - 1) { child->pending.width = parent->x + parent->width - child->pending.x; } + + // Arbitrary lower bound for window size + if (child->pending.width < 10 || child->pending.height < 10) { + child->pending.width = 0; + child->pending.height = 0; + } + child_x += child->pending.width + inner_gap; } } @@ -161,12 +167,18 @@ static void apply_vert_layout(list_t *children, struct wlr_box *parent) { child->pending.y = child_y; child->pending.width = parent->width; child->pending.height = round(child->height_fraction * child_total_height); - child_y += child->pending.height + inner_gap; // Make last child use remaining height of parent if (i == children->length - 1) { child->pending.height = parent->y + parent->height - child->pending.y; } + + // Arbitrary lower bound for window size + if (child->pending.width < 10 || child->pending.height < 10) { + child->pending.width = 0; + child->pending.height = 0; + } + child_y += child->pending.height + inner_gap; } } From ab455bbadae5f115262161c165fdd46d1cc4295d Mon Sep 17 00:00:00 2001 From: Dennis Baurichter Date: Tue, 11 Feb 2025 18:06:19 +0100 Subject: [PATCH 33/61] man: clarify criteria (incl. PCRE2 usage) Replace the XWayland-only class attribute in the examples: - The first example given should work for Wayland-native windows. - The example 'Kill all windows with the title "Emacs"' should use title, not class. Also, it's a substring (regex) match. There are many different implementations of regular expressions with incompatible syntax. For example, GNU grep alone provides three different ones. Clarify the use of PCRE2 by sway criteria. --- sway/sway.5.scd | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index f580b2af..9bc03c9d 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -972,7 +972,7 @@ The default colors are: A criteria is a string in the form of, for example: ``` -[class="[Rr]egex.*" title="some title"] +[app_id="some-application" title="[Rr]egex.*"] ``` The string contains one or more (space separated) attribute/value pairs. They @@ -992,10 +992,19 @@ Focus on a window with the mark "IRC": [con_mark="IRC"] focus ``` -Kill all windows with the title "Emacs": +Kill all windows where the title contains "Emacs": ``` -[class="Emacs"] kill +[title="Emacs"] kill +``` + +Several attributes allow regular expressions. These use Perl-compatible regular +expressions (PCRE2), which are documented in *pcre2pattern*(3) and summarized in +*pcre2syntax*(3). For example, this moves all windows with titles ending in +"sway" or "Sway" to workspace 1: + +``` +[title="[Ss]way$"] move workspace 1 ``` You may like to use swaymsg -t get_tree for finding the values of these @@ -1094,3 +1103,4 @@ The following attributes may be matched with: # SEE ALSO *sway*(1) *sway-input*(5) *sway-output*(5) *sway-bar*(5) *sway-ipc*(7) +*pcre2pattern*(3) *pcre2syntax*(3) From a25645a5a6e205fccee1ca5abc00f8ee31b3bf44 Mon Sep 17 00:00:00 2001 From: Claudia Date: Wed, 26 Mar 2025 19:34:44 +0100 Subject: [PATCH 34/61] Fix tabbed/stacking container height regression Commit c2d6aff added a bounds check on `height - title_bar_height`, repurposing the local variable `height` in an attempt to DRY out the expression. However, because re-assignment occurs inside the loop body, its result would leak across loop iterations, compounding its effect and leading to the artifact reported in issue #8625, where each child except the first in a tabbed container would acquire a visible waterline. Introduce a second variable and reset it in each loop iteration to get rid of the waterline. Fixes #8625. --- sway/desktop/transaction.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 16ce8e2e..01fe3128 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -312,9 +312,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_bar_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - height -= title_bar_height; - if (activated && width > 0 && height > 0) { - arrange_container(child, width, height, title_bar_height == 0, 0); + int net_height = height - title_bar_height; + if (activated && width > 0 && net_height > 0) { + arrange_container(child, width, net_height, title_bar_height == 0, 0); } else { disable_container(child); } @@ -341,9 +341,9 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_position(&child->scene_tree->node, 0, title_height); wlr_scene_node_reparent(&child->scene_tree->node, content); - height -= title_bar_height; - if (activated && width > 0 && height > 0) { - arrange_container(child, width, height, title_bar_height == 0, 0); + int net_height = height - title_bar_height; + if (activated && width > 0 && net_height > 0) { + arrange_container(child, width, net_height, title_bar_height == 0, 0); } else { disable_container(child); } From cb246cb9c2c1440e05998d82eada21eaba107d59 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Tue, 8 Apr 2025 17:13:03 -0400 Subject: [PATCH 35/61] ipc: standardize pretty print with raw print `swaymsg -t get_inputs --raw` calls it a pointer but `--pretty` calls it a Mouse. Previous commit 6737b90cb that set this to pointer probably forgo to update the pretty one. closes #8584 --- swaymsg/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swaymsg/main.c b/swaymsg/main.c index 7534ea6d..dc1c7407 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -99,7 +99,7 @@ static const char *pretty_type_name(const char *name) { const char *b; } type_names[] = { { "keyboard", "Keyboard" }, - { "pointer", "Mouse" }, + { "pointer", "Pointer" }, { "touchpad", "Touchpad" }, { "tablet_pad", "Tablet pad" }, { "tablet_tool", "Tablet tool" }, From 8f089f0229eabbcd3f68d641d5b826220f1edb0c Mon Sep 17 00:00:00 2001 From: Daniel De Graaf Date: Sun, 9 Mar 2025 22:51:48 -0400 Subject: [PATCH 36/61] Use wl_event_loop_add_signal for exit signals This avoids calling non-async-signal-safe functions from within a signal handler. --- sway/main.c | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/sway/main.c b/sway/main.c index cabdd3aa..0af977d4 100644 --- a/sway/main.c +++ b/sway/main.c @@ -44,10 +44,6 @@ void sway_terminate(int exit_code) { } } -void sig_handler(int signal) { - sway_terminate(EXIT_SUCCESS); -} - void run_as_ipc_client(char *command, char *socket_path) { int socketfd = ipc_open_socket(socket_path); uint32_t len = strlen(command); @@ -152,6 +148,22 @@ void restore_nofile_limit(void) { } } +static int term_signal(int signal, void *data) { + sway_terminate(EXIT_SUCCESS); + return 0; +} + +static void init_signals(void) { + wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); + wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); + + // avoid need to reap children + signal(SIGCHLD, SIG_IGN); + + // prevent ipc write errors from crashing sway + signal(SIGPIPE, SIG_IGN); +} + void restore_signals(void) { sigset_t set; sigemptyset(&set); @@ -330,22 +342,14 @@ int main(int argc, char **argv) { increase_nofile_limit(); - // handle SIGTERM signals - signal(SIGTERM, sig_handler); - signal(SIGINT, sig_handler); - - // avoid need to reap children - signal(SIGCHLD, SIG_IGN); - - // prevent ipc from crashing sway - signal(SIGPIPE, SIG_IGN); - sway_log(SWAY_INFO, "Starting sway version " SWAY_VERSION); if (!server_init(&server)) { return 1; } + init_signals(); + if (server.linux_dmabuf_v1) { wlr_scene_set_linux_dmabuf_v1(root->root_scene, server.linux_dmabuf_v1); } From 5e6a6ea3404330f99d2425d20cd9e298164d5988 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 23 May 2024 14:34:24 +0200 Subject: [PATCH 37/61] idle_inhibit: Ignore inhibitors when locked When a session is locked, no views are visible and there is no way to interact with the user session. This means that all inhibitors based on visibility - with the exception being inhibitors on the session lock surfaces themselves - become inert, allowing the session to go idle. The only inhibitor type on normal views that one could argue should remain active is INHIBIT_IDLE_OPEN, but for now we disable all view inhibitors regardless of type. --- sway/desktop/idle_inhibit_v1.c | 14 ++++++++++++++ sway/lock.c | 7 +++++++ sway/tree/view.c | 4 ++++ 3 files changed, 25 insertions(+) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index f3af7aa1..d241cdaf 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -1,5 +1,6 @@ #include #include +#include #include "log.h" #include "sway/desktop/idle_inhibit_v1.h" #include "sway/input/seat.h" @@ -103,6 +104,19 @@ void sway_idle_inhibit_v1_user_inhibitor_destroy( } bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { + if (server.session_lock.lock) { + // A session lock is active. In this case, only application inhibitors + // on the session lock surface can have any effect. + if (inhibitor->mode != INHIBIT_IDLE_APPLICATION) { + return false; + } + struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; + if (!wlr_session_lock_surface_v1_try_from_wlr_surface(wlr_surface)) { + return false; + } + return wlr_surface->mapped; + } + switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION:; // If there is no view associated with the inhibitor, assume visible diff --git a/sway/lock.c b/sway/lock.c index 43f31330..c8975c74 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -234,6 +234,9 @@ static void handle_unlock(struct wl_listener *listener, void *data) { struct sway_output *output = root->outputs->items[i]; arrange_layers(output); } + + // Views are now visible, so check if we need to activate inhibition again. + sway_idle_inhibit_v1_check_active(); } static void handle_abandon(struct wl_listener *listener, void *data) { @@ -297,6 +300,10 @@ static void handle_session_lock(struct wl_listener *listener, void *data) { wlr_session_lock_v1_send_locked(lock); server.session_lock.lock = sway_lock; + + // The lock screen covers everything, so check if any active inhibition got + // deactivated due to lost visibility. + sway_idle_inhibit_v1_check_active(); } static void handle_session_lock_destroy(struct wl_listener *listener, void *data) { diff --git a/sway/tree/view.c b/sway/tree/view.c index a5617fb4..16080a2f 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -218,6 +218,10 @@ uint32_t view_configure(struct sway_view *view, double lx, double ly, int width, } bool view_inhibit_idle(struct sway_view *view) { + if (server.session_lock.lock) { + return false; + } + struct sway_idle_inhibitor_v1 *user_inhibitor = sway_idle_inhibit_v1_user_inhibitor_for_view(view); From 541183b3228b986b5d09f12513506c386456df7e Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 23 May 2024 15:01:46 +0200 Subject: [PATCH 38/61] idle_inhibit: Explicitly handle layer surfaces Layer surfaces do not have a view, and while they can be occluded they are always visible on their associated output - assuming it is enabled. --- sway/desktop/idle_inhibit_v1.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index d241cdaf..0fc79989 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -119,8 +119,15 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION:; + struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; + if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface)) { + // Layer surfaces can be occluded but are always on screen after + // they have been mapped. + return wlr_surface->mapped; + } + // If there is no view associated with the inhibitor, assume visible - struct sway_view *view = view_from_wlr_surface(inhibitor->wlr_inhibitor->surface); + struct sway_view *view = view_from_wlr_surface(wlr_surface); return !view || !view->container || view_is_visible(view); case INHIBIT_IDLE_FOCUS:; struct sway_seat *seat = NULL; From cc482228a41e9e3e31b6b27143d5ed24e7dc5069 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 23 May 2024 15:03:44 +0200 Subject: [PATCH 39/61] idle_inhibit: Assume view is invisible by default We have historically considered surfaces without a view visible. This made sense in case of layer surfaces which do not have a view, but it also allows unmapped surfaces to act as global inhibitors irrespective of the current view state, which is not the intention fo the protocol. As we now explicitly handle layer surfaces, assume that views are only visible if they can be found and their visibility checked. --- sway/desktop/idle_inhibit_v1.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 0fc79989..9fc223d0 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -126,9 +126,9 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { return wlr_surface->mapped; } - // If there is no view associated with the inhibitor, assume visible + // If there is no view associated with the inhibitor, assume invisible struct sway_view *view = view_from_wlr_surface(wlr_surface); - return !view || !view->container || view_is_visible(view); + return view && view->container && view_is_visible(view); case INHIBIT_IDLE_FOCUS:; struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { From 583862e6d11c44bb7a66dbc4dbdf029b517df18b Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 11 Jun 2024 00:12:02 +0200 Subject: [PATCH 40/61] idle_inhibit: Check if layer surface output is enabled While we we cannot easily check for true visibility of layer surfaces as easily as for views, we can check at least check that the output associated with the surface is enabled. --- sway/desktop/idle_inhibit_v1.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index 9fc223d0..b495c204 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -120,10 +120,13 @@ bool sway_idle_inhibit_v1_is_active(struct sway_idle_inhibitor_v1 *inhibitor) { switch (inhibitor->mode) { case INHIBIT_IDLE_APPLICATION:; struct wlr_surface *wlr_surface = inhibitor->wlr_inhibitor->surface; - if (wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface)) { + struct wlr_layer_surface_v1 *layer_surface = + wlr_layer_surface_v1_try_from_wlr_surface(wlr_surface); + if (layer_surface) { // Layer surfaces can be occluded but are always on screen after // they have been mapped. - return wlr_surface->mapped; + return layer_surface->output && layer_surface->output->enabled && + wlr_surface->mapped; } // If there is no view associated with the inhibitor, assume invisible From 0a9b0b83ebfffc3b5b456b49d8cfe76736fe011b Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 01:51:49 +0100 Subject: [PATCH 41/61] server: remove event listeners on fini This fixes a crash in wlroots listener checks. See #8509. --- sway/server.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/sway/server.c b/sway/server.c index 5de6648f..fc2ba819 100644 --- a/sway/server.c +++ b/sway/server.c @@ -460,8 +460,29 @@ bool server_init(struct sway_server *server) { } void server_fini(struct sway_server *server) { + // remove listeners + wl_list_remove(&server->renderer_lost.link); + wl_list_remove(&server->new_output.link); + wl_list_remove(&server->layer_shell_surface.link); + wl_list_remove(&server->xdg_shell_toplevel.link); + wl_list_remove(&server->server_decoration.link); + wl_list_remove(&server->xdg_decoration.link); + wl_list_remove(&server->pointer_constraint.link); + wl_list_remove(&server->output_manager_apply.link); + wl_list_remove(&server->output_manager_test.link); + wl_list_remove(&server->output_power_manager_set_mode.link); +#if WLR_HAS_DRM_BACKEND + wl_list_remove(&server->drm_lease_request.link); +#endif + wl_list_remove(&server->tearing_control_new_object.link); + wl_list_remove(&server->xdg_activation_v1_request_activate.link); + wl_list_remove(&server->xdg_activation_v1_new_token.link); + wl_list_remove(&server->request_set_cursor_shape.link); + // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND + wl_list_remove(&server->xwayland_surface.link); + wl_list_remove(&server->xwayland_ready.link); wlr_xwayland_destroy(server->xwayland.wlr_xwayland); #endif wl_display_destroy_clients(server->wl_display); From e51ecf71aa0afdcce4c9768f0a614222ee79cee2 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 01:52:25 +0100 Subject: [PATCH 42/61] input/input-manager: remove event listeners on fini This fixes a crash in wlroots listener checks. See #8509. --- include/sway/input/input-manager.h | 2 ++ sway/input/input-manager.c | 8 ++++++++ sway/server.c | 1 + 3 files changed, 11 insertions(+) diff --git a/include/sway/input/input-manager.h b/include/sway/input/input-manager.h index b014e18f..5113844d 100644 --- a/include/sway/input/input-manager.h +++ b/include/sway/input/input-manager.h @@ -39,6 +39,8 @@ struct sway_input_manager { struct sway_input_manager *input_manager_create(struct sway_server *server); +void input_manager_finish(struct sway_input_manager *input); + bool input_manager_has_focus(struct sway_node *node); void input_manager_set_focus(struct sway_node *node); diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 99af4c69..ffcf8fc5 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -493,6 +493,14 @@ struct sway_input_manager *input_manager_create(struct sway_server *server) { return input; } +void input_manager_finish(struct sway_input_manager *input) { + wl_list_remove(&input->new_input.link); + wl_list_remove(&input->virtual_keyboard_new.link); + wl_list_remove(&input->virtual_pointer_new.link); + wl_list_remove(&input->keyboard_shortcuts_inhibit_new_inhibitor.link); + wl_list_remove(&input->transient_seat_create.link); +} + bool input_manager_has_focus(struct sway_node *node) { struct sway_seat *seat = NULL; wl_list_for_each(seat, &server.input->seats, link) { diff --git a/sway/server.c b/sway/server.c index fc2ba819..ff7ed42e 100644 --- a/sway/server.c +++ b/sway/server.c @@ -478,6 +478,7 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->xdg_activation_v1_request_activate.link); wl_list_remove(&server->xdg_activation_v1_new_token.link); wl_list_remove(&server->request_set_cursor_shape.link); + input_manager_finish(server->input); // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND From 92c82e6952394b714d7818deb1d46d616a39dfc0 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 01:52:57 +0100 Subject: [PATCH 43/61] desktop/idle_inhibit: remove event listeners on destroy This fixes a crash in wlroots listener checks. See #8509. --- include/sway/desktop/idle_inhibit_v1.h | 1 + sway/desktop/idle_inhibit_v1.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/sway/desktop/idle_inhibit_v1.h b/include/sway/desktop/idle_inhibit_v1.h index 84cc666d..447ac870 100644 --- a/include/sway/desktop/idle_inhibit_v1.h +++ b/include/sway/desktop/idle_inhibit_v1.h @@ -13,6 +13,7 @@ enum sway_idle_inhibit_mode { struct sway_idle_inhibit_manager_v1 { struct wlr_idle_inhibit_manager_v1 *wlr_manager; struct wl_listener new_idle_inhibitor_v1; + struct wl_listener manager_destroy; struct wl_list inhibitors; }; diff --git a/sway/desktop/idle_inhibit_v1.c b/sway/desktop/idle_inhibit_v1.c index b495c204..6b2761fc 100644 --- a/sway/desktop/idle_inhibit_v1.c +++ b/sway/desktop/idle_inhibit_v1.c @@ -45,6 +45,14 @@ void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data) { sway_idle_inhibit_v1_check_active(); } +void handle_manager_destroy(struct wl_listener *listener, void *data) { + struct sway_idle_inhibit_manager_v1 *manager = + wl_container_of(listener, manager, manager_destroy); + + wl_list_remove(&manager->manager_destroy.link); + wl_list_remove(&manager->new_idle_inhibitor_v1.link); +} + void sway_idle_inhibit_v1_user_inhibitor_register(struct sway_view *view, enum sway_idle_inhibit_mode mode) { struct sway_idle_inhibit_manager_v1 *manager = &server.idle_inhibit_manager_v1; @@ -177,6 +185,9 @@ bool sway_idle_inhibit_manager_v1_init(void) { wl_signal_add(&manager->wlr_manager->events.new_inhibitor, &manager->new_idle_inhibitor_v1); manager->new_idle_inhibitor_v1.notify = handle_idle_inhibitor_v1; + wl_signal_add(&manager->wlr_manager->events.destroy, + &manager->manager_destroy); + manager->manager_destroy.notify = handle_manager_destroy; wl_list_init(&manager->inhibitors); return true; From 53126cdceb6dbf6ee163ca0db960cf3900870075 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 22:45:53 +0100 Subject: [PATCH 44/61] input/text_input: remove event listeners on destroy sway_input_method_relay can be destroyed from two sources, either the seat is destroyed or the manager protocol objects are destroyed due compositor exit. This fixes a crash in wlroots listener checks. See #8509. --- include/sway/input/text_input.h | 2 ++ sway/input/text_input.c | 38 +++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/sway/input/text_input.h b/include/sway/input/text_input.h index 1993f928..1818749a 100644 --- a/include/sway/input/text_input.h +++ b/include/sway/input/text_input.h @@ -25,8 +25,10 @@ struct sway_input_method_relay { struct wlr_input_method_v2 *input_method; // doesn't have to be present struct wl_listener text_input_new; + struct wl_listener text_input_manager_destroy; struct wl_listener input_method_new; + struct wl_listener input_method_manager_destroy; struct wl_listener input_method_commit; struct wl_listener input_method_new_popup_surface; struct wl_listener input_method_grab_keyboard; diff --git a/sway/input/text_input.c b/sway/input/text_input.c index a46f833c..c84fac8f 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -597,6 +597,34 @@ static void relay_handle_input_method(struct wl_listener *listener, } } +static void sway_input_method_relay_finish_text_input(struct sway_input_method_relay *relay) { + wl_list_remove(&relay->text_input_new.link); + wl_list_remove(&relay->text_input_manager_destroy.link); + wl_list_init(&relay->text_input_new.link); + wl_list_init(&relay->text_input_manager_destroy.link); +} + +static void relay_handle_text_input_manager_destroy(struct wl_listener *listener, void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + text_input_manager_destroy); + + sway_input_method_relay_finish_text_input(relay); +} + +static void sway_input_method_relay_finish_input_method(struct sway_input_method_relay *relay) { + wl_list_remove(&relay->input_method_new.link); + wl_list_remove(&relay->input_method_manager_destroy.link); + wl_list_init(&relay->input_method_new.link); + wl_list_init(&relay->input_method_manager_destroy.link); +} + +static void relay_handle_input_method_manager_destroy(struct wl_listener *listener, void *data) { + struct sway_input_method_relay *relay = wl_container_of(listener, relay, + input_method_manager_destroy); + + sway_input_method_relay_finish_input_method(relay); +} + void sway_input_method_relay_init(struct sway_seat *seat, struct sway_input_method_relay *relay) { relay->seat = seat; @@ -606,16 +634,22 @@ void sway_input_method_relay_init(struct sway_seat *seat, relay->text_input_new.notify = relay_handle_text_input; wl_signal_add(&server.text_input->events.text_input, &relay->text_input_new); + relay->text_input_manager_destroy.notify = relay_handle_text_input_manager_destroy; + wl_signal_add(&server.text_input->events.destroy, + &relay->text_input_manager_destroy); relay->input_method_new.notify = relay_handle_input_method; wl_signal_add( &server.input_method->events.input_method, &relay->input_method_new); + relay->input_method_manager_destroy.notify = relay_handle_input_method_manager_destroy; + wl_signal_add(&server.input_method->events.destroy, + &relay->input_method_manager_destroy); } void sway_input_method_relay_finish(struct sway_input_method_relay *relay) { - wl_list_remove(&relay->input_method_new.link); - wl_list_remove(&relay->text_input_new.link); + sway_input_method_relay_finish_text_input(relay); + sway_input_method_relay_finish_input_method(relay); } void sway_input_method_relay_set_focus(struct sway_input_method_relay *relay, From ab2e1f5817a8024366fcb02285c978c5fef7dae1 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Tue, 18 Feb 2025 23:14:06 +0100 Subject: [PATCH 45/61] tree/container: remove event listeners on destroy Change begin_destroy to remove event listeners before the final destroy, since otherwise event listeners would be removed twice, which crashes. This fixes a crash in wlroots listener checks. See #8509. --- include/sway/tree/container.h | 1 + sway/tree/container.c | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 4608b8ac..4fb2d720 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -95,6 +95,7 @@ struct sway_container { struct wl_listener output_enter; struct wl_listener output_leave; + struct wl_listener output_handler_destroy; struct sway_container_state current; struct sway_container_state pending; diff --git a/sway/tree/container.c b/sway/tree/container.c index 6ff4036f..0385d7c1 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -50,6 +50,14 @@ static void handle_output_leave( } } +static void handle_destroy( + struct wl_listener *listener, void *data) { + struct sway_container *con = wl_container_of( + listener, con, output_handler_destroy); + + container_begin_destroy(con); +} + static bool handle_point_accepts_input( struct wlr_scene_buffer *buffer, double *x, double *y) { return false; @@ -135,6 +143,9 @@ struct sway_container *container_create(struct sway_view *view) { c->output_leave.notify = handle_output_leave; wl_signal_add(&c->output_handler->events.output_leave, &c->output_leave); + c->output_handler_destroy.notify = handle_destroy; + wl_signal_add(&c->output_handler->node.events.destroy, + &c->output_handler_destroy); c->output_handler->point_accepts_input = handle_point_accepts_input; } } @@ -508,8 +519,6 @@ void container_destroy(struct sway_container *con) { if (con->view && con->view->container == con) { con->view->container = NULL; - wl_list_remove(&con->output_enter.link); - wl_list_remove(&con->output_leave.link); wlr_scene_node_destroy(&con->output_handler->node); if (con->view->destroying) { view_destroy(con->view); @@ -552,6 +561,12 @@ void container_begin_destroy(struct sway_container *con) { if (con->pending.parent || con->pending.workspace) { container_detach(con); } + + if (con->view && con->view->container == con) { + wl_list_remove(&con->output_enter.link); + wl_list_remove(&con->output_leave.link); + wl_list_remove(&con->output_handler_destroy.link); + } } void container_reap_empty(struct sway_container *con) { From 240a69ad63ad36893132ab1187035654d9478436 Mon Sep 17 00:00:00 2001 From: Ferdinand Bachmann Date: Fri, 21 Mar 2025 18:35:36 +0100 Subject: [PATCH 46/61] server: recreate renderer in idle callback to avoid UAF Destroying the wlr_renderer in a callback to its own renderer_lost event is unsafe due to wl_signal_emit*() still accessing it after it was destroyed. Delegate recreation of renderer to an idle callback and ensure that only one such idle callback is scheduled at a time by storing the returned event source. --- include/sway/server.h | 1 + sway/server.c | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index feb516c5..b1d7523c 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -46,6 +46,7 @@ struct sway_server { struct wl_listener new_output; struct wl_listener renderer_lost; + struct wl_event_source *recreating_renderer; struct wlr_idle_notifier_v1 *idle_notifier_v1; struct sway_idle_inhibit_manager_v1 idle_inhibit_manager_v1; diff --git a/sway/server.c b/sway/server.c index ff7ed42e..9d882862 100644 --- a/sway/server.c +++ b/sway/server.c @@ -182,11 +182,11 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) { drmFreeVersion(version); } -static void handle_renderer_lost(struct wl_listener *listener, void *data) { - struct sway_server *server = wl_container_of(listener, server, renderer_lost); +static void do_renderer_recreate(void *data) { + struct sway_server *server = data; + server->recreating_renderer = NULL; sway_log(SWAY_INFO, "Re-creating renderer after GPU reset"); - struct wlr_renderer *renderer = wlr_renderer_autocreate(server->backend); if (renderer == NULL) { sway_log(SWAY_ERROR, "Unable to create renderer"); @@ -221,6 +221,18 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { wlr_renderer_destroy(old_renderer); } +static void handle_renderer_lost(struct wl_listener *listener, void *data) { + struct sway_server *server = wl_container_of(listener, server, renderer_lost); + + if (server->recreating_renderer != NULL) { + sway_log(SWAY_DEBUG, "Re-creation of renderer already scheduled"); + return; + } + + sway_log(SWAY_INFO, "Scheduling re-creation of renderer after GPU reset"); + server->recreating_renderer = wl_event_loop_add_idle(server->wl_event_loop, do_renderer_recreate, server); +} + bool server_init(struct sway_server *server) { sway_log(SWAY_DEBUG, "Initializing Wayland server"); server->wl_display = wl_display_create(); From 4943534929dfd3f0ea55e26241544354f8365e60 Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Mon, 14 Apr 2025 08:57:40 +0200 Subject: [PATCH 47/61] server: fix shutdown crash when running on x11 backend Signed-off-by: Loukas Agorgianitis --- sway/server.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 9d882862..f7155a77 100644 --- a/sway/server.c +++ b/sway/server.c @@ -484,7 +484,9 @@ void server_fini(struct sway_server *server) { wl_list_remove(&server->output_manager_test.link); wl_list_remove(&server->output_power_manager_set_mode.link); #if WLR_HAS_DRM_BACKEND - wl_list_remove(&server->drm_lease_request.link); + if (server->drm_lease_manager) { + wl_list_remove(&server->drm_lease_request.link); + } #endif wl_list_remove(&server->tearing_control_new_object.link); wl_list_remove(&server->xdg_activation_v1_request_activate.link); From 3f0b3f8f9b3b737fd0e6d36e2a2c469b07268ec2 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 14 Apr 2025 09:29:45 +0200 Subject: [PATCH 48/61] Fix crash on shutdown when Xwayland is disabled --- sway/server.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/server.c b/sway/server.c index f7155a77..5aa5f4f9 100644 --- a/sway/server.c +++ b/sway/server.c @@ -496,9 +496,11 @@ void server_fini(struct sway_server *server) { // TODO: free sway-specific resources #if WLR_HAS_XWAYLAND - wl_list_remove(&server->xwayland_surface.link); - wl_list_remove(&server->xwayland_ready.link); - wlr_xwayland_destroy(server->xwayland.wlr_xwayland); + if (server->xwayland.wlr_xwayland != NULL) { + wl_list_remove(&server->xwayland_surface.link); + wl_list_remove(&server->xwayland_ready.link); + wlr_xwayland_destroy(server->xwayland.wlr_xwayland); + } #endif wl_display_destroy_clients(server->wl_display); wlr_backend_destroy(server->backend); From 7733bf9963d6a18df548164642e6637c2e71f1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Vuji=C4=8Di=C4=87?= Date: Mon, 14 Apr 2025 20:58:20 +1200 Subject: [PATCH 49/61] Remove duplicate arrange_container --- sway/desktop/transaction.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 01fe3128..0b3cbfb4 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -375,7 +375,6 @@ static void arrange_children(enum sway_container_layout layout, list_t *children wlr_scene_node_set_enabled(&child->border.tree->node, true); wlr_scene_node_set_position(&child->scene_tree->node, off, 0); wlr_scene_node_reparent(&child->scene_tree->node, content); - arrange_container(child, cwidth, height, true, gaps); if (cwidth > 0 && height > 0) { arrange_container(child, cwidth, height, true, gaps); off += cwidth + gaps; From 8a8c78deacd388dabbe82a4a5055458494cd6258 Mon Sep 17 00:00:00 2001 From: llyyr Date: Wed, 16 Apr 2025 17:32:47 +0530 Subject: [PATCH 50/61] layer_shell: destroy layer_surface on assigned output destruction According to the spec, the closed event should be sent when the surface is no longer shown, because the output may have been destroyed or the user may have asked for it to be removed. In such cases, the clients should destroy the resource. This fixes mako not being able to show notifications if the assigned output was destroyed while a notificataion was still visible Fixes: 188811f80861 ("scene_graph: Port layer_shell") --- sway/desktop/layer_shell.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 05faa465..b14a9a4b 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -221,7 +221,7 @@ static void handle_output_destroy(struct wl_listener *listener, void *data) { wl_container_of(listener, layer, output_destroy); layer->output = NULL; - wlr_scene_node_destroy(&layer->scene->tree->node); + wlr_layer_surface_v1_destroy(layer->layer_surface); } static void handle_node_destroy(struct wl_listener *listener, void *data) { From d3e1c13e1f40d38a454fa0236975c4d5196bd77e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 17 Apr 2025 19:11:10 +0200 Subject: [PATCH 51/61] swaymsg, swaynag: drop sway_terminate() definitions These are unused. --- swaymsg/main.c | 4 ---- swaynag/main.c | 5 ----- 2 files changed, 9 deletions(-) diff --git a/swaymsg/main.c b/swaymsg/main.c index dc1c7407..6a9eb198 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -16,10 +16,6 @@ #include "ipc-client.h" #include "log.h" -void sway_terminate(int exit_code) { - exit(exit_code); -} - static bool success_object(json_object *result) { json_object *success; diff --git a/swaynag/main.c b/swaynag/main.c index 634bddbf..54317dce 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -13,11 +13,6 @@ void sig_handler(int signal) { exit(EXIT_FAILURE); } -void sway_terminate(int code) { - swaynag_destroy(&swaynag); - exit(code); -} - int main(int argc, char **argv) { int status = EXIT_SUCCESS; From 0153bc92abb4974c1a3421a79e976dcf9938e50a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 17 Apr 2025 19:11:37 +0200 Subject: [PATCH 52/61] server: move sway_terminate() definition to header --- include/sway/server.h | 2 ++ sway/commands/exit.c | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index b1d7523c..6152651e 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -158,6 +158,8 @@ extern struct sway_debug debug; extern bool allow_unsupported_gpu; +void sway_terminate(int exit_code); + bool server_init(struct sway_server *server); void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); diff --git a/sway/commands/exit.c b/sway/commands/exit.c index 10cde640..0f326cea 100644 --- a/sway/commands/exit.c +++ b/sway/commands/exit.c @@ -1,8 +1,7 @@ #include #include "sway/commands.h" #include "sway/config.h" - -void sway_terminate(int exit_code); +#include "sway/server.h" struct cmd_results *cmd_exit(int argc, char **argv) { struct cmd_results *error = NULL; From 1d4632f97fb6ee61abe350ae9f76270562396553 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 13 Jul 2024 13:13:08 +0200 Subject: [PATCH 53/61] Drop wl_drm again In [1] we re-introduced a debug flag to enable wl_drm. Time has passed and Xwayland + VA-API + amdvlk now all support linux-dmabuf-v1. [1]: https://github.com/swaywm/sway/pull/7916 --- include/sway/server.h | 1 - sway/main.c | 2 -- sway/server.c | 4 ---- 3 files changed, 7 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 6152651e..66f0967c 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -151,7 +151,6 @@ struct sway_debug { bool noatomic; // Ignore atomic layout updates bool txn_timings; // Log verbose messages about transactions bool txn_wait; // Always wait for the timeout before applying - bool legacy_wl_drm; // Enable the legacy wl_drm interface }; extern struct sway_debug debug; diff --git a/sway/main.c b/sway/main.c index 0af977d4..bebb5e1b 100644 --- a/sway/main.c +++ b/sway/main.c @@ -181,8 +181,6 @@ void enable_debug_flag(const char *flag) { debug.txn_timings = true; } else if (has_prefix(flag, "txn-timeout=")) { server.txn_timeout_ms = atoi(&flag[strlen("txn-timeout=")]); - } else if (strcmp(flag, "legacy-wl-drm") == 0) { - debug.legacy_wl_drm = true; } else { sway_log(SWAY_ERROR, "Unknown debug flag: %s", flag); } diff --git a/sway/server.c b/sway/server.c index 5aa5f4f9..97976148 100644 --- a/sway/server.c +++ b/sway/server.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -265,9 +264,6 @@ bool server_init(struct sway_server *server) { if (wlr_renderer_get_texture_formats(server->renderer, WLR_BUFFER_CAP_DMABUF) != NULL) { server->linux_dmabuf_v1 = wlr_linux_dmabuf_v1_create_with_renderer( server->wl_display, 4, server->renderer); - if (debug.legacy_wl_drm) { - wlr_drm_create(server->wl_display, server->renderer); - } } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && server->renderer->features.timeline && From 86ff19fadeaa45f7f6398d62be1ee6149a0889a8 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 13:31:10 +0200 Subject: [PATCH 54/61] build: bump version to 1.11-rc1 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9ce5723e..2d406dec 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.10-dev', + version: '1.11-rc1', license: 'MIT', meson_version: '>=1.3', default_options: [ From 0e19d85d37e556721f982c3f63d4e2927f306b18 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 20 Apr 2025 21:09:57 +0200 Subject: [PATCH 55/61] Use pthread_atfork() to restore signals and NOFILE limit This ensures these functions are always called (even when a library such as wlroots or libc perform the fork) and removes the need to manually call them. --- include/sway/server.h | 3 --- meson.build | 2 +- sway/commands/exec_always.c | 2 -- sway/config/bar.c | 2 -- sway/config/output.c | 2 -- sway/main.c | 38 +++++++++++++++++++++---------------- sway/swaynag.c | 2 -- 7 files changed, 23 insertions(+), 28 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index 66f0967c..e7d7094f 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -164,9 +164,6 @@ void server_fini(struct sway_server *server); bool server_start(struct sway_server *server); void server_run(struct sway_server *server); -void restore_nofile_limit(void); -void restore_signals(void); - void handle_new_output(struct wl_listener *listener, void *data); void handle_idle_inhibitor_v1(struct wl_listener *listener, void *data); diff --git a/meson.build b/meson.build index 2d406dec..9e8e798e 100644 --- a/meson.build +++ b/meson.build @@ -79,7 +79,7 @@ libudev = wlroots_features['libinput_backend'] ? dependency('libudev') : null_de math = cc.find_library('m') rt = cc.find_library('rt') xcb_icccm = wlroots_features['xwayland'] ? dependency('xcb-icccm') : null_dep -threads = dependency('threads') # for pthread_setschedparam +threads = dependency('threads') # for pthread_setschedparam and pthread_atfork if get_option('sd-bus-provider') == 'auto' if not get_option('tray').disabled() diff --git a/sway/commands/exec_always.c b/sway/commands/exec_always.c index 8f02bbdc..a966696c 100644 --- a/sway/commands/exec_always.c +++ b/sway/commands/exec_always.c @@ -51,8 +51,6 @@ struct cmd_results *cmd_exec_process(int argc, char **argv) { // Fork process pid_t child = fork(); if (child == 0) { - restore_nofile_limit(); - restore_signals(); setsid(); if (ctx) { diff --git a/sway/config/bar.c b/sway/config/bar.c index 6cace0da..f4efb276 100644 --- a/sway/config/bar.c +++ b/sway/config/bar.c @@ -213,8 +213,6 @@ static void invoke_swaybar(struct bar_config *bar) { sway_log(SWAY_ERROR, "Failed to create fork for swaybar"); return; } else if (pid == 0) { - restore_nofile_limit(); - restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } diff --git a/sway/config/output.c b/sway/config/output.c index b8a613cc..5ed518bf 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1060,8 +1060,6 @@ static bool _spawn_swaybg(char **command) { sway_log_errno(SWAY_ERROR, "fork failed"); return false; } else if (pid == 0) { - restore_nofile_limit(); - restore_signals(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } diff --git a/sway/main.c b/sway/main.c index bebb5e1b..56f09b7e 100644 --- a/sway/main.c +++ b/sway/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -121,6 +122,16 @@ static bool detect_suid(void) { return true; } +static void restore_nofile_limit(void) { + if (original_nofile_rlimit.rlim_cur == 0) { + return; + } + if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { + sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " + "setrlimit(NOFILE) failed"); + } +} + static void increase_nofile_limit(void) { if (getrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { sway_log_errno(SWAY_ERROR, "Failed to bump max open files limit: " @@ -135,17 +146,10 @@ static void increase_nofile_limit(void) { "setrlimit(NOFILE) failed"); sway_log(SWAY_INFO, "Running with %d max open files", (int)original_nofile_rlimit.rlim_cur); - } -} - -void restore_nofile_limit(void) { - if (original_nofile_rlimit.rlim_cur == 0) { return; } - if (setrlimit(RLIMIT_NOFILE, &original_nofile_rlimit) != 0) { - sway_log_errno(SWAY_ERROR, "Failed to restore max open files limit: " - "setrlimit(NOFILE) failed"); - } + + pthread_atfork(NULL, NULL, restore_nofile_limit); } static int term_signal(int signal, void *data) { @@ -153,6 +157,14 @@ static int term_signal(int signal, void *data) { return 0; } +static void restore_signals(void) { + sigset_t set; + sigemptyset(&set); + sigprocmask(SIG_SETMASK, &set, NULL); + signal(SIGCHLD, SIG_DFL); + signal(SIGPIPE, SIG_DFL); +} + static void init_signals(void) { wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); @@ -162,14 +174,8 @@ static void init_signals(void) { // prevent ipc write errors from crashing sway signal(SIGPIPE, SIG_IGN); -} -void restore_signals(void) { - sigset_t set; - sigemptyset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); + pthread_atfork(NULL, NULL, restore_signals); } void enable_debug_flag(const char *flag) { diff --git a/sway/swaynag.c b/sway/swaynag.c index f0a31218..204a5791 100644 --- a/sway/swaynag.c +++ b/sway/swaynag.c @@ -63,7 +63,6 @@ bool swaynag_spawn(const char *swaynag_command, sway_log(SWAY_ERROR, "Failed to create fork for swaynag"); goto failed; } else if (pid == 0) { - restore_nofile_limit(); if (!sway_set_cloexec(sockets[1], false)) { _exit(EXIT_FAILURE); } @@ -148,4 +147,3 @@ void swaynag_show(struct swaynag_instance *swaynag) { close(swaynag->fd[1]); } } - From 38a42f97d46931e97693610c999ea51834b71352 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Apr 2025 18:36:18 +0200 Subject: [PATCH 56/61] Replace signal() with sigaction() The man page for signal(3) reads: > new applications should use sigaction() rather than signal() --- sway/main.c | 12 +++++++----- swaybar/main.c | 5 +++-- swaynag/main.c | 3 ++- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sway/main.c b/sway/main.c index 56f09b7e..69efd6cb 100644 --- a/sway/main.c +++ b/sway/main.c @@ -161,19 +161,21 @@ static void restore_signals(void) { sigset_t set; sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, NULL); - signal(SIGCHLD, SIG_DFL); - signal(SIGPIPE, SIG_DFL); + + struct sigaction sa_dfl = { .sa_handler = SIG_DFL }; + sigaction(SIGCHLD, &sa_dfl, NULL); + sigaction(SIGPIPE, &sa_dfl, NULL); } static void init_signals(void) { wl_event_loop_add_signal(server.wl_event_loop, SIGTERM, term_signal, NULL); wl_event_loop_add_signal(server.wl_event_loop, SIGINT, term_signal, NULL); + struct sigaction sa_ign = { .sa_handler = SIG_IGN }; // avoid need to reap children - signal(SIGCHLD, SIG_IGN); - + sigaction(SIGCHLD, &sa_ign, NULL); // prevent ipc write errors from crashing sway - signal(SIGPIPE, SIG_IGN); + sigaction(SIGPIPE, &sa_ign, NULL); pthread_atfork(NULL, NULL, restore_signals); } diff --git a/swaybar/main.c b/swaybar/main.c index 3dc67233..e1b0ceca 100644 --- a/swaybar/main.c +++ b/swaybar/main.c @@ -93,8 +93,9 @@ int main(int argc, char **argv) { free(socket_path); - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); + struct sigaction sa = { .sa_handler = sig_handler }; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); swaybar.running = true; bar_run(&swaybar); diff --git a/swaynag/main.c b/swaynag/main.c index 54317dce..b68157ff 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -102,7 +102,8 @@ int main(int argc, char **argv) { sway_log(SWAY_DEBUG, "\t[%s] `%s`", button->text, button->action); } - signal(SIGTERM, sig_handler); + struct sigaction sa = { .sa_handler = sig_handler }; + sigaction(SIGTERM, &sa, NULL); swaynag_setup(&swaynag); swaynag_run(&swaynag); From 5b8874e3f428b54aaa2676346954dc712320d219 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Thu, 24 Apr 2025 15:41:20 -0400 Subject: [PATCH 57/61] sway/commands: Handle incorrect resize unit problem: an invalid usage of the command resize set will cause sway to crash because it doesn't check for an invalid height. solution: validate height along with width. --- sway/commands/resize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/resize.c b/sway/commands/resize.c index 32b746ea..49731a64 100644 --- a/sway/commands/resize.c +++ b/sway/commands/resize.c @@ -457,7 +457,7 @@ static struct cmd_results *cmd_resize_set(int argc, char **argv) { if (argc > num_consumed_args) { return cmd_results_new(CMD_INVALID, "%s", usage); } - if (width.unit == MOVEMENT_UNIT_INVALID) { + if (height.unit == MOVEMENT_UNIT_INVALID) { return cmd_results_new(CMD_INVALID, "%s", usage); } } From 6894b498a889d809093c920d397a42db7f5e4970 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Apr 2025 22:52:48 +0200 Subject: [PATCH 58/61] build: bump version to 1.11-rc2 --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 9e8e798e..646f694c 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ project( 'sway', 'c', - version: '1.11-rc1', + version: '1.11-rc2', license: 'MIT', meson_version: '>=1.3', default_options: [ From 6cac61b6b96c4a48a69f8ec3c06c2df560b01827 Mon Sep 17 00:00:00 2001 From: Pavel 'LEdoian' Turinsky Date: Fri, 3 May 2024 09:09:14 +0200 Subject: [PATCH 59/61] Fix includes with relative paths The function `load_include_configs` already changes the directory to the one containing the parent config. Therefore, `load_include_config` trying to assemble the "full" path leads to repetition of path segments, making the `realpath` call fail with ENOENT. Just calling `realpath` on the path itself from the directory with the parent configuration is sufficient, so there is no point in passing `parent_dir` to `load_include_config`. --- sway/config.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/sway/config.c b/sway/config.c index ec705968..d579022d 100644 --- a/sway/config.c +++ b/sway/config.c @@ -552,28 +552,12 @@ bool load_main_config(const char *file, bool is_active, bool validating) { return success; } -static bool load_include_config(const char *path, const char *parent_dir, - struct sway_config *config, struct swaynag_instance *swaynag) { +static bool load_include_config(const char *path, struct sway_config *config, + struct swaynag_instance *swaynag) { // save parent config const char *parent_config = config->current_config_path; - char *full_path; - int len = strlen(path); - if (len >= 1 && path[0] != '/') { - len = len + strlen(parent_dir) + 2; - full_path = malloc(len * sizeof(char)); - if (!full_path) { - sway_log(SWAY_ERROR, - "Unable to allocate full path to included config"); - return false; - } - snprintf(full_path, len, "%s/%s", parent_dir, path); - } else { - full_path = strdup(path); - } - - char *real_path = realpath(full_path, NULL); - free(full_path); + char *real_path = realpath(path, NULL); if (real_path == NULL) { sway_log(SWAY_DEBUG, "%s not found.", path); @@ -625,7 +609,7 @@ void load_include_configs(const char *path, struct sway_config *config, char **w = p.we_wordv; size_t i; for (i = 0; i < p.we_wordc; ++i) { - load_include_config(w[i], parent_dir, config, swaynag); + load_include_config(w[i], config, swaynag); } wordfree(&p); } From 8ac1f72c9ef88b2919457e3598059f963df69305 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 30 Apr 2025 13:34:31 +0200 Subject: [PATCH 60/61] config/output: Use INT_MAX as x/y unset value We oftne use -1 to indicate unset values. In case of output (x, y), we would consider the fields set if they are not both -1. This means that (0, -1) and (-1, 0) are valid coordinates, but (-1, -1) is not. We support negative output positioning, so we cannot use -1 to mean unset. Zero is also not an option as that would disallow reverting a set position back to (0, 0). INT_MAX is an unreasonable output position, so use it to indicate unset values, and only use the value when both are set. --- sway/config/output.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 5ed518bf..e061e25b 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -67,7 +67,7 @@ struct output_config *new_output_config(const char *name) { oc->refresh_rate = -1; oc->custom_mode = -1; oc->drm_mode.type = -1; - oc->x = oc->y = -1; + oc->x = oc->y = INT_MAX; oc->scale = -1; oc->scale_filter = SCALE_FILTER_DEFAULT; oc->transform = -1; @@ -93,11 +93,11 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->height != -1) { dst->height = -1; } - if (src->x != -1) { - dst->x = -1; + if (src->x != INT_MAX) { + dst->x = INT_MAX; } - if (src->y != -1) { - dst->y = -1; + if (src->y != INT_MAX) { + dst->y = INT_MAX; } if (src->scale != -1) { dst->scale = -1; @@ -157,10 +157,10 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->height != -1) { dst->height = src->height; } - if (src->x != -1) { + if (src->x != INT_MAX) { dst->x = src->x; } - if (src->y != -1) { + if (src->y != INT_MAX) { dst->y = src->y; } if (src->scale != -1) { @@ -527,7 +527,7 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output } // Find position for it - if (oc && (oc->x != -1 || oc->y != -1)) { + if (oc && oc->x != INT_MAX && oc->y != INT_MAX) { sway_log(SWAY_DEBUG, "Set %s position to %d, %d", oc->name, oc->x, oc->y); wlr_output_layout_add(root->output_layout, wlr_output, oc->x, oc->y); } else { From f9945d81fb52c81ab60034dcfc41a2f36f0ed226 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 29 Apr 2025 13:39:00 +0200 Subject: [PATCH 61/61] config/output: Fix missing output config supersedes color_transform and allow_tearing was not handled by supersede_output_config which could lead to configuration being incorrectly applied. --- sway/config/output.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/config/output.c b/sway/config/output.c index e061e25b..df80cab6 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -129,6 +129,13 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; } + if (src->set_color_transform) { + if (dst->color_transform) { + wlr_color_transform_unref(dst->color_transform); + dst->color_transform = NULL; + } + dst->set_color_transform = false; + } if (src->background) { free(dst->background); dst->background = NULL; @@ -144,6 +151,9 @@ static void supersede_output_config(struct output_config *dst, struct output_con if (src->power != -1) { dst->power = -1; } + if (src->allow_tearing != -1) { + dst->allow_tearing = -1; + } } // merge_output_config sets all fields in dst that were set in src