From 1e53007bc33c1afa2c3ea580050f94f29c5ce8e4 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 11:47:10 +0100 Subject: [PATCH 01/10] desktop/output: Store output config on request_state An output backend might request any change to an output state at any time, although currently only this is currently only used for changing window size on the wayland and x11 backend. Applying the configuration directly means that the current output state becomes inconsistent with the configured state, which can cause the new state to be reverted later if apply_stored_output_configs is called. Before 4f9ce4675cf4. the output geometry would be updated by arrange_outputs, but this is only done by the modeset logic now, resulting in the stored geometry never being updated on wayland backend window resize. This was not discovered as the stored geometry is not used particularly often. Solve both by storing a new output configuration and relying on the modeset logic to apply a new state. Fixes: 4f9ce4675cf4 ("tree/arrange: Remove redundant output geometry update") --- sway/desktop/output.c | 48 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 18a04c090d..394c545deb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -457,19 +457,47 @@ static void handle_request_state(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; + const struct wlr_output_state *state = event->state; - uint32_t committed = event->state->committed; - wlr_output_commit_state(output->wlr_output, event->state); + // Store the requested changes so that the active configuration is + // consistent with the current state, and to avoid duplicate logic to apply + // the changes. + struct output_config *oc = new_output_config(output->wlr_output->name); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + return; + } - if (committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE)) { - arrange_layers(output); - arrange_output(output); - transaction_commit_dirty(); + int committed = state->committed; + if (committed & WLR_OUTPUT_STATE_MODE) { + if (state->mode != NULL) { + oc->width = state->mode->width; + oc->height = state->mode->height; + oc->refresh_rate = state->mode->refresh / 1000.f; + } else { + oc->width = state->custom_mode.width; + oc->height = state->custom_mode.height; + oc->refresh_rate = state->custom_mode.refresh / 1000.f; + } + committed &= ~WLR_OUTPUT_STATE_MODE; + } + if (committed & WLR_OUTPUT_STATE_SCALE) { + oc->scale = state->scale; + committed &= ~WLR_OUTPUT_STATE_SCALE; + } + if (committed & WLR_OUTPUT_STATE_TRANSFORM) { + oc->transform = state->transform; + committed &= ~WLR_OUTPUT_STATE_TRANSFORM; + } + + // We do not expect or support any other changes here + assert(committed == 0); + store_output_config(oc); + apply_stored_output_configs(); - update_output_manager_config(output->server); + if (server.delayed_modeset != NULL) { + wl_event_source_remove(server.delayed_modeset); + server.delayed_modeset = NULL; } } From f38719f575ab88e9d38bf4cfe3cb744071772bf2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 12:07:18 +0100 Subject: [PATCH 02/10] desktop/output: Add missing output config allocation checks --- sway/desktop/output.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 394c545deb..064b5449b8 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -582,6 +582,10 @@ void handle_new_output(struct wl_listener *listener, void *data) { static struct output_config *output_config_for_config_head( struct wlr_output_configuration_head_v1 *config_head) { struct output_config *oc = new_output_config(config_head->state.output->name); + if (!oc) { + return NULL; + } + oc->enabled = config_head->state.enabled; if (!oc->enabled) { return oc; @@ -612,7 +616,8 @@ static void output_manager_apply(struct sway_server *server, size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads); struct output_config **configs = calloc(configs_len, sizeof(*configs)); if (!configs) { - goto done; + sway_log(SWAY_ERROR, "Allocation failed"); + goto error; } size_t start_new_configs = config->output_configs->length; for (size_t idx = 0; idx < start_new_configs; idx++) { @@ -625,6 +630,10 @@ static void output_manager_apply(struct sway_server *server, // Generate the configuration and store it as a temporary // config. We keep a record of it so we can remove it later. struct output_config *oc = output_config_for_config_head(config_head); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + goto error_config; + } configs[config_idx++] = oc; } @@ -632,6 +641,8 @@ static void output_manager_apply(struct sway_server *server, // if any output configured for enablement fails to be enabled, even if it // was not part of the config heads we were asked to configure. ok = apply_output_configs(configs, configs_len, test_only, false); + +error_config: for (size_t idx = start_new_configs; idx < configs_len; idx++) { struct output_config *cfg = configs[idx]; if (!test_only && ok) { @@ -642,7 +653,7 @@ static void output_manager_apply(struct sway_server *server, } free(configs); -done: +error: if (ok) { wlr_output_configuration_v1_send_succeeded(cfg); if (server->delayed_modeset != NULL) { @@ -677,6 +688,11 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct sway_output *output = event->output->data; struct output_config *oc = new_output_config(output->wlr_output->name); + if (!oc) { + sway_log(SWAY_ERROR, "Allocation failed"); + return; + } + switch (event->mode) { case ZWLR_OUTPUT_POWER_V1_MODE_OFF: oc->power = 0; From d417a8fcd0a701395c2029adade261d19800f763 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 27 Oct 2024 21:55:24 +0100 Subject: [PATCH 03/10] release.sh: read meson-rewrite output from stdout Since version 1.6, Meson now uses stdout: https://github.com/mesonbuild/meson/commit/3f4957c713f70d708f066fff119088040bb1d287 --- release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.sh b/release.sh index c5644cfd05..6ad8f2c5ef 100755 --- a/release.sh +++ b/release.sh @@ -1,7 +1,7 @@ #!/bin/sh -eu prev=$(git describe --tags --abbrev=0) -next=$(meson rewrite kwargs info project / 2>&1 >/dev/null | jq -r '.kwargs["project#/"].version') +next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version') case "$next" in *-dev) From 4cfcb3643bc2123c65c3748761d624c86df0002e Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 3 Nov 2024 16:01:09 -0500 Subject: [PATCH 04/10] container: Properly constrain title bar padding Important for centered titles --- sway/tree/container.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index f482b06bc4..62bff1ea5d 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -349,7 +349,7 @@ void container_arrange_title_bar(struct sway_container *con) { h_padding = width - config->titlebar_h_padding - marks_buffer_width; } - h_padding = MAX(h_padding, 0); + h_padding = MAX(h_padding, config->titlebar_h_padding); int alloc_width = MIN((int)node->width, width - h_padding - config->titlebar_h_padding); @@ -375,7 +375,7 @@ void container_arrange_title_bar(struct sway_container *con) { h_padding = config->titlebar_h_padding; } - h_padding = MAX(h_padding, 0); + h_padding = MAX(h_padding, config->titlebar_h_padding); int alloc_width = MIN((int) node->width, width - h_padding - config->titlebar_h_padding); From 78fa4e985618209a289f892dc54d67c83494a9d2 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 5 Nov 2024 15:31:13 +0100 Subject: [PATCH 05/10] config/output: Update output position in two passes The modeset logic iterates over all outputs at the end, sets their new position in the layout and takes a copy of its geometry that is later referenced by layout and scene management code. If one output is auto configured, then a later output that is manually configured can lead to the first output being moved without the stored geometry being updated. Split this into two passes: The first pass finalizes the output config and makes updates to the layout, while the second pass updates the copy of the geometry and arranges things as a result of it. --- sway/config/output.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 5fb282d4da..f8922ea527 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -533,14 +533,6 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output wlr_output_layout_add_auto(root->output_layout, wlr_output); } - // Update output->{lx, ly, width, height} - struct wlr_box output_box; - wlr_output_layout_get_box(root->output_layout, wlr_output, &output_box); - output->lx = output_box.x; - output->ly = output_box.y; - output->width = output_box.width; - output->height = output_box.height; - if (!output->enabled) { output_enable(output); } @@ -562,6 +554,15 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } +static void output_update_position(struct sway_output *output) { + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, output->wlr_output, &output_box); + output->lx = output_box.x; + output->ly = output_box.y; + output->width = output_box.width; + output->height = output_box.height; +} + // find_output_config_from_list returns a merged output_config containing all // stored configuration that applies to the specified output. static struct output_config *find_output_config_from_list( @@ -933,6 +934,13 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, sway_log(SWAY_DEBUG, "Finalizing config for %s", cfg->output->wlr_output->name); finalize_output_config(cfg->config, cfg->output); + } + + // Output layout being applied in finalize_output_config can shift outputs + // around, so we do a second pass to update positions and arrange. + for (size_t idx = 0; idx < configs_len; idx++) { + struct matched_output_config *cfg = &configs[idx]; + output_update_position(cfg->output); arrange_layers(cfg->output); } From 62fd8c4d011ea4dc360831723a3844c0e4439f32 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Tue, 29 Oct 2024 14:56:33 +0530 Subject: [PATCH 06/10] desktop/transaction: clamp vertical border length to 0 Fixes #8120 --- sway/desktop/transaction.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 8f12832a02..50a597a662 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -424,13 +424,14 @@ static void arrange_container(struct sway_container *con, int border_bottom = con->current.border_bottom ? border_width : 0; int border_left = con->current.border_left ? border_width : 0; int border_right = con->current.border_right ? border_width : 0; + int vert_border_height = MAX(0, height - border_top - border_bottom); wlr_scene_rect_set_size(con->border.top, width, border_top); wlr_scene_rect_set_size(con->border.bottom, width, border_bottom); wlr_scene_rect_set_size(con->border.left, - border_left, height - border_top - border_bottom); + border_left, vert_border_height); wlr_scene_rect_set_size(con->border.right, - border_right, height - border_top - border_bottom); + border_right, vert_border_height); wlr_scene_node_set_position(&con->border.top->node, 0, 0); wlr_scene_node_set_position(&con->border.bottom->node, From 03483ff3707a358d935e451d39748e58c205ce8a Mon Sep 17 00:00:00 2001 From: Manuel Stoeckl Date: Fri, 8 Nov 2024 19:44:30 -0500 Subject: [PATCH 07/10] swaynag: fix null dereference on scale change If cursor-shape-v1 is available, the old wl_cursor_theme path should not be used. --- swaynag/swaynag.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/swaynag/swaynag.c b/swaynag/swaynag.c index 50eea1483a..da32eeb769 100644 --- a/swaynag/swaynag.c +++ b/swaynag/swaynag.c @@ -324,7 +324,9 @@ static void output_scale(void *data, struct wl_output *output, swaynag_output->scale = factor; if (swaynag_output->swaynag->output == swaynag_output) { swaynag_output->swaynag->scale = swaynag_output->scale; - update_all_cursors(swaynag_output->swaynag); + if (!swaynag_output->swaynag->cursor_shape_manager) { + update_all_cursors(swaynag_output->swaynag); + } render_frame(swaynag_output->swaynag); } } From f23d10074739de31e9339796dc06348fd919c515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Bruguera=20Mic=C3=B3?= Date: Sun, 10 Nov 2024 15:24:15 +0000 Subject: [PATCH 08/10] swaybar: Emit property changes for SNI watcher Emit property change signals for the IsStatusNotifierHostRegistered and RegisteredStatusNotifierItems properties in StatusNotifierWatcher, so code relying on the PropertiesChanged signal, instead of signals such as StatusNotifierHostRegistered, can work properly. A library that is affected by this is the libappindicator-gtk3* library and it can cause tray icons to be missing after starting swaybar due to a race condition, as follows: * An application using libappindicator-gtk3 starts, e.g. nm-applet. * Some time later, swaybar starts. * swaybar creates the StatusNotifierWatcher. * libappindicator-gtk3 observes the new watcher, but it sees that IsStatusNotifierHostRegistered=false, so it falls back to the Freedesktop System tray protocol. * swaybar creates the StatusNotifierHost. At this point, libappindicator-gtk3 should "un-fallback" back to SNI. However, since swaybar does not emit the PropertiesChange signal on IsStatusNotifierHostRegistered, libappindicator-gtk3 doesn't get notified, and stays in fallback state forever. * As a result, nm-applet will not show in the swaybar tray. This race can be made reliable by inserting a 1-second long sleep here: https://github.com/swaywm/sway/blob/03483ff3707a358d935e451d39748e58c205ce8a/swaybar/tray/tray.c#L57 (*) Note that the libappindicator-gtk3 library has been mostly replaced by libayatana-appindicator, which is not affected by this. The affected version is still used by Arch Linux, source code at: https://bazaar.launchpad.net/~indicator-applet-developers/libappindicator/trunk/files/298 --- swaybar/tray/watcher.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 3cfea8d8ea..54f000bd65 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -38,6 +38,8 @@ static int handle_lost_service(sd_bus_message *msg, list_del(watcher->items, idx--); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierItemUnregistered", "s", id); + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "RegisteredStatusNotifierItems", NULL); free(id); if (using_standard_protocol(watcher)) { break; @@ -50,6 +52,10 @@ static int handle_lost_service(sd_bus_message *msg, sway_log(SWAY_DEBUG, "Unregistering Status Notifier Host '%s'", service); free(watcher->hosts->items[idx]); list_del(watcher->hosts, idx); + if (watcher->hosts->length == 0) { + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "IsStatusNotifierHostRegistered", NULL); + } } } @@ -82,6 +88,8 @@ static int register_sni(sd_bus_message *msg, void *data, sd_bus_error *error) { if (list_seq_find(watcher->items, cmp_id, id) == -1) { sway_log(SWAY_DEBUG, "Registering Status Notifier Item '%s'", id); list_add(watcher->items, id); + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "RegisteredStatusNotifierItems", NULL); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierItemRegistered", "s", id); } else { @@ -104,6 +112,10 @@ static int register_host(sd_bus_message *msg, void *data, sd_bus_error *error) { if (list_seq_find(watcher->hosts, cmp_id, service) == -1) { sway_log(SWAY_DEBUG, "Registering Status Notifier Host '%s'", service); list_add(watcher->hosts, strdup(service)); + if (watcher->hosts->length == 1) { + sd_bus_emit_properties_changed(watcher->bus, obj_path, watcher->interface, + "IsStatusNotifierHostRegistered", NULL); + } sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, "StatusNotifierHostRegistered", ""); } else { From 463c4c9369dc551c51c0888b411d49f8d9660a85 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 11 Nov 2024 12:47:52 +0100 Subject: [PATCH 09/10] desktop/output: Clean up output state if build_state fails wlr_scene_output_build_state can fail for various reasons. Ensure that the pending output state is cleaned up in that case. --- sway/desktop/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 064b5449b8..9b98e29a43 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -282,6 +282,7 @@ static int output_repaint_timer_handler(void *data) { struct wlr_output_state pending; wlr_output_state_init(&pending); if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { + wlr_output_state_finish(&pending); return 0; } From fdc4318ac66d257d21e8f3b953e341d5e80a1ddc Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 11 Nov 2024 12:48:50 +0100 Subject: [PATCH 10/10] desktop/output: Clear frame_pending even output is disabled frame_pending should always be cleared once the repaint callback is fired to ensure that future frame scheduling is not accidentally held back. --- sway/desktop/output.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9b98e29a43..8682cc6564 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -262,12 +262,11 @@ static bool output_can_tear(struct sway_output *output) { static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; + output->wlr_output->frame_pending = false; if (!output->enabled) { return 0; } - output->wlr_output->frame_pending = false; - output_configure_scene(output, &root->root_scene->tree.node, 1.0f); struct wlr_scene_output_state_options opts = {