From b7eb6177e1d634620d73d56224cf154e3b854e0f Mon Sep 17 00:00:00 2001 From: bonsaiiV <68749581+bonsaiiV@users.noreply.github.com> Date: Sat, 18 Oct 2025 16:55:46 +0000 Subject: [PATCH 01/22] sway/commands/layout: fix flatten parent once Fixes: f50e307227c8 ("sway/commands/layout: flatten parent once") --- sway/commands/layout.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sway/commands/layout.c b/sway/commands/layout.c index a32c908b8..22dfdf3d7 100644 --- a/sway/commands/layout.c +++ b/sway/commands/layout.c @@ -137,11 +137,15 @@ struct cmd_results *cmd_layout(int argc, char **argv) { // If parent has only a singe child operate on its parent and // flatten once, like i3 if (container && container->pending.children->length == 1) { - struct sway_container *child = container->pending.children->items[0]; + // Also check grandparent to avoid restricting layouts struct sway_container *parent = container->pending.parent; - container_replace(container, child); - container_begin_destroy(container); - container = parent; + if (parent && parent->pending.children->length == 1) { + struct sway_container *child = container->pending.children->items[0]; + struct sway_container *parent = container->pending.parent; + container_replace(container, child); + container_begin_destroy(container); + container = parent; + } } } From 055be4ec35eec4eaaf066a18ccbf5132ebed0694 Mon Sep 17 00:00:00 2001 From: llyyr Date: Wed, 29 Oct 2025 03:53:24 +0530 Subject: [PATCH 02/22] sway/server: advertise gamma2.2 and bt1886 transfer functions --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 4e606a391..063b96331 100644 --- a/sway/server.c +++ b/sway/server.c @@ -462,6 +462,8 @@ bool server_init(struct sway_server *server) { WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ, WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, + WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886, }; const enum wp_color_manager_v1_primaries primaries[] = { WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, From d735bb99a9e97619e633257ea001b42e2bfe95a5 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 13 Nov 2025 21:21:09 +0530 Subject: [PATCH 03/22] sway/server: advertise wlr_color_representation_manager_v1 --- sway/server.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/server.c b/sway/server.c index 063b96331..7f399a2bb 100644 --- a/sway/server.c +++ b/sway/server.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -485,6 +486,9 @@ bool server_init(struct sway_server *server) { wlr_scene_set_color_manager_v1(root->root_scene, cm); } + wlr_color_representation_manager_v1_create_with_renderer( + server->wl_display, 1, server->renderer); + wl_list_init(&server->pending_launcher_ctxs); // Avoid using "wayland-0" as display socket From b54d1205d866a0bbc3b00c511dace77aa7113160 Mon Sep 17 00:00:00 2001 From: g-rden <94605617+g-rden@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:21:25 +0000 Subject: [PATCH 04/22] swaynag/main: free config_path Otherwise config_path is never free'd --- swaynag/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/swaynag/main.c b/swaynag/main.c index b68157ff2..53c2203ff 100644 --- a/swaynag/main.c +++ b/swaynag/main.c @@ -45,6 +45,7 @@ int main(int argc, char **argv) { if (config_path) { sway_log(SWAY_DEBUG, "Loading config file: %s", config_path); status = swaynag_load_config(config_path, &swaynag, types); + free(config_path); if (status != 0) { goto cleanup; } From 842ea067737934823ccdef9adb8fbec6481bc472 Mon Sep 17 00:00:00 2001 From: Damino Date: Thu, 20 Nov 2025 21:03:11 -0500 Subject: [PATCH 05/22] tree/view: check for null workspace output Fixes #8950 --- sway/tree/view.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index 3d6211249..be813be98 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -307,7 +307,7 @@ void view_autoconfigure(struct sway_view *view) { } struct sway_output *output = ws ? ws->output : NULL; - if (con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { + if (output && con->pending.fullscreen_mode == FULLSCREEN_WORKSPACE) { con->pending.content_x = output->lx; con->pending.content_y = output->ly; con->pending.content_width = output->width; From 897e7688bb415c40f8bc104bd526ddba537255f1 Mon Sep 17 00:00:00 2001 From: johnpyp Date: Mon, 17 Nov 2025 21:29:49 -0500 Subject: [PATCH 06/22] commit dirty transactions upon window attribute changes --- sway/desktop/xdg_shell.c | 3 +++ sway/desktop/xwayland.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 696a45ada..48638817c 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -354,6 +354,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; view_update_title(view, false); view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_app_id(struct wl_listener *listener, void *data) { @@ -362,6 +363,7 @@ static void handle_set_app_id(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; view_update_app_id(view); view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_new_popup(struct wl_listener *listener, void *data) { @@ -594,4 +596,5 @@ void xdg_toplevel_tag_manager_v1_handle_set_tag(struct wl_listener *listener, vo free(xdg_shell_view->tag); xdg_shell_view->tag = strdup(event->tag); view_execute_criteria(view); + transaction_commit_dirty(); } diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 76e63ce1f..65016b3b1 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -689,6 +689,7 @@ static void handle_set_title(struct wl_listener *listener, void *data) { } view_update_title(view, false); view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_class(struct wl_listener *listener, void *data) { @@ -700,6 +701,7 @@ static void handle_set_class(struct wl_listener *listener, void *data) { return; } view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_role(struct wl_listener *listener, void *data) { @@ -711,6 +713,7 @@ static void handle_set_role(struct wl_listener *listener, void *data) { return; } view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_startup_id(struct wl_listener *listener, void *data) { @@ -747,6 +750,7 @@ static void handle_set_window_type(struct wl_listener *listener, void *data) { return; } view_execute_criteria(view); + transaction_commit_dirty(); } static void handle_set_hints(struct wl_listener *listener, void *data) { From f4aba22582184c9a4a20fd7a9ffd70c63b4b393d Mon Sep 17 00:00:00 2001 From: rewine Date: Thu, 27 Nov 2025 22:03:48 +0800 Subject: [PATCH 07/22] Drop org_kde_kwin_idle files Has been replaced by ext_idle_notifier_v1 Signed-off-by: rewine --- protocols/idle.xml | 49 ------------------------------------------- protocols/meson.build | 1 - 2 files changed, 50 deletions(-) delete mode 100644 protocols/idle.xml diff --git a/protocols/idle.xml b/protocols/idle.xml deleted file mode 100644 index 92d9989c7..000000000 --- a/protocols/idle.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - . - ]]> - - - This interface allows to monitor user idle time on a given seat. The interface - allows to register timers which trigger after no user activity was registered - on the seat for a given interval. It notifies when user activity resumes. - - This is useful for applications wanting to perform actions when the user is not - interacting with the system, e.g. chat applications setting the user as away, power - management features to dim screen, etc.. - - - - - - - - - - - - - - - - - - - - - - diff --git a/protocols/meson.build b/protocols/meson.build index e7ada59b8..a1cb35344 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -12,7 +12,6 @@ protocols = [ wl_protocol_dir / 'staging/cursor-shape/cursor-shape-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', ] From fa81ce8ee64d690b881d97b734583e26cb2acb72 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 4 Oct 2025 11:54:31 +0200 Subject: [PATCH 08/22] Use helpers to get supported TFs/primaries References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5086 --- sway/server.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/sway/server.c b/sway/server.c index 7f399a2bb..3446cc97f 100644 --- a/sway/server.c +++ b/sway/server.c @@ -459,17 +459,12 @@ bool server_init(struct sway_server *server) { const enum wp_color_manager_v1_render_intent render_intents[] = { WP_COLOR_MANAGER_V1_RENDER_INTENT_PERCEPTUAL, }; - const enum wp_color_manager_v1_transfer_function transfer_functions[] = { - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, - WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886, - }; - const enum wp_color_manager_v1_primaries primaries[] = { - WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, - WP_COLOR_MANAGER_V1_PRIMARIES_BT2020, - }; + size_t transfer_functions_len = 0; + enum wp_color_manager_v1_transfer_function *transfer_functions = + wlr_color_manager_v1_transfer_function_list_from_renderer(server->renderer, &transfer_functions_len); + size_t primaries_len = 0; + enum wp_color_manager_v1_primaries *primaries = + wlr_color_manager_v1_primaries_list_from_renderer(server->renderer, &primaries_len); struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( server->wl_display, 1, &(struct wlr_color_manager_v1_options){ .features = { @@ -479,10 +474,12 @@ bool server_init(struct sway_server *server) { .render_intents = render_intents, .render_intents_len = sizeof(render_intents) / sizeof(render_intents[0]), .transfer_functions = transfer_functions, - .transfer_functions_len = sizeof(transfer_functions) / sizeof(transfer_functions[0]), + .transfer_functions_len = transfer_functions_len, .primaries = primaries, - .primaries_len = sizeof(primaries) / sizeof(primaries[0]), + .primaries_len = primaries_len, }); + free(transfer_functions); + free(primaries); wlr_scene_set_color_manager_v1(root->root_scene, cm); } From 238f0d4a8b399f0df6791c47eb54c8636722d5a9 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 18 Dec 2025 23:02:32 +0530 Subject: [PATCH 09/22] tiling_resize: fix use-after-free on view unmap during resize Closing a tiled window (mod+shift+q) while resizing (mod+click) causes an use-after-free in handle_unref. Both conditions can be true in this case, which will result in dereferencing `e` on the second check after it has already been freed by the first `seatop_begin_default`. Fix by combining separate checks for the main container and its horizontal/vertical siblings into a single condition. The second check was added in 9e272a7986aa586a73951069aa76068e408a2c3f and I've checked that this fix does not regress that issue. --- sway/input/seatop_resize_tiling.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sway/input/seatop_resize_tiling.c b/sway/input/seatop_resize_tiling.c index 15fd333b3..be7b3c128 100644 --- a/sway/input/seatop_resize_tiling.c +++ b/sway/input/seatop_resize_tiling.c @@ -105,10 +105,7 @@ static void handle_pointer_motion(struct sway_seat *seat, uint32_t time_msec) { static void handle_unref(struct sway_seat *seat, struct sway_container *con) { struct seatop_resize_tiling_event *e = seat->seatop_data; - if (e->con == con) { - seatop_begin_default(seat); - } - if (e->h_sib == con || e->v_sib == con) { + if (e->con == con || e->h_sib == con || e->v_sib == con) { seatop_begin_default(seat); } } From dbe86400357fb1d41693874f7d9f70f285fd1737 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 21 Dec 2025 13:52:52 +0100 Subject: [PATCH 10/22] desktop/xdg_shell: skip configure in request_maximize handler if unmapped wlr_xdg_surface_schedule_configure() requires the xdg_surface to be initialized. Mirror the early return from request_fullscreen. --- sway/desktop/xdg_shell.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index 48638817c..7217e1369 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -386,6 +386,9 @@ static void handle_request_maximize(struct wl_listener *listener, void *data) { struct sway_xdg_shell_view *xdg_shell_view = wl_container_of(listener, xdg_shell_view, request_maximize); struct wlr_xdg_toplevel *toplevel = xdg_shell_view->view.wlr_xdg_toplevel; + if (!toplevel->base->surface->mapped) { + return; + } wlr_xdg_surface_schedule_configure(toplevel->base); } From f654fe8edeefa17f2e63b9e21fd62aa7bb1ef122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Thu, 30 Oct 2025 11:42:20 +0000 Subject: [PATCH 11/22] sway/config: apply output CT before initial commit Currently, config apply sets the output's image description before the initial commit, but sets the output's color transform after the commit. In the case of a config reload removing a color profile and enabling HDR, both the color transform and image description will be set, which trips an assert in wlroots --- sway/config/output.c | 51 +++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index ed8147b8b..b884a785d 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -562,8 +562,28 @@ static void queue_output_config(struct output_config *oc, set_hdr(wlr_output, pending, hdr); } +struct config_output_state { + struct wlr_color_transform *color_transform; +}; + +static void config_output_state_finish(struct config_output_state *state) { + wlr_color_transform_unref(state->color_transform); +} + +static struct wlr_color_transform *get_color_profile(struct wlr_output *output, + struct output_config *oc) { + if (oc && oc->set_color_transform) { + if (oc->color_transform) { + wlr_color_transform_ref(oc->color_transform); + } + return oc->color_transform; + } else { + return NULL; + } +} + static bool finalize_output_config(struct output_config *oc, struct sway_output *output, - const struct wlr_output_state *applied) { + const struct wlr_output_state *applied, const struct config_output_state *config_applied) { if (output == root->fallback_output) { return false; } @@ -609,16 +629,11 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output_enable(output); } - if (oc && oc->set_color_transform) { - if (oc->color_transform) { - wlr_color_transform_ref(oc->color_transform); - } - wlr_color_transform_unref(output->color_transform); - output->color_transform = oc->color_transform; - } else { - wlr_color_transform_unref(output->color_transform); - output->color_transform = NULL; + wlr_color_transform_unref(output->color_transform); + if (config_applied->color_transform != NULL) { + wlr_color_transform_ref(config_applied->color_transform); } + output->color_transform = config_applied->color_transform; output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; output->allow_tearing = oc && oc->allow_tearing > 0; @@ -935,17 +950,25 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, if (!states) { return false; } + struct config_output_state *config_states = calloc(configs_len, sizeof(*config_states)); + if (!config_states) { + free(states); + return false; + } sway_log(SWAY_DEBUG, "Committing %zd outputs", configs_len); for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; struct wlr_backend_output_state *backend_state = &states[idx]; + struct config_output_state *config_state = &config_states[idx]; backend_state->output = cfg->output->wlr_output; wlr_output_state_init(&backend_state->base); queue_output_config(cfg->config, cfg->output, &backend_state->base); dump_output_state(cfg->output->wlr_output, &backend_state->base); + + config_state->color_transform = get_color_profile(cfg->output->wlr_output, cfg->config); } struct wlr_output_swapchain_manager swapchain_mgr; @@ -975,11 +998,12 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; struct wlr_backend_output_state *backend_state = &states[idx]; + struct config_output_state *config_state = &config_states[idx]; struct wlr_scene_output_state_options opts = { .swapchain = wlr_output_swapchain_manager_get_swapchain( &swapchain_mgr, backend_state->output), - .color_transform = cfg->output->color_transform, + .color_transform = config_state->color_transform, }; struct wlr_scene_output *scene_output = cfg->output->scene_output; struct wlr_output_state *state = &backend_state->base; @@ -1003,9 +1027,10 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; struct wlr_backend_output_state *backend_state = &states[idx]; + struct config_output_state *config_state = &config_states[idx]; sway_log(SWAY_DEBUG, "Finalizing config for %s", cfg->output->wlr_output->name); - finalize_output_config(cfg->config, cfg->output, &backend_state->base); + finalize_output_config(cfg->config, cfg->output, &backend_state->base, config_state); } // Output layout being applied in finalize_output_config can shift outputs @@ -1026,8 +1051,10 @@ out: for (size_t idx = 0; idx < configs_len; idx++) { struct wlr_backend_output_state *backend_state = &states[idx]; wlr_output_state_finish(&backend_state->base); + config_output_state_finish(&config_states[idx]); } free(states); + free(config_states); // Reconfigure all devices, since input config may have been applied before // this output came online, and some config items (like map_to_output) are From 776d445ec5f8a8d5791511c3909d2b02de7c3c31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Sat, 1 Nov 2025 16:15:26 +0000 Subject: [PATCH 12/22] sway/commands/output: color_profile "srgb" applies the piece-wise TF Document that "gamma22" replaced the previous default. --- sway/commands/output/color_profile.c | 13 +++++++++++-- sway/sway-output.5.scd | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c index 98481329b..b145d449b 100644 --- a/sway/commands/output/color_profile.c +++ b/sway/commands/output/color_profile.c @@ -59,11 +59,19 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); } - if (strcmp(*argv, "srgb") == 0) { + if (strcmp(*argv, "gamma22") == 0) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = NULL; config->handler_context.output_config->set_color_transform = true; + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + } else if (strcmp(*argv, "srgb") == 0) { + wlr_color_transform_unref(config->handler_context.output_config->color_transform); + config->handler_context.output_config->color_transform = + wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_SRGB); + config->handler_context.output_config->set_color_transform = true; + config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; } else if (strcmp(*argv, "icc") == 0) { @@ -106,7 +114,8 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { config->handler_context.leftovers.argv = argv + 2; } else { return cmd_results_new(CMD_INVALID, - "Invalid color profile specification: first argument should be icc|srgb"); + "Invalid color profile specification: " + "first argument should be gamma22|icc|srgb"); } return NULL; diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index cc48589c3..afe213643 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -178,8 +178,8 @@ must be separated by one space. For example: updated to work with different bit depths. This command is experimental, and may be removed or changed in the future. -*output* color_profile srgb|[icc ] - Sets the color profile for an output. The default is _srgb_. should be a +*output* color_profile gamma22|srgb|[icc ] + Sets the color profile for an output. The default is _gamma22_. should be a path to a display ICC profile. Not all renderers support this feature; currently it only works with the From 26eb393d6dd7631dc5b7edd0df95342d606e907c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Poisot?= Date: Fri, 5 Dec 2025 21:51:54 +0000 Subject: [PATCH 13/22] sway/commands/output: add color_profile "--device-primaries" When a display is connected, create a color transform from its self-reported color characteristics --- include/sway/config.h | 8 +++- sway/commands/output/color_profile.c | 18 +++++++-- sway/config/output.c | 59 +++++++++++++++++++++++++--- sway/sway-output.5.scd | 16 ++++++-- 4 files changed, 88 insertions(+), 13 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 3c380933e..16b822fea 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -267,6 +267,12 @@ enum render_bit_depth { RENDER_BIT_DEPTH_10, }; +enum color_profile { + COLOR_PROFILE_DEFAULT, // default is Transform with NULL color_transform + COLOR_PROFILE_TRANSFORM, // use color_transform from output_config + COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES, // create transform from wlr_output +}; + /** * Size and position configuration for a particular output. * @@ -288,7 +294,7 @@ struct output_config { int max_render_time; // In milliseconds int adaptive_sync; enum render_bit_depth render_bit_depth; - bool set_color_transform; + enum color_profile color_profile; struct wlr_color_transform *color_transform; int allow_tearing; int hdr; diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c index b145d449b..0456b19b7 100644 --- a/sway/commands/output/color_profile.c +++ b/sway/commands/output/color_profile.c @@ -55,6 +55,14 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { if (!config->handler_context.output_config) { return cmd_results_new(CMD_FAILURE, "Missing output config"); } + + enum color_profile new_mode = COLOR_PROFILE_TRANSFORM; + if (argc >= 2 && strcmp(*argv, "--device-primaries") == 0) { + new_mode = COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES; + argc--; + argv++; + } + if (!argc) { return cmd_results_new(CMD_INVALID, "Missing color_profile first argument."); } @@ -62,7 +70,7 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { if (strcmp(*argv, "gamma22") == 0) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = NULL; - config->handler_context.output_config->set_color_transform = true; + config->handler_context.output_config->color_profile = new_mode; config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; @@ -70,7 +78,7 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_SRGB); - config->handler_context.output_config->set_color_transform = true; + config->handler_context.output_config->color_profile = new_mode; config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; @@ -79,6 +87,10 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Invalid color profile specification: icc type requires a file"); } + if (new_mode != COLOR_PROFILE_TRANSFORM) { + return cmd_results_new(CMD_INVALID, + "Invalid color profile specification: --device-primaries cannot be used with icc"); + } char *icc_path = strdup(argv[1]); if (!expand_path(&icc_path)) { @@ -108,7 +120,7 @@ struct cmd_results *output_cmd_color_profile(int argc, char **argv) { wlr_color_transform_unref(config->handler_context.output_config->color_transform); config->handler_context.output_config->color_transform = tmp; - config->handler_context.output_config->set_color_transform = true; + config->handler_context.output_config->color_profile = COLOR_PROFILE_TRANSFORM; config->handler_context.leftovers.argc = argc - 2; config->handler_context.leftovers.argv = argv + 2; diff --git a/sway/config/output.c b/sway/config/output.c index b884a785d..3d25b46c7 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -75,7 +75,7 @@ struct output_config *new_output_config(const char *name) { oc->max_render_time = -1; oc->adaptive_sync = -1; oc->render_bit_depth = RENDER_BIT_DEPTH_DEFAULT; - oc->set_color_transform = false; + oc->color_profile = COLOR_PROFILE_DEFAULT; oc->color_transform = NULL; oc->power = -1; oc->allow_tearing = -1; @@ -130,12 +130,12 @@ 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 (src->color_profile != COLOR_PROFILE_DEFAULT) { if (dst->color_transform) { wlr_color_transform_unref(dst->color_transform); dst->color_transform = NULL; } - dst->set_color_transform = false; + dst->color_profile = COLOR_PROFILE_DEFAULT; } if (src->background) { free(dst->background); @@ -207,12 +207,12 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { dst->render_bit_depth = src->render_bit_depth; } - if (src->set_color_transform) { + if (src->color_profile != COLOR_PROFILE_DEFAULT) { if (src->color_transform) { wlr_color_transform_ref(src->color_transform); } wlr_color_transform_unref(dst->color_transform); - dst->set_color_transform = true; + dst->color_profile = src->color_profile; dst->color_transform = src->color_transform; } if (src->background) { @@ -570,13 +570,60 @@ static void config_output_state_finish(struct config_output_state *state) { wlr_color_transform_unref(state->color_transform); } +static struct wlr_color_transform *color_profile_from_device(struct wlr_output *wlr_output, + struct wlr_color_transform *transfer_function) { + struct wlr_color_primaries srgb_primaries; + wlr_color_primaries_from_named(&srgb_primaries, WLR_COLOR_NAMED_PRIMARIES_SRGB); + + const struct wlr_color_primaries *primaries = wlr_output->default_primaries; + if (primaries == NULL) { + sway_log(SWAY_INFO, "output has no reported color information"); + if (transfer_function) { + wlr_color_transform_ref(transfer_function); + } + return transfer_function; + } else if (memcmp(primaries, &srgb_primaries, sizeof(*primaries)) == 0) { + sway_log(SWAY_INFO, "output reports sRGB colors, no correction needed"); + if (transfer_function) { + wlr_color_transform_ref(transfer_function); + } + return transfer_function; + } else { + sway_log(SWAY_INFO, "Creating color profile from reported color primaries: " + "R(%f, %f) G(%f, %f) B(%f, %f) W(%f, %f)", + primaries->red.x, primaries->red.y, primaries->green.x, primaries->green.y, + primaries->blue.x, primaries->blue.y, primaries->white.x, primaries->white.y); + float matrix[9]; + wlr_color_primaries_transform_absolute_colorimetric(&srgb_primaries, primaries, matrix); + struct wlr_color_transform *matrix_transform = wlr_color_transform_init_matrix(matrix); + if (matrix_transform == NULL) { + return NULL; + } + struct wlr_color_transform *resolved_tf = transfer_function ? + wlr_color_transform_ref(transfer_function) : + wlr_color_transform_init_linear_to_inverse_eotf(WLR_COLOR_TRANSFER_FUNCTION_GAMMA22); + if (resolved_tf == NULL) { + wlr_color_transform_unref(matrix_transform); + return NULL; + } + struct wlr_color_transform *transforms[] = { matrix_transform, resolved_tf }; + size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]); + struct wlr_color_transform *result = wlr_color_transform_init_pipeline(transforms, transforms_len); + wlr_color_transform_unref(matrix_transform); + wlr_color_transform_unref(resolved_tf); + return result; + } +} + static struct wlr_color_transform *get_color_profile(struct wlr_output *output, struct output_config *oc) { - if (oc && oc->set_color_transform) { + if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM) { if (oc->color_transform) { wlr_color_transform_ref(oc->color_transform); } return oc->color_transform; + } else if (oc && oc->color_profile == COLOR_PROFILE_TRANSFORM_WITH_DEVICE_PRIMARIES) { + return color_profile_from_device(output, oc->color_transform); } else { return NULL; } diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index afe213643..faf59a1f2 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -178,9 +178,19 @@ must be separated by one space. For example: updated to work with different bit depths. This command is experimental, and may be removed or changed in the future. -*output* color_profile gamma22|srgb|[icc ] - Sets the color profile for an output. The default is _gamma22_. should be a - path to a display ICC profile. +*output* color_profile [--device-primaries] gamma22|srgb + Sets the color profile for an output. The default is _gamma22_. + + _--device-primaries_ will use the output's self-reported color primaries + when available (e.g. from display EDID). + + Not all renderers support this feature; currently it only works with the + the Vulkan renderer. It is not compatible with HDR support features. + +*output* color_profile icc + Sets the color profile for an output. + + should be a path to a display ICC profile. Not all renderers support this feature; currently it only works with the the Vulkan renderer. Even where supported, the application of the color From 723b1ea3b7617b6d61143d5cb723c59c916db515 Mon Sep 17 00:00:00 2001 From: llyyr Date: Mon, 5 Jan 2026 20:56:45 +0530 Subject: [PATCH 14/22] sway/input/seat: chase wlroots breaking change Ref: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5230 --- sway/input/seat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/seat.c b/sway/input/seat.c index 1b63f625b..ab31b6746 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1015,9 +1015,9 @@ void seat_configure_xcursor(struct sway_seat *seat) { server.xwayland.xcursor_manager, "default", 1); if (xcursor != NULL) { struct wlr_xcursor_image *image = xcursor->images[0]; + struct wlr_buffer *buffer = wlr_xcursor_image_get_buffer(image); wlr_xwayland_set_cursor( - server.xwayland.wlr_xwayland, image->buffer, - image->width * 4, image->width, image->height, + server.xwayland.wlr_xwayland, buffer, image->hotspot_x, image->hotspot_y); } } From c09d0f1d4a10e958465b38d9b5cf379cdbdfd304 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 6 Jan 2026 00:19:57 +0100 Subject: [PATCH 15/22] Use goto-based error handling in view_init() This is less surprising than checking for the failed flag in the happy path. --- sway/tree/view.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/sway/tree/view.c b/sway/tree/view.c index be813be98..7fd066978 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -43,20 +43,17 @@ bool view_init(struct sway_view *view, enum sway_view_type type, bool failed = false; view->scene_tree = alloc_scene_tree(root->staging, &failed); view->content_tree = alloc_scene_tree(view->scene_tree, &failed); + if (failed) { + goto err; + } - if (!failed && !scene_descriptor_assign(&view->scene_tree->node, - SWAY_SCENE_DESC_VIEW, view)) { - failed = true; + if (!scene_descriptor_assign(&view->scene_tree->node, SWAY_SCENE_DESC_VIEW, view)) { + goto err; } view->image_capture_scene = wlr_scene_create(); if (view->image_capture_scene == NULL) { - failed = true; - } - - if (failed) { - wlr_scene_node_destroy(&view->scene_tree->node); - return false; + goto err; } view->type = type; @@ -67,6 +64,10 @@ bool view_init(struct sway_view *view, enum sway_view_type type, view->tearing_mode = TEARING_WINDOW_HINT; wl_signal_init(&view->events.unmap); return true; + +err: + wlr_scene_node_destroy(&view->scene_tree->node); + return false; } void view_destroy(struct sway_view *view) { From 8224d5fcf89e2cab19c742538f191c67952c9d8b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 4 Jan 2026 17:52:58 +0100 Subject: [PATCH 16/22] Disable Xwayland restacking for toplevel capture scenes The main scene should drive Xwayland restacking. References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5108 --- sway/tree/view.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/tree/view.c b/sway/tree/view.c index 7fd066978..eab2a5e2b 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -55,6 +55,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type, if (view->image_capture_scene == NULL) { goto err; } + view->image_capture_scene->restack_xwayland_surfaces = false; view->type = type; view->impl = impl; From 3c0588a44fbb190af2b6d137910c11cf2793c33e Mon Sep 17 00:00:00 2001 From: Rex Fernando Date: Tue, 6 Jan 2026 16:54:35 +0100 Subject: [PATCH 17/22] sway/input/cursor: fix crash on first stylus tap after reboot via null check handle_tool_tip() previously used event->tool->data without checking for NULL. When launching sway for the first time after a reboot, and then tapping with a USI 2.0 stylus, the tool tip event is fired before the tool proximity event. As event->tool is initialized during the tool proximity handler handle_tool_proximity(), this was causing a crash. The fix adds a NULL check before accessing the fields of event->tool->data. In case of a NULL, a log message is emitted indicating that the tool tip event fired before proximity. This logic is identical to the logic in handle_tool_axis(). Fixes issue #8907 --- sway/input/cursor.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/input/cursor.c b/sway/input/cursor.c index df62ef41b..fc0f11fd8 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -644,6 +644,11 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { cursor_handle_activity_from_device(cursor, &event->tablet->base); struct sway_tablet_tool *sway_tool = event->tool->data; + if (!sway_tool) { + sway_log(SWAY_DEBUG, "tool tip before proximity"); + return; + } + struct wlr_tablet_v2_tablet *tablet_v2 = sway_tool->tablet->tablet_v2; struct sway_seat *seat = cursor->seat; From 40aabb80c645519107dc325abc53e4176e896fb9 Mon Sep 17 00:00:00 2001 From: Ole Date: Wed, 14 Jan 2026 00:12:18 +0100 Subject: [PATCH 18/22] sway-ipc(7): Clarify that the reply shown is an example Fix documentation of sway-ipc. The example reply for GET_BINDIG_STATE is an example and not the exact reply. This bug seems to come from copy/paste from the ipc command SYNC directly above. --- sway/sway-ipc.7.scd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index 4b0d5c962..2658228a1 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1116,7 +1116,7 @@ Returns the currently active binding mode. A single object that contains the property _name_, which is set to the currently active binding mode as a string. -*Exact Reply:* +*Example Reply:* ``` { "name": "default" From e3c24125658c895bf23ab5ebddde55560d3f3ac5 Mon Sep 17 00:00:00 2001 From: llyyr Date: Thu, 29 Jan 2026 04:44:02 +0530 Subject: [PATCH 19/22] sway: advertise color_manager_v1 version 2 Ref: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/5223 --- sway/server.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 3446cc97f..6003f4559 100644 --- a/sway/server.c +++ b/sway/server.c @@ -466,7 +466,7 @@ bool server_init(struct sway_server *server) { enum wp_color_manager_v1_primaries *primaries = wlr_color_manager_v1_primaries_list_from_renderer(server->renderer, &primaries_len); struct wlr_color_manager_v1 *cm = wlr_color_manager_v1_create( - server->wl_display, 1, &(struct wlr_color_manager_v1_options){ + server->wl_display, 2, &(struct wlr_color_manager_v1_options){ .features = { .parametric = true, .set_mastering_display_primaries = true, From b081eba05d463cace54eebc1e2f73b23f470949a Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sat, 31 Jan 2026 17:17:07 -0500 Subject: [PATCH 20/22] bar_cmd_colors: fix crash on incorrect usage should display error message instead to be consistent with other subcommands. --- sway/commands/bar/colors.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/commands/bar/colors.c b/sway/commands/bar/colors.c index 275fa3c64..da606c1b8 100644 --- a/sway/commands/bar/colors.c +++ b/sway/commands/bar/colors.c @@ -72,6 +72,10 @@ static struct cmd_results *parse_three_colors(char ***colors, } struct cmd_results *bar_cmd_colors(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "colors", EXPECTED_AT_LEAST, 1))) { + return error; + } return config_subcommand(argv, argc, bar_colors_handlers, sizeof(bar_colors_handlers)); } From 992d201512a26b49f070f22b32e988d2bd51105e Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Tue, 3 Feb 2026 10:26:50 +0000 Subject: [PATCH 21/22] sway: change unsupported GPU message to swaynag This commit shows a swaynag message when an unsupported GPU is detected which must be acknowledged by users. It also adds an environment variable (`SWAY_UNSUPPORTED_GPU`) which may be used instead of the `--unsupported-gpu` argument. The `static` storage class for flag variables in main has also been removed, as this should have no effect on the program. Resolves: #8999 --- include/sway/server.h | 2 +- sway/main.c | 24 +++++++++++++++++++++++- sway/server.c | 17 +++-------------- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/include/sway/server.h b/include/sway/server.h index f50d48f05..978f05d0a 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -160,7 +160,7 @@ struct sway_debug { extern struct sway_debug debug; -extern bool allow_unsupported_gpu; +extern bool unsupported_gpu_detected; void sway_terminate(int exit_code); diff --git a/sway/main.c b/sway/main.c index 44d07679e..e5dc497b3 100644 --- a/sway/main.c +++ b/sway/main.c @@ -224,7 +224,7 @@ static const char usage[] = "\n"; int main(int argc, char **argv) { - static bool verbose = false, debug = false, validate = false; + bool verbose = false, debug = false, validate = false, allow_unsupported_gpu = false; char *config_path = NULL; @@ -286,6 +286,12 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } + char *unsupported_gpu_env = getenv("SWAY_UNSUPPORTED_GPU"); + // we let the flag override the environment variable + if (!allow_unsupported_gpu && unsupported_gpu_env) { + allow_unsupported_gpu = parse_boolean(unsupported_gpu_env, false); + } + // As the 'callback' function for wlr_log is equivalent to that for // sway, we do not need to override it. if (debug) { @@ -373,6 +379,18 @@ int main(int argc, char **argv) { swaynag_show(&config->swaynag_config_errors); } + struct swaynag_instance nag_gpu = (struct swaynag_instance){ + .args = "--type error " + "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' ", + .detailed = false, + }; + + if (unsupported_gpu_detected && !allow_unsupported_gpu) { + if (!swaynag_spawn(config->swaynag_command, &nag_gpu)) { + sway_log(SWAY_ERROR, "Unable to start swaynag"); + } + } + server_run(&server); shutdown: @@ -385,6 +403,10 @@ shutdown: free(config_path); free_config(config); + if (nag_gpu.client != NULL) { + wl_client_destroy(nag_gpu.client); + } + pango_cairo_font_map_set_default(NULL); return exit_value; diff --git a/sway/server.c b/sway/server.c index 6003f4559..aef0d0b0c 100644 --- a/sway/server.c +++ b/sway/server.c @@ -78,7 +78,7 @@ #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 #define SWAY_PRESENTATION_VERSION 2 -bool allow_unsupported_gpu = false; +bool unsupported_gpu_detected = false; #if WLR_HAS_DRM_BACKEND static void handle_drm_lease_request(struct wl_listener *listener, void *data) { @@ -161,27 +161,16 @@ static void detect_proprietary(struct wlr_backend *backend, void *data) { return; } - bool is_unsupported = false; if (strcmp(version->name, "nvidia-drm") == 0) { - is_unsupported = true; + unsupported_gpu_detected = true; sway_log(SWAY_ERROR, "!!! Proprietary Nvidia drivers are in use !!!"); - if (!allow_unsupported_gpu) { - sway_log(SWAY_ERROR, "Use Nouveau instead"); - } } if (strcmp(version->name, "evdi") == 0) { - is_unsupported = true; + unsupported_gpu_detected = true; sway_log(SWAY_ERROR, "!!! Proprietary DisplayLink drivers are in use !!!"); } - if (!allow_unsupported_gpu && is_unsupported) { - sway_log(SWAY_ERROR, - "Proprietary drivers are NOT supported. To launch sway anyway, " - "launch with --unsupported-gpu and DO NOT report issues."); - exit(EXIT_FAILURE); - } - drmFreeVersion(version); } From 845cdb190f0c577789425cd9ea4772bd2fcfc836 Mon Sep 17 00:00:00 2001 From: Calvin Lee Date: Tue, 3 Feb 2026 14:51:08 +0000 Subject: [PATCH 22/22] sway: add details to unsupported gpu nag --- sway/main.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/sway/main.c b/sway/main.c index e5dc497b3..a94389266 100644 --- a/sway/main.c +++ b/sway/main.c @@ -381,14 +381,16 @@ int main(int argc, char **argv) { struct swaynag_instance nag_gpu = (struct swaynag_instance){ .args = "--type error " - "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' ", - .detailed = false, + "--message 'Proprietary GPU drivers are not supported by sway. Do not report issues.' " + "--detailed-message", + .detailed = true, }; if (unsupported_gpu_detected && !allow_unsupported_gpu) { - if (!swaynag_spawn(config->swaynag_command, &nag_gpu)) { - sway_log(SWAY_ERROR, "Unable to start swaynag"); - } + swaynag_log(config->swaynag_command, &nag_gpu, + "To remove this message, launch sway with --unsupported-gpu " + "or set the environment variable SWAY_UNSUPPORTED_GPU=true."); + swaynag_show(&nag_gpu); } server_run(&server);