From fc2796aee8e169f0d1d8ddcb4db2a0ee7cc7421b Mon Sep 17 00:00:00 2001 From: Bill Li Date: Sun, 14 Jul 2024 16:24:14 +0800 Subject: [PATCH 001/108] Chase wlroots!2434 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/2434 --- sway/desktop/xwayland.c | 4 ++-- sway/input/cursor.c | 6 +++--- sway/input/seatop_default.c | 6 +++--- sway/input/tablet.c | 2 +- sway/tree/view.c | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 270cf08f8..0d45543a7 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -71,7 +71,7 @@ static void unmanaged_handle_map(struct wl_listener *listener, void *data) { surface->set_geometry.notify = unmanaged_handle_set_geometry; } - if (wlr_xwayland_or_surface_wants_focus(xsurface)) { + if (wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { struct sway_seat *seat = input_manager_current_seat(); struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); @@ -96,7 +96,7 @@ static void unmanaged_handle_unmap(struct wl_listener *listener, void *data) { // This simply returns focus to the parent surface if there's one available. // This seems to handle JetBrains issues. if (xsurface->parent && xsurface->parent->surface - && wlr_xwayland_or_surface_wants_focus(xsurface->parent)) { + && wlr_xwayland_surface_override_redirect_wants_focus(xsurface->parent)) { seat_set_focus_surface(seat, xsurface->parent->surface, false); return; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index 235951d4c..bbd16717f 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -578,7 +578,7 @@ static void handle_tablet_tool_position(struct sway_cursor *cursor, // tablet events until the drag is released, even if we are now over a // non-tablet surface. if (!cursor->simulating_pointer_from_tool_tip && - ((surface && wlr_surface_accepts_tablet_v2(tablet->tablet_v2, surface)) || + ((surface && wlr_surface_accepts_tablet_v2(surface, tablet->tablet_v2)) || wlr_tablet_tool_v2_has_implicit_grab(tool->tablet_v2_tool))) { seatop_tablet_tool_motion(seat, tool, time_msec); } else { @@ -664,7 +664,7 @@ static void handle_tool_tip(struct wl_listener *listener, void *data) { dispatch_cursor_button(cursor, &event->tablet->base, event->time_msec, BTN_LEFT, WL_POINTER_BUTTON_STATE_RELEASED); wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); - } else if (!surface || !wlr_surface_accepts_tablet_v2(tablet_v2, surface)) { + } else if (!surface || !wlr_surface_accepts_tablet_v2(surface, tablet_v2)) { // If we started holding the tool tip down on a surface that accepts // tablet v2, we should notify that surface if it gets released over a // surface that doesn't support v2. @@ -749,7 +749,7 @@ static void handle_tool_button(struct wl_listener *listener, void *data) { bool mod_pressed = modifiers & config->floating_mod; bool surface_supports_tablet_events = - surface && wlr_surface_accepts_tablet_v2(tablet_v2, surface); + surface && wlr_surface_accepts_tablet_v2(surface, tablet_v2); // Simulate pointer when: // 1. The modifier key is pressed, OR diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index f4a0f4634..42ce333b8 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -273,7 +273,7 @@ static void handle_tablet_tool_tip(struct sway_seat *seat, // Handle tapping on an xwayland unmanaged view else if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { + wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); seat_set_focus_surface(seat, xsurface->surface, false); @@ -521,7 +521,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, if (surface && (xsurface = wlr_xwayland_surface_try_from_wlr_surface(surface)) && xsurface->override_redirect && - wlr_xwayland_or_surface_wants_focus(xsurface)) { + wlr_xwayland_surface_override_redirect_wants_focus(xsurface)) { struct wlr_xwayland *xwayland = server.xwayland.wlr_xwayland; wlr_xwayland_set_seat(xwayland, seat->wlr_seat); seat_set_focus_surface(seat, xsurface->surface, false); @@ -667,7 +667,7 @@ static void handle_touch_down(struct sway_seat *seat, double sx, sy; node_at_coords(seat, seat->touch_x, seat->touch_y, &surface, &sx, &sy); - if (surface && wlr_surface_accepts_touch(wlr_seat, surface)) { + if (surface && wlr_surface_accepts_touch(surface, wlr_seat)) { if (seat_is_input_allowed(seat, surface)) { cursor->simulating_pointer_from_touch = false; seatop_begin_touch_down(seat, surface, event, sx, sy, lx, ly); diff --git a/sway/input/tablet.c b/sway/input/tablet.c index ec1e4f682..19d5debf9 100644 --- a/sway/input/tablet.c +++ b/sway/input/tablet.c @@ -363,7 +363,7 @@ void sway_tablet_pad_set_focus(struct sway_tablet_pad *tablet_pad, } if (surface == NULL || - !wlr_surface_accepts_tablet_v2(tablet_pad->tablet->tablet_v2, surface)) { + !wlr_surface_accepts_tablet_v2(surface, tablet_pad->tablet->tablet_v2)) { return; } diff --git a/sway/tree/view.c b/sway/tree/view.c index 4f757acf1..229d765ea 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -850,7 +850,7 @@ void view_map(struct sway_view *view, struct wlr_surface *wlr_surface, #if WLR_HAS_XWAYLAND struct wlr_xwayland_surface *xsurface; if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(wlr_surface))) { - set_focus &= wlr_xwayland_icccm_input_model(xsurface) != + set_focus &= wlr_xwayland_surface_icccm_input_model(xsurface) != WLR_ICCCM_INPUT_MODEL_NONE; } #endif From 50073dc579fffffda8a4de903719b9bbb9d5ac3d Mon Sep 17 00:00:00 2001 From: Bill Li Date: Mon, 15 Jul 2024 05:16:40 +0800 Subject: [PATCH 002/108] ci: use package x11-servers/xwayland instead of x11-servers/xwayland-devel --- .builds/freebsd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8084574c2..977fe467e 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -27,7 +27,7 @@ packages: - x11/libX11 - x11/pixman - x11/xcb-util-wm -- x11-servers/xwayland-devel +- x11-servers/xwayland - misc/hwdata sources: - https://github.com/swaywm/sway From a3a9ec1211fcf857aa2e047f9a1c1388d17194c3 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 14 Jul 2024 23:22:54 +0200 Subject: [PATCH 003/108] build: use fs.relative_to() instead of hand-rolled logic Meson has introduced a relative_to() function [1] in its fs module since version 1.3. [1]: https://mesonbuild.com/Fs-module.html#relative_to --- meson.build | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/meson.build b/meson.build index c5595a86a..0d5b0cc6c 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ project( 'c', version: '1.10-dev', license: 'MIT', - meson_version: '>=0.60.0', + meson_version: '>=1.3', default_options: [ 'c_std=c11', 'warning_level=2', @@ -172,31 +172,10 @@ if git.found() endif add_project_arguments('-DSWAY_VERSION=@0@'.format(version), language: 'c') -# Compute the relative path used by compiler invocations. -source_root = meson.current_source_dir().split('/') -build_root = meson.global_build_root().split('/') -relative_dir_parts = [] -i = 0 -in_prefix = true -foreach p : build_root - if i >= source_root.length() or not in_prefix or p != source_root[i] - in_prefix = false - relative_dir_parts += '..' - endif - i += 1 -endforeach -i = 0 -in_prefix = true -foreach p : source_root - if i >= build_root.length() or not in_prefix or build_root[i] != p - in_prefix = false - relative_dir_parts += p - endif - i += 1 -endforeach -relative_dir = join_paths(relative_dir_parts) + '/' +fs = import('fs') # Strip relative path prefixes from the code if possible, otherwise hide them. +relative_dir = fs.relative_to(meson.current_source_dir(), meson.global_build_root()) + '/' if cc.has_argument('-fmacro-prefix-map=/prefix/to/hide=') add_project_arguments( '-fmacro-prefix-map=@0@='.format(relative_dir), From 3f327b3db0c1fc6985c0ed3231e1bd6296584dad Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 15 Jul 2024 00:12:39 +0200 Subject: [PATCH 004/108] desktop/output: Stop repaint loop when not needed 1e0031781fc9 refactored repaint to accumulate all changes in a single wlr_output_state and commit them at the end of the repaint loop, replacing a call to wlr_scene_output_commit. wlr_scene_output_commit contains an early bail-out when no frame has been requested and no damage has accumulated, which was not replicated as part of this refactor, causing the repaint loop to never pause. Replicate the logic to stop the repaint loop as needed. Fixes: 1e0031781fc9 ("desktop/output: unify page-flip codepath") --- sway/desktop/output.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f936b2a8d..27ede68ee 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -247,6 +247,13 @@ static int output_repaint_timer_handler(void *data) { .color_transform = output->color_transform, }; + struct wlr_output *wlr_output = output->wlr_output; + struct wlr_scene_output *scene_output = output->scene_output; + if (!wlr_output->needs_frame && !output->gamma_lut_changed && + !pixman_region32_not_empty(&scene_output->pending_commit_damage)) { + return 0; + } + struct wlr_output_state pending; wlr_output_state_init(&pending); if (!wlr_scene_output_build_state(output->scene_output, &pending, &opts)) { From 4d4c88f0a73f6ee3da1c99355f04362ef2ad68c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joan=20Bruguera=20Mic=C3=B3?= Date: Sat, 20 Jul 2024 22:34:01 +0000 Subject: [PATCH 005/108] layer-shell: Restore interactive layer focus code Commit 188811f80861 ("scene_graph: Port layer_shell") accidentally removed code in `arrange_layers` to handle focus on layer shell surfaces with keyboard interactivity. Due to this, layer shell surfaces requesting exclusive keyboard interactivity may not get automatically focused, and layer shell surfaces giving up exclusive keyboard interactivity can remain focused. Add the previous code back to fix the problem. Note the non-rename change included in b4d7e84d3852 ("desktop: Rename layers to shell_layers") is not included as it also seems accidental. Fixes: #7936 --- sway/desktop/layer_shell.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 6221b7b97..b136a24e7 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -90,6 +90,43 @@ void arrange_layers(struct sway_output *output) { } else { arrange_popups(root->layers.popup); } + + // Find topmost keyboard interactive layer, if such a layer exists + struct wlr_scene_tree *layers_above_shell[] = { + output->layers.shell_overlay, + output->layers.shell_top, + }; + size_t nlayers = sizeof(layers_above_shell) / sizeof(layers_above_shell[0]); + struct wlr_scene_node *node; + struct sway_layer_surface *topmost = NULL; + for (size_t i = 0; i < nlayers; ++i) { + wl_list_for_each_reverse(node, + &layers_above_shell[i]->children, link) { + struct sway_layer_surface *surface = scene_descriptor_try_get(node, + SWAY_SCENE_DESC_LAYER_SHELL); + if (surface && surface->layer_surface->current.keyboard_interactive + == ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE && + surface->layer_surface->surface->mapped) { + topmost = surface; + break; + } + } + if (topmost != NULL) { + break; + } + } + + struct sway_seat *seat; + wl_list_for_each(seat, &server.input->seats, link) { + seat->has_exclusive_layer = false; + if (topmost != NULL) { + seat_set_focus_layer(seat, topmost->layer_surface); + } else if (seat->focused_layer && + seat->focused_layer->current.keyboard_interactive + != ZWLR_LAYER_SURFACE_V1_KEYBOARD_INTERACTIVITY_EXCLUSIVE) { + seat_set_focus_layer(seat, NULL); + } + } } static struct wlr_scene_tree *sway_layer_get_scene(struct sway_output *output, From 7e74a4914261cf32c45017521960adf7ff6dac8f Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Mon, 29 Jul 2024 20:14:18 +0200 Subject: [PATCH 006/108] desktop/xwayland: don't restack when marking window as inactive daaec72ac01f ("desktop/xwayland: restack surface upon activation") has updated Sway for wlroots commit bfc69decdd04 ("xwm: do not restack surfaces on activation"). However, it unconditionally restacks the window above all other windows even if marking the window as inactive. Closes: https://github.com/swaywm/sway/issues/7974 --- sway/desktop/xwayland.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 0d45543a7..e4d54ca6c 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -289,7 +289,9 @@ static void set_activated(struct sway_view *view, bool activated) { } wlr_xwayland_surface_activate(surface, activated); - wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); + if (activated) { + wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); + } } static void set_tiled(struct sway_view *view, bool tiled) { From 9bb45a403758c8606fe9a7f0b5b5316bae1a12dd Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 2 Aug 2024 17:49:44 +0300 Subject: [PATCH 007/108] xwayland: chase wlr_xwayland_surface_set_maximized() change See https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4670. --- sway/desktop/xwayland.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index e4d54ca6c..805c9b9d5 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -299,7 +299,7 @@ static void set_tiled(struct sway_view *view, bool tiled) { return; } struct wlr_xwayland_surface *surface = view->wlr_xwayland_surface; - wlr_xwayland_surface_set_maximized(surface, tiled); + wlr_xwayland_surface_set_maximized(surface, tiled, tiled); } static void set_fullscreen(struct sway_view *view, bool fullscreen) { From 6e4ccb99c3a2197468f8f34c290b7cd5612ff80b Mon Sep 17 00:00:00 2001 From: James Knight Date: Sat, 3 Aug 2024 12:30:17 -0400 Subject: [PATCH 008/108] build: avoid git repository discovery when determining version When attempting to use Git to populate commit/branch information in a version string, it is possible through repository discovery that it uses Git information not relevant to project. For example, if repository content is extract into an interim build location when using an embedded build framework (e.g. Buildroot), the project will not have its Git repository to refer to. When it cannot find its repository, it will look into its parent folders and may find the Git repository of another project and use its branch/commit information. This commit provides an explicit path to the project's Git repository when consider commit/branch information. This will prevent any repository discovery from occurring. Signed-off-by: James Knight --- meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/meson.build b/meson.build index 0d5b0cc6c..71e75fd9b 100644 --- a/meson.build +++ b/meson.build @@ -160,8 +160,8 @@ add_project_arguments('-DSYSCONFDIR="/@0@"'.format(join_paths(prefix, sysconfdir version = '"@0@"'.format(meson.project_version()) git = find_program('git', native: true, required: false) if git.found() - git_commit = run_command([git, 'rev-parse', '--short', 'HEAD'], check: false) - git_branch = run_command([git, 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) + git_commit = run_command([git, '--git-dir=.git', 'rev-parse', '--short', 'HEAD'], check: false) + git_branch = run_command([git, '--git-dir=.git', 'rev-parse', '--abbrev-ref', 'HEAD'], check: false) if git_commit.returncode() == 0 and git_branch.returncode() == 0 version = '"@0@-@1@ (" __DATE__ ", branch \'@2@\')"'.format( meson.project_version(), From b881c2e84c4be3c7b996f85200cfe391a7979267 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 10 Jul 2024 12:20:53 -0400 Subject: [PATCH 009/108] transaction: Reparent all container children when disabling for scratchpad Fixes: #8205 --- sway/desktop/transaction.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 7568990bf..2ee5a5dff 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -632,6 +632,15 @@ static void arrange_root(struct sway_root *root) { for (int i = 0; i < root->scratchpad->length; i++) { struct sway_container *con = root->scratchpad->items[i]; + // When a container is moved to a scratchpad, it's possible that it + // was moved into a floating container as part of the same transaction. + // In this case, we need to make sure we reparent all the container's + // children so that disabling the container will disable all descendants. + if (!con->view) for (int ii = 0; ii < con->current.children->length; ii++) { + struct sway_container *child = con->current.children->items[ii]; + wlr_scene_node_reparent(&child->scene_tree->node, con->content_tree); + } + wlr_scene_node_set_enabled(&con->scene_tree->node, false); } From 9a1c411abd8261c121dcd50dfe54132718768084 Mon Sep 17 00:00:00 2001 From: Ricardo Steijn <61013287+RicArch97@users.noreply.github.com> Date: Mon, 5 Aug 2024 02:13:49 +0200 Subject: [PATCH 010/108] Add support for tearing-control-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3871 Adds option to allow tearing per output, as well as an option to force enable or disable tearing for a specific application using a window rule. Only works with fullscreen applications. --- include/sway/commands.h | 2 + include/sway/config.h | 1 + include/sway/output.h | 1 + include/sway/server.h | 6 +++ include/sway/tree/view.h | 12 ++++++ protocols/meson.build | 1 + sway/commands.c | 1 + sway/commands/allow_tearing.c | 24 +++++++++++ sway/commands/output.c | 1 + sway/commands/output/allow_tearing.c | 23 +++++++++++ sway/config/output.c | 16 ++++++- sway/desktop/output.c | 28 +++++++++++++ sway/desktop/tearing.c | 62 ++++++++++++++++++++++++++++ sway/ipc-json.c | 4 ++ sway/meson.build | 3 ++ sway/server.c | 7 ++++ sway/sway-output.5.scd | 20 +++++++++ sway/sway.5.scd | 14 +++++++ sway/tree/view.c | 14 +++++++ swaymsg/main.c | 6 ++- 20 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 sway/commands/allow_tearing.c create mode 100644 sway/commands/output/allow_tearing.c create mode 100644 sway/desktop/tearing.c diff --git a/include/sway/commands.h b/include/sway/commands.h index 15cd86982..5210d3ba7 100644 --- a/include/sway/commands.h +++ b/include/sway/commands.h @@ -104,6 +104,7 @@ struct sway_container *container_find_resize_parent(struct sway_container *con, sway_cmd cmd_exec_validate; sway_cmd cmd_exec_process; +sway_cmd cmd_allow_tearing; sway_cmd cmd_assign; sway_cmd cmd_bar; sway_cmd cmd_bindcode; @@ -283,6 +284,7 @@ sway_cmd input_cmd_xkb_switch_layout; sway_cmd input_cmd_xkb_variant; sway_cmd output_cmd_adaptive_sync; +sway_cmd output_cmd_allow_tearing; sway_cmd output_cmd_background; sway_cmd output_cmd_color_profile; sway_cmd output_cmd_disable; diff --git a/include/sway/config.h b/include/sway/config.h index dfa3c1b7b..d9f561571 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -289,6 +289,7 @@ struct output_config { enum render_bit_depth render_bit_depth; bool set_color_transform; struct wlr_color_transform *color_transform; + int allow_tearing; char *background; char *background_option; diff --git a/include/sway/output.h b/include/sway/output.h index 2189c6e87..7e2d58927 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -73,6 +73,7 @@ struct sway_output { int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; bool gamma_lut_changed; + bool allow_tearing; }; struct sway_output_non_desktop { diff --git a/include/sway/server.h b/include/sway/server.h index abf1b6b4e..460f9e17f 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -115,6 +115,10 @@ struct sway_server { struct wl_listener xdg_activation_v1_new_token; struct wl_listener request_set_cursor_shape; + + struct wlr_tearing_control_manager_v1 *tearing_control_v1; + struct wl_listener tearing_control_new_object; + struct wl_list tearing_controllers; // sway_tearing_controller::link struct wl_list pending_launcher_ctxs; // launcher_ctx::link @@ -182,4 +186,6 @@ void xdg_activation_v1_handle_new_token(struct wl_listener *listener, void set_rr_scheduling(void); +void handle_new_tearing_hint(struct wl_listener *listener, void *data); + #endif diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 3ae8cf224..14aad1a18 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "sway/config.h" #if WLR_HAS_XWAYLAND #include @@ -34,6 +35,12 @@ enum sway_view_prop { #endif }; +enum sway_view_tearing_mode { + TEARING_OVERRIDE_FALSE, + TEARING_OVERRIDE_TRUE, + TEARING_WINDOW_HINT, +}; + struct sway_view_impl { void (*get_constraints)(struct sway_view *view, double *min_width, double *max_width, double *min_height, double *max_height); @@ -111,6 +118,9 @@ struct sway_view { int max_render_time; // In milliseconds enum seat_config_shortcuts_inhibit shortcuts_inhibit; + + enum sway_view_tearing_mode tearing_mode; + enum wp_tearing_control_v1_presentation_hint tearing_hint; }; struct sway_xdg_shell_view { @@ -335,4 +345,6 @@ void view_assign_ctx(struct sway_view *view, struct launcher_ctx *ctx); void view_send_frame_done(struct sway_view *view); +bool view_can_tear(struct sway_view *view); + #endif diff --git a/protocols/meson.build b/protocols/meson.build index 6eac8542c..d96f87575 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -14,6 +14,7 @@ protocols = [ 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/tearing-control/tearing-control-v1.xml', 'wlr-layer-shell-unstable-v1.xml', 'idle.xml', 'wlr-output-power-management-unstable-v1.xml', diff --git a/sway/commands.c b/sway/commands.c index 8d003dfa6..c2c12ee65 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -112,6 +112,7 @@ static const struct cmd_handler config_handlers[] = { /* Runtime-only commands. Keep alphabetized */ static const struct cmd_handler command_handlers[] = { + { "allow_tearing", cmd_allow_tearing }, { "border", cmd_border }, { "create_output", cmd_create_output }, { "exit", cmd_exit }, diff --git a/sway/commands/allow_tearing.c b/sway/commands/allow_tearing.c new file mode 100644 index 000000000..ee5941381 --- /dev/null +++ b/sway/commands/allow_tearing.c @@ -0,0 +1,24 @@ +#include +#include "sway/config.h" +#include "sway/tree/view.h" +#include "util.h" + +struct cmd_results *cmd_allow_tearing(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "allow_tearing", EXPECTED_AT_LEAST, 1))) { + return error; + } + + struct sway_container *container = config->handler_context.container; + if (!container || !container->view) { + return cmd_results_new(CMD_INVALID, "Tearing can only be allowed on views"); + } + + bool wants_tearing = parse_boolean(argv[0], true); + + struct sway_view *view = container->view; + view->tearing_mode = wants_tearing ? TEARING_OVERRIDE_TRUE : + TEARING_OVERRIDE_FALSE; + + return cmd_results_new(CMD_SUCCESS, NULL); +} diff --git a/sway/commands/output.c b/sway/commands/output.c index b822e770a..9478e0bad 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -8,6 +8,7 @@ // must be in order for the bsearch static const struct cmd_handler output_handlers[] = { { "adaptive_sync", output_cmd_adaptive_sync }, + { "allow_tearing", output_cmd_allow_tearing }, { "background", output_cmd_background }, { "bg", output_cmd_background }, { "color_profile", output_cmd_color_profile }, diff --git a/sway/commands/output/allow_tearing.c b/sway/commands/output/allow_tearing.c new file mode 100644 index 000000000..9a183b96c --- /dev/null +++ b/sway/commands/output/allow_tearing.c @@ -0,0 +1,23 @@ +#include "sway/commands.h" +#include "sway/config.h" +#include "util.h" + +struct cmd_results *output_cmd_allow_tearing(int argc, char **argv) { + if (!config->handler_context.output_config) { + return cmd_results_new(CMD_FAILURE, "Missing output config"); + } + if (argc == 0) { + return cmd_results_new(CMD_INVALID, "Missing allow_tearing argument"); + } + + if (parse_boolean(argv[0], + (config->handler_context.output_config->allow_tearing == 1))) { + config->handler_context.output_config->allow_tearing = 1; + } else { + config->handler_context.output_config->allow_tearing = 0; + } + + config->handler_context.leftovers.argc = argc - 1; + config->handler_context.leftovers.argv = argv + 1; + return NULL; +} diff --git a/sway/config/output.c b/sway/config/output.c index e64efb7ff..940c75fe3 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -79,6 +79,7 @@ struct output_config *new_output_config(const char *name) { oc->set_color_transform = false; oc->color_transform = NULL; oc->power = -1; + oc->allow_tearing = -1; return oc; } @@ -216,6 +217,9 @@ static void merge_output_config(struct output_config *dst, struct output_config if (src->power != -1) { dst->power = src->power; } + if (src->allow_tearing != -1) { + dst->allow_tearing = src->allow_tearing; + } } void store_output_config(struct output_config *oc) { @@ -258,11 +262,11 @@ void store_output_config(struct output_config *oc) { sway_log(SWAY_DEBUG, "Config stored for output %s (enabled: %d) (%dx%d@%fHz " "position %d,%d scale %f subpixel %s transform %d) (bg %s %s) (power %d) " - "(max render time: %d)", + "(max render time: %d) (allow tearing: %d)", oc->name, oc->enabled, oc->width, oc->height, oc->refresh_rate, oc->x, oc->y, oc->scale, sway_wl_output_subpixel_to_string(oc->subpixel), oc->transform, oc->background, oc->background_option, oc->power, - oc->max_render_time); + oc->max_render_time, oc->allow_tearing); // If the configuration was not merged into an existing configuration, add // it to the list. Otherwise we're done with it and can free it. @@ -574,6 +578,13 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output wlr_color_transform_unref(output->color_transform); output->color_transform = oc->color_transform; } + + if (oc && oc->allow_tearing >= 0) { + sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", + oc->name, oc->allow_tearing); + output->allow_tearing = oc->allow_tearing; + } + return true; } @@ -594,6 +605,7 @@ static void default_output_config(struct output_config *oc, oc->subpixel = output->detected_subpixel; oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; oc->max_render_time = 0; + oc->allow_tearing = 0; } // find_output_config returns a merged output_config containing all stored diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 27ede68ee..f1e08eff8 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -232,6 +232,23 @@ static void output_configure_scene(struct sway_output *output, } } +static bool output_can_tear(struct sway_output *output) { + struct sway_workspace *workspace = output->current.active_workspace; + if (!workspace) { + return false; + } + + struct sway_container *fullscreen_con = root->fullscreen_global; + if (!fullscreen_con) { + fullscreen_con = workspace->current.fullscreen; + } + if (fullscreen_con && fullscreen_con->view) { + return (output->allow_tearing && view_can_tear(fullscreen_con->view)); + } + + return false; +} + static int output_repaint_timer_handler(void *data) { struct sway_output *output = data; @@ -275,6 +292,17 @@ static int output_repaint_timer_handler(void *data) { wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } } + + if (output_can_tear(output)) { + pending.tearing_page_flip = true; + + if (!wlr_output_test_state(output->wlr_output, &pending)) { + sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip", + output->wlr_output->name); + + pending.tearing_page_flip = false; + } + } if (!wlr_output_commit_state(output->wlr_output, &pending)) { sway_log(SWAY_ERROR, "Page-flip failed on output %s", output->wlr_output->name); diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c new file mode 100644 index 000000000..36cc48bfe --- /dev/null +++ b/sway/desktop/tearing.c @@ -0,0 +1,62 @@ +#include +#include +#include "sway/server.h" +#include "sway/tree/view.h" +#include "sway/output.h" +#include "log.h" + +struct sway_tearing_controller { + struct wlr_tearing_control_v1 *tearing_control; + struct wl_listener set_hint; + struct wl_listener destroy; + + struct wl_list link; // sway_server::tearing_controllers +}; + +static void handle_tearing_controller_set_hint(struct wl_listener *listener, + void *data) { + struct sway_tearing_controller *controller = + wl_container_of(listener, controller, set_hint); + + struct sway_view *view = view_from_wlr_surface( + controller->tearing_control->surface); + if (view) { + view->tearing_hint = controller->tearing_control->current; + } +} + +static void handle_tearing_controller_destroy(struct wl_listener *listener, + void *data) { + struct sway_tearing_controller *controller = + wl_container_of(listener, controller, destroy); + wl_list_remove(&controller->link); + free(controller); +} + +void handle_new_tearing_hint(struct wl_listener *listener, + void *data) { + struct sway_server *server = + wl_container_of(listener, server, tearing_control_new_object); + struct wlr_tearing_control_v1 *tearing_control = data; + + enum wp_tearing_control_v1_presentation_hint hint = + wlr_tearing_control_manager_v1_surface_hint_from_surface( + server->tearing_control_v1, tearing_control->surface); + sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p", + hint, tearing_control->surface); + + struct sway_tearing_controller *controller = + calloc(1, sizeof(struct sway_tearing_controller)); + if (!controller) { + return; + } + + controller->tearing_control = tearing_control; + controller->set_hint.notify = handle_tearing_controller_set_hint; + wl_signal_add(&tearing_control->events.set_hint, &controller->set_hint); + controller->destroy.notify = handle_tearing_controller_destroy; + wl_signal_add(&tearing_control->events.destroy, &controller->destroy); + wl_list_init(&controller->link); + + wl_list_insert(&server->tearing_controllers, &controller->link); +} diff --git a/sway/ipc-json.c b/sway/ipc-json.c index e512a2239..8eaa8324a 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -399,6 +399,8 @@ static void ipc_json_describe_enabled_output(struct sway_output *output, } json_object_object_add(object, "max_render_time", json_object_new_int(output->max_render_time)); + + json_object_object_add(object, "allow_tearing", json_object_new_boolean(output->allow_tearing)); } json_object *ipc_json_describe_disabled_output(struct sway_output *output) { @@ -593,6 +595,8 @@ static void ipc_json_describe_view(struct sway_container *c, json_object *object json_object_object_add(object, "max_render_time", json_object_new_int(c->view->max_render_time)); + json_object_object_add(object, "allow_tearing", json_object_new_boolean(view_can_tear(c->view))); + json_object_object_add(object, "shell", json_object_new_string(view_get_shell(c->view))); json_object_object_add(object, "inhibit_idle", diff --git a/sway/meson.build b/sway/meson.build index 2f4406abb..8042c89be 100644 --- a/sway/meson.build +++ b/sway/meson.build @@ -18,6 +18,7 @@ sway_sources = files( 'desktop/idle_inhibit_v1.c', 'desktop/layer_shell.c', 'desktop/output.c', + 'desktop/tearing.c', 'desktop/transaction.c', 'desktop/xdg_shell.c', 'desktop/launcher.c', @@ -41,6 +42,7 @@ sway_sources = files( 'config/seat.c', 'config/input.c', + 'commands/allow_tearing.c', 'commands/assign.c', 'commands/bar.c', 'commands/bind.c', @@ -188,6 +190,7 @@ sway_sources = files( 'commands/input/xkb_variant.c', 'commands/output/adaptive_sync.c', + 'commands/output/allow_tearing.c', 'commands/output/background.c', 'commands/output/disable.c', 'commands/output/dpms.c', diff --git a/sway/server.c b/sway/server.c index edbc1a4b1..bb895377b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -371,6 +371,13 @@ bool server_init(struct sway_server *server) { server->content_type_manager_v1 = wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); + + server->tearing_control_v1 = + wlr_tearing_control_manager_v1_create(server->wl_display, 1); + server->tearing_control_new_object.notify = handle_new_tearing_hint; + wl_signal_add(&server->tearing_control_v1->events.new_object, + &server->tearing_control_new_object); + wl_list_init(&server->tearing_controllers); struct wlr_xdg_foreign_registry *foreign_registry = wlr_xdg_foreign_registry_create(server->wl_display); diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index 6d7c08604..d9a28807a 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -190,6 +190,26 @@ must be separated by one space. For example: may have no effect or produce unexpected output when used together with future HDR support features. +*output* allow_tearing yes|no + Allows or disallows screen tearing as a result of immediate page flips, + and an immediate presentation mode from a client. The default is that no + screen tearing is allowed. + + With immediate page flips, frames from the client are presented as soon + as possible instead of synchronizing with the monitor's vblank interval + (VSync). + + It is recommended to set *max_render_time* to *off*. In that case a page flip + happens as soon as a client updates. Otherwise, tearing will only happen if + rendering takes longer than the configured milliseconds before the next + display refresh. + + To adjust whether tearing is allowed for specific applications, see + *allow_tearing* in *sway*(5). Note that tearing will only be enabled + when it's allowed for both the output and the application. + + This setting only has effect when a window is fullscreen on the output. + # SEE ALSO *sway*(5) *sway-input*(5) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 9f8239473..0b36a7572 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -215,6 +215,20 @@ set|plus|minus|toggle effect on the output the window is currently on. See *sway-output*(5) for further details. +*allow_tearing* yes|no + Allows or disallows screen tearing as a result of immediate page flips + for a fullscreen application. + + When this option is not set, the tearing hints provided by the application + determine whether tearing is allowed. When _yes_ is specified, + the application allows tearing regardless of the tearing hints. + When _no_ is specified, tearing will never be allowed on the application, + regardless of the tearing hints. + + This setting only has an effect if tearing is allowed on the output through + the per-output *allow_tearing* setting. See *sway-output*(5) + for further details. + *move* left|right|up|down [ px] Moves the focused container in the direction specified. The optional _px_ argument specifies how many pixels to move the container. If unspecified, diff --git a/sway/tree/view.c b/sway/tree/view.c index 229d765ea..423473879 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -58,6 +58,7 @@ bool view_init(struct sway_view *view, enum sway_view_type type, view->executed_criteria = create_list(); view->allow_request_urgent = true; view->shortcuts_inhibit = SHORTCUTS_INHIBIT_DEFAULT; + view->tearing_mode = TEARING_WINDOW_HINT; wl_signal_init(&view->events.unmap); return true; } @@ -1260,6 +1261,19 @@ bool view_is_transient_for(struct sway_view *child, child->impl->is_transient_for(child, ancestor); } +bool view_can_tear(struct sway_view *view) { + switch (view->tearing_mode) { + case TEARING_OVERRIDE_FALSE: + return false; + case TEARING_OVERRIDE_TRUE: + return true; + case TEARING_WINDOW_HINT: + return view->tearing_hint == + WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; + } + return false; +} + static void send_frame_done_iterator(struct wlr_scene_buffer *scene_buffer, int x, int y, void *data) { struct timespec *when = data; diff --git a/swaymsg/main.c b/swaymsg/main.c index 573a7b166..5c57171e7 100644 --- a/swaymsg/main.c +++ b/swaymsg/main.c @@ -193,7 +193,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "current_workspace", &ws); json_object_object_get_ex(o, "non_desktop", &non_desktop); json_object *make, *model, *serial, *scale, *scale_filter, *subpixel, - *transform, *max_render_time, *adaptive_sync_status; + *transform, *max_render_time, *adaptive_sync_status, *allow_tearing; json_object_object_get_ex(o, "make", &make); json_object_object_get_ex(o, "model", &model); json_object_object_get_ex(o, "serial", &serial); @@ -203,6 +203,7 @@ static void pretty_print_output(json_object *o) { json_object_object_get_ex(o, "transform", &transform); json_object_object_get_ex(o, "max_render_time", &max_render_time); json_object_object_get_ex(o, "adaptive_sync_status", &adaptive_sync_status); + json_object_object_get_ex(o, "allow_tearing", &allow_tearing); json_object *x, *y; json_object_object_get_ex(rect, "x", &x); json_object_object_get_ex(rect, "y", &y); @@ -256,6 +257,9 @@ static void pretty_print_output(json_object *o) { printf(" Adaptive sync: %s\n", json_object_get_string(adaptive_sync_status)); + + printf(" Allow tearing: %s\n", + json_object_get_boolean(allow_tearing) ? "yes" : "no"); } else { printf( "Output %s '%s %s %s' (disabled)\n", From 05e895c4638293a6bfe594ff0cae4eaab63b740e Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 10 May 2024 16:11:38 +0200 Subject: [PATCH 011/108] Add support for linux-drm-syncobj-v1 References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4262 --- sway/server.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sway/server.c b/sway/server.c index bb895377b..537febe83 100644 --- a/sway/server.c +++ b/sway/server.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -248,6 +249,11 @@ bool server_init(struct sway_server *server) { wlr_drm_create(server->wl_display, server->renderer); } } + if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && + server->renderer->features.timeline) { + wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, + wlr_renderer_get_drm_fd(server->renderer)); + } server->allocator = wlr_allocator_autocreate(server->backend, server->renderer); From 3e956b922958b182912775497812cd42439b2955 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 7 Aug 2024 15:26:49 +0300 Subject: [PATCH 012/108] tearing: remove trailing whitespace --- sway/config/output.c | 4 ++-- sway/desktop/output.c | 5 ++--- sway/desktop/tearing.c | 16 ++++++++-------- sway/server.c | 4 ++-- sway/tree/view.c | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 940c75fe3..1b378078e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -578,9 +578,9 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output wlr_color_transform_unref(output->color_transform); output->color_transform = oc->color_transform; } - + if (oc && oc->allow_tearing >= 0) { - sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", + sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", oc->name, oc->allow_tearing); output->allow_tearing = oc->allow_tearing; } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f1e08eff8..c1aa4483e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -292,14 +292,13 @@ static int output_repaint_timer_handler(void *data) { wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); } } - + if (output_can_tear(output)) { pending.tearing_page_flip = true; - + if (!wlr_output_test_state(output->wlr_output, &pending)) { sway_log(SWAY_DEBUG, "Output test failed on '%s', retrying without tearing page-flip", output->wlr_output->name); - pending.tearing_page_flip = false; } } diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c index 36cc48bfe..b74c74d25 100644 --- a/sway/desktop/tearing.c +++ b/sway/desktop/tearing.c @@ -17,7 +17,7 @@ static void handle_tearing_controller_set_hint(struct wl_listener *listener, void *data) { struct sway_tearing_controller *controller = wl_container_of(listener, controller, set_hint); - + struct sway_view *view = view_from_wlr_surface( controller->tearing_control->surface); if (view) { @@ -25,27 +25,27 @@ static void handle_tearing_controller_set_hint(struct wl_listener *listener, } } -static void handle_tearing_controller_destroy(struct wl_listener *listener, +static void handle_tearing_controller_destroy(struct wl_listener *listener, void *data) { - struct sway_tearing_controller *controller = + struct sway_tearing_controller *controller = wl_container_of(listener, controller, destroy); wl_list_remove(&controller->link); free(controller); } -void handle_new_tearing_hint(struct wl_listener *listener, +void handle_new_tearing_hint(struct wl_listener *listener, void *data) { - struct sway_server *server = + struct sway_server *server = wl_container_of(listener, server, tearing_control_new_object); struct wlr_tearing_control_v1 *tearing_control = data; - - enum wp_tearing_control_v1_presentation_hint hint = + + enum wp_tearing_control_v1_presentation_hint hint = wlr_tearing_control_manager_v1_surface_hint_from_surface( server->tearing_control_v1, tearing_control->surface); sway_log(SWAY_DEBUG, "New presentation hint %d received for surface %p", hint, tearing_control->surface); - struct sway_tearing_controller *controller = + struct sway_tearing_controller *controller = calloc(1, sizeof(struct sway_tearing_controller)); if (!controller) { return; diff --git a/sway/server.c b/sway/server.c index 537febe83..f89bd529b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -377,8 +377,8 @@ bool server_init(struct sway_server *server) { server->content_type_manager_v1 = wlr_content_type_manager_v1_create(server->wl_display, 1); wlr_fractional_scale_manager_v1_create(server->wl_display, 1); - - server->tearing_control_v1 = + + server->tearing_control_v1 = wlr_tearing_control_manager_v1_create(server->wl_display, 1); server->tearing_control_new_object.notify = handle_new_tearing_hint; wl_signal_add(&server->tearing_control_v1->events.new_object, diff --git a/sway/tree/view.c b/sway/tree/view.c index 423473879..d25a09c2a 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -1268,7 +1268,7 @@ bool view_can_tear(struct sway_view *view) { case TEARING_OVERRIDE_TRUE: return true; case TEARING_WINDOW_HINT: - return view->tearing_hint == + return view->tearing_hint == WP_TEARING_CONTROL_V1_PRESENTATION_HINT_ASYNC; } return false; From 32e5e5232d1b0b5a34b4296a79a4e8cfa32b5090 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 7 Aug 2024 15:27:02 +0300 Subject: [PATCH 013/108] tearing: fix UAF on destroy Fixes: 9a1c411abd8261c121dcd50dfe54132718768084 --- sway/desktop/tearing.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/desktop/tearing.c b/sway/desktop/tearing.c index b74c74d25..d8d276451 100644 --- a/sway/desktop/tearing.c +++ b/sway/desktop/tearing.c @@ -29,6 +29,8 @@ static void handle_tearing_controller_destroy(struct wl_listener *listener, void *data) { struct sway_tearing_controller *controller = wl_container_of(listener, controller, destroy); + wl_list_remove(&controller->set_hint.link); + wl_list_remove(&controller->destroy.link); wl_list_remove(&controller->link); free(controller); } From 951a22c2445f5c32b831bac0db86869627940402 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 7 Aug 2024 16:52:49 -0400 Subject: [PATCH 014/108] xwayland: Let scene restack --- sway/desktop/xwayland.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/sway/desktop/xwayland.c b/sway/desktop/xwayland.c index 805c9b9d5..b83537a0a 100644 --- a/sway/desktop/xwayland.c +++ b/sway/desktop/xwayland.c @@ -289,9 +289,6 @@ static void set_activated(struct sway_view *view, bool activated) { } wlr_xwayland_surface_activate(surface, activated); - if (activated) { - wlr_xwayland_surface_restack(surface, NULL, XCB_STACK_MODE_ABOVE); - } } static void set_tiled(struct sway_view *view, bool tiled) { From f344e9d5a5afe6ba1aeaf781be3bd18dbf8596f1 Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Fri, 9 Aug 2024 00:26:03 +0200 Subject: [PATCH 015/108] Add null-safety check for virtual keyboard keymaps Note that in the `sway_keyboard_configure` function of sway/input/keyboard.c, we have skipped the `sway_keyboard_set_layout` function for virtual keyboards, which then have null keymaps. Hence, a null-safety check is needed at runtime. --- sway/commands/input/xkb_switch_layout.c | 10 +++++++++- sway/ipc-json.c | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/sway/commands/input/xkb_switch_layout.c b/sway/commands/input/xkb_switch_layout.c index 8d600fcad..623dfa186 100644 --- a/sway/commands/input/xkb_switch_layout.c +++ b/sway/commands/input/xkb_switch_layout.c @@ -95,10 +95,18 @@ struct cmd_results *input_cmd_xkb_switch_layout(int argc, char **argv) { continue; } + struct wlr_keyboard *keyboard = + wlr_keyboard_from_input_device(dev->wlr_device); + if (keyboard->keymap == NULL && dev->is_virtual) { + // The `sway_keyboard_set_layout` function is by default skipped + // when configuring virtual keyboards. + continue; + } + struct xkb_switch_layout_action *action = &actions[actions_len++]; + action->keyboard = keyboard; - action->keyboard = wlr_keyboard_from_input_device(dev->wlr_device); if (relative) { action->layout = get_layout_relative(action->keyboard, relative); } else { diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 8eaa8324a..571338a4f 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -1133,7 +1133,9 @@ json_object *ipc_json_describe_input(struct sway_input_device *device) { json_object *layouts_arr = json_object_new_array(); json_object_object_add(object, "xkb_layout_names", layouts_arr); - xkb_layout_index_t num_layouts = xkb_keymap_num_layouts(keymap); + xkb_layout_index_t num_layouts = + keymap ? xkb_keymap_num_layouts(keymap) : 0; + // Virtual keyboards might have null keymap xkb_layout_index_t layout_idx; for (layout_idx = 0; layout_idx < num_layouts; layout_idx++) { const char *layout = xkb_keymap_layout_get_name(keymap, layout_idx); From 9ba1beee580d07adfba903257ce8762b96ea3833 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 29 Feb 2024 01:03:11 +0100 Subject: [PATCH 016/108] Bind a few utilities to special keys in default config --- config.in | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/config.in b/config.in index a5173165b..811365e1a 100644 --- a/config.in +++ b/config.in @@ -195,6 +195,19 @@ mode "resize" { bindsym Escape mode "default" } bindsym $mod+r mode "resize" +# +# Utilities: +# + # Special keys to adjust volume via PulseAudio + bindsym --locked XF86AudioMute exec pactl set-sink-mute \@DEFAULT_SINK@ toggle + bindsym --locked XF86AudioLowerVolume exec pactl set-sink-volume \@DEFAULT_SINK@ -5% + bindsym --locked XF86AudioRaiseVolume exec pactl set-sink-volume \@DEFAULT_SINK@ +5% + bindsym --locked XF86AudioMicMute exec pactl set-source-mute \@DEFAULT_SOURCE@ toggle + # Special keys to adjust brightness via brightnessctl + bindsym --locked XF86MonBrightnessDown exec brightnessctl set 5%- + bindsym --locked XF86MonBrightnessUp exec brightnessctl set 5%+ + # Special key to take a screenshot with grim + bindsym Print exec grim # # Status Bar: From b44015578a3d53cdd9436850202d4405696c1f52 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 11 Aug 2024 19:03:19 +0200 Subject: [PATCH 017/108] Switch default config to wmenu-run This removes the last dependency bit on dmenu. No need for "swaymsg exec" anymore: wmenu-run handles the xdg-activation shenanigans. --- config.in | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config.in b/config.in index 811365e1a..a2a01dda9 100644 --- a/config.in +++ b/config.in @@ -16,9 +16,7 @@ set $right l # Your preferred terminal emulator set $term foot # Your preferred application launcher -# Note: pass the final command to swaymsg so that the resulting window can be opened -# on the original workspace that the command was run on. -set $menu dmenu_path | wmenu | xargs swaymsg exec -- +set $menu wmenu-run ### Output configuration # From 6576b99c243e2b66d077db0a99ec9683747e3fe9 Mon Sep 17 00:00:00 2001 From: Felix Pehla <74104874+FelixPehla@users.noreply.github.com> Date: Wed, 7 Aug 2024 18:34:59 +0200 Subject: [PATCH 018/108] commands/output/color_profile: allows use of relative path for ICC profile --- sway/commands/output/color_profile.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/sway/commands/output/color_profile.c b/sway/commands/output/color_profile.c index 792bd55fb..98481329b 100644 --- a/sway/commands/output/color_profile.c +++ b/sway/commands/output/color_profile.c @@ -5,6 +5,7 @@ #include #include "sway/commands.h" #include "sway/config.h" +#include "stringop.h" static bool read_file_into_buf(const char *path, void **buf, size_t *size) { /* Why not use fopen/fread directly? glibc will succesfully open directories, @@ -70,12 +71,23 @@ 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"); } + + char *icc_path = strdup(argv[1]); + if (!expand_path(&icc_path)) { + struct cmd_results *cmd_res = cmd_results_new(CMD_INVALID, + "Invalid color profile specification: invalid file path"); + free(icc_path); + return cmd_res; + } + void *data = NULL; size_t size = 0; - if (!read_file_into_buf(argv[1], &data, &size)) { + if (!read_file_into_buf(icc_path, &data, &size)) { + free(icc_path); return cmd_results_new(CMD_FAILURE, "Failed to load color profile: could not read ICC file"); } + free(icc_path); struct wlr_color_transform *tmp = wlr_color_transform_init_linear_to_icc(data, size); From 5a3621460f193416605ad786e33687952527df21 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 12:52:51 -0400 Subject: [PATCH 019/108] output: Use wlr_scene_output_needs_frame --- 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 c1aa4483e..7f5ec5f82 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -266,8 +266,7 @@ static int output_repaint_timer_handler(void *data) { struct wlr_output *wlr_output = output->wlr_output; struct wlr_scene_output *scene_output = output->scene_output; - if (!wlr_output->needs_frame && !output->gamma_lut_changed && - !pixman_region32_not_empty(&scene_output->pending_commit_damage)) { + if (!wlr_scene_output_needs_frame(scene_output)) { return 0; } From c3279944fb195a5169eb540ef8285533dc5edfba Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 14 Aug 2024 13:42:53 -0400 Subject: [PATCH 020/108] output: Use wlr_scene_set_gamma_control_manager_v1 --- include/sway/output.h | 3 --- sway/desktop/output.c | 37 ------------------------------------- sway/server.c | 5 ++--- 3 files changed, 2 insertions(+), 43 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 7e2d58927..1dcabc256 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -72,7 +72,6 @@ struct sway_output { uint32_t refresh_nsec; int max_render_time; // In milliseconds struct wl_event_source *repaint_timer; - bool gamma_lut_changed; bool allow_tearing; }; @@ -138,8 +137,6 @@ enum wlr_direction opposite_direction(enum wlr_direction d); void handle_output_layout_change(struct wl_listener *listener, void *data); -void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data); - void handle_output_manager_apply(struct wl_listener *listener, void *data); void handle_output_manager_test(struct wl_listener *listener, void *data); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 7f5ec5f82..72f753b02 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -264,7 +264,6 @@ static int output_repaint_timer_handler(void *data) { .color_transform = output->color_transform, }; - struct wlr_output *wlr_output = output->wlr_output; struct wlr_scene_output *scene_output = output->scene_output; if (!wlr_scene_output_needs_frame(scene_output)) { return 0; @@ -276,22 +275,6 @@ static int output_repaint_timer_handler(void *data) { return 0; } - if (output->gamma_lut_changed) { - output->gamma_lut_changed = false; - struct wlr_gamma_control_v1 *gamma_control = - wlr_gamma_control_manager_v1_get_control( - server.gamma_control_manager_v1, output->wlr_output); - if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) { - wlr_output_state_finish(&pending); - return 0; - } - - if (!wlr_output_test_state(output->wlr_output, &pending)) { - wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); - wlr_output_state_set_gamma_lut(&pending, 0, NULL, NULL, NULL); - } - } - if (output_can_tear(output)) { pending.tearing_page_flip = true; @@ -472,11 +455,6 @@ static void handle_commit(struct wl_listener *listener, void *data) { update_output_manager_config(output->server); } - - // Next time the output is enabled, try to re-apply the gamma LUT - if ((event->state->committed & WLR_OUTPUT_STATE_ENABLED) && !output->wlr_output->enabled) { - output->gamma_lut_changed = true; - } } static void handle_present(struct wl_listener *listener, void *data) { @@ -585,21 +563,6 @@ void handle_output_layout_change(struct wl_listener *listener, update_output_manager_config(server); } -void handle_gamma_control_set_gamma(struct wl_listener *listener, void *data) { - struct sway_server *server = - wl_container_of(listener, server, gamma_control_set_gamma); - const struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; - - struct sway_output *output = event->output->data; - - if(!output) { - return; - } - - output->gamma_lut_changed = true; - wlr_output_schedule_frame(output->wlr_output); -} - static struct output_config *output_config_for_config_head( struct wlr_output_configuration_head_v1 *config_head, struct sway_output *output) { diff --git a/sway/server.c b/sway/server.c index f89bd529b..1ca560869 100644 --- a/sway/server.c +++ b/sway/server.c @@ -272,9 +272,8 @@ bool server_init(struct sway_server *server) { server->gamma_control_manager_v1 = wlr_gamma_control_manager_v1_create(server->wl_display); - server->gamma_control_set_gamma.notify = handle_gamma_control_set_gamma; - wl_signal_add(&server->gamma_control_manager_v1->events.set_gamma, - &server->gamma_control_set_gamma); + wlr_scene_set_gamma_control_manager_v1(root->root_scene, + server->gamma_control_manager_v1); server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); From c30c4519079e804c35e71810875c10f48097d230 Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Wed, 14 Aug 2024 21:57:47 +0300 Subject: [PATCH 021/108] xdg-shell: chase xdg_surface geometry updates --- sway/desktop/xdg_shell.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/sway/desktop/xdg_shell.c b/sway/desktop/xdg_shell.c index fdfa7b652..3aed4ec7e 100644 --- a/sway/desktop/xdg_shell.c +++ b/sway/desktop/xdg_shell.c @@ -299,18 +299,17 @@ static void handle_commit(struct wl_listener *listener, void *data) { return; } - struct wlr_box new_geo; - wlr_xdg_surface_get_geometry(xdg_surface, &new_geo); - bool new_size = new_geo.width != view->geometry.width || - new_geo.height != view->geometry.height || - new_geo.x != view->geometry.x || - new_geo.y != view->geometry.y; + struct wlr_box *new_geo = &xdg_surface->geometry; + bool new_size = new_geo->width != view->geometry.width || + new_geo->height != view->geometry.height || + new_geo->x != view->geometry.x || + new_geo->y != view->geometry.y; if (new_size) { // The client changed its surface size in this commit. For floating // containers, we resize the container to match. For tiling containers, // we only recenter the surface. - memcpy(&view->geometry, &new_geo, sizeof(struct wlr_box)); + memcpy(&view->geometry, new_geo, sizeof(struct wlr_box)); if (container_is_floating(view->container)) { view_update_size(view); // Only set the toplevel size the current container actually has a size. @@ -463,12 +462,8 @@ static void handle_map(struct wl_listener *listener, void *data) { struct sway_view *view = &xdg_shell_view->view; struct wlr_xdg_toplevel *toplevel = view->wlr_xdg_toplevel; - view->natural_width = toplevel->base->current.geometry.width; - view->natural_height = toplevel->base->current.geometry.height; - if (!view->natural_width && !view->natural_height) { - view->natural_width = toplevel->base->surface->current.width; - view->natural_height = toplevel->base->surface->current.height; - } + view->natural_width = toplevel->base->geometry.width; + view->natural_height = toplevel->base->geometry.height; bool csd = false; From ae7c1b139a3c71d3e11fe2477d8b21c36de6770e Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 18 Aug 2024 14:50:48 +0200 Subject: [PATCH 022/108] config/output: Do not set adaptive_sync if not supported After 4e38f93f367d ("config/output: Skip VRR tests when not supported"), the configuration search no longer touches VRR state for outputs that are known to not support it. This also means that it will not remove VRR if already set, which could cause output configuration to fail. Ensure that VRR state is never set for outputs that do not support it by adding the same test for support to queue_output_config. Fixes: 4e38f93f367d ("config/output: Skip VRR tests when not supported") Fixes: https://github.com/swaywm/sway/issues/8296 --- sway/config/output.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index 1b378078e..cc3bf457f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -491,7 +491,7 @@ static void queue_output_config(struct output_config *oc, wlr_output_state_set_scale(pending, scale); } - if (oc && oc->adaptive_sync != -1) { + if (oc && oc->adaptive_sync != -1 && wlr_output->adaptive_sync_supported) { sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, oc->adaptive_sync); wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); From f9c0f043e5ec39574c9d9b0fb3dece6169a0e67d Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 19 Aug 2024 13:02:55 +0200 Subject: [PATCH 023/108] config/output: Skip search if config has a mode When doing an output configuration search, the intent is to only look for modes if the output's configuration does not contain a specific mode. This was done by testing if config_has_auto_mode returned false. config_has_auto_mode had its return values backwards, leading to other modes being tested if the output configuration had specified modes or modelines, leading to unwanted modes being selected. Invert the function to config_has_manual_mode to give it a clearer name, and fix the return values in the process. --- sway/config/output.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index cc3bf457f..d33ea11a9 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -651,9 +651,9 @@ struct output_config *find_output_config(struct sway_output *sway_output) { return result; } -static bool config_has_auto_mode(struct output_config *oc) { +static bool config_has_manual_mode(struct output_config *oc) { if (!oc) { - return true; + return false; } if (oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t)-1) { return true; @@ -754,7 +754,8 @@ static bool search_mode(struct search_context *ctx, size_t output_idx) { struct wlr_output_state *state = &backend_state->base; struct wlr_output *wlr_output = backend_state->output; - if (!config_has_auto_mode(cfg->config)) { + // We only search for mode if one is not explicitly specified in the config + if (config_has_manual_mode(cfg->config)) { return search_adaptive_sync(ctx, output_idx); } From 7288f77bbe275825a0e0b011db873d9367782af0 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Wed, 21 Aug 2024 10:58:18 -0400 Subject: [PATCH 024/108] output: Chase wlroots!4803 --- 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 72f753b02..a71430fe5 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -465,7 +465,7 @@ static void handle_present(struct wl_listener *listener, void *data) { return; } - output->last_presentation = *output_event->when; + output->last_presentation = output_event->when; output->refresh_nsec = output_event->refresh; } From f00f964abf0eae36a1cce03c532115319499e570 Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Sat, 29 Jun 2024 11:00:09 +0200 Subject: [PATCH 025/108] sway/commands/move.c: arrange new workspace When moving a container to a new workspace, the workspace's dimension are left unset. Usually this doesn't matter, but when moving a floating container to a new workspace on a different output, this leads to the position of the container being calculated with 0, so the container ends up halfway offscreen on the leftmost topmost monitor. Signed-off-by: Anna (navi) Figueiredo Gomes --- sway/commands/move.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/commands/move.c b/sway/commands/move.c index ff656cfbd..ad106c648 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -510,6 +510,7 @@ static struct cmd_results *cmd_move_container(bool no_auto_back_and_forth, } } ws = workspace_create(NULL, ws_name); + arrange_workspace(ws); } free(ws_name); struct sway_container *dst = seat_get_focus_inactive_tiling(seat, ws); From 77b9ddabe2a97c5d04c30929b0f8cbde3470fdd7 Mon Sep 17 00:00:00 2001 From: llyyr Date: Fri, 23 Aug 2024 01:55:42 +0530 Subject: [PATCH 026/108] sway/tree/container: don't trunc coords in `floating_fix_coordinates` This can cause issues such as the window not being shown at the exact same coordinates when the old and new wlr_box aren't the same dimensions and the container is being moved back-and-forth between them. For example, in the case where a floating window gets moved from one output to another but the outputs aren't the same resolution. For e.g. have two displays that aren't the same resolution then: 1. Open a floating window and set it to pos 0,0 on output 2 2. Send it to scratchpad then `scratchpad show` on output 1 3. `scratchpad show` on output 2 again Observe that the window isn't at 0,0 on output 2 anymore. --- sway/tree/container.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sway/tree/container.c b/sway/tree/container.c index 80ef34fe9..46c388b3e 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -773,11 +773,11 @@ void floating_fix_coordinates(struct sway_container *con, struct wlr_box *old, s // Fall back to centering on the workspace. container_floating_move_to_center(con); } else { - int rel_x = con->pending.x - old->x + (con->pending.width / 2); - int rel_y = con->pending.y - old->y + (con->pending.height / 2); + double rel_x = con->pending.x - old->x + (con->pending.width / 2); + double rel_y = con->pending.y - old->y + (con->pending.height / 2); - con->pending.x = new->x + (double)(rel_x * new->width) / old->width - (con->pending.width / 2); - con->pending.y = new->y + (double)(rel_y * new->height) / old->height - (con->pending.height / 2); + con->pending.x = new->x + (rel_x * new->width) / old->width - (con->pending.width / 2); + con->pending.y = new->y + (rel_y * new->height) / old->height - (con->pending.height / 2); sway_log(SWAY_DEBUG, "Transformed container %p to coords (%f, %f)", con, con->pending.x, con->pending.y); } From f2b2a8114900060f667d7ddd55ef9f8a1e74c1b4 Mon Sep 17 00:00:00 2001 From: Jon Wallace Date: Mon, 26 Aug 2024 22:15:55 -0700 Subject: [PATCH 027/108] Use heading markdown to demarcate sections of commands Its a little tought to notice that the COMMANDS section is actually 3 sections. Use markdown to make this easier to see for the user. --- sway/sway.5.scd | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 0b36a7572..c867c5f7d 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -52,7 +52,7 @@ Throughout the documentation, *|* is used to distinguish between arguments for which you may only select one. *[...]* is used for optional arguments, and *<...>* for arguments where you are expected to supply some value. -# COMMANDS +# COMMANDS - CONFIG ONLY This section only lists general commands. For input and output commands, refer to *sway-input*(5) and *sway-output*(5). @@ -98,6 +98,8 @@ The following commands may only be used in the configuration file. machines, it may be desirable to have Xwayland started immediately by using _force_ instead of _enable_. +# COMMANDS - RUNTIME ONLY + The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). @@ -385,6 +387,8 @@ set|plus|minus|toggle The default format is "%title". +# COMMANDS - CONFIG OR RUNTIME + The following commands may be used either in the configuration file or at runtime. From 980a4e02113789d0cca94aa023557c6f6e87ec73 Mon Sep 17 00:00:00 2001 From: Jon Wallace Date: Tue, 27 Aug 2024 10:42:58 -0700 Subject: [PATCH 028/108] use subheadings instead --- sway/sway.5.scd | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index c867c5f7d..bb958ebfd 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -52,11 +52,12 @@ Throughout the documentation, *|* is used to distinguish between arguments for which you may only select one. *[...]* is used for optional arguments, and *<...>* for arguments where you are expected to supply some value. -# COMMANDS - CONFIG ONLY +# COMMANDS This section only lists general commands. For input and output commands, refer to *sway-input*(5) and *sway-output*(5). +## Config only commands The following commands may only be used in the configuration file. *bar* [] @@ -98,8 +99,7 @@ The following commands may only be used in the configuration file. machines, it may be desirable to have Xwayland started immediately by using _force_ instead of _enable_. -# COMMANDS - RUNTIME ONLY - +## Runtime only commands The following commands cannot be used directly in the configuration file. They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). @@ -387,8 +387,7 @@ set|plus|minus|toggle The default format is "%title". -# COMMANDS - CONFIG OR RUNTIME - +## Config or runtime commands The following commands may be used either in the configuration file or at runtime. From be840f730e747a24106c8366ecb89e6b982cfa38 Mon Sep 17 00:00:00 2001 From: Norbert Bolanowski Date: Mon, 2 Sep 2024 20:02:42 +0200 Subject: [PATCH 029/108] move title_format to container --- include/sway/tree/container.h | 4 ++ include/sway/tree/view.h | 2 - sway/commands/title_format.c | 18 ++++--- sway/tree/container.c | 89 +++++++++++++++++++++++++++++++++-- sway/tree/view.c | 78 +----------------------------- 5 files changed, 102 insertions(+), 89 deletions(-) diff --git a/include/sway/tree/container.h b/include/sway/tree/container.h index 8bf1955d7..4608b8ac2 100644 --- a/include/sway/tree/container.h +++ b/include/sway/tree/container.h @@ -102,6 +102,8 @@ struct sway_container { char *title; // The view's title (unformatted) char *formatted_title; // The title displayed in the title bar int title_width; + + char *title_format; enum sway_container_layout prev_split_layout; @@ -183,6 +185,8 @@ void container_update_title_bar(struct sway_container *container); void container_update_marks(struct sway_container *container); +size_t parse_title_format(struct sway_container *container, char *buffer); + size_t container_build_representation(enum sway_container_layout layout, list_t *children, char *buffer); diff --git a/include/sway/tree/view.h b/include/sway/tree/view.h index 14aad1a18..f60322212 100644 --- a/include/sway/tree/view.h +++ b/include/sway/tree/view.h @@ -80,8 +80,6 @@ struct sway_view { // Used when changing a view from tiled to floating. int natural_width, natural_height; - char *title_format; - bool using_csd; struct timespec urgent; diff --git a/sway/commands/title_format.c b/sway/commands/title_format.c index 0b2ea2659..fbade4731 100644 --- a/sway/commands/title_format.c +++ b/sway/commands/title_format.c @@ -1,3 +1,4 @@ +#define _POSIX_C_SOURCE 200809L #include #include "sway/commands.h" #include "sway/config.h" @@ -11,16 +12,19 @@ struct cmd_results *cmd_title_format(int argc, char **argv) { return error; } struct sway_container *container = config->handler_context.container; - if (!container || !container->view) { + if (!container) { return cmd_results_new(CMD_INVALID, - "Only views can have a title_format"); + "Only valid containers can have a title_format"); } - struct sway_view *view = container->view; char *format = join_args(argv, argc); - if (view->title_format) { - free(view->title_format); + if (container->title_format) { + free(container->title_format); + } + container->title_format = format; + if (container->view) { + view_update_title(container->view, true); + } else { + container_update_representation(container); } - view->title_format = format; - view_update_title(view, true); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/tree/container.c b/sway/tree/container.c index 46c388b3e..188d3223a 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -22,6 +22,7 @@ #include "sway/tree/workspace.h" #include "sway/xdg_decoration.h" #include "list.h" +#include "pango.h" #include "log.h" #include "stringop.h" @@ -499,6 +500,7 @@ void container_destroy(struct sway_container *con) { } free(con->title); free(con->formatted_title); + free(con->title_format); list_free(con->pending.children); list_free(con->current.children); @@ -645,6 +647,87 @@ bool container_has_ancestor(struct sway_container *descendant, return false; } +static char *escape_pango_markup(const char *buffer) { + size_t length = escape_markup_text(buffer, NULL); + char *escaped_title = calloc(length + 1, sizeof(char)); + escape_markup_text(buffer, escaped_title); + return escaped_title; +} + +static size_t append_prop(char *buffer, const char *value) { + if (!value) { + return 0; + } + // If using pango_markup in font, we need to escape all markup chars + // from values to make sure tags are not inserted by clients + if (config->pango_markup) { + char *escaped_value = escape_pango_markup(value); + lenient_strcat(buffer, escaped_value); + size_t len = strlen(escaped_value); + free(escaped_value); + return len; + } else { + lenient_strcat(buffer, value); + return strlen(value); + } +} + +/** + * Calculate and return the length of the formatted title. + * If buffer is not NULL, also populate the buffer with the formatted title. + */ +size_t parse_title_format(struct sway_container *container, char *buffer) { + if (!container->title_format || strcmp(container->title_format, "%title") == 0) { + if (container->view) { + return append_prop(buffer, view_get_title(container->view)); + } else { + return container_build_representation(container->pending.layout, container->pending.children, buffer); + } + } + + size_t len = 0; + char *format = container->title_format; + char *next = strchr(format, '%'); + while (next) { + // Copy everything up to the % + lenient_strncat(buffer, format, next - format); + len += next - format; + format = next; + + if (strncmp(next, "%title", 6) == 0) { + if (container->view) { + len += append_prop(buffer, view_get_title(container->view)); + } else { + len += container_build_representation(container->pending.layout, container->pending.children, buffer); + } + format += 6; + } else if (container->view) { + if (strncmp(next, "%app_id", 7) == 0) { + len += append_prop(buffer, view_get_app_id(container->view)); + format += 7; + } else if (strncmp(next, "%class", 6) == 0) { + len += append_prop(buffer, view_get_class(container->view)); + format += 6; + } else if (strncmp(next, "%instance", 9) == 0) { + len += append_prop(buffer, view_get_instance(container->view)); + format += 9; + } else if (strncmp(next, "%shell", 6) == 0) { + len += append_prop(buffer, view_get_shell(container->view)); + format += 6; + } + } else { + lenient_strcat(buffer, "%"); + ++format; + ++len; + } + next = strchr(format, '%'); + } + lenient_strcat(buffer, format); + len += strlen(format); + + return len; +} + /** * Calculate and return the length of the tree representation. * An example tree representation is: V[Terminal, Firefox] @@ -700,16 +783,14 @@ size_t container_build_representation(enum sway_container_layout layout, void container_update_representation(struct sway_container *con) { if (!con->view) { - size_t len = container_build_representation(con->pending.layout, - con->pending.children, NULL); + size_t len = parse_title_format(con, NULL); free(con->formatted_title); con->formatted_title = calloc(len + 1, sizeof(char)); if (!sway_assert(con->formatted_title, "Unable to allocate title string")) { return; } - container_build_representation(con->pending.layout, con->pending.children, - con->formatted_title); + parse_title_format(con, con->formatted_title); if (con->title_bar.title_text) { sway_text_node_set_text(con->title_bar.title_text, con->formatted_title); diff --git a/sway/tree/view.c b/sway/tree/view.c index d25a09c2a..492095b93 100644 --- a/sway/tree/view.c +++ b/sway/tree/view.c @@ -34,7 +34,6 @@ #include "sway/tree/workspace.h" #include "sway/config.h" #include "sway/xdg_decoration.h" -#include "pango.h" #include "stringop.h" bool view_init(struct sway_view *view, enum sway_view_type type, @@ -81,8 +80,6 @@ void view_destroy(struct sway_view *view) { view_assign_ctx(view, NULL); wlr_scene_node_destroy(&view->scene_tree->node); - free(view->title_format); - if (view->impl->destroy) { view->impl->destroy(view); } else { @@ -991,77 +988,6 @@ struct sway_view *view_from_wlr_surface(struct wlr_surface *wlr_surface) { return NULL; } -static char *escape_pango_markup(const char *buffer) { - size_t length = escape_markup_text(buffer, NULL); - char *escaped_title = calloc(length + 1, sizeof(char)); - escape_markup_text(buffer, escaped_title); - return escaped_title; -} - -static size_t append_prop(char *buffer, const char *value) { - if (!value) { - return 0; - } - // If using pango_markup in font, we need to escape all markup chars - // from values to make sure tags are not inserted by clients - if (config->pango_markup) { - char *escaped_value = escape_pango_markup(value); - lenient_strcat(buffer, escaped_value); - size_t len = strlen(escaped_value); - free(escaped_value); - return len; - } else { - lenient_strcat(buffer, value); - return strlen(value); - } -} - -/** - * Calculate and return the length of the formatted title. - * If buffer is not NULL, also populate the buffer with the formatted title. - */ -static size_t parse_title_format(struct sway_view *view, char *buffer) { - if (!view->title_format || strcmp(view->title_format, "%title") == 0) { - return append_prop(buffer, view_get_title(view)); - } - - size_t len = 0; - char *format = view->title_format; - char *next = strchr(format, '%'); - while (next) { - // Copy everything up to the % - lenient_strncat(buffer, format, next - format); - len += next - format; - format = next; - - if (strncmp(next, "%title", 6) == 0) { - len += append_prop(buffer, view_get_title(view)); - format += 6; - } else if (strncmp(next, "%app_id", 7) == 0) { - len += append_prop(buffer, view_get_app_id(view)); - format += 7; - } else if (strncmp(next, "%class", 6) == 0) { - len += append_prop(buffer, view_get_class(view)); - format += 6; - } else if (strncmp(next, "%instance", 9) == 0) { - len += append_prop(buffer, view_get_instance(view)); - format += 9; - } else if (strncmp(next, "%shell", 6) == 0) { - len += append_prop(buffer, view_get_shell(view)); - format += 6; - } else { - lenient_strcat(buffer, "%"); - ++format; - ++len; - } - next = strchr(format, '%'); - } - lenient_strcat(buffer, format); - len += strlen(format); - - return len; -} - void view_update_app_id(struct sway_view *view) { const char *app_id = view_get_app_id(view); @@ -1090,7 +1016,7 @@ void view_update_title(struct sway_view *view, bool force) { free(view->container->title); free(view->container->formatted_title); - size_t len = parse_title_format(view, NULL); + size_t len = parse_title_format(view->container, NULL); if (len) { char *buffer = calloc(len + 1, sizeof(char)); @@ -1098,7 +1024,7 @@ void view_update_title(struct sway_view *view, bool force) { return; } - parse_title_format(view, buffer); + parse_title_format(view->container, buffer); view->container->formatted_title = buffer; } else { view->container->formatted_title = NULL; From b83e5aaa546792336ecc60d2e61708f3cd6b1f5d Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 3 Sep 2024 15:28:29 +0200 Subject: [PATCH 030/108] desktop/output: Do not use commit listener to arrange The reasoning for using a commit handler is to ensure that all paths for output changes are correctly handled. With the centralized modeset infrastructure in place, we can move the logic there. This allows us to be smarter and avoid extraneous arranges, output manager updates and transaction commits. The side-effect is a minor duplication for the special-case request_state, but the shared path will be relied upon further in future commits to justify this duplication. --- include/sway/output.h | 3 ++- sway/config/output.c | 8 ++++++++ sway/desktop/output.c | 41 ++++++++++++++--------------------------- 3 files changed, 24 insertions(+), 28 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 1dcabc256..990fa5062 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -57,7 +57,6 @@ struct sway_output { struct wl_listener layout_destroy; struct wl_listener destroy; - struct wl_listener commit; struct wl_listener present; struct wl_listener frame; struct wl_listener request_state; @@ -146,4 +145,6 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, struct sway_output_non_desktop *output_non_desktop_create(struct wlr_output *wlr_output); +void update_output_manager_config(struct sway_server *server); + #endif diff --git a/sway/config/output.c b/sway/config/output.c index d33ea11a9..cc6aedb77 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -11,9 +11,12 @@ #include #include #include "sway/config.h" +#include "sway/desktop/transaction.h" #include "sway/input/cursor.h" +#include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/tree/arrange.h" #include "sway/tree/root.h" #include "log.h" #include "util.h" @@ -963,8 +966,13 @@ bool apply_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); + arrange_layers(cfg->output); } + arrange_root(); + update_output_manager_config(&server); + transaction_commit_dirty(); + out: wlr_output_swapchain_manager_finish(&swapchain_mgr); for (size_t idx = 0; idx < configs_len; idx++) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a71430fe5..f6a26b0e3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -356,7 +356,7 @@ static void handle_frame(struct wl_listener *listener, void *user_data) { wlr_scene_output_for_each_buffer(output->scene_output, send_frame_done_iterator, &data); } -static void update_output_manager_config(struct sway_server *server) { +void update_output_manager_config(struct sway_server *server) { struct wlr_output_configuration_v1 *config = wlr_output_configuration_v1_create(); @@ -387,9 +387,6 @@ static int timer_modeset_handle(void *data) { server->delayed_modeset = NULL; apply_all_output_configs(); - transaction_commit_dirty(); - update_output_manager_config(server); - return 0; } @@ -414,7 +411,6 @@ static void begin_destroy(struct sway_output *output) { wl_list_remove(&output->layout_destroy.link); wl_list_remove(&output->destroy.link); - wl_list_remove(&output->commit.link); wl_list_remove(&output->present.link); wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); @@ -437,26 +433,6 @@ static void handle_layout_destroy(struct wl_listener *listener, void *data) { begin_destroy(output); } -static void handle_commit(struct wl_listener *listener, void *data) { - struct sway_output *output = wl_container_of(listener, output, commit); - struct wlr_output_event_commit *event = data; - - if (!output->enabled) { - return; - } - - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_TRANSFORM | - WLR_OUTPUT_STATE_SCALE)) { - arrange_layers(output); - arrange_output(output); - transaction_commit_dirty(); - - update_output_manager_config(output->server); - } -} - static void handle_present(struct wl_listener *listener, void *data) { struct sway_output *output = wl_container_of(listener, output, present); struct wlr_output_event_present *output_event = data; @@ -473,7 +449,20 @@ 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; + + uint32_t committed = event->state->committed; wlr_output_commit_state(output->wlr_output, event->state); + + if (committed & ( + WLR_OUTPUT_STATE_MODE | + WLR_OUTPUT_STATE_TRANSFORM | + WLR_OUTPUT_STATE_SCALE)) { + arrange_layers(output); + arrange_output(output); + transaction_commit_dirty(); + + update_output_manager_config(output->server); + } } static unsigned int last_headless_num = 0; @@ -537,8 +526,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { output->layout_destroy.notify = handle_layout_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); output->destroy.notify = handle_destroy; - wl_signal_add(&wlr_output->events.commit, &output->commit); - output->commit.notify = handle_commit; wl_signal_add(&wlr_output->events.present, &output->present); output->present.notify = handle_present; wl_signal_add(&wlr_output->events.frame, &output->frame); From 6045ad9a0224ea2aab2d488cd356b5557007f5d1 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 3 Sep 2024 15:36:47 +0200 Subject: [PATCH 031/108] tree/output: Rely on modeset arranging root output_enable/output_disable are only called from modeset, and from output destroy which requests modeset. As such, they can rely on the modeset handling arrange. --- sway/tree/output.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/sway/tree/output.c b/sway/tree/output.c index 6c8dd6dc9..f3b0b27a2 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -183,9 +183,6 @@ void output_enable(struct sway_output *output) { input_manager_configure_xcursor(); wl_signal_emit_mutable(&root->events.new_node, &output->node); - - arrange_layers(output); - arrange_root(); } static void evacuate_sticky(struct sway_workspace *old_ws, @@ -300,13 +297,6 @@ void output_disable(struct sway_output *output) { list_del(root->outputs, index); output->enabled = false; - - arrange_root(); - - // Reconfigure all devices, since devices with map_to_output directives for - // an output that goes offline should stop sending events as long as the - // output remains offline. - input_manager_configure_all_input_mappings(); } void output_begin_destroy(struct sway_output *output) { From af28ac04a4d523aecd74dacc94a91f7d9e537982 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Tue, 3 Sep 2024 15:39:39 +0200 Subject: [PATCH 032/108] (desktop|tree)/output: Do not use layout listener to arrange Output layout changes originate from the centralized modeset infrastructure and request_state which already takes care of arranging and updating outputs as needed. --- include/sway/output.h | 2 -- include/sway/server.h | 1 - include/sway/tree/root.h | 2 -- sway/desktop/output.c | 7 ------- sway/server.c | 3 --- sway/tree/root.c | 10 ---------- 6 files changed, 25 deletions(-) diff --git a/include/sway/output.h b/include/sway/output.h index 990fa5062..4584ea453 100644 --- a/include/sway/output.h +++ b/include/sway/output.h @@ -134,8 +134,6 @@ enum sway_container_layout output_get_default_layout( enum wlr_direction opposite_direction(enum wlr_direction d); -void handle_output_layout_change(struct wl_listener *listener, void *data); - void handle_output_manager_apply(struct wl_listener *listener, void *data); void handle_output_manager_test(struct wl_listener *listener, void *data); diff --git a/include/sway/server.h b/include/sway/server.h index 460f9e17f..ccf4a9cc2 100644 --- a/include/sway/server.h +++ b/include/sway/server.h @@ -45,7 +45,6 @@ struct sway_server { struct sway_input_manager *input; struct wl_listener new_output; - struct wl_listener output_layout_change; struct wl_listener renderer_lost; struct wlr_idle_notifier_v1 *idle_notifier_v1; diff --git a/include/sway/tree/root.h b/include/sway/tree/root.h index 7de0abcdd..10d9ea981 100644 --- a/include/sway/tree/root.h +++ b/include/sway/tree/root.h @@ -16,8 +16,6 @@ struct sway_root { struct sway_node node; struct wlr_output_layout *output_layout; - struct wl_listener output_layout_change; - // scene node layout: // - root // - staging diff --git a/sway/desktop/output.c b/sway/desktop/output.c index f6a26b0e3..9f4290cd6 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -543,13 +543,6 @@ void handle_new_output(struct wl_listener *listener, void *data) { request_modeset(server); } -void handle_output_layout_change(struct wl_listener *listener, - void *data) { - struct sway_server *server = - wl_container_of(listener, server, output_layout_change); - update_output_manager_config(server); -} - static struct output_config *output_config_for_config_head( struct wlr_output_configuration_head_v1 *config_head, struct sway_output *output) { diff --git a/sway/server.c b/sway/server.c index 1ca560869..30896baf6 100644 --- a/sway/server.c +++ b/sway/server.c @@ -277,9 +277,6 @@ bool server_init(struct sway_server *server) { server->new_output.notify = handle_new_output; wl_signal_add(&server->backend->events.new_output, &server->new_output); - server->output_layout_change.notify = handle_output_layout_change; - wl_signal_add(&root->output_layout->events.change, - &server->output_layout_change); server->xdg_output_manager_v1 = wlr_xdg_output_manager_v1_create(server->wl_display, root->output_layout); diff --git a/sway/tree/root.c b/sway/tree/root.c index 20fcfa595..19d072b5c 100644 --- a/sway/tree/root.c +++ b/sway/tree/root.c @@ -19,12 +19,6 @@ struct sway_root *root; -static void output_layout_handle_change(struct wl_listener *listener, - void *data) { - arrange_root(); - transaction_commit_dirty(); -} - struct sway_root *root_create(struct wl_display *wl_display) { struct sway_root *root = calloc(1, sizeof(struct sway_root)); if (!root) { @@ -81,14 +75,10 @@ struct sway_root *root_create(struct wl_display *wl_display) { root->non_desktop_outputs = create_list(); root->scratchpad = create_list(); - root->output_layout_change.notify = output_layout_handle_change; - wl_signal_add(&root->output_layout->events.change, - &root->output_layout_change); return root; } void root_destroy(struct sway_root *root) { - wl_list_remove(&root->output_layout_change.link); list_free(root->scratchpad); list_free(root->non_desktop_outputs); list_free(root->outputs); From cfb292cca7cabfb42b27c82691bf47527d2237fb Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 5 Sep 2024 18:32:00 +0200 Subject: [PATCH 033/108] desktop/output: Avoid duplicate output manager update --- sway/desktop/output.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 9f4290cd6..8711a2481 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -633,10 +633,6 @@ static void output_manager_apply(struct sway_server *server, wlr_output_configuration_v1_send_failed(config); } wlr_output_configuration_v1_destroy(config); - - if (!test_only) { - update_output_manager_config(server); - } } void handle_output_manager_apply(struct wl_listener *listener, void *data) { From 4fe054c6db74401f4afc7453fc74665097b5261d Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 5 Sep 2024 18:32:51 +0200 Subject: [PATCH 034/108] tree/output: Avoid duplicate input mapping configure --- sway/tree/output.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/sway/tree/output.c b/sway/tree/output.c index f3b0b27a2..44b941ca2 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -180,8 +180,6 @@ void output_enable(struct sway_output *output) { ws->layout = output_get_default_layout(output); } - input_manager_configure_xcursor(); - wl_signal_emit_mutable(&root->events.new_node, &output->node); } From fc6b8d6af2a8b5c68bbf49753b0e560ad2cff208 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Fri, 6 Sep 2024 19:09:41 -0400 Subject: [PATCH 035/108] container: Skip % char if it doesn't match a view property The else condition was missed here and we would never skip the % char if it didn't end up matching with any property. Since we fail to skip we would re-evaluate the % in an infinite loop never achieving any forward-progress. Fixes: https://github.com/swaywm/sway/issues/8333 --- sway/tree/container.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 188d3223a..f482b06bc 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -714,6 +714,10 @@ size_t parse_title_format(struct sway_container *container, char *buffer) { } else if (strncmp(next, "%shell", 6) == 0) { len += append_prop(buffer, view_get_shell(container->view)); format += 6; + } else { + lenient_strcat(buffer, "%"); + ++format; + ++len; } } else { lenient_strcat(buffer, "%"); From 4f9ce4675cf428e8acd632de31482981e1bedcf8 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 00:35:57 +0200 Subject: [PATCH 036/108] tree/arrange: Remove redundant output geometry update This is handled by apply_output_configs. --- sway/tree/arrange.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index d4003fe65..352ec0e57 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -314,14 +314,6 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } - 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; - for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; arrange_workspace(workspace); From 14bff7b451d865f16e3fc82279dfba314b11269c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 00:36:29 +0200 Subject: [PATCH 037/108] desktop/transaction: Deactivate workspace on inactive outputs If the output is not active, it might not have a valid geometry to arrange for. Outputs do not gain a geometry until modeset, so if an output is connected with a configuration present to disable it, it will not have a geometry. If the output has a past workspace restored, this will be attemtped arranged to fit a 0x0 rectangle, which asserts when trying to sort out borders. Consider the workspace activated only if the output itself is active to get the scene nodes disabled. --- sway/desktop/transaction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 2ee5a5dff..931189891 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -559,7 +559,7 @@ static void arrange_output(struct sway_output *output, int width, int height) { for (int i = 0; i < output->current.workspaces->length; i++) { struct sway_workspace *child = output->current.workspaces->items[i]; - bool activated = output->current.active_workspace == child; + bool activated = output->current.active_workspace == child && output->wlr_output->enabled; wlr_scene_node_reparent(&child->layers.tiling->node, output->layers.tiling); wlr_scene_node_reparent(&child->layers.fullscreen->node, output->layers.fullscreen); From f4a6b0395f3fe38cb14bec1f5ac30445496e525c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 6 Sep 2024 00:42:49 +0200 Subject: [PATCH 038/108] tree/arrange; Skip arranging disabled outputs Disabled outputs might not have a geometry to arrange for, so skip the arrange to avoid messing up the workspace geometry. --- sway/tree/arrange.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sway/tree/arrange.c b/sway/tree/arrange.c index 352ec0e57..2b95a6dc9 100644 --- a/sway/tree/arrange.c +++ b/sway/tree/arrange.c @@ -314,6 +314,9 @@ void arrange_output(struct sway_output *output) { if (config->reloading) { return; } + if (!output->wlr_output->enabled) { + return; + } for (int i = 0; i < output->workspaces->length; ++i) { struct sway_workspace *workspace = output->workspaces->items[i]; arrange_workspace(workspace); From c5ba7f23a50cd43d39fbb45274484abbcaa4e157 Mon Sep 17 00:00:00 2001 From: llyyr Date: Sun, 8 Sep 2024 17:28:34 +0530 Subject: [PATCH 039/108] sway/input/keyboard: always set active keyboard if there is none Previously, we incorrectly only set active keyboard for non-virtual devices. 4c3c0602116c12c2821e1e505e7248b3c642b4ca incorrectly put unrelated code in `sway_keyboard_set_layout`. Fixes: 4c3c0602116c12c2821e1e505e7248b3c642b4ca --- sway/input/keyboard.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index efb9ac394..1a73df014 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -1028,13 +1028,6 @@ static void sway_keyboard_set_layout(struct sway_keyboard *keyboard, } } - // If the seat has no active keyboard, set this one - struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; - struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; - if (current_keyboard == NULL) { - wlr_seat_set_keyboard(seat, keyboard->wlr); - } - if (keymap_changed) { ipc_event_input("xkb_keymap", keyboard->seat_device->input_device); @@ -1078,6 +1071,13 @@ void sway_keyboard_configure(struct sway_keyboard *keyboard) { sway_keyboard_set_layout(keyboard, input_config); } + // If the seat has no active keyboard, set this one + struct wlr_seat *seat = keyboard->seat_device->sway_seat->wlr_seat; + struct wlr_keyboard *current_keyboard = seat->keyboard_state.keyboard; + if (current_keyboard == NULL) { + wlr_seat_set_keyboard(seat, keyboard->wlr); + } + wl_list_remove(&keyboard->keyboard_key.link); wl_signal_add(&keyboard->wlr->events.key, &keyboard->keyboard_key); keyboard->keyboard_key.notify = handle_keyboard_key; From fb5eadc363a7f8b9eeeb0ba562ecb3c40e0e6e5a Mon Sep 17 00:00:00 2001 From: Adam Chovanec Date: Sat, 7 Sep 2024 21:28:25 +0200 Subject: [PATCH 040/108] readme: update Czech translation --- README.cs.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.cs.md b/README.cs.md index 41efba54a..fbb23b34e 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,6 +1,6 @@ # sway -[English][en] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [Svenska][sv] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] +[English][en] - [عربي][ar] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] sway je s [i3] kompatibilní [Wayland] kompozitor. Přečtěte si [FAQ]. Připojte se na [IRC kanál][IRC channel] \(#sway na irc.libera.chat). @@ -32,10 +32,11 @@ Nainstalujte závislosti: * pango * cairo * gdk-pixbuf2 (volitelné: oznamovací oblast) +* [swaybg] (volitelné: tapeta) * [scdoc] (volitelné: manuálové stránky) \* * git (volitelné: informace o verzi) \* -_\* Závislost pouze pro sestavení_ +_\* Závislost pouze pro kompilaci_ Spusťte tyto příkazy: @@ -56,12 +57,13 @@ Spusťte `sway` z TTY. Některé správce zobrazení mohou fungovat, ale nejsou podporovány sway (je známo, že gdm funguje docela dobře). [en]: https://github.com/swaywm/sway#readme +[ar]: README.ar.md [cs]: README.cs.md [de]: README.de.md [dk]: README.dk.md [es]: README.es.md [fr]: README.fr.md -[sv]: README.sv.md +[ge]: README.ge.md [gr]: README.gr.md [hi]: README.hi.md [hu]: README.hu.md @@ -70,10 +72,12 @@ podporovány sway (je známo, že gdm funguje docela dobře). [ja]: README.ja.md [ko]: README.ko.md [nl]: README.nl.md +[no]: README.no.md [pl]: README.pl.md [pt]: README.pt.md [ro]: README.ro.md [ru]: README.ru.md +[sv]: README.sv.md [tr]: README.tr.md [uk]: README.uk.md [zh-CN]: README.zh-CN.md @@ -86,4 +90,5 @@ podporovány sway (je známo, že gdm funguje docela dobře). [GitHub releases]: https://github.com/swaywm/sway/releases [Development setup]: https://github.com/swaywm/sway/wiki/Development-Setup [wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots +[swaybg]: https://github.com/swaywm/swaybg/ [scdoc]: https://git.sr.ht/~sircmpwn/scdoc From a0c03499348a4a3d4d2e9a387bf366ccbcf68186 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 5 Sep 2024 22:25:44 +0200 Subject: [PATCH 041/108] config/output: Support multiple matches in find_output_config Simplify find_output_config and inline the search through the output configs instead of using list_seq_find with a comparator function. The new implementation will merge any amount of matched configs in order, which will be relied upon in a future commit. --- sway/config/output.c | 39 +++++++++++++-------------------------- 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index cc6aedb77..aa8504ed2 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -25,13 +25,6 @@ #include #endif -int output_name_cmp(const void *item, const void *data) { - const struct output_config *output = item; - const char *name = data; - - return strcmp(output->name, name); -} - void output_get_identifier(char *identifier, size_t len, struct sway_output *output) { struct wlr_output *wlr_output = output->wlr_output; @@ -615,8 +608,6 @@ static void default_output_config(struct output_config *oc, // configuration that applies to the specified output. struct output_config *find_output_config(struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; - struct output_config *oc = NULL; - struct output_config *result = new_output_config(name); if (config->reloading) { default_output_config(result, sway_output->wlr_output); @@ -625,25 +616,21 @@ struct output_config *find_output_config(struct sway_output *sway_output) { char id[128]; output_get_identifier(id, sizeof(id), sway_output); - int i; - bool match = false; - if ((i = list_seq_find(config->output_configs, output_name_cmp, "*")) >= 0) { - match = true; - oc = config->output_configs->items[i]; - merge_output_config(result, oc); - } - if ((i = list_seq_find(config->output_configs, output_name_cmp, name)) >= 0) { - match = true; - oc = config->output_configs->items[i]; - merge_output_config(result, oc); - } - if ((i = list_seq_find(config->output_configs, output_name_cmp, id)) >= 0) { - match = true; - oc = config->output_configs->items[i]; - merge_output_config(result, oc); + // We take a new config and merge on top, in order, the wildcard config, + // output config by name, and output config by identifier to form the final + // config. If there are multiple matches, they are merged in order. + struct output_config *oc = NULL; + const char *names[] = {"*", name, id, NULL}; + for (const char **name = &names[0]; *name; name++) { + for (int idx = 0; idx < config->output_configs->length; idx++) { + oc = config->output_configs->items[idx]; + if (strcmp(oc->name, *name) == 0) { + merge_output_config(result, oc); + } + } } - if (!match && !config->reloading) { + if (oc == NULL && !config->reloading) { // No name, identifier, or wildcard config. Since we are not // reloading with defaults, the output config will be empty, so // just return NULL From 0496477f92e60d504c3938a54e823ad56c8b1868 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 9 Sep 2024 11:57:06 +0200 Subject: [PATCH 042/108] config/output: Always start with default in find_output_config We always need to start out with the default configuration, regardless of whether the config is reloading or not to ensure that config decisions are stable given a specific configuration. --- sway/config/output.c | 41 +++++++++++------------------------------ 1 file changed, 11 insertions(+), 30 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index aa8504ed2..46a0ffd3a 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -584,35 +584,24 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } -static void default_output_config(struct output_config *oc, - struct wlr_output *wlr_output) { - oc->enabled = 1; - oc->power = 1; - struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); - if (mode != NULL) { - oc->width = mode->width; - oc->height = mode->height; - oc->refresh_rate = mode->refresh / 1000.f; - } - oc->x = oc->y = -1; - oc->scale = 0; // auto - oc->scale_filter = SCALE_FILTER_DEFAULT; - struct sway_output *output = wlr_output->data; - oc->subpixel = output->detected_subpixel; - oc->transform = WL_OUTPUT_TRANSFORM_NORMAL; - oc->max_render_time = 0; - oc->allow_tearing = 0; -} - // find_output_config returns a merged output_config containing all stored // configuration that applies to the specified output. struct output_config *find_output_config(struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; struct output_config *result = new_output_config(name); - if (config->reloading) { - default_output_config(result, sway_output->wlr_output); + if (result == NULL) { + return NULL; } + // Set output defaults for the "base" configuration + result->enabled = 1; + result->power = 1; + result->scale = 0; // auto + result->subpixel = sway_output->detected_subpixel; + result->transform = WL_OUTPUT_TRANSFORM_NORMAL; + result->max_render_time = 0; + result->allow_tearing = 0; + char id[128]; output_get_identifier(id, sizeof(id), sway_output); @@ -630,14 +619,6 @@ struct output_config *find_output_config(struct sway_output *sway_output) { } } - if (oc == NULL && !config->reloading) { - // No name, identifier, or wildcard config. Since we are not - // reloading with defaults, the output config will be empty, so - // just return NULL - free_output_config(result); - return NULL; - } - return result; } From 29b3f00e6fd99296cde7e94b7063acfd075c559c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 9 Sep 2024 15:28:22 +0200 Subject: [PATCH 043/108] config/output: Accept a list of output_configs to use Instead of using a single finalized output config per output, accept a regular list of output configs like the one ultimately stored for configuration purposes. This allows the output management code to test an augmented configuration while still using the same output config logic, without having to mutate the stored configuration. This in turn allows us to make a few APIs private. A bug note about an existing issue with derade to off is added as well. --- include/sway/config.h | 15 ++------ sway/config/output.c | 47 ++++++++++++++++++------- sway/desktop/output.c | 80 ++++++++++++++++--------------------------- 3 files changed, 67 insertions(+), 75 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index d9f561571..f8007c92e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -296,14 +296,6 @@ struct output_config { char *background_fallback; }; -/** - * An output config pre-matched to an output - */ -struct matched_output_config { - struct sway_output *output; - struct output_config *config; -}; - /** * Stores size of gaps for each side */ @@ -693,14 +685,11 @@ const char *sway_output_scale_filter_to_string(enum scale_filter_mode scale_filt struct output_config *new_output_config(const char *name); -bool apply_output_configs(struct matched_output_config *configs, - size_t configs_len, bool test_only, bool degrade_to_off); +bool apply_output_configs(struct output_config **ocs, size_t ocs_len, + bool test_only, bool degrade_to_off); void apply_all_output_configs(void); -void sort_output_configs_by_priority(struct matched_output_config *configs, - size_t configs_len); - /** * store_output_config stores a new output config. An output may be matched by * three different config types, in order of precedence: Identifier, name and diff --git a/sway/config/output.c b/sway/config/output.c index 46a0ffd3a..5006a385e 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -584,9 +584,11 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } -// find_output_config returns a merged output_config containing all stored -// configuration that applies to the specified output. -struct output_config *find_output_config(struct sway_output *sway_output) { +// 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( + struct output_config **configs, size_t configs_len, + struct sway_output *sway_output) { const char *name = sway_output->wlr_output->name; struct output_config *result = new_output_config(name); if (result == NULL) { @@ -611,8 +613,8 @@ struct output_config *find_output_config(struct sway_output *sway_output) { struct output_config *oc = NULL; const char *names[] = {"*", name, id, NULL}; for (const char **name = &names[0]; *name; name++) { - for (int idx = 0; idx < config->output_configs->length; idx++) { - oc = config->output_configs->items[idx]; + for (size_t idx = 0; idx < configs_len; idx++) { + oc = configs[idx]; if (strcmp(oc->name, *name) == 0) { merge_output_config(result, oc); } @@ -622,6 +624,12 @@ struct output_config *find_output_config(struct sway_output *sway_output) { return result; } +struct output_config *find_output_config(struct sway_output *sway_output) { + return find_output_config_from_list( + (struct output_config **)config->output_configs->items, + config->output_configs->length, sway_output); +} + static bool config_has_manual_mode(struct output_config *oc) { if (!oc) { return false; @@ -634,6 +642,14 @@ static bool config_has_manual_mode(struct output_config *oc) { return false; } +/** + * An output config pre-matched to an output + */ +struct matched_output_config { + struct sway_output *output; + struct output_config *config; +}; + struct search_context { struct wlr_output_swapchain_manager *swapchain_mgr; struct wlr_backend_output_state *states; @@ -852,12 +868,12 @@ static int compare_matched_output_config_priority(const void *a, const void *b) return 0; } -void sort_output_configs_by_priority(struct matched_output_config *configs, - size_t configs_len) { +static void sort_output_configs_by_priority( + struct matched_output_config *configs, size_t configs_len) { qsort(configs, configs_len, sizeof(*configs), compare_matched_output_config_priority); } -bool apply_output_configs(struct matched_output_config *configs, +static bool apply_resolved_output_configs(struct matched_output_config *configs, size_t configs_len, bool test_only, bool degrade_to_off) { struct wlr_backend_output_state *states = calloc(configs_len, sizeof(*states)); if (!states) { @@ -965,11 +981,12 @@ out: return ok; } -void apply_all_output_configs(void) { +bool apply_output_configs(struct output_config **ocs, size_t ocs_len, + bool test_only, bool degrade_to_off) { size_t configs_len = wl_list_length(&root->all_outputs); struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); if (!configs) { - return; + return false; } int config_idx = 0; @@ -982,16 +999,22 @@ void apply_all_output_configs(void) { struct matched_output_config *config = &configs[config_idx++]; config->output = sway_output; - config->config = find_output_config(sway_output); + config->config = find_output_config_from_list(ocs, ocs_len, sway_output); } sort_output_configs_by_priority(configs, configs_len); - apply_output_configs(configs, configs_len, false, true); + bool ok = apply_resolved_output_configs(configs, configs_len, test_only, degrade_to_off); for (size_t idx = 0; idx < configs_len; idx++) { struct matched_output_config *cfg = &configs[idx]; free_output_config(cfg->config); } free(configs); + return ok; +} + +void apply_all_output_configs(void) { + apply_output_configs((struct output_config **)config->output_configs->items, + config->output_configs->length, false, true); } void free_output_config(struct output_config *oc) { diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8711a2481..8ce31d859 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -544,9 +544,8 @@ 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 sway_output *output) { - struct output_config *oc = new_output_config(output->wlr_output->name); + struct wlr_output_configuration_head_v1 *config_head) { + struct output_config *oc = new_output_config(config_head->state.output->name); oc->enabled = config_head->state.enabled; if (!oc->enabled) { return oc; @@ -572,67 +571,48 @@ static struct output_config *output_config_for_config_head( } static void output_manager_apply(struct sway_server *server, - struct wlr_output_configuration_v1 *config, bool test_only) { - size_t configs_len = wl_list_length(&root->all_outputs); - struct matched_output_config *configs = calloc(configs_len, sizeof(*configs)); + struct wlr_output_configuration_v1 *cfg, bool test_only) { + bool ok = false; + size_t configs_len = config->output_configs->length + wl_list_length(&cfg->heads); + struct output_config **configs = calloc(configs_len, sizeof(*configs)); if (!configs) { - return; + goto done; + } + size_t start_new_configs = config->output_configs->length; + for (size_t idx = 0; idx < start_new_configs; idx++) { + configs[idx] = config->output_configs->items[idx]; } - int config_idx = 0; - struct sway_output *sway_output; - wl_list_for_each(sway_output, &root->all_outputs, link) { - if (sway_output == root->fallback_output) { - configs_len--; - continue; - } - - struct matched_output_config *cfg = &configs[config_idx++]; - cfg->output = sway_output; - - struct wlr_output_configuration_head_v1 *config_head; - wl_list_for_each(config_head, &config->heads, link) { - if (config_head->state.output == sway_output->wlr_output) { - cfg->config = output_config_for_config_head(config_head, sway_output); - break; - } - } - if (!cfg->config) { - cfg->config = find_output_config(sway_output); - } + size_t config_idx = start_new_configs; + struct wlr_output_configuration_head_v1 *config_head; + wl_list_for_each(config_head, &cfg->heads, link) { + // 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); + configs[config_idx++] = oc; } - sort_output_configs_by_priority(configs, configs_len); - bool ok = apply_output_configs(configs, configs_len, test_only, false); - for (size_t idx = 0; idx < configs_len; idx++) { - struct matched_output_config *cfg = &configs[idx]; - - // Only store new configs for successful non-test commits. Old configs, - // test-only and failed commits just get freed. - bool store_config = false; + // Try to commit without degrade to off enabled. Note that this will fail + // 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); + for (size_t idx = start_new_configs; idx < configs_len; idx++) { + struct output_config *cfg = configs[idx]; if (!test_only && ok) { - struct wlr_output_configuration_head_v1 *config_head; - wl_list_for_each(config_head, &config->heads, link) { - if (config_head->state.output == cfg->output->wlr_output) { - store_config = true; - break; - } - } - } - if (store_config) { - store_output_config(cfg->config); + store_output_config(cfg); } else { - free_output_config(cfg->config); + free_output_config(cfg); } } free(configs); +done: if (ok) { - wlr_output_configuration_v1_send_succeeded(config); + wlr_output_configuration_v1_send_succeeded(cfg); } else { - wlr_output_configuration_v1_send_failed(config); + wlr_output_configuration_v1_send_failed(cfg); } - wlr_output_configuration_v1_destroy(config); + wlr_output_configuration_v1_destroy(cfg); } void handle_output_manager_apply(struct wl_listener *listener, void *data) { From d7a76d381bbe4321578bc3a95fbc82d76b67ef05 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 9 Sep 2024 20:04:17 +0200 Subject: [PATCH 044/108] config/output: Rename to apply_stored_output_configs --- include/sway/config.h | 2 +- sway/commands/output.c | 2 +- sway/config.c | 2 +- sway/config/output.c | 2 +- sway/desktop/output.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index f8007c92e..13f576b00 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -688,7 +688,7 @@ struct output_config *new_output_config(const char *name); bool apply_output_configs(struct output_config **ocs, size_t ocs_len, bool test_only, bool degrade_to_off); -void apply_all_output_configs(void); +void apply_stored_output_configs(void); /** * store_output_config stores a new output config. An output may be matched by diff --git a/sway/commands/output.c b/sway/commands/output.c index 9478e0bad..3f65b909c 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -111,7 +111,7 @@ struct cmd_results *cmd_output(int argc, char **argv) { // entire config and before the deferred commands so that an auto generated // workspace name is not given to re-enabled outputs. if (!config->reloading && !config->validating) { - apply_all_output_configs(); + apply_stored_output_configs(); if (background) { if (!spawn_swaybg()) { return cmd_results_new(CMD_FAILURE, diff --git a/sway/config.c b/sway/config.c index 5058efcc0..5fc414a14 100644 --- a/sway/config.c +++ b/sway/config.c @@ -533,7 +533,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - apply_all_output_configs(); + apply_stored_output_configs(); spawn_swaybg(); config->reloading = false; diff --git a/sway/config/output.c b/sway/config/output.c index 5006a385e..4a6ba1555 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -1012,7 +1012,7 @@ bool apply_output_configs(struct output_config **ocs, size_t ocs_len, return ok; } -void apply_all_output_configs(void) { +void apply_stored_output_configs(void) { apply_output_configs((struct output_config **)config->output_configs->items, config->output_configs->length, false, true); } diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8ce31d859..0b0d5291e 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -386,7 +386,7 @@ static int timer_modeset_handle(void *data) { wl_event_source_remove(server->delayed_modeset); server->delayed_modeset = NULL; - apply_all_output_configs(); + apply_stored_output_configs(); return 0; } From f957c7e658871c27935f88f6e1d18b9db67f3808 Mon Sep 17 00:00:00 2001 From: Steffen Dirkwinkel Date: Fri, 13 Sep 2024 08:58:55 +0000 Subject: [PATCH 045/108] config/output: support DRM_FORMAT_ARGB8888 Some display output hardware [1] doesn't support any of the current formats, but works with ARGB8888. Fall back to it if available. [1] https://github.com/torvalds/linux/blob/196145c606d0f816fd3926483cb1ff87e09c2c0b/drivers/gpu/drm/xlnx/zynqmp_disp.c#L313 Signed-off-by: Steffen Dirkwinkel --- sway/config/output.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/config/output.c b/sway/config/output.c index 4a6ba1555..1ba72517f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -783,6 +783,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) DRM_FORMAT_XRGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, DRM_FORMAT_INVALID, }; if (render_format_is_bgr(wlr_output->render_format)) { From 785a459a55d8b55b4bed1fdc55b04c32be5b450c Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 18 Sep 2024 00:46:29 +0200 Subject: [PATCH 046/108] ext-session-lock: Do not use commit listener to arrange Arranging lock surfaces rely on the sway_output width and height being updated, but these are only updated after the commit has been completed and all commit listeners have executed. This means that the lock surfaces will not be appropriately scaled to match a change in output dimensions, and may reveal what is under the lock background. Replace the implicit arrange through the output commit listener with an explicit arrange after the output configuration is finalized. This might have regressed by other transition away from output commit listeners for other arrange tasks, but even then it would have erroneously relied on signalling order. --- include/sway/lock.h | 6 ++++++ sway/config/output.c | 2 ++ sway/lock.c | 29 ++++++++++++----------------- 3 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 include/sway/lock.h diff --git a/include/sway/lock.h b/include/sway/lock.h new file mode 100644 index 000000000..5be0f9695 --- /dev/null +++ b/include/sway/lock.h @@ -0,0 +1,6 @@ +#ifndef _SWAY_LOCK_H +#define _SWAY_LOCK_H + +void arrange_locks(void); + +#endif diff --git a/sway/config/output.c b/sway/config/output.c index 1ba72517f..25f51dc7f 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -14,6 +14,7 @@ #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" #include "sway/layers.h" +#include "sway/lock.h" #include "sway/output.h" #include "sway/server.h" #include "sway/tree/arrange.h" @@ -955,6 +956,7 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, } arrange_root(); + arrange_locks(); update_output_manager_config(&server); transaction_commit_dirty(); diff --git a/sway/lock.c b/sway/lock.c index 289e8ca46..43f313308 100644 --- a/sway/lock.c +++ b/sway/lock.c @@ -8,6 +8,7 @@ #include "sway/layers.h" #include "sway/output.h" #include "sway/server.h" +#include "sway/lock.h" struct sway_session_lock_output { struct wlr_scene_tree *tree; @@ -19,7 +20,6 @@ struct sway_session_lock_output { struct wl_list link; // sway_session_lock::outputs struct wl_listener destroy; - struct wl_listener commit; struct wlr_session_lock_surface_v1 *surface; @@ -89,6 +89,17 @@ static void lock_output_reconfigure(struct sway_session_lock_output *output) { } } +void arrange_locks(void) { + if (server.session_lock.lock == NULL) { + return; + } + + struct sway_session_lock_output *lock_output; + wl_list_for_each(lock_output, &server.session_lock.lock->outputs, link) { + lock_output_reconfigure(lock_output); + } +} + static void handle_new_surface(struct wl_listener *listener, void *data) { struct sway_session_lock *lock = wl_container_of(listener, lock, new_surface); struct wlr_session_lock_surface_v1 *lock_surface = data; @@ -125,7 +136,6 @@ static void sway_session_lock_output_destroy(struct sway_session_lock_output *ou wl_list_remove(&output->surface_map.link); } - wl_list_remove(&output->commit.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->link); @@ -138,18 +148,6 @@ static void lock_node_handle_destroy(struct wl_listener *listener, void *data) { sway_session_lock_output_destroy(output); } -static void lock_output_handle_commit(struct wl_listener *listener, void *data) { - struct wlr_output_event_commit *event = data; - struct sway_session_lock_output *output = - wl_container_of(listener, output, commit); - if (event->state->committed & ( - WLR_OUTPUT_STATE_MODE | - WLR_OUTPUT_STATE_SCALE | - WLR_OUTPUT_STATE_TRANSFORM)) { - lock_output_reconfigure(output); - } -} - static struct sway_session_lock_output *session_lock_output_create( struct sway_session_lock *lock, struct sway_output *output) { struct sway_session_lock_output *lock_output = calloc(1, sizeof(*lock_output)); @@ -186,9 +184,6 @@ static struct sway_session_lock_output *session_lock_output_create( lock_output->destroy.notify = lock_node_handle_destroy; wl_signal_add(&tree->node.events.destroy, &lock_output->destroy); - lock_output->commit.notify = lock_output_handle_commit; - wl_signal_add(&output->wlr_output->events.commit, &lock_output->commit); - lock_output_reconfigure(lock_output); wl_list_insert(&lock->outputs, &lock_output->link); From 034d02f8a5099ad1283ce3bd1ced524a17f8ba2f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 29 Aug 2024 23:40:19 +0200 Subject: [PATCH 047/108] config/output: Add support for 6-bit render fmt GUD devices uses RGB565 by default for performance reasons. Allow specifying render_bit_depth 6 to pick this format. The definition works out if you consider the maximum number of bits per channel instead of the average. --- include/sway/config.h | 1 + sway/commands/output/render_bit_depth.c | 7 +++++-- sway/config/output.c | 25 +++++++++++++++++++------ sway/sway-output.5.scd | 6 +++--- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 13f576b00..3cd59722f 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -262,6 +262,7 @@ enum scale_filter_mode { enum render_bit_depth { RENDER_BIT_DEPTH_DEFAULT, // the default is currently 8 + RENDER_BIT_DEPTH_6, RENDER_BIT_DEPTH_8, RENDER_BIT_DEPTH_10, }; diff --git a/sway/commands/output/render_bit_depth.c b/sway/commands/output/render_bit_depth.c index c419321ea..3fa30e045 100644 --- a/sway/commands/output/render_bit_depth.c +++ b/sway/commands/output/render_bit_depth.c @@ -11,7 +11,10 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing bit depth argument."); } - if (strcmp(*argv, "8") == 0) { + if (strcmp(*argv, "6") == 0) { + config->handler_context.output_config->render_bit_depth = + RENDER_BIT_DEPTH_6; + } else if (strcmp(*argv, "8") == 0) { config->handler_context.output_config->render_bit_depth = RENDER_BIT_DEPTH_8; } else if (strcmp(*argv, "10") == 0) { @@ -19,7 +22,7 @@ struct cmd_results *output_cmd_render_bit_depth(int argc, char **argv) { RENDER_BIT_DEPTH_10; } else { return cmd_results_new(CMD_INVALID, - "Invalid bit depth. Must be a value in (8|10)."); + "Invalid bit depth. Must be a value in (6|8|10)."); } config->handler_context.leftovers.argc = argc - 1; diff --git a/sway/config/output.c b/sway/config/output.c index 25f51dc7f..d72307f74 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -398,9 +398,15 @@ static int compute_default_scale(struct wlr_output *output, return 2; } -static bool render_format_is_10bit(uint32_t render_format) { - return render_format == DRM_FORMAT_XRGB2101010 || - render_format == DRM_FORMAT_XBGR2101010; +static enum render_bit_depth bit_depth_from_format(uint32_t render_format) { + if (render_format == DRM_FORMAT_XRGB2101010 || render_format == DRM_FORMAT_XBGR2101010) { + return RENDER_BIT_DEPTH_10; + } else if (render_format == DRM_FORMAT_XRGB8888 || render_format == DRM_FORMAT_ARGB8888) { + return RENDER_BIT_DEPTH_8; + } else if (render_format == DRM_FORMAT_RGB565) { + return RENDER_BIT_DEPTH_6; + } + return RENDER_BIT_DEPTH_DEFAULT; } static bool render_format_is_bgr(uint32_t fmt) { @@ -496,11 +502,13 @@ static void queue_output_config(struct output_config *oc, if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { if (oc->render_bit_depth == RENDER_BIT_DEPTH_10 && - render_format_is_10bit(output->wlr_output->render_format)) { + bit_depth_from_format(output->wlr_output->render_format) == oc->render_bit_depth) { // 10-bit was set successfully before, try to save some tests by reusing the format wlr_output_state_set_render_format(pending, output->wlr_output->render_format); } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_10) { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB2101010); + } else if (oc->render_bit_depth == RENDER_BIT_DEPTH_6){ + wlr_output_state_set_render_format(pending, DRM_FORMAT_RGB565); } else { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } @@ -785,6 +793,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) DRM_FORMAT_XBGR2101010, DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, DRM_FORMAT_INVALID, }; if (render_format_is_bgr(wlr_output->render_format)) { @@ -795,9 +804,13 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) const struct wlr_drm_format_set *primary_formats = wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); - bool need_10bit = cfg->config && cfg->config->render_bit_depth == RENDER_BIT_DEPTH_10; + enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8; + if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { + needed_bits = cfg->config->render_bit_depth; + } for (size_t idx = 0; fmts[idx] != DRM_FORMAT_INVALID; idx++) { - if (!need_10bit && render_format_is_10bit(fmts[idx])) { + enum render_bit_depth format_bits = bit_depth_from_format(fmts[idx]); + if (needed_bits < format_bits) { continue; } if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index d9a28807a..c5087b1bd 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -163,9 +163,9 @@ must be separated by one space. For example: adaptive sync can improve latency, but can cause flickering on some hardware. -*output* render_bit_depth 8|10 - Controls the color channel bit depth at which frames are rendered; the - default is currently 8 bits per channel. +*output* render_bit_depth 6|8|10 + Controls the maximum color channel bit depth at which frames are + rendered; the default is currently 8 bits per channel. Setting higher values will not have an effect if hardware and software lack support for such bit depths. Successfully increasing the render bit depth From 9765c29be11f04f903a45f34b9142a865784fc46 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Fri, 30 Aug 2024 02:10:38 +0200 Subject: [PATCH 048/108] config/output: Stringify render format when logging it --- sway/config/output.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index d72307f74..8e2528c80 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "sway/config.h" #include "sway/desktop/transaction.h" #include "sway/input/cursor.h" @@ -673,7 +674,9 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s sway_log(SWAY_DEBUG, " enabled: %s", state->enabled ? "yes" : "no"); } if (state->committed & WLR_OUTPUT_STATE_RENDER_FORMAT) { - sway_log(SWAY_DEBUG, " render_format: %d", state->render_format); + char *format_name = drmGetFormatName(state->render_format); + sway_log(SWAY_DEBUG, " render_format: %s", format_name); + free(format_name); } if (state->committed & WLR_OUTPUT_STATE_MODE) { if (state->mode_type == WLR_OUTPUT_STATE_MODE_CUSTOM) { From e940acd3749a5af08d5c404cae242c8693784ddc Mon Sep 17 00:00:00 2001 From: Emil Engberg Date: Fri, 20 Sep 2024 15:36:58 +0200 Subject: [PATCH 049/108] Add toggle for output adaptive_sync --- sway/commands/output/adaptive_sync.c | 24 ++++++++++++++++++++---- sway/sway-output.5.scd | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/sway/commands/output/adaptive_sync.c b/sway/commands/output/adaptive_sync.c index 7382e2ee3..4ce88f885 100644 --- a/sway/commands/output/adaptive_sync.c +++ b/sway/commands/output/adaptive_sync.c @@ -1,5 +1,7 @@ +#include #include "sway/commands.h" #include "sway/config.h" +#include "sway/output.h" #include "util.h" struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { @@ -10,12 +12,26 @@ struct cmd_results *output_cmd_adaptive_sync(int argc, char **argv) { return cmd_results_new(CMD_INVALID, "Missing adaptive_sync argument"); } - if (parse_boolean(argv[0], true)) { - config->handler_context.output_config->adaptive_sync = 1; - } else { - config->handler_context.output_config->adaptive_sync = 0; + bool current_value = true; + if (strcasecmp(argv[0], "toggle") == 0) { + const char *oc_name = config->handler_context.output_config->name; + if (strcmp(oc_name, "*") == 0) { + return cmd_results_new(CMD_INVALID, + "Cannot apply toggle to all outputs"); + } + + struct sway_output *sway_output = all_output_by_name_or_id(oc_name); + if (!sway_output || !sway_output->wlr_output) { + return cmd_results_new(CMD_FAILURE, + "Cannot apply toggle to unknown output %s", oc_name); + } + + current_value = + sway_output->wlr_output->adaptive_sync_status == WLR_OUTPUT_ADAPTIVE_SYNC_ENABLED; } + config->handler_context.output_config->adaptive_sync = parse_boolean(argv[0], current_value); + config->handler_context.leftovers.argc = argc - 1; config->handler_context.leftovers.argv = argv + 1; return NULL; diff --git a/sway/sway-output.5.scd b/sway/sway-output.5.scd index c5087b1bd..dc16c257b 100644 --- a/sway/sway-output.5.scd +++ b/sway/sway-output.5.scd @@ -154,7 +154,7 @@ must be separated by one space. For example: This setting only has an effect on Wayland and DRM backends, as support for presentation timestamps and predicted output refresh rate is required. -*output* adaptive_sync on|off +*output* adaptive_sync on|off|toggle Enables or disables adaptive synchronization (often referred to as Variable Refresh Rate, or by the vendor-specific names FreeSync/G-Sync). From 266cd4515a015b5684eaf6c0b48ce1afa9be2739 Mon Sep 17 00:00:00 2001 From: Scott Dubinsky Date: Fri, 20 Sep 2024 18:12:08 +0300 Subject: [PATCH 050/108] Remove unguarded double include --- sway/input/input-manager.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index 248ca34ee..ddfe14689 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -4,7 +4,6 @@ #include #include #include -#include #include #include #include From 48069097ea55021afa0af3c5148cb7caab724dcf Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Tue, 4 Jun 2024 20:05:58 -0400 Subject: [PATCH 051/108] text_input: Check for allocation failure --- sway/input/text_input.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 580a9f545..4d52fedc4 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -448,6 +448,11 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, struct sway_input_method_relay *relay = wl_container_of(listener, relay, input_method_new_popup_surface); struct sway_input_popup *popup = calloc(1, sizeof(*popup)); + if (!popup) { + sway_log(SWAY_ERROR, "Failed to allocate an input method popup"); + return; + } + popup->relay = relay; popup->popup_surface = data; popup->popup_surface->data = popup; From 1537c9dae53eebea4926321aa9f7fd982375859f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Aug 2024 13:06:06 -0400 Subject: [PATCH 052/108] text_input: Move popup placement to own function --- include/sway/input/text_input_popup.h | 1 + sway/input/text_input.c | 152 ++++++++++++++------------ 2 files changed, 84 insertions(+), 69 deletions(-) diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h index e5f6ab8b5..97dd6444a 100644 --- a/include/sway/input/text_input_popup.h +++ b/include/sway/input/text_input_popup.h @@ -9,6 +9,7 @@ struct sway_input_popup { struct wlr_scene_tree *scene_tree; struct sway_popup_desc desc; struct wlr_input_popup_surface_v2 *popup_surface; + struct wlr_output *fixed_output; struct wl_list link; diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 4d52fedc4..9be418f56 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -128,6 +128,80 @@ static void handle_im_destroy(struct wl_listener *listener, void *data) { } } +static void constrain_popup(struct sway_input_popup *popup) { + struct sway_text_input *text_input = + relay_get_focused_text_input(popup->relay); + + + struct wlr_box parent = {0}; + wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y); + + struct wlr_box geo = {0}; + struct wlr_output *output; + + if (popup->desc.view) { + struct sway_view *view = popup->desc.view; + output = wlr_output_layout_output_at(root->output_layout, + view->container->pending.content_x + view->geometry.x, + view->container->pending.content_y + view->geometry.y); + + parent.width = view->geometry.width; + parent.height = view->geometry.height; + geo = view->geometry; + } else { + output = popup->fixed_output; + } + + struct wlr_box output_box; + wlr_output_layout_get_box(root->output_layout, output, &output_box); + + bool cursor_rect = text_input->input->current.features & + WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; + struct wlr_box cursor_area; + if (cursor_rect) { + cursor_area = text_input->input->current.cursor_rectangle; + } else { + cursor_area = (struct wlr_box) { + .width = parent.width, + .height = parent.height, + }; + } + + int popup_width = popup->popup_surface->surface->current.width; + int popup_height = popup->popup_surface->surface->current.height; + int x1 = parent.x + cursor_area.x; + int x2 = parent.x + cursor_area.x + cursor_area.width; + int y1 = parent.y + cursor_area.y; + int y2 = parent.y + cursor_area.y + cursor_area.height; + int x = x1; + int y = y2; + + int available_right = output_box.x + output_box.width - x1; + int available_left = x2 - output_box.x; + if (available_right < popup_width && available_left > available_right) { + x = x2 - popup_width; + } + + int available_down = output_box.y + output_box.height - y2; + int available_up = y1 - output_box.y; + if (available_down < popup_height && available_up > available_down) { + y = y1 - popup_height; + } + + wlr_scene_node_set_position(popup->desc.relative, x - parent.x - geo.x, y - parent.y - geo.y); + if (cursor_rect) { + struct wlr_box box = { + .x = x1 - x, + .y = y1 - y, + .width = cursor_area.width, + .height = cursor_area.height, + }; + wlr_input_popup_surface_v2_send_text_input_rectangle( + popup->popup_surface, &box); + } + wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); +} + static void relay_send_im_state(struct sway_input_method_relay *relay, struct wlr_text_input_v3 *input) { struct wlr_input_method_v2 *input_method = relay->input_method; @@ -150,8 +224,7 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, } struct sway_input_popup *popup; wl_list_for_each(popup, &relay->input_popups, link) { - // send_text_input_rectangle is called in this function - input_popup_update(popup); + constrain_popup(popup); } wlr_input_method_v2_send_done(input_method); // TODO: pass intent, display popup size @@ -297,50 +370,30 @@ static void input_popup_update(struct sway_input_popup *popup) { return; } - bool cursor_rect = text_input->input->current.features - & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE; struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_box cursor_area = text_input->input->current.cursor_rectangle; - struct wlr_box output_box; - struct wlr_box parent = {0}; struct wlr_layer_surface_v1 *layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); struct wlr_scene_tree *relative_parent; - struct wlr_box geo = {0}; - popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); if (layer_surface != NULL) { - struct sway_layer_surface *layer = - layer_surface->data; + struct sway_layer_surface *layer = layer_surface->data; if (layer == NULL) { return; } relative_parent = layer->scene->tree; - struct wlr_output *output = layer->layer_surface->output; - wlr_output_layout_get_box(root->output_layout, output, &output_box); - int lx, ly; - wlr_scene_node_coords(&layer->tree->node, &lx, &ly); - parent.x = lx; - parent.y = ly; popup->desc.view = NULL; + + // 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 = layer->layer_surface->output; } else { struct sway_view *view = view_from_wlr_surface(focused_surface); relative_parent = view->scene_tree; - geo = view->geometry; - int lx, ly; - wlr_scene_node_coords(&view->scene_tree->node, &lx, &ly); - struct wlr_output *output = wlr_output_layout_output_at(root->output_layout, - view->container->pending.content_x + view->geometry.x, - view->container->pending.content_y + view->geometry.y); - wlr_output_layout_get_box(root->output_layout, output, &output_box); - parent.x = lx; - parent.y = ly; - - parent.width = view->geometry.width; - parent.height = view->geometry.height; popup->desc.view = view; } @@ -354,46 +407,7 @@ static void input_popup_update(struct sway_input_popup *popup) { return; } - if (!cursor_rect) { - cursor_area.x = 0; - cursor_area.y = 0; - cursor_area.width = parent.width; - cursor_area.height = parent.height; - } - - int popup_width = popup->popup_surface->surface->current.width; - int popup_height = popup->popup_surface->surface->current.height; - int x1 = parent.x + cursor_area.x; - int x2 = parent.x + cursor_area.x + cursor_area.width; - int y1 = parent.y + cursor_area.y; - int y2 = parent.y + cursor_area.y + cursor_area.height; - int x = x1; - int y = y2; - - int available_right = output_box.x + output_box.width - x1; - int available_left = x2 - output_box.x; - if (available_right < popup_width && available_left > available_right) { - x = x2 - popup_width; - } - - int available_down = output_box.y + output_box.height - y2; - int available_up = y1 - output_box.y; - if (available_down < popup_height && available_up > available_down) { - y = y1 - popup_height; - } - - wlr_scene_node_set_position(&relative->node, x - parent.x - geo.x, y - parent.y - geo.y); - if (cursor_rect) { - struct wlr_box box = { - .x = x1 - x, - .y = y1 - y, - .width = cursor_area.width, - .height = cursor_area.height, - }; - wlr_input_popup_surface_v2_send_text_input_rectangle( - popup->popup_surface, &box); - } - wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + constrain_popup(popup); } static void input_popup_set_focus(struct sway_input_popup *popup, From 023f6b0a50dd4fe17a29d7f02922e18ef37df857 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Aug 2024 13:02:37 -0400 Subject: [PATCH 053/108] transaction: Allow no popup descriptor in popup list Input method popups in the future will destroy the scene descriptor when it isn't mapped and therefore shouldn't be tampered with here. --- sway/desktop/transaction.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sway/desktop/transaction.c b/sway/desktop/transaction.c index 931189891..8f12832a0 100644 --- a/sway/desktop/transaction.c +++ b/sway/desktop/transaction.c @@ -612,9 +612,11 @@ void arrange_popups(struct wlr_scene_tree *popups) { struct sway_popup_desc *popup = scene_descriptor_try_get(node, SWAY_SCENE_DESC_POPUP); - int lx, ly; - wlr_scene_node_coords(popup->relative, &lx, &ly); - wlr_scene_node_set_position(node, lx, ly); + if (popup) { + int lx, ly; + wlr_scene_node_coords(popup->relative, &lx, &ly); + wlr_scene_node_set_position(node, lx, ly); + } } } From 74e507962e32ec8d6606d28383ac109fbf2370e4 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 4 Aug 2024 13:03:59 -0400 Subject: [PATCH 054/108] text_input: Properly handle map/unmap events The last implementation would ignore these and get it could get into a bad state where it would start crashing sway. --- include/sway/input/text_input_popup.h | 2 + sway/input/text_input.c | 64 +++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/include/sway/input/text_input_popup.h b/include/sway/input/text_input_popup.h index 97dd6444a..7e838ed2a 100644 --- a/include/sway/input/text_input_popup.h +++ b/include/sway/input/text_input_popup.h @@ -15,6 +15,8 @@ struct sway_input_popup { struct wl_listener popup_destroy; struct wl_listener popup_surface_commit; + struct wl_listener popup_surface_map; + struct wl_listener popup_surface_unmap; struct wl_listener focused_surface_unmap; }; diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 9be418f56..1414215bc 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -132,6 +132,9 @@ static void constrain_popup(struct sway_input_popup *popup) { struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay); + if (!popup->desc.relative) { + return; + } struct wlr_box parent = {0}; wlr_scene_node_coords(&popup->desc.relative->parent->node, &parent.x, &parent.y); @@ -199,7 +202,10 @@ static void constrain_popup(struct sway_input_popup *popup) { wlr_input_popup_surface_v2_send_text_input_rectangle( popup->popup_surface, &box); } - wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + + if (popup->scene_tree) { + wlr_scene_node_set_position(&popup->scene_tree->node, x - geo.x, y - geo.y); + } } static void relay_send_im_state(struct sway_input_method_relay *relay, @@ -376,7 +382,6 @@ static void input_popup_update(struct sway_input_popup *popup) { wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); struct wlr_scene_tree *relative_parent; - popup->scene_tree = wlr_scene_subsurface_tree_create(root->layers.popup, popup->popup_surface->surface); if (layer_surface != NULL) { struct sway_layer_surface *layer = layer_surface->data; if (layer == NULL) { @@ -435,19 +440,43 @@ static void input_popup_set_focus(struct sway_input_popup *popup, static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { struct sway_input_popup *popup = wl_container_of(listener, popup, popup_destroy); + wlr_scene_node_destroy(&popup->scene_tree->node); wl_list_remove(&popup->focused_surface_unmap.link); wl_list_remove(&popup->popup_surface_commit.link); + wl_list_remove(&popup->popup_surface_map.link); + wl_list_remove(&popup->popup_surface_unmap.link); wl_list_remove(&popup->popup_destroy.link); wl_list_remove(&popup->link); free(popup); } -static void handle_im_popup_surface_commit(struct wl_listener *listener, - void *data) { +static void handle_im_popup_surface_map(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_map); + struct sway_text_input *text_input = relay_get_focused_text_input(popup->relay); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } +} + +static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *data) { + struct sway_input_popup *popup = + wl_container_of(listener, popup, popup_surface_unmap); + + // relative should already be freed as it should be a child of the just unmapped scene + popup->desc.relative = NULL; + + input_popup_set_focus(popup, NULL); +} + +static void handle_im_popup_surface_commit(struct wl_listener *listener, void *data) { struct sway_input_popup *popup = wl_container_of(listener, popup, popup_surface_commit); - input_popup_update(popup); + + constrain_popup(popup); } static void handle_im_focused_surface_unmap( @@ -471,12 +500,29 @@ static void handle_im_new_popup_surface(struct wl_listener *listener, popup->popup_surface = data; popup->popup_surface->data = popup; - wl_signal_add( - &popup->popup_surface->events.destroy, &popup->popup_destroy); + popup->scene_tree = wlr_scene_tree_create(root->layers.popup); + if (!popup->scene_tree) { + sway_log(SWAY_ERROR, "Failed to allocate scene tree"); + free(popup); + return; + } + + if (!wlr_scene_subsurface_tree_create(popup->scene_tree, + popup->popup_surface->surface)) { + sway_log(SWAY_ERROR, "Failed to allocate subsurface tree"); + wlr_scene_node_destroy(&popup->scene_tree->node); + free(popup); + return; + } + + wl_signal_add(&popup->popup_surface->events.destroy, &popup->popup_destroy); popup->popup_destroy.notify = handle_im_popup_destroy; - wl_signal_add(&popup->popup_surface->surface->events.commit, - &popup->popup_surface_commit); + wl_signal_add(&popup->popup_surface->surface->events.commit, &popup->popup_surface_commit); popup->popup_surface_commit.notify = handle_im_popup_surface_commit; + wl_signal_add(&popup->popup_surface->surface->events.map, &popup->popup_surface_map); + popup->popup_surface_map.notify = handle_im_popup_surface_map; + wl_signal_add(&popup->popup_surface->surface->events.unmap, &popup->popup_surface_unmap); + popup->popup_surface_unmap.notify = handle_im_popup_surface_unmap; wl_list_init(&popup->focused_surface_unmap.link); popup->focused_surface_unmap.notify = handle_im_focused_surface_unmap; From e9dd2182313e9a480e2b3d48162142414d1fee48 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sat, 8 Jun 2024 15:58:56 -0400 Subject: [PATCH 055/108] text_input: Inline input_popup_update into input_popup_set_focus This seems to be the intention of input_popup_update in the first place: handle the scenario where the focus moves. --- sway/input/text_input.c | 75 ++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 43 deletions(-) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 1414215bc..6bcd02341 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -11,8 +11,6 @@ #include "sway/layers.h" #include "sway/server.h" -static void input_popup_update(struct sway_input_popup *popup); - static struct sway_text_input *relay_get_focusable_text_input( struct sway_input_method_relay *relay) { struct sway_text_input *text_input = NULL; @@ -208,6 +206,9 @@ static void constrain_popup(struct sway_input_popup *popup) { } } +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface); + static void relay_send_im_state(struct sway_input_method_relay *relay, struct wlr_text_input_v3 *input) { struct wlr_input_method_v2 *input_method = relay->input_method; @@ -228,9 +229,16 @@ static void relay_send_im_state(struct sway_input_method_relay *relay, input->current.content_type.hint, input->current.content_type.purpose); } + + struct sway_text_input *text_input = relay_get_focused_text_input(relay); + struct sway_input_popup *popup; wl_list_for_each(popup, &relay->input_popups, link) { - constrain_popup(popup); + if (text_input != NULL) { + input_popup_set_focus(popup, text_input->input->focused_surface); + } else { + input_popup_set_focus(popup, NULL); + } } wlr_input_method_v2_send_done(input_method); // TODO: pass intent, display popup size @@ -354,35 +362,35 @@ static void relay_handle_text_input(struct wl_listener *listener, sway_text_input_create(relay, wlr_text_input); } -static void input_popup_update(struct sway_input_popup *popup) { - struct sway_text_input *text_input = - relay_get_focused_text_input(popup->relay); +static void input_popup_set_focus(struct sway_input_popup *popup, + struct wlr_surface *surface) { + wl_list_remove(&popup->focused_surface_unmap.link); - if (text_input == NULL || text_input->input->focused_surface == NULL) { + if (!popup->scene_tree) { + wl_list_init(&popup->focused_surface_unmap.link); return; } - if (popup->scene_tree != NULL) { - wlr_scene_node_destroy(&popup->scene_tree->node); - popup->scene_tree = NULL; - } - if (popup->desc.relative != NULL) { + if (popup->desc.relative) { + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); wlr_scene_node_destroy(popup->desc.relative); popup->desc.relative = NULL; } - popup->desc.view = NULL; - if (!popup->popup_surface->surface->mapped) { + if (surface == NULL) { + wl_list_init(&popup->focused_surface_unmap.link); + wlr_scene_node_set_enabled(&popup->scene_tree->node, false); return; } - struct wlr_surface *focused_surface = text_input->input->focused_surface; - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(focused_surface); - struct wlr_scene_tree *relative_parent; + wlr_layer_surface_v1_try_from_wlr_surface(surface); + + struct wlr_scene_tree *relative_parent; + if (layer_surface) { + wl_signal_add(&layer_surface->surface->events.unmap, + &popup->focused_surface_unmap); - if (layer_surface != NULL) { struct sway_layer_surface *layer = layer_surface->data; if (layer == NULL) { return; @@ -397,7 +405,8 @@ static void input_popup_update(struct sway_input_popup *popup) { // destroyed, thus also trickling down to popups. popup->fixed_output = layer->layer_surface->output; } else { - struct sway_view *view = view_from_wlr_surface(focused_surface); + struct sway_view *view = view_from_wlr_surface(surface); + wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); relative_parent = view->scene_tree; popup->desc.view = view; } @@ -413,28 +422,7 @@ static void input_popup_update(struct sway_input_popup *popup) { } constrain_popup(popup); -} - -static void input_popup_set_focus(struct sway_input_popup *popup, - struct wlr_surface *surface) { - wl_list_remove(&popup->focused_surface_unmap.link); - - if (surface == NULL) { - wl_list_init(&popup->focused_surface_unmap.link); - input_popup_update(popup); - return; - } - struct wlr_layer_surface_v1 *layer_surface = - wlr_layer_surface_v1_try_from_wlr_surface(surface); - if (layer_surface != NULL) { - wl_signal_add( - &layer_surface->surface->events.unmap, &popup->focused_surface_unmap); - input_popup_update(popup); - return; - } - - struct sway_view *view = view_from_wlr_surface(surface); - wl_signal_add(&view->events.unmap, &popup->focused_surface_unmap); + wlr_scene_node_set_enabled(&popup->scene_tree->node, true); } static void handle_im_popup_destroy(struct wl_listener *listener, void *data) { @@ -483,7 +471,8 @@ static void handle_im_focused_surface_unmap( struct wl_listener *listener, void *data) { struct sway_input_popup *popup = wl_container_of(listener, popup, focused_surface_unmap); - input_popup_update(popup); + + input_popup_set_focus(popup, NULL); } static void handle_im_new_popup_surface(struct wl_listener *listener, From 861dde100ab5536bea190b078c6c51adb6814be5 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 00:53:26 +0200 Subject: [PATCH 056/108] commands/gaps: Check config->reading instead Checking if the config is not active or is reloading is just a convoluted way of checking if the config is being read. --- sway/commands/gaps.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/sway/commands/gaps.c b/sway/commands/gaps.c index 1deeb56e1..7ac6fcff5 100644 --- a/sway/commands/gaps.c +++ b/sway/commands/gaps.c @@ -215,15 +215,13 @@ struct cmd_results *cmd_gaps(int argc, char **argv) { return error; } - bool config_loading = !config->active || config->reloading; - if (argc == 2) { return gaps_set_defaults(argc, argv); } - if (argc == 4 && !config_loading) { + if (argc == 4 && !config->reading) { return gaps_set_runtime(argc, argv); } - if (config_loading) { + if (config->reading) { return cmd_results_new(CMD_INVALID, "Expected %s", expected_defaults); } return cmd_results_new(CMD_INVALID, "Expected %s or %s", From b6da218974d2e0508e7816f707fe0b1f1c97f0d6 Mon Sep 17 00:00:00 2001 From: Olivia Taliesin Date: Tue, 15 Feb 2022 14:46:12 -0700 Subject: [PATCH 057/108] Removed destination-is-ancestor check from container_move_to_container to match i3 behaviour --- sway/commands/move.c | 1 - 1 file changed, 1 deletion(-) diff --git a/sway/commands/move.c b/sway/commands/move.c index ad106c648..8891514ce 100644 --- a/sway/commands/move.c +++ b/sway/commands/move.c @@ -240,7 +240,6 @@ static void container_move_to_workspace(struct sway_container *container, static void container_move_to_container(struct sway_container *container, struct sway_container *destination) { if (container == destination - || container_has_ancestor(container, destination) || container_has_ancestor(destination, container)) { return; } From b73f54a966a30c2253818b89fefda16477531c14 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 00:51:14 +0200 Subject: [PATCH 058/108] desktop/output: Expose request_modeset We remove the struct sway_server argument for consistency with the rest of our internal APIs which rely on the global server instance. --- include/sway/config.h | 2 ++ sway/desktop/output.c | 18 ++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 3cd59722f..71721393e 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -704,6 +704,8 @@ struct output_config *find_output_config(struct sway_output *output); void free_output_config(struct output_config *oc); +void request_modeset(void); + bool spawn_swaybg(void); int workspace_output_cmp_workspace(const void *a, const void *b); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 0b0d5291e..c919f1398 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -390,17 +390,15 @@ static int timer_modeset_handle(void *data) { return 0; } -static void request_modeset(struct sway_server *server) { - if (server->delayed_modeset == NULL) { - server->delayed_modeset = wl_event_loop_add_timer(server->wl_event_loop, - timer_modeset_handle, server); - wl_event_source_timer_update(server->delayed_modeset, 10); +void request_modeset(void) { + if (server.delayed_modeset == NULL) { + server.delayed_modeset = wl_event_loop_add_timer(server.wl_event_loop, + timer_modeset_handle, &server); + wl_event_source_timer_update(server.delayed_modeset, 10); } } static void begin_destroy(struct sway_output *output) { - struct sway_server *server = output->server; - if (output->enabled) { output_disable(output); } @@ -420,7 +418,7 @@ static void begin_destroy(struct sway_output *output) { output->wlr_output->data = NULL; output->wlr_output = NULL; - request_modeset(server); + request_modeset(); } static void handle_destroy(struct wl_listener *listener, void *data) { @@ -540,7 +538,7 @@ void handle_new_output(struct wl_listener *listener, void *data) { sway_session_lock_add_output(server->session_lock.lock, output); } - request_modeset(server); + request_modeset(); } static struct output_config *output_config_for_config_head( @@ -646,5 +644,5 @@ void handle_output_power_manager_set_mode(struct wl_listener *listener, break; } store_output_config(oc); - request_modeset(output->server); + request_modeset(); } From cdff4f7c74b76e9141164b8c154621646140d8ec Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 01:00:04 +0200 Subject: [PATCH 059/108] config: Batch input/output configuration on load We batch modesets and input configuration performed during config reload but commit for every command during the intial config load. There is no need to perform commits during the initial config load as outputs have not yet been created, but swaybg spawn should still be batched. At the same time, replace direct calls to apply output configuration with request_modeset to properly handle the modeset timer. --- sway/commands/input.c | 2 +- sway/commands/output.c | 21 ++++++++++----------- sway/config.c | 10 ++++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/sway/commands/input.c b/sway/commands/input.c index 35846b1cf..310375a94 100644 --- a/sway/commands/input.c +++ b/sway/commands/input.c @@ -94,7 +94,7 @@ struct cmd_results *cmd_input(int argc, char **argv) { return res; } - if (!config->reloading) { + if (!config->reading) { input_manager_apply_input_config(ic); } } else { diff --git a/sway/commands/output.c b/sway/commands/output.c index 3f65b909c..9d58413f2 100644 --- a/sway/commands/output.c +++ b/sway/commands/output.c @@ -107,17 +107,16 @@ struct cmd_results *cmd_output(int argc, char **argv) { store_output_config(output); - // If reloading, the output configs will be applied after reading the - // entire config and before the deferred commands so that an auto generated - // workspace name is not given to re-enabled outputs. - if (!config->reloading && !config->validating) { - apply_stored_output_configs(); - if (background) { - if (!spawn_swaybg()) { - return cmd_results_new(CMD_FAILURE, - "Failed to apply background configuration"); - } - } + if (config->reading) { + // When reading the config file, we wait till the end to do a single + // modeset and swaybg spawn. + return cmd_results_new(CMD_SUCCESS, NULL); + } + request_modeset(); + + if (background && !spawn_swaybg()) { + return cmd_results_new(CMD_FAILURE, + "Failed to apply background configuration"); } return cmd_results_new(CMD_SUCCESS, NULL); diff --git a/sway/config.c b/sway/config.c index 5fc414a14..1090edc5c 100644 --- a/sway/config.c +++ b/sway/config.c @@ -516,7 +516,7 @@ bool load_main_config(const char *file, bool is_active, bool validating) { // Only really necessary if not explicitly `font` is set in the config. config_update_font_height(); - if (is_active && !validating) { + if (!validating) { input_manager_verify_fallback_seat(); for (int i = 0; i < config->input_configs->length; i++) { @@ -533,12 +533,14 @@ bool load_main_config(const char *file, bool is_active, bool validating) { } sway_switch_retrigger_bindings_for_all(); - apply_stored_output_configs(); spawn_swaybg(); config->reloading = false; - if (config->swaynag_config_errors.client != NULL) { - swaynag_show(&config->swaynag_config_errors); + if (is_active) { + request_modeset(); + if (config->swaynag_config_errors.client != NULL) { + swaynag_show(&config->swaynag_config_errors); + } } } From 63345977e2c411359a049c40cf2c1044a22b4f4a Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sat, 21 Sep 2024 01:02:54 +0200 Subject: [PATCH 060/108] desktop/output: Clear modeset timer on output manager apply If a modeset timer exists at the time we apply an output manager config, clear it to avoid a useless double commit. --- sway/desktop/output.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c919f1398..1f1f8d682 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -607,6 +607,10 @@ static void output_manager_apply(struct sway_server *server, done: if (ok) { wlr_output_configuration_v1_send_succeeded(cfg); + if (server->delayed_modeset != NULL) { + wl_event_source_remove(server->delayed_modeset); + server->delayed_modeset = NULL; + } } else { wlr_output_configuration_v1_send_failed(cfg); } From 00e9a941523baa4afa1f9c077235aa7aa5e8aeab Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Wed, 25 Sep 2024 06:35:30 -0500 Subject: [PATCH 061/108] swaybar: Fix 100% cpu usage if dbus dies. Currently, swaybar does not gracefully die if it detects that the dbus connection was lost. Although it's not recommended to restart dbus without restarting the compositor, it can very easily happen. In the case it does, compositor's tray should not consume 100% cpu until it has to be force killed. apply suggestions just setting the bar to not running will call teardown and unref the dbus. --- swaybar/bar.c | 2 +- swaybar/ipc.c | 3 +-- swaybar/tray/tray.c | 12 ++++++++++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/swaybar/bar.c b/swaybar/bar.c index 5b1213a8d..4d20f20f0 100644 --- a/swaybar/bar.c +++ b/swaybar/bar.c @@ -508,7 +508,7 @@ void bar_run(struct swaybar *bar) { } #if HAVE_TRAY if (bar->tray) { - loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar->tray->bus); + loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar); } #endif while (bar->running) { diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 03500bdf3..71c9a4c5e 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -518,8 +518,7 @@ static bool handle_barconfig_update(struct swaybar *bar, const char *payload, #if HAVE_TRAY if (oldcfg->tray_hidden && !newcfg->tray_hidden) { bar->tray = create_tray(bar); - loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, - bar->tray->bus); + loop_add_fd(bar->eventloop, bar->tray->fd, POLLIN, tray_in, bar); } else if (bar->tray && newcfg->tray_hidden) { loop_remove_fd(bar->eventloop, bar->tray->fd); destroy_tray(bar->tray); diff --git a/swaybar/tray/tray.c b/swaybar/tray/tray.c index b0545f4a7..a4f382bfb 100644 --- a/swaybar/tray/tray.c +++ b/swaybar/tray/tray.c @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -90,9 +91,16 @@ void destroy_tray(struct swaybar_tray *tray) { } void tray_in(int fd, short mask, void *data) { - sd_bus *bus = data; + struct swaybar *bar = data; int ret; - while ((ret = sd_bus_process(bus, NULL)) > 0) { + + if (mask & (POLLHUP | POLLERR)) { + sway_log(SWAY_ERROR, "D-Bus connection closed unexpectedly"); + bar->running = false; + return; + } + + while ((ret = sd_bus_process(bar->tray->bus, NULL)) > 0) { // This space intentionally left blank } if (ret < 0) { From a0b3606f1725ee56e8dc15ae51ce62d042c0668a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Sep 2024 16:57:52 +0200 Subject: [PATCH 062/108] Add support for alpha-modifier-v1 --- sway/server.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/server.c b/sway/server.c index 30896baf6..f16a55e20 100644 --- a/sway/server.c +++ b/sway/server.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -326,6 +327,7 @@ bool server_init(struct sway_server *server) { &server->pointer_constraint); wlr_presentation_create(server->wl_display, server->backend); + wlr_alpha_modifier_v1_create(server->wl_display); server->output_manager_v1 = wlr_output_manager_v1_create(server->wl_display); From a2757e5f165eae445ae550fd1d13f9ec0db44efc Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 29 Sep 2024 17:38:27 +0200 Subject: [PATCH 063/108] release: push tags before creating GitHub release Otherwise the GitHub release isn't attached to the Git tag. --- release.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/release.sh b/release.sh index 62baf4153..c5644cfd0 100755 --- a/release.sh +++ b/release.sh @@ -28,4 +28,5 @@ archive=$prefix.tar.gz git archive --prefix="$prefix/" -o "$archive" "$next" gpg --output "$archive".sig --detach-sig "$archive" +git push --follow-tags gh release create "sway $next" -t "$next" -n "" -d "$archive" "$archive.sig" From 9a9be01ad4130e4e19b437fd064f90982974971f Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 29 Sep 2024 16:50:00 -0400 Subject: [PATCH 064/108] Fix alpha-modifier-v1 --- sway/desktop/output.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 1f1f8d682..a4eaa4b33 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -216,6 +217,15 @@ static void output_configure_scene(struct sway_output *output, if (node->type == WLR_SCENE_NODE_BUFFER) { struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node); + struct wlr_scene_surface *surface = wlr_scene_surface_try_from_buffer(buffer); + + if (surface) { + const struct wlr_alpha_modifier_surface_v1_state *alpha_modifier_state = + wlr_alpha_modifier_v1_get_surface_state(surface->surface); + if (alpha_modifier_state != NULL) { + opacity *= (float)alpha_modifier_state->multiplier; + } + } // 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 From c90cb37b2a0861548461daa9b75d75317e01b679 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 2 Oct 2024 15:55:17 +0200 Subject: [PATCH 065/108] Re-init renderer for all outputs on lost context sway_root.outputs only include enabled outputs. We also need to re-init the renderer for any disabled outputs, so use sway_root.all_outputs instead. Resolves the following heap-use-after-free accessing the render formats when a disabled output is modeset after a GPU reset has occurred. --- sway/server.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/server.c b/sway/server.c index f16a55e20..941f4cb26 100644 --- a/sway/server.c +++ b/sway/server.c @@ -205,8 +205,8 @@ static void handle_renderer_lost(struct wl_listener *listener, void *data) { wlr_compositor_set_renderer(server->compositor, renderer); - for (int i = 0; i < root->outputs->length; ++i) { - struct sway_output *output = root->outputs->items[i]; + struct sway_output *output; + wl_list_for_each(output, &root->all_outputs, link) { wlr_output_init_render(output->wlr_output, server->allocator, server->renderer); } From f855b0898bf00285d5a7b840963b327230486632 Mon Sep 17 00:00:00 2001 From: ShootingStarDragons Date: Mon, 7 Oct 2024 18:49:44 +0900 Subject: [PATCH 066/108] fix: sway crashes if switch to another workspace with surface when IME popup is shown in pr https://github.com/swaywm/sway/pull/8196, when im_popup_surface is unmapped, author set the popup->relative to NULL, butt popup is still in popup groups, where assert the relative is not NULL, this cause the panic Take the suggestion of Nefsen402, remove the line where set relative to NULL, and add NULL check in scene_descriptor_destory --- sway/input/text_input.c | 1 + sway/scene_descriptor.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/sway/input/text_input.c b/sway/input/text_input.c index 6bcd02341..e16724671 100644 --- a/sway/input/text_input.c +++ b/sway/input/text_input.c @@ -454,6 +454,7 @@ static void handle_im_popup_surface_unmap(struct wl_listener *listener, void *da struct sway_input_popup *popup = wl_container_of(listener, popup, popup_surface_unmap); + scene_descriptor_destroy(&popup->scene_tree->node, SWAY_SCENE_DESC_POPUP); // relative should already be freed as it should be a child of the just unmapped scene popup->desc.relative = NULL; diff --git a/sway/scene_descriptor.c b/sway/scene_descriptor.c index a30d46646..92bdda00c 100644 --- a/sway/scene_descriptor.c +++ b/sway/scene_descriptor.c @@ -39,6 +39,9 @@ void *scene_descriptor_try_get(struct wlr_scene_node *node, void scene_descriptor_destroy(struct wlr_scene_node *node, enum sway_scene_descriptor_type type) { struct scene_descriptor *desc = scene_node_get_descriptor(node, type); + if (!desc) { + return; + } descriptor_destroy(desc); } From 7f1cd0b73ba3290f8ee5f81fdf7f1ffa4c642ea7 Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Tue, 8 Oct 2024 11:09:57 -0500 Subject: [PATCH 067/108] input/mouse: bugfix button2 being interpreted as trying to move the container Man sway(5) specifies that when tiling_drag is enable, the floating_mod can be used to drag tiling, as well as floating containers. However the current code indiscriminately assumes any button press to be intended for moving the container, consequently causing an unintended call to `seatop_move_tilting:handle_button` rather than `seatop_default:handle_button` to pass `state=WL_POINTER_BUTTON_STATE_RELEASED` to `get_active_mouse_binding` My idea was to make 'Handle moving a tiling container' follow the same path as 'Handle moving a floating container' because the initial call to handle moving a floating correctly exits that branch and ends up passing the RELEASED state to `get_active_mouse_binding`. Fixes #8334 --- sway/input/seatop_default.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index 42ce333b8..d8b3b5bff 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -491,7 +491,9 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle moving a tiling container if (config->tiling_drag && (mod_pressed || on_titlebar) && state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && - cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { + cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE && + button == (config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT)) { + // If moving a container by its title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont); From 17e2e52c6d1bf4bfebde8f6b2869702aacc3750a Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 10 Oct 2024 16:32:59 +0200 Subject: [PATCH 068/108] server: check backend support for timelines References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4848 --- sway/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 941f4cb26..8b122446b 100644 --- a/sway/server.c +++ b/sway/server.c @@ -251,7 +251,8 @@ bool server_init(struct sway_server *server) { } } if (wlr_renderer_get_drm_fd(server->renderer) >= 0 && - server->renderer->features.timeline) { + server->renderer->features.timeline && + server->backend->features.timeline) { wlr_linux_drm_syncobj_manager_v1_create(server->wl_display, 1, wlr_renderer_get_drm_fd(server->renderer)); } From dd063a0ef7941404d40578bfbdc8c11c70647baa Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Thu, 8 Aug 2024 23:39:30 +0200 Subject: [PATCH 069/108] input/keyboard: add support for pointer keys References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4775 --- sway/input/keyboard.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/sway/input/keyboard.c b/sway/input/keyboard.c index 1a73df014..651c44c5a 100644 --- a/sway/input/keyboard.c +++ b/sway/input/keyboard.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -267,6 +268,7 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, const xkb_keysym_t *pressed_keysyms, uint32_t modifiers, size_t keysyms_len) { for (size_t i = 0; i < keysyms_len; ++i) { xkb_keysym_t keysym = pressed_keysyms[i]; + if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { #if WLR_HAS_SESSION @@ -282,6 +284,36 @@ static bool keyboard_execute_compositor_binding(struct sway_keyboard *keyboard, return false; } +static bool keyboard_execute_pointer_keysyms(struct sway_keyboard *keyboard, + uint32_t time, const xkb_keysym_t *pressed_keysyms, size_t keysyms_len, + enum wl_keyboard_key_state state) { + struct sway_cursor *cursor = keyboard->seat_device->sway_seat->cursor; + + for (size_t i = 0; i < keysyms_len; ++i) { + xkb_keysym_t keysym = pressed_keysyms[i]; + + uint32_t button = wlr_keyboard_keysym_to_pointer_button(keysym); + if (button != 0) { + dispatch_cursor_button(cursor, &keyboard->wlr->base, time, button, + (enum wl_pointer_button_state)state); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + return true; + } + + int dx, dy; + wlr_keyboard_keysym_to_pointer_motion(keysym, &dx, &dy); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED && (dx != 0 || dy != 0)) { + dx *= 10; + dy *= 10; + pointer_motion(cursor, time, &keyboard->wlr->base, dx, dy, dx, dy); + wlr_seat_pointer_notify_frame(cursor->seat->wlr_seat); + return true; + } + } + + return false; +} + /** * Get keysyms and modifiers from the keyboard as xkb sees them. * @@ -507,6 +539,11 @@ static void handle_key_event(struct sway_keyboard *keyboard, keyboard, keyinfo.raw_keysyms, keyinfo.raw_modifiers, keyinfo.raw_keysyms_len); } + if (!handled) { + handled = keyboard_execute_pointer_keysyms(keyboard, event->time_msec, + keyinfo.translated_keysyms, keyinfo.translated_keysyms_len, + event->state); + } if (event->state == WL_KEYBOARD_KEY_STATE_RELEASED) { // If the pressed event was sent to a client and we have a focused From db76fefd0c61d2c85f448eeb43ca3a97c10770a5 Mon Sep 17 00:00:00 2001 From: Jan Palus Date: Wed, 16 Oct 2024 19:47:54 +0200 Subject: [PATCH 070/108] trigger container update after disabling urgent in timer switching workspace directly to urgent window creates timer which delays reset of urgent state so user is able to notice it. make sure state change is reflected visually as well (border change) by triggering container update Fixes: #8377 --- sway/input/seat.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sway/input/seat.c b/sway/input/seat.c index 9a00a3e24..0dd26290e 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -1094,6 +1094,7 @@ static void seat_send_unfocus(struct sway_node *node, struct sway_seat *seat) { static int handle_urgent_timeout(void *data) { struct sway_view *view = data; view_set_urgent(view, false); + container_update_itself_and_parents(view->container); return 0; } From ce6b2db0f2e9c71dda496d1aaaafcdcb9dade150 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Mon, 14 Oct 2024 16:03:55 -0400 Subject: [PATCH 071/108] layer_shell: Arrange exclusive zone clients first This makes layer_shell more stable against the order of clients. --- sway/desktop/layer_shell.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index b136a24e7..62c6a5118 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -54,7 +54,7 @@ struct wlr_layer_surface_v1 *toplevel_layer_surface_from_surface( } static void arrange_surface(struct sway_output *output, const struct wlr_box *full_area, - struct wlr_box *usable_area, struct wlr_scene_tree *tree) { + struct wlr_box *usable_area, struct wlr_scene_tree *tree, bool exclusive) { struct wlr_scene_node *node; wl_list_for_each(node, &tree->children, link) { struct sway_layer_surface *surface = scene_descriptor_try_get(node, @@ -68,6 +68,10 @@ static void arrange_surface(struct sway_output *output, const struct wlr_box *fu continue; } + if ((surface->scene->layer_surface->current.exclusive_zone > 0) != exclusive) { + continue; + } + wlr_scene_layer_surface_v1_configure(surface->scene, full_area, usable_area); } } @@ -78,10 +82,14 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); From 8363699f145fca844772643ceedcdaa7c6b90982 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Thu, 17 Oct 2024 10:10:02 -0400 Subject: [PATCH 072/108] layer_shell: Restore sway 1.9 ordering --- sway/desktop/layer_shell.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/sway/desktop/layer_shell.c b/sway/desktop/layer_shell.c index 62c6a5118..333c09b49 100644 --- a/sway/desktop/layer_shell.c +++ b/sway/desktop/layer_shell.c @@ -82,14 +82,15 @@ void arrange_layers(struct sway_output *output) { &usable_area.width, &usable_area.height); const struct wlr_box full_area = usable_area; - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); - arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, true); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_overlay, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_top, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_bottom, false); + arrange_surface(output, &full_area, &usable_area, output->layers.shell_background, false); if (!wlr_box_equal(&usable_area, &output->usable_area)) { sway_log(SWAY_DEBUG, "Usable area changed, rearranging output"); From 35d8adefc456735a1487e5feb103f30199688c7d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sun, 13 Oct 2024 13:25:47 +0200 Subject: [PATCH 073/108] input/seatop_default: refactor move/resize button logic Make it so config->floating_mod_inverse only applies when pressing mod, not when clicking on titlebars. Centralize logic into shared variables. --- sway/input/seatop_default.c | 71 +++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/sway/input/seatop_default.c b/sway/input/seatop_default.c index d8b3b5bff..6a7dddd9a 100644 --- a/sway/input/seatop_default.c +++ b/sway/input/seatop_default.c @@ -355,6 +355,13 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat->wlr_seat); uint32_t modifiers = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; + bool mod_pressed = modifiers & config->floating_mod; + uint32_t mod_move_btn = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; + uint32_t mod_resize_btn = config->floating_mod_inverse ? BTN_LEFT : BTN_RIGHT; + bool mod_move_btn_pressed = mod_pressed && button == mod_move_btn; + bool mod_resize_btn_pressed = mod_pressed && button == mod_resize_btn; + bool titlebar_left_btn_pressed = on_titlebar && button == BTN_LEFT; + // Handle mouse bindings if (trigger_pointer_button_binding(seat, device, button, state, modifiers, on_titlebar, on_border, on_contents, on_workspace)) { @@ -403,33 +410,28 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle tiling resize via mod - bool mod_pressed = modifiers & config->floating_mod; - if (cont && !is_floating_or_child && mod_pressed && + if (cont && !is_floating_or_child && mod_pressed && mod_resize_btn_pressed && state == WL_POINTER_BUTTON_STATE_PRESSED) { - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (button == btn_resize) { - edge = 0; - edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? - WLR_EDGE_RIGHT : WLR_EDGE_LEFT; - edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? - WLR_EDGE_BOTTOM : WLR_EDGE_TOP; + edge = 0; + edge |= cursor->cursor->x > cont->pending.x + cont->pending.width / 2 ? + WLR_EDGE_RIGHT : WLR_EDGE_LEFT; + edge |= cursor->cursor->y > cont->pending.y + cont->pending.height / 2 ? + WLR_EDGE_BOTTOM : WLR_EDGE_TOP; - const char *image = NULL; - if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { - image = "nw-resize"; - } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { - image = "ne-resize"; - } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { - image = "se-resize"; - } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { - image = "sw-resize"; - } - cursor_set_image(seat->cursor, image, NULL); - seat_set_focus_container(seat, cont); - seatop_begin_resize_tiling(seat, cont, edge); - return; + const char *image = NULL; + if (edge == (WLR_EDGE_LEFT | WLR_EDGE_TOP)) { + image = "nw-resize"; + } else if (edge == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)) { + image = "ne-resize"; + } else if (edge == (WLR_EDGE_RIGHT | WLR_EDGE_BOTTOM)) { + image = "se-resize"; + } else if (edge == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT)) { + image = "sw-resize"; } + cursor_set_image(seat->cursor, image, NULL); + seat_set_focus_container(seat, cont); + seatop_begin_resize_tiling(seat, cont, edge); + return; } // Handle changing focus when clicking on a container @@ -454,12 +456,10 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, // Handle beginning floating move if (cont && is_floating_or_child && !is_fullscreen_or_child && - state == WL_POINTER_BUTTON_STATE_PRESSED) { - uint32_t btn_move = config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT; - if (button == btn_move && (mod_pressed || on_titlebar)) { - seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); - return; - } + state == WL_POINTER_BUTTON_STATE_PRESSED && + (mod_move_btn_pressed || titlebar_left_btn_pressed)) { + seatop_begin_move_floating(seat, container_toplevel_ancestor(cont)); + return; } // Handle beginning floating resize @@ -473,9 +473,7 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Via mod+click - uint32_t btn_resize = config->floating_mod_inverse ? - BTN_LEFT : BTN_RIGHT; - if (mod_pressed && button == btn_resize) { + if (mod_resize_btn_pressed) { struct sway_container *floater = container_toplevel_ancestor(cont); edge = 0; edge |= cursor->cursor->x > floater->pending.x + floater->pending.width / 2 ? @@ -489,18 +487,15 @@ static void handle_button(struct sway_seat *seat, uint32_t time_msec, } // Handle moving a tiling container - if (config->tiling_drag && (mod_pressed || on_titlebar) && + if (config->tiling_drag && (mod_move_btn_pressed || titlebar_left_btn_pressed) && state == WL_POINTER_BUTTON_STATE_PRESSED && !is_floating_or_child && - cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE && - button == (config->floating_mod_inverse ? BTN_RIGHT : BTN_LEFT)) { - + cont && cont->pending.fullscreen_mode == FULLSCREEN_NONE) { // If moving a container by its title bar, use a threshold for the drag if (!mod_pressed && config->tiling_drag_threshold > 0) { seatop_begin_move_tiling_threshold(seat, cont); } else { seatop_begin_move_tiling(seat, cont); } - return; } From 7d93652105c370518f1e5856f8586b2297cab772 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Wed, 16 Oct 2024 21:55:57 +0200 Subject: [PATCH 074/108] config/output: Improve modeset state logging Include scale and subpixel in the output state log, and log the output state on first commit attempt instead of just during fallback search. --- sway/config/output.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 8e2528c80..d774d17eb 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -692,6 +692,13 @@ static void dump_output_state(struct wlr_output *wlr_output, struct wlr_output_s sway_log(SWAY_DEBUG, " adaptive_sync: %s", state->adaptive_sync_enabled ? "enabled": "disabled"); } + if (state->committed & WLR_OUTPUT_STATE_SCALE) { + sway_log(SWAY_DEBUG, " scale: %f", state->scale); + } + if (state->committed & WLR_OUTPUT_STATE_SUBPIXEL) { + sway_log(SWAY_DEBUG, " subpixel: %s", + sway_wl_output_subpixel_to_string(state->subpixel)); + } } static bool search_valid_config(struct search_context *ctx, size_t output_idx); @@ -906,9 +913,8 @@ static bool apply_resolved_output_configs(struct matched_output_config *configs, backend_state->output = cfg->output->wlr_output; wlr_output_state_init(&backend_state->base); - sway_log(SWAY_DEBUG, "Preparing config for %s", - cfg->output->wlr_output->name); queue_output_config(cfg->config, cfg->output, &backend_state->base); + dump_output_state(cfg->output->wlr_output, &backend_state->base); } struct wlr_output_swapchain_manager swapchain_mgr; From 7e0c0dda42183cf3f6a64bace230252cbeadbbd6 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 00:19:29 +0200 Subject: [PATCH 075/108] config/output: Always set output states from config queue_output_config had some remaining logic that would avoid setting output states if they already appeared to be in effect. That is not what most of the states did nor what is currently expected, so clean that up. --- sway/config/output.c | 53 ++++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index d774d17eb..b9fb773a0 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -451,54 +451,41 @@ static void queue_output_config(struct output_config *oc, wlr_output_state_set_mode(pending, preferred_mode); } - if (oc && (oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN || config->reloading)) { - sway_log(SWAY_DEBUG, "Set %s subpixel to %s", oc->name, - sway_wl_output_subpixel_to_string(oc->subpixel)); + if (oc && oc->subpixel != WL_OUTPUT_SUBPIXEL_UNKNOWN) { wlr_output_state_set_subpixel(pending, oc->subpixel); + } else { + wlr_output_state_set_subpixel(pending, output->detected_subpixel); } - enum wl_output_transform tr = WL_OUTPUT_TRANSFORM_NORMAL; if (oc && oc->transform >= 0) { - tr = oc->transform; + wlr_output_state_set_transform(pending, oc->transform); #if WLR_HAS_DRM_BACKEND } else if (wlr_output_is_drm(wlr_output)) { - tr = wlr_drm_connector_get_panel_orientation(wlr_output); - sway_log(SWAY_DEBUG, "Auto-detected output transform: %d", tr); + wlr_output_state_set_transform(pending, + wlr_drm_connector_get_panel_orientation(wlr_output)); #endif - } - if (wlr_output->transform != tr) { - sway_log(SWAY_DEBUG, "Set %s transform to %d", wlr_output->name, tr); - wlr_output_state_set_transform(pending, tr); + } else { + wlr_output_state_set_transform(pending, WL_OUTPUT_TRANSFORM_NORMAL); } - // Apply the scale last before the commit, because the scale auto-detection - // reads the pending output size - float scale; + // Apply the scale after sorting out the mode, because the scale + // auto-detection reads the pending output size if (oc && oc->scale > 0) { - scale = oc->scale; - // The factional-scale-v1 protocol uses increments of 120ths to send // the scale factor to the client. Adjust the scale so that we use the // same value as the clients'. - float adjusted_scale = round(scale * 120) / 120; - if (scale != adjusted_scale) { - sway_log(SWAY_INFO, "Adjusting output scale from %f to %f", - scale, adjusted_scale); - scale = adjusted_scale; - } + wlr_output_state_set_scale(pending, round(oc->scale * 120) / 120); } else { - scale = compute_default_scale(wlr_output, pending); - sway_log(SWAY_DEBUG, "Auto-detected output scale: %f", scale); - } - if (scale != wlr_output->scale) { - sway_log(SWAY_DEBUG, "Set %s scale to %f", wlr_output->name, scale); - wlr_output_state_set_scale(pending, scale); + wlr_output_state_set_scale(pending, + compute_default_scale(wlr_output, pending)); } - if (oc && oc->adaptive_sync != -1 && wlr_output->adaptive_sync_supported) { - sway_log(SWAY_DEBUG, "Set %s adaptive sync to %d", wlr_output->name, - oc->adaptive_sync); - wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + if (wlr_output->adaptive_sync_supported) { + if (oc && oc->adaptive_sync != -1) { + wlr_output_state_set_adaptive_sync_enabled(pending, oc->adaptive_sync == 1); + } else { + wlr_output_state_set_adaptive_sync_enabled(pending, false); + } } if (oc && oc->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { @@ -513,6 +500,8 @@ static void queue_output_config(struct output_config *oc, } else { wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } + } else { + wlr_output_state_set_render_format(pending, DRM_FORMAT_XRGB8888); } } From af0d4a048a38847769fda4898a07a72401ee40be Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 01:08:54 +0200 Subject: [PATCH 076/108] config/output: Always set all output fields on finalize --- sway/config/output.c | 51 +++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index b9fb773a0..f39082d04 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -520,24 +520,23 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output return true; } - if (oc) { - enum scale_filter_mode scale_filter_old = output->scale_filter; - switch (oc->scale_filter) { - case SCALE_FILTER_DEFAULT: - case SCALE_FILTER_SMART: - output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? - SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; - break; - case SCALE_FILTER_LINEAR: - case SCALE_FILTER_NEAREST: - output->scale_filter = oc->scale_filter; - break; - } - if (scale_filter_old != output->scale_filter) { - sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, - sway_output_scale_filter_to_string(output->scale_filter)); - wlr_damage_ring_add_whole(&output->scene_output->damage_ring); - } + enum scale_filter_mode scale_filter_old = output->scale_filter; + enum scale_filter_mode scale_filter_new = oc ? oc->scale_filter : SCALE_FILTER_DEFAULT; + switch (scale_filter_new) { + case SCALE_FILTER_DEFAULT: + case SCALE_FILTER_SMART: + output->scale_filter = ceilf(wlr_output->scale) == wlr_output->scale ? + SCALE_FILTER_NEAREST : SCALE_FILTER_LINEAR; + break; + case SCALE_FILTER_LINEAR: + case SCALE_FILTER_NEAREST: + output->scale_filter = scale_filter_new; + break; + } + if (scale_filter_old != output->scale_filter) { + sway_log(SWAY_DEBUG, "Set %s scale_filter to %s", oc->name, + sway_output_scale_filter_to_string(output->scale_filter)); + wlr_damage_ring_add_whole(&output->scene_output->damage_ring); } // Find position for it @@ -560,25 +559,19 @@ static bool finalize_output_config(struct output_config *oc, struct sway_output output_enable(output); } - if (oc && oc->max_render_time >= 0) { - sway_log(SWAY_DEBUG, "Set %s max render time to %d", - oc->name, oc->max_render_time); - output->max_render_time = oc->max_render_time; - } - 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; } - if (oc && oc->allow_tearing >= 0) { - sway_log(SWAY_DEBUG, "Set %s allow tearing to %d", - oc->name, oc->allow_tearing); - output->allow_tearing = oc->allow_tearing; - } + output->max_render_time = oc && oc->max_render_time > 0 ? oc->max_render_time : 0; + output->allow_tearing = oc && oc->allow_tearing > 0; return true; } From 17ecb9eb1d355c677dc9cf772d372982b7b9541b Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 01:19:28 +0200 Subject: [PATCH 077/108] config/output: Remove initial values in find_output_config Starting by setting some special initial output config values settings was something sway used to do when the config was initially being processed. This was later changed to always happen, as there shouldn't be differences in how output config is calculated during config load and after. Most of these values are redundant, as they are either the zero value or a value that would be selected if the unset (-1) value was found. For output transforms, the automatic panel orientation code would only trigger if the final output config has an unset transform, which the initial values set in find_output_config made impossible. Remove these initial values and instead use a fresh output config as is. --- sway/config/output.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index f39082d04..6db10b076 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -587,15 +587,6 @@ static struct output_config *find_output_config_from_list( return NULL; } - // Set output defaults for the "base" configuration - result->enabled = 1; - result->power = 1; - result->scale = 0; // auto - result->subpixel = sway_output->detected_subpixel; - result->transform = WL_OUTPUT_TRANSFORM_NORMAL; - result->max_render_time = 0; - result->allow_tearing = 0; - char id[128]; output_get_identifier(id, sizeof(id), sway_output); From a63027245a6805bb952e47c5751ecdd7d1063d2f Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Thu, 17 Oct 2024 01:28:38 +0200 Subject: [PATCH 078/108] config/output: Remove remaining logs from queue_output_config The job of queue_output_config is now just to fill out a wlr_output_state according to the output configuration, but it still has a lot of logging from before we had wlr_output_state or the new modeset logic, when queue_output_state instead touched the implicit pending state of wlr_output. Whatever debug logs it had would already be covered by the output state debug logs or the command debug logs, so let's just remove it. --- sway/config/output.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/sway/config/output.c b/sway/config/output.c index 6db10b076..5fb282d4d 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -285,7 +285,6 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending mhz = mhz <= 0 ? INT_MAX : mhz; if (wl_list_empty(&output->modes) || custom) { - sway_log(SWAY_DEBUG, "Assigning custom mode to %s", output->name); wlr_output_state_set_custom_mode(pending, width, height, refresh_rate > 0 ? mhz : 0); return; @@ -305,10 +304,7 @@ static void set_mode(struct wlr_output *output, struct wlr_output_state *pending } } } - if (best) { - sway_log(SWAY_INFO, "Assigning configured mode (%dx%d@%.3fHz) to %s", - best->width, best->height, best->refresh / 1000.f, output->name); - } else { + if (!best) { best = wlr_output_preferred_mode(output); sway_log(SWAY_INFO, "Configured mode (%dx%d@%.3fHz) not available, " "applying preferred mode (%dx%d@%.3fHz)", @@ -325,7 +321,6 @@ static void set_modeline(struct wlr_output *output, sway_log(SWAY_ERROR, "Modeline can only be set to DRM output"); return; } - sway_log(SWAY_DEBUG, "Assigning custom modeline to %s", output->name); struct wlr_output_mode *mode = wlr_drm_connector_add_mode(output, drm_mode); if (mode) { wlr_output_state_set_mode(pending, mode); @@ -391,7 +386,6 @@ static int compute_default_scale(struct wlr_output *output, double dpi_x = (double) width / (output->phys_width / MM_PER_INCH); double dpi_y = (double) height / (output->phys_height / MM_PER_INCH); - sway_log(SWAY_DEBUG, "Output DPI: %fx%f", dpi_x, dpi_y); if (dpi_x <= HIDPI_DPI_LIMIT || dpi_y <= HIDPI_DPI_LIMIT) { return 1; } @@ -427,25 +421,17 @@ static void queue_output_config(struct output_config *oc, struct wlr_output *wlr_output = output->wlr_output; if (output_config_is_disabling(oc)) { - sway_log(SWAY_DEBUG, "Turning off output %s", wlr_output->name); wlr_output_state_set_enabled(pending, false); return; } - - sway_log(SWAY_DEBUG, "Turning on output %s", wlr_output->name); wlr_output_state_set_enabled(pending, true); if (oc && oc->drm_mode.type != 0 && oc->drm_mode.type != (uint32_t) -1) { - sway_log(SWAY_DEBUG, "Set %s modeline", - wlr_output->name); set_modeline(wlr_output, pending, &oc->drm_mode); } else if (oc && oc->width > 0 && oc->height > 0) { - sway_log(SWAY_DEBUG, "Set %s mode to %dx%d (%f Hz)", - wlr_output->name, oc->width, oc->height, oc->refresh_rate); set_mode(wlr_output, pending, oc->width, oc->height, oc->refresh_rate, oc->custom_mode == 1); } else if (!wl_list_empty(&wlr_output->modes)) { - sway_log(SWAY_DEBUG, "Set preferred mode"); struct wlr_output_mode *preferred_mode = wlr_output_preferred_mode(wlr_output); wlr_output_state_set_mode(pending, preferred_mode); From 015e357fce8250cdde03844ca81e9ae982ef4bfb Mon Sep 17 00:00:00 2001 From: Kirill Primak Date: Fri, 25 Oct 2024 18:38:55 +0300 Subject: [PATCH 079/108] desktop/output: chase wlroots private fields update This will be replaced with a proper solution later. --- sway/desktop/output.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index a4eaa4b33..18a04c090 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -188,8 +188,8 @@ static enum wlr_scale_filter_mode get_scale_filter(struct sway_output *output, struct wlr_scene_buffer *buffer) { // if we are scaling down, we should always choose linear if (buffer->dst_width > 0 && buffer->dst_height > 0 && ( - buffer->dst_width < buffer->buffer_width || - buffer->dst_height < buffer->buffer_height)) { + buffer->dst_width < buffer->WLR_PRIVATE.buffer_width || + buffer->dst_height < buffer->WLR_PRIVATE.buffer_height)) { return WLR_SCALE_FILTER_BILINEAR; } From 839434abc0438b4ea8d9bc497dcd2c33c5d77037 Mon Sep 17 00:00:00 2001 From: llyyr Date: Tue, 15 Oct 2024 23:07:46 +0530 Subject: [PATCH 080/108] sway/server: bind to presentation-time-v2 Depends on: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4858 --- sway/server.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/server.c b/sway/server.c index 8b122446b..e091d9464 100644 --- a/sway/server.c +++ b/sway/server.c @@ -70,6 +70,7 @@ #define SWAY_XDG_SHELL_VERSION 5 #define SWAY_LAYER_SHELL_VERSION 4 #define SWAY_FOREIGN_TOPLEVEL_LIST_VERSION 1 +#define SWAY_PRESENTATION_VERSION 2 bool allow_unsupported_gpu = false; @@ -327,7 +328,7 @@ bool server_init(struct sway_server *server) { wl_signal_add(&server->pointer_constraints->events.new_constraint, &server->pointer_constraint); - wlr_presentation_create(server->wl_display, server->backend); + wlr_presentation_create(server->wl_display, server->backend, SWAY_PRESENTATION_VERSION); wlr_alpha_modifier_v1_create(server->wl_display); server->output_manager_v1 = From e7c972b04a109c59dae1dd73da4d9bd7cbc6800c Mon Sep 17 00:00:00 2001 From: AsciiWolf Date: Mon, 28 Oct 2024 12:36:08 +0100 Subject: [PATCH 081/108] Remove language bars from remaining non-English README files --- README.cs.md | 2 -- README.sv.md | 2 -- 2 files changed, 4 deletions(-) diff --git a/README.cs.md b/README.cs.md index fbb23b34e..14320a390 100644 --- a/README.cs.md +++ b/README.cs.md @@ -1,7 +1,5 @@ # sway -[English][en] - [عربي][ar] - **[Česky][cs]** - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - [ქართული][ge] - [Ελληνικά][gr] - [हिन्दी][hi] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Norsk][no] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Svenska][sv] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] - sway je s [i3] kompatibilní [Wayland] kompozitor. Přečtěte si [FAQ]. Připojte se na [IRC kanál][IRC channel] \(#sway na irc.libera.chat). diff --git a/README.sv.md b/README.sv.md index c50ca068c..49caf9285 100644 --- a/README.sv.md +++ b/README.sv.md @@ -1,7 +1,5 @@ # sway -[English][en] - [Deutsch][de] - [Dansk][dk] - [Español][es] - [Français][fr] - **[Svenska][sv]** - [Ελληνικά][gr] - [Magyar][hu] - [فارسی][ir] - [Italiano][it] - [日本語][ja] - [한국어][ko] - [Nederlands][nl] - [Polski][pl] - [Português][pt] - [Română][ro] - [Русский][ru] - [Türkçe][tr] - [Українська][uk] - [中文-简体][zh-CN] - [中文-繁體][zh-TW] - sway är en [i3]-kompatibel [Wayland] compositor. Läs våran [FAQ]-sida. Gå med i vår [IRC-kanal] \(#sway på irc.libera.chat). From 1e53007bc33c1afa2c3ea580050f94f29c5ce8e4 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 28 Oct 2024 11:47:10 +0100 Subject: [PATCH 082/108] 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 18a04c090..394c545de 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; + } - update_output_manager_config(output->server); + // We do not expect or support any other changes here + assert(committed == 0); + store_output_config(oc); + apply_stored_output_configs(); + + 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 083/108] 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 394c545de..064b5449b 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 084/108] 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 c5644cfd0..6ad8f2c5e 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 085/108] 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 f482b06bc..62bff1ea5 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 086/108] 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 5fb282d4d..f8922ea52 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 087/108] 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 8f12832a0..50a597a66 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 088/108] 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 50eea1483..da32eeb76 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 089/108] 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 3cfea8d8e..54f000bd6 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 090/108] 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 064b5449b..9b98e29a4 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 091/108] 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 9b98e29a4..8682cc656 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 = { From 96db66abf0622c8590d60ad6638b35d90e185f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Solt=20Budav=C3=A1ri?= <3123434+solt87@users.noreply.github.com> Date: Sat, 16 Nov 2024 13:47:50 +0000 Subject: [PATCH 092/108] Fix orthographic mistakes in Hungarian README Fix a few mistakes so the text conforms to Hungarian orthography. The following rules were applied (the whole linked page is in Hungarian): - - - - - --- README.hu.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.hu.md b/README.hu.md index 82ca6785b..2fcc36dd5 100644 --- a/README.hu.md +++ b/README.hu.md @@ -1,10 +1,10 @@ # sway -A Sway egy [i3]-kompatibilis [Wayland] kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en). +A Sway egy [i3]-kompatibilis [Wayland]-kompozitor. Olvasd el a [Gyarkan Ismételt Kérdéseket][FAQ]. Csatlakozz az [IRC-csatornához][IRC channel] \(`#sway` az `irc.libera.chat`-en). -## Csomag aláírások +## Csomagaláírások -A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHub releases] publikálva. +A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva, és [GitHubon][GitHub releases] publikálva. ## Telepítés @@ -13,12 +13,12 @@ A kiadott csomagok az [E88F5E48] kulccsal vannak aláírva és [GitHub-on][GitHu A Sway sok disztribúció csomagkezelőjéből elérhető, próbáld meg a "sway" csomagot telepíteni az általad használt eszközzel. -Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC +Ha szeretnél csomagot készíteni a saját disztribúciódhoz, ugorj be az IRC- csatornára, vagy küldj levelet a sir@cmpwn.com címre tanácsokért. ### Fordítás forráskódból -Olvasd el [ezt a wiki oldalt][Development setup], ha szeretnéd tesztelési vagy +Olvasd el [ezt a wikioldalt][Development setup], ha szeretnéd tesztelési vagy fejlesztési célokból lefordítani az aktuális (HEAD) állapotát a `sway`-nek és a `wlroots`-nak. @@ -46,7 +46,7 @@ Futtasd ezeket a parancsokat: ## Konfiguráció -Ha előzőleg i3-mat használtál, akkor átmásolhatod az i3 beállításaidat a +Ha előzőleg i3-at használtál, akkor átmásolhatod az i3-beállításaidat a `~/.config/sway/config` file-ba és ugyanúgy működni fognak. Egyéb esetben másold le kiindulási alapnak a mintát, ami általában az `etc/sway/config` elérési útvonalon található. @@ -55,7 +55,7 @@ kapcsolatban. ## Futtatás -Futtasd a `sway` parancsot egy TTY felületről. Néhány bejelentkezéskezelő +Futtasd a `sway` parancsot egy TTY-felületről. Néhány bejelentkezéskezelő (display manager) működhet, de alapvetően nem támogatottak a sway által. (A gdm-ről ismeretes, hogy egész jól működik.) From 6111297d91faf5f2820acaa14d76d6389b469b77 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Mon, 11 Nov 2024 12:16:44 +0100 Subject: [PATCH 093/108] config: Force modeset before running deferred configs Some commands require outputs to be enabled. These commands are deferred to allow outputs to be discovered, but the delayed modeset might only run some time later. Force a modeset to occur before running deferred commands. Fixes: https://github.com/swaywm/sway/issues/8433 --- include/sway/config.h | 1 + sway/desktop/output.c | 14 +++++++++----- sway/main.c | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/sway/config.h b/include/sway/config.h index 71721393e..013853c85 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -705,6 +705,7 @@ struct output_config *find_output_config(struct sway_output *output); void free_output_config(struct output_config *oc); void request_modeset(void); +void force_modeset(void); bool spawn_swaybg(void); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 8682cc656..5c2332ceb 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -408,6 +408,14 @@ void request_modeset(void) { } } +void force_modeset(void) { + if (server.delayed_modeset != NULL) { + wl_event_source_remove(server.delayed_modeset); + server.delayed_modeset = NULL; + } + apply_stored_output_configs(); +} + static void begin_destroy(struct sway_output *output) { if (output->enabled) { output_disable(output); @@ -493,12 +501,8 @@ static void handle_request_state(struct wl_listener *listener, void *data) { // We do not expect or support any other changes here assert(committed == 0); store_output_config(oc); - apply_stored_output_configs(); - if (server.delayed_modeset != NULL) { - wl_event_source_remove(server.delayed_modeset); - server.delayed_modeset = NULL; - } + force_modeset(); } static unsigned int last_headless_num = 0; diff --git a/sway/main.c b/sway/main.c index 1c4939aa0..165ccc095 100644 --- a/sway/main.c +++ b/sway/main.c @@ -361,6 +361,7 @@ int main(int argc, char **argv) { } config->active = true; + force_modeset(); load_swaybars(); run_deferred_commands(); run_deferred_bindings(); From a2c73c9b8b426417e0253ea0420b98298af1c963 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 17 Nov 2024 12:38:32 +0100 Subject: [PATCH 094/108] ipc-server: Force modeset if needed after executing commands IPC clients generally expect executed commands to have taken effect when the command completes, while delayed modeset means that it can take several milliseconds more before e.g. an output is enabled. However, modesetting on every output command in the IPC call could on systems with already slow modesetting behavior lead to an unresponsive system for a not insignificant period of time. To strike a balance, force modeset once all the commands of this IPC call have executed if a modeset is pending. --- include/sway/config.h | 1 + sway/desktop/output.c | 4 ++++ sway/ipc-server.c | 6 ++++++ 3 files changed, 11 insertions(+) diff --git a/include/sway/config.h b/include/sway/config.h index 013853c85..bb770c6f7 100644 --- a/include/sway/config.h +++ b/include/sway/config.h @@ -706,6 +706,7 @@ void free_output_config(struct output_config *oc); void request_modeset(void); void force_modeset(void); +bool modeset_is_pending(void); bool spawn_swaybg(void); diff --git a/sway/desktop/output.c b/sway/desktop/output.c index 5c2332ceb..c7ad8f937 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -408,6 +408,10 @@ void request_modeset(void) { } } +bool modeset_is_pending(void) { + return server.delayed_modeset != NULL; +} + void force_modeset(void) { if (server.delayed_modeset != NULL) { wl_event_source_remove(server.delayed_modeset); diff --git a/sway/ipc-server.c b/sway/ipc-server.c index 7f353c0ec..b934bb568 100644 --- a/sway/ipc-server.c +++ b/sway/ipc-server.c @@ -648,6 +648,12 @@ void ipc_client_handle_command(struct ipc_client *client, uint32_t payload_lengt } list_t *res_list = execute_command(buf, NULL, NULL); + if (modeset_is_pending()) { + // IPC expects commands to have taken immediate effect, so we need + // to force a modeset after output commands. We do a single modeset + // here to avoid modesetting for every output command in sequence. + force_modeset(); + } transaction_commit_dirty(); char *json = cmd_results_to_json(res_list); int length = strlen(json); From fec3da7d58c06421355e4b3dd1dcf291fd20627c Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Wed, 13 Nov 2024 22:32:00 +0530 Subject: [PATCH 095/108] commands/include: handle many files in single line i3 supports including multiple files in a single line, whereas sway limits to single file per line. --- sway/commands/include.c | 6 +++--- sway/sway.5.scd | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sway/commands/include.c b/sway/commands/include.c index d4c14c35f..e0d0c0640 100644 --- a/sway/commands/include.c +++ b/sway/commands/include.c @@ -3,12 +3,12 @@ struct cmd_results *cmd_include(int argc, char **argv) { struct cmd_results *error = NULL; - if ((error = checkarg(argc, "include", EXPECTED_EQUAL_TO, 1))) { + if ((error = checkarg(argc, "include", EXPECTED_AT_LEAST, 1))) { return error; } - + char *files = join_args(argv, argc); // We don't care if the included config(s) fails to load. - load_include_configs(argv[0], config, &config->swaynag_config_errors); + load_include_configs(files, config, &config->swaynag_config_errors); return cmd_results_new(CMD_SUCCESS, NULL); } diff --git a/sway/sway.5.scd b/sway/sway.5.scd index bb958ebfd..50de552f0 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -66,8 +66,8 @@ The following commands may only be used in the configuration file. *default_orientation* horizontal|vertical|auto Sets the default container layout for tiled containers. -*include* - Includes another file from _path_. _path_ can be either a full path or a +*include* + Include files from _paths_. _paths_ can include either a full path or a path relative to the parent config, and expands shell syntax (see *wordexp*(3) for details). The same include file can only be included once; subsequent attempts will be ignored. From 5312376077254d6431bb92ba22de3840b9933f67 Mon Sep 17 00:00:00 2001 From: Kenny Levinsen Date: Sun, 17 Nov 2024 21:43:31 +0100 Subject: [PATCH 096/108] desktop/output: Clear repaint timer earlier in destroy The teardown of a sway_output is split in two: begin_destroy and output_destroy. The former clears some state such as NULL'ing the reference to wlr_output, while the latter frees the struct and its remaining resources. If an output is destroyed while a repaint timer is pending, future frame callbacks will no longer occur as the listener is torn down in begin_destroy, but the repaint timer is not torn down and may still fire until output_destroy is hit. As begin_destroy cleared the reference to wlr_output, this leads to a NULL-pointer dereference. Tear down the repaint timer in begin_destroy as there is no need for it. Fixes: fdc4318ac66d ("desktop/output: Clear frame_pending even output is disabled") --- sway/desktop/output.c | 3 +++ sway/tree/output.c | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/sway/desktop/output.c b/sway/desktop/output.c index c7ad8f937..607eca2c3 100644 --- a/sway/desktop/output.c +++ b/sway/desktop/output.c @@ -440,6 +440,9 @@ static void begin_destroy(struct sway_output *output) { output->wlr_output->data = NULL; output->wlr_output = NULL; + wl_event_source_remove(output->repaint_timer); + output->repaint_timer = NULL; + request_modeset(); } diff --git a/sway/tree/output.c b/sway/tree/output.c index 44b941ca2..65d9e3e71 100644 --- a/sway/tree/output.c +++ b/sway/tree/output.c @@ -273,7 +273,6 @@ void output_destroy(struct sway_output *output) { destroy_scene_layers(output); list_free(output->workspaces); list_free(output->current.workspaces); - wl_event_source_remove(output->repaint_timer); wlr_color_transform_unref(output->color_transform); free(output); } From e2409aa49611bee1e1b99033461bfab0a7550c48 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Nov 2024 20:41:16 +0100 Subject: [PATCH 097/108] ipc-json: handle LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY New entry introduced in libinput 1.27.0. --- meson.build | 8 +++----- sway/ipc-json.c | 5 +++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 71e75fd9b..9ce5723e7 100644 --- a/meson.build +++ b/meson.build @@ -109,11 +109,9 @@ conf_data.set10('HAVE_LIBSYSTEMD', sdbus.found() and sdbus.name() == 'libsystemd conf_data.set10('HAVE_LIBELOGIND', sdbus.found() and sdbus.name() == 'libelogind') conf_data.set10('HAVE_BASU', sdbus.found() and sdbus.name() == 'basu') conf_data.set10('HAVE_TRAY', have_tray) -conf_data.set10('HAVE_LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', cc.has_header_symbol( - 'libinput.h', - 'LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', - dependencies: libinput, -)) +foreach sym : ['LIBINPUT_CONFIG_ACCEL_PROFILE_CUSTOM', 'LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY'] + conf_data.set10('HAVE_' + sym, cc.has_header_symbol('libinput.h', sym, dependencies: libinput)) +endforeach scdoc = dependency('scdoc', version: '>=1.9.2', native: true, required: get_option('man-pages')) if scdoc.found() diff --git a/sway/ipc-json.c b/sway/ipc-json.c index 571338a4f..fc1df2ac6 100644 --- a/sway/ipc-json.c +++ b/sway/ipc-json.c @@ -931,6 +931,11 @@ static json_object *describe_libinput_device(struct libinput_device *device) { case LIBINPUT_CONFIG_DRAG_LOCK_DISABLED: drag_lock = "disabled"; break; +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + case LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY: + drag_lock = "enabled_sticky"; + break; +#endif } json_object_object_add(object, "tap_drag_lock", json_object_new_string(drag_lock)); From bbadf9b8b10d171a6d5196da7716ea50ee7a6062 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 20 Nov 2024 20:50:27 +0100 Subject: [PATCH 098/108] Add support for LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY Use it as the default, as recommended by the libinput release notes: https://lists.freedesktop.org/archives/wayland-devel/2024-November/043860.html --- sway/commands/input/drag_lock.c | 5 +++++ sway/input/libinput.c | 8 ++++++++ sway/sway-input.5.scd | 5 +++-- sway/sway-ipc.7.scd | 3 ++- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 24c548e2d..55a39a64a 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -15,6 +15,11 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { return cmd_results_new(CMD_FAILURE, "No input device defined."); } +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + if (strcmp(argv[0], "enabled_sticky")) { + ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; + } else +#endif if (parse_boolean(argv[0], true)) { ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED; } else { diff --git a/sway/input/libinput.c b/sway/input/libinput.c index 2fec290e7..ad8592703 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -272,6 +272,10 @@ bool sway_input_configure_libinput_device(struct sway_input_device *input_device } if (ic->drag_lock != INT_MIN) { changed |= set_tap_drag_lock(device, ic->drag_lock); + } else { +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY); +#endif } if (ic->pointer_accel != FLT_MIN) { changed |= set_accel_speed(device, ic->pointer_accel); @@ -354,8 +358,12 @@ void sway_input_reset_libinput_device(struct sway_input_device *input_device) { libinput_device_config_tap_get_default_button_map(device)); changed |= set_tap_drag(device, libinput_device_config_tap_get_default_drag_enabled(device)); +#if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY + changed |= set_tap_drag_lock(device, LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY); +#else changed |= set_tap_drag_lock(device, libinput_device_config_tap_get_default_drag_lock_enabled(device)); +#endif changed |= set_accel_speed(device, libinput_device_config_accel_get_default_speed(device)); changed |= set_rotation_angle(device, diff --git a/sway/sway-input.5.scd b/sway/sway-input.5.scd index fbef2a321..18744a22a 100644 --- a/sway/sway-input.5.scd +++ b/sway/sway-input.5.scd @@ -152,8 +152,9 @@ The following commands may only be used in the configuration file. *input* drag enabled|disabled Enables or disables tap-and-drag for specified input device. -*input* drag_lock enabled|disabled - Enables or disables drag lock for specified input device. +*input* drag_lock enabled|disabled|enabled_sticky + Enables or disables drag lock for specified input device. The default is + _enabled_sticky_. *input* dwt enabled|disabled Enables or disables disable-while-typing for the specified input device. diff --git a/sway/sway-ipc.7.scd b/sway/sway-ipc.7.scd index e90abcbb8..fe6bb92e2 100644 --- a/sway/sway-ipc.7.scd +++ b/sway/sway-ipc.7.scd @@ -1174,7 +1174,8 @@ following properties will be included for devices that support them: : Whether tap-and-drag is enabled. It can be _enabled_ or _disabled_ |- tap_drag_lock : string -: Whether drag-lock is enabled. It can be _enabled_ or _disabled_ +: Whether drag-lock is enabled. It can be _enabled_, _disabled_ or + _enabled_sticky_ |- accel_speed : double : The pointer-acceleration in use From 4faf0f90988b854d354ce1c8d152884294269de0 Mon Sep 17 00:00:00 2001 From: Violet Purcell Date: Fri, 22 Nov 2024 17:31:15 -0500 Subject: [PATCH 099/108] tree/container: remove output_{enter,leave} listeners in destroy https://gitlab.freedesktop.org/wlroots/wlroots/-/commit/0d6cc471e95c1632b234fc9152659d24fe5982f1 added an assert that all signals are clear when destroying a wlr_scene_buffer, which is currently triggering due to sway not removing the output_enter and output_leave listeners on the container before calling wlr_scene_node_destroy on output_handler. Remove the listeners before wlr_scene_node_destroy is called. --- sway/tree/container.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sway/tree/container.c b/sway/tree/container.c index 62bff1ea5..a04c4c36b 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -508,6 +508,8 @@ 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); From 1d783794b508e529bdc665296d690057c93997df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gy=C3=B6rgy=20Kurucz?= Date: Wed, 20 Nov 2024 19:15:31 +0100 Subject: [PATCH 100/108] input/libinput: fix builtin device detection logic Fixes: #8468 --- sway/input/libinput.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/input/libinput.c b/sway/input/libinput.c index ad8592703..b9df8d0bf 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -418,8 +418,8 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { } const char prefix_platform[] = "platform-"; - if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) != 0) { - return false; + if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) == 0) { + return true; } const char prefix_pci[] = "pci-"; From 4eb86fce07dfe8cb9073739c84eb334cc0d262a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Sun, 8 Dec 2024 16:05:42 +0100 Subject: [PATCH 101/108] input/libinput: fix parsing input drag_lock command Regression introduced by by b160fac9f7a --- sway/commands/input/drag_lock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sway/commands/input/drag_lock.c b/sway/commands/input/drag_lock.c index 55a39a64a..915a7ada1 100644 --- a/sway/commands/input/drag_lock.c +++ b/sway/commands/input/drag_lock.c @@ -16,7 +16,7 @@ struct cmd_results *input_cmd_drag_lock(int argc, char **argv) { } #if HAVE_LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY - if (strcmp(argv[0], "enabled_sticky")) { + if (strcmp(argv[0], "enabled_sticky") == 0) { ic->drag_lock = LIBINPUT_CONFIG_DRAG_LOCK_ENABLED_STICKY; } else #endif From f293418d9d8a9e71fc52bc75a83d24ec2cc934c9 Mon Sep 17 00:00:00 2001 From: Alexander Orzechowski Date: Sun, 8 Dec 2024 11:53:11 -0500 Subject: [PATCH 102/108] swaybar: Handle opaque region properly The background color can be set individually for the different elements of the bar. If any of the backgrounds have transparency, we have to bail out from advertising an opaque surface. --- swaybar/render.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/swaybar/render.c b/swaybar/render.c index 879a4e42a..13cfdc97a 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -29,6 +29,7 @@ struct render_context { cairo_font_options_t *textaa_sharp; cairo_font_options_t *textaa_safe; uint32_t background_color; + bool has_transparency; }; static void choose_text_aa_mode(struct render_context *ctx, uint32_t fontcolor) { @@ -265,6 +266,7 @@ static uint32_t render_status_block(struct render_context *ctx, uint32_t bg_color = block->urgent ? config->colors.urgent_workspace.background : block->background; + ctx->has_transparency |= (bg_color & 0xFF) != 0xFF; if (bg_color) { render_sharp_rectangle(cairo, bg_color, x_pos, y_pos, block_width, render_height); @@ -574,6 +576,7 @@ static uint32_t render_binding_mode_indicator(struct render_context *ctx, cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); cairo_set_source_u32(cairo, config->colors.binding_mode.background); ctx->background_color = config->colors.binding_mode.background; + ctx->has_transparency |= (config->colors.binding_mode.background & 0xFF) != 0xFF; cairo_rectangle(cairo, x, 0, width, height); cairo_fill(cairo); @@ -653,6 +656,7 @@ static uint32_t render_workspace_button(struct render_context *ctx, cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); cairo_set_source_u32(cairo, box_colors.background); ctx->background_color = box_colors.background; + ctx->has_transparency |= (box_colors.background & 0xFF) != 0xFF; cairo_rectangle(cairo, *x, 0, width, height); cairo_fill(cairo); @@ -760,10 +764,12 @@ void render_frame(struct swaybar_output *output) { background_color = output->bar->config->colors.background; } - struct render_context ctx = { 0 }; - ctx.output = output; - // initial background color used for deciding the best way to antialias text - ctx.background_color = background_color; + struct render_context ctx = { + .output = output, + // initial background color used for deciding the best way to antialias text + .background_color = background_color, + .has_transparency = (background_color & 0xFF) != 0xFF, + }; cairo_surface_t *recorder = cairo_recording_surface_create( CAIRO_CONTENT_COLOR_ALPHA, NULL); @@ -834,8 +840,7 @@ void render_frame(struct swaybar_output *output) { wl_surface_damage(output->surface, 0, 0, output->width, output->height); - uint32_t bg_alpha = background_color & 0xFF; - if (bg_alpha == 0xFF) { + if (!ctx.has_transparency) { struct wl_region *region = wl_compositor_create_region(output->bar->compositor); wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); From 801bc76ce3ab2f620e30d1d0ad979caa5b03a164 Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Mon, 16 Dec 2024 21:17:05 -0800 Subject: [PATCH 103/108] Explain that the title bar always shows --- sway/sway.5.scd | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sway/sway.5.scd b/sway/sway.5.scd index 50de552f0..5e4df0dc7 100644 --- a/sway/sway.5.scd +++ b/sway/sway.5.scd @@ -106,9 +106,9 @@ They are expected to be used with *bindsym* or at runtime through *swaymsg*(1). *border* none|normal|csd|pixel [] Set border style for focused window. _normal_ includes a border of thickness _n_ and a title bar. _pixel_ is a border without title bar _n_ - pixels thick. Default is _normal_ with border thickness 2. _csd_ is short - for client-side-decorations, which allows the client to draw its own - decorations. + pixels thick. The title bar always shows in stacking or tabbed layouts. + _csd_ is short for client-side-decorations, which allows the client to draw + its own decorations. Default is _normal_ with border thickness 2. *border* toggle Cycles through the available border styles. From c55dff95bcf2875ff26a0505c6eda44947ed927d Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Jan 2025 13:21:37 +0100 Subject: [PATCH 104/108] stringop: move over has_prefix() --- common/stringop.c | 4 ++++ include/stringop.h | 2 ++ sway/input/seat.c | 4 ---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/stringop.c b/common/stringop.c index 16d04917e..dcbd16564 100644 --- a/common/stringop.c +++ b/common/stringop.c @@ -360,3 +360,7 @@ char *format_str(const char *fmt, ...) { va_end(args); return str; } + +bool has_prefix(const char *str, const char *prefix) { + return strncmp(str, prefix, strlen(prefix)) == 0; +} diff --git a/include/stringop.h b/include/stringop.h index 19a50f237..ffc355cf0 100644 --- a/include/stringop.h +++ b/include/stringop.h @@ -40,4 +40,6 @@ bool expand_path(char **path); char *vformat_str(const char *fmt, va_list args) _SWAY_ATTRIB_PRINTF(1, 0); char *format_str(const char *fmt, ...) _SWAY_ATTRIB_PRINTF(1, 2); +bool has_prefix(const char *str, const char *prefix); + #endif diff --git a/sway/input/seat.c b/sway/input/seat.c index 0dd26290e..1b63f625b 100644 --- a/sway/input/seat.c +++ b/sway/input/seat.c @@ -647,10 +647,6 @@ static void seat_reset_input_config(struct sway_seat *seat, sway_device->input_device->wlr_device, NULL); } -static bool has_prefix(const char *str, const char *prefix) { - return strncmp(str, prefix, strlen(prefix)) == 0; -} - /** * Get the name of the built-in output, if any. Returns NULL if there isn't * exactly one built-in output. From 0c60d1581f7b12ae472c786b7dfe27a1c6ec9a47 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Jan 2025 13:21:56 +0100 Subject: [PATCH 105/108] Use has_prefix() instead of strncmp() throughout This is safer than hardcoded string lengths. --- sway/commands/assign.c | 2 +- sway/commands/bar/font.c | 2 +- sway/commands/bind.c | 5 ++--- sway/commands/font.c | 4 ++-- sway/commands/gesture.c | 3 +-- sway/commands/input/events.c | 5 ++--- sway/commands/mark.c | 2 +- sway/config.c | 4 ++-- sway/config/input.c | 2 +- sway/input/cursor.c | 4 ++-- sway/input/input-manager.c | 2 +- sway/input/libinput.c | 8 ++------ sway/main.c | 4 ++-- sway/tree/container.c | 22 +++++++++++----------- sway/tree/workspace.c | 2 +- swaybar/ipc.c | 5 +++-- swaybar/render.c | 6 +++--- swaybar/tray/item.c | 5 +++-- swaybar/tray/watcher.c | 6 +++--- 19 files changed, 44 insertions(+), 49 deletions(-) diff --git a/sway/commands/assign.c b/sway/commands/assign.c index bf95cf002..5bcbb1644 100644 --- a/sway/commands/assign.c +++ b/sway/commands/assign.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_assign(int argc, char **argv) { --argc; ++argv; - if (strncmp(*argv, "→", strlen("→")) == 0) { + if (has_prefix(*argv, "→")) { if (argc < 2) { free(criteria); return cmd_results_new(CMD_INVALID, "Missing workspace"); diff --git a/sway/commands/bar/font.c b/sway/commands/bar/font.c index 0c074679a..51ca20ed5 100644 --- a/sway/commands/bar/font.c +++ b/sway/commands/bar/font.c @@ -11,7 +11,7 @@ struct cmd_results *bar_cmd_font(int argc, char **argv) { char *font = join_args(argv, argc); free(config->current_bar->font); - if (strncmp(font, "pango:", 6) == 0) { + if (has_prefix(font, "pango:")) { if (config->current_bar->pango_markup == PANGO_MARKUP_DEFAULT) { config->current_bar->pango_markup = true; } diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 268f28553..0d9347007 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -367,8 +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 (strncmp("--input-device=", argv[0], - strlen("--input-device=")) == 0) { + } else if (has_prefix("--input-device=", argv[0])) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); strip_quotes(binding->input); @@ -399,7 +398,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for group - if (strncmp(split->items[i], "Group", strlen("Group")) == 0) { + if (has_prefix(split->items[i], "Group") == 0) { if (binding->group != XKB_LAYOUT_INVALID) { free_sway_binding(binding); list_free_items_and_destroy(split); diff --git a/sway/commands/font.c b/sway/commands/font.c index 9920d03eb..842e8ae61 100644 --- a/sway/commands/font.c +++ b/sway/commands/font.c @@ -13,9 +13,9 @@ struct cmd_results *cmd_font(int argc, char **argv) { char *font = join_args(argv, argc); free(config->font); - if (strncmp(font, "pango:", 6) == 0) { + if (has_prefix(font, "pango:")) { config->pango_markup = true; - config->font = strdup(font + 6); + config->font = strdup(font + strlen("pango:")); free(font); } else { config->pango_markup = false; diff --git a/sway/commands/gesture.c b/sway/commands/gesture.c index 90a20716d..e63b7ac6e 100644 --- a/sway/commands/gesture.c +++ b/sway/commands/gesture.c @@ -121,8 +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 (strncmp("--input-device=", argv[0], - strlen("--input-device=")) == 0) { + } else if (has_prefix("--input-device=", argv[0])) { free(binding->input); binding->input = strdup(argv[0] + strlen("--input-device=")); } else { diff --git a/sway/commands/input/events.c b/sway/commands/input/events.c index 3cea026ec..4650108a1 100644 --- a/sway/commands/input/events.c +++ b/sway/commands/input/events.c @@ -86,7 +86,7 @@ static void toggle_select_send_events_for_device(struct input_config *ic, static void toggle_send_events(int argc, char **argv) { struct input_config *ic = config->handler_context.input_config; bool wildcard = strcmp(ic->identifier, "*") == 0; - const char *type = strncmp(ic->identifier, "type:", strlen("type:")) == 0 + const char *type = has_prefix(ic->identifier, "type:") ? ic->identifier + strlen("type:") : NULL; struct sway_input_device *device = NULL; wl_list_for_each(device, &server.input->devices, link) { @@ -146,8 +146,7 @@ struct cmd_results *input_cmd_events(int argc, char **argv) { toggle_send_events(argc - 1, argv + 1); - if (strcmp(ic->identifier, "*") == 0 || - strncmp(ic->identifier, "type:", strlen("type:")) == 0) { + if (strcmp(ic->identifier, "*") == 0 || has_prefix(ic->identifier, "type:")) { // Update the device input configs and then reset the type/wildcard // config send events mode so that is does not override the device // ones. The device ones will be applied when attempting to apply diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 2bfc86b30..81bf0e38b 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } bool add = false, toggle = false; - while (argc > 0 && strncmp(*argv, "--", 2) == 0) { + while (argc > 0 && has_prefix(*argv, "--") == 0) { if (strcmp(*argv, "--add") == 0) { add = true; } else if (strcmp(*argv, "--replace") == 0) { diff --git a/sway/config.c b/sway/config.c index 1090edc5c..ec7059687 100644 --- a/sway/config.c +++ b/sway/config.c @@ -925,8 +925,8 @@ char *do_var_replacement(char *str) { // Find matching variable for (i = 0; i < config->symbols->length; ++i) { struct sway_variable *var = config->symbols->items[i]; - int vnlen = strlen(var->name); - if (strncmp(find, var->name, vnlen) == 0) { + if (has_prefix(find, var->name)) { + int vnlen = strlen(var->name); int vvlen = strlen(var->value); char *newstr = malloc(strlen(str) - vnlen + vvlen + 1); if (!newstr) { diff --git a/sway/config/input.c b/sway/config/input.c index e5694effc..1fb737b65 100644 --- a/sway/config/input.c +++ b/sway/config/input.c @@ -300,7 +300,7 @@ struct input_config *store_input_config(struct input_config *ic, return NULL; } - bool type = strncmp(ic->identifier, "type:", strlen("type:")) == 0; + bool type = has_prefix(ic->identifier, "type:"); if (type && error && !validate_type_on_existing(ic, error)) { return NULL; } diff --git a/sway/input/cursor.c b/sway/input/cursor.c index bbd16717f..e8c451183 100644 --- a/sway/input/cursor.c +++ b/sway/input/cursor.c @@ -1212,7 +1212,7 @@ uint32_t get_mouse_bindsym(const char *name, char **error) { SWAY_SCROLL_UP, SWAY_SCROLL_DOWN, SWAY_SCROLL_LEFT, SWAY_SCROLL_RIGHT, BTN_SIDE, BTN_EXTRA}; return buttons[number - 1]; - } else if (strncmp(name, "BTN_", strlen("BTN_")) == 0) { + } else if (has_prefix(name, "BTN_")) { // Get event code from name int code = libevdev_event_code_from_name(EV_KEY, name); if (code == -1) { @@ -1237,7 +1237,7 @@ uint32_t get_mouse_bindcode(const char *name, char **error) { return 0; } const char *event = libevdev_event_code_get_name(EV_KEY, code); - if (!event || strncmp(event, "BTN_", strlen("BTN_")) != 0) { + if (!event || !has_prefix(event, "BTN_")) { *error = format_str("Event code %d (%s) is not a button", code, event ? event : "(null)"); return 0; diff --git a/sway/input/input-manager.c b/sway/input/input-manager.c index ddfe14689..99af4c691 100644 --- a/sway/input/input-manager.c +++ b/sway/input/input-manager.c @@ -577,7 +577,7 @@ void input_manager_configure_all_input_mappings(void) { void input_manager_apply_input_config(struct input_config *input_config) { struct sway_input_device *input_device = NULL; bool wildcard = strcmp(input_config->identifier, "*") == 0; - bool type_wildcard = strncmp(input_config->identifier, "type:", 5) == 0; + bool type_wildcard = has_prefix(input_config->identifier, "type:"); wl_list_for_each(input_device, &server.input->devices, link) { bool type_matches = type_wildcard && strcmp(input_device_get_type(input_device), input_config->identifier + 5) == 0; diff --git a/sway/input/libinput.c b/sway/input/libinput.c index b9df8d0bf..f76c6505c 100644 --- a/sway/input/libinput.c +++ b/sway/input/libinput.c @@ -417,13 +417,9 @@ bool sway_libinput_device_is_builtin(struct sway_input_device *sway_device) { return false; } - const char prefix_platform[] = "platform-"; - if (strncmp(id_path, prefix_platform, strlen(prefix_platform)) == 0) { + if (has_prefix(id_path, "platform-")) { return true; } - const char prefix_pci[] = "pci-"; - const char infix_platform[] = "-platform-"; - return (strncmp(id_path, prefix_pci, strlen(prefix_pci)) == 0) && - strstr(id_path, infix_platform); + return has_prefix(id_path, "pci-") && strstr(id_path, "-platform-"); } diff --git a/sway/main.c b/sway/main.c index 165ccc095..accffc71f 100644 --- a/sway/main.c +++ b/sway/main.c @@ -159,8 +159,8 @@ void enable_debug_flag(const char *flag) { debug.txn_wait = true; } else if (strcmp(flag, "txn-timings") == 0) { debug.txn_timings = true; - } else if (strncmp(flag, "txn-timeout=", 12) == 0) { - server.txn_timeout_ms = atoi(&flag[12]); + } 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 { diff --git a/sway/tree/container.c b/sway/tree/container.c index a04c4c36b..0288beacb 100644 --- a/sway/tree/container.c +++ b/sway/tree/container.c @@ -696,26 +696,26 @@ size_t parse_title_format(struct sway_container *container, char *buffer) { len += next - format; format = next; - if (strncmp(next, "%title", 6) == 0) { + if (has_prefix(next, "%title")) { if (container->view) { len += append_prop(buffer, view_get_title(container->view)); } else { len += container_build_representation(container->pending.layout, container->pending.children, buffer); } - format += 6; + format += strlen("%title"); } else if (container->view) { - if (strncmp(next, "%app_id", 7) == 0) { + if (has_prefix(next, "%app_id")) { len += append_prop(buffer, view_get_app_id(container->view)); - format += 7; - } else if (strncmp(next, "%class", 6) == 0) { + format += strlen("%app_id"); + } else if (has_prefix(next, "%class")) { len += append_prop(buffer, view_get_class(container->view)); - format += 6; - } else if (strncmp(next, "%instance", 9) == 0) { + format += strlen("%class"); + } else if (has_prefix(next, "%instance")) { len += append_prop(buffer, view_get_instance(container->view)); - format += 9; - } else if (strncmp(next, "%shell", 6) == 0) { + format += strlen("%instance"); + } else if (has_prefix(next, "%shell")) { len += append_prop(buffer, view_get_shell(container->view)); - format += 6; + format += strlen("%shell"); } else { lenient_strcat(buffer, "%"); ++format; @@ -778,7 +778,7 @@ size_t container_build_representation(enum sway_container_layout layout, len += strlen(identifier); lenient_strcat(buffer, identifier); } else { - len += 6; + len += strlen("(null)"); lenient_strcat(buffer, "(null)"); } } diff --git a/sway/tree/workspace.c b/sway/tree/workspace.c index f8709a4c7..a09dc3a45 100644 --- a/sway/tree/workspace.c +++ b/sway/tree/workspace.c @@ -246,7 +246,7 @@ static void workspace_name_from_binding(const struct sway_binding * binding, } // If the command is workspace number , isolate the name - if (strncmp(_target, "number ", strlen("number ")) == 0) { + if (has_prefix(_target, "number ")) { size_t length = strlen(_target) - strlen("number ") + 1; char *temp = malloc(length); strncpy(temp, _target + strlen("number "), length - 1); diff --git a/swaybar/ipc.c b/swaybar/ipc.c index 71c9a4c5e..f651f0359 100644 --- a/swaybar/ipc.c +++ b/swaybar/ipc.c @@ -15,6 +15,7 @@ #include "list.h" #include "log.h" #include "loop.h" +#include "stringop.h" #include "util.h" void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { @@ -45,8 +46,8 @@ void ipc_send_workspace_command(struct swaybar *bar, const char *ws) { char *parse_font(const char *font) { char *new_font = NULL; - if (strncmp("pango:", font, 6) == 0) { - font += 6; + if (has_prefix("pango:", font)) { + font += strlen("pango:"); } new_font = strdup(font); return new_font; diff --git a/swaybar/render.c b/swaybar/render.c index 13cfdc97a..45faefa97 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -293,11 +293,11 @@ static uint32_t render_status_block(struct render_context *ctx, } double offset = 0; - if (strncmp(block->align, "left", 4) == 0) { + if (has_prefix(block->align, "left")) { offset = x_pos; - } else if (strncmp(block->align, "right", 5) == 0) { + } else if (has_prefix(block->align, "right")) { offset = x_pos + width - text_width; - } else if (strncmp(block->align, "center", 6) == 0) { + } else if (has_prefix(block->align, "center")) { offset = x_pos + (width - text_width) / 2; } double text_y = height / 2.0 - text_height / 2.0; diff --git a/swaybar/tray/item.c b/swaybar/tray/item.c index ca6c03ad5..12929743b 100644 --- a/swaybar/tray/item.c +++ b/swaybar/tray/item.c @@ -15,6 +15,7 @@ #include "cairo_util.h" #include "list.h" #include "log.h" +#include "stringop.h" #include "wlr-layer-shell-unstable-v1-client-protocol.h" // TODO menu @@ -161,7 +162,7 @@ static int get_property_callback(sd_bus_message *msg, void *data, } if (strcmp(prop, "Status") == 0 || (sni->status && (sni->status[0] == 'N' ? - prop[0] == 'A' : strncmp(prop, "Icon", 4) == 0))) { + prop[0] == 'A' : has_prefix(prop, "Icon")))) { set_sni_dirty(sni); } cleanup: @@ -364,7 +365,7 @@ static void handle_click(struct swaybar_sni *sni, int x, int y, method = "ContextMenu"; } - if (strncmp(method, "Scroll", strlen("Scroll")) == 0) { + if (has_prefix(method, "Scroll")) { char dir = method[strlen("Scroll")]; char *orientation = (dir == 'U' || dir == 'D') ? "vertical" : "horizontal"; int sign = (dir == 'U' || dir == 'L') ? -1 : 1; diff --git a/swaybar/tray/watcher.c b/swaybar/tray/watcher.c index 54f000bd6..284964030 100644 --- a/swaybar/tray/watcher.c +++ b/swaybar/tray/watcher.c @@ -31,9 +31,9 @@ static int handle_lost_service(sd_bus_message *msg, struct swaybar_watcher *watcher = data; for (int idx = 0; idx < watcher->items->length; ++idx) { char *id = watcher->items->items[idx]; - int cmp_res = using_standard_protocol(watcher) ? - cmp_id(id, service) : strncmp(id, service, strlen(service)); - if (cmp_res == 0) { + bool cmp_res = using_standard_protocol(watcher) ? + cmp_id(id, service) == 0 : has_prefix(id, service); + if (cmp_res) { sway_log(SWAY_DEBUG, "Unregistering Status Notifier Item '%s'", id); list_del(watcher->items, idx--); sd_bus_emit_signal(watcher->bus, obj_path, watcher->interface, From a1838c5522e4c386245f17823d247dc76ad91d71 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Tue, 7 Jan 2025 18:50:11 +0100 Subject: [PATCH 106/108] Fix has_prefix() comparisons with 0 has_prefix() returns a bool, unlike strncmp() which returns an int. Fixes: 0c60d1581f7b ("Use has_prefix() instead of strncmp() throughout") Closes: https://github.com/swaywm/sway/issues/8527 --- sway/commands/bind.c | 2 +- sway/commands/mark.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sway/commands/bind.c b/sway/commands/bind.c index 0d9347007..32d4f8916 100644 --- a/sway/commands/bind.c +++ b/sway/commands/bind.c @@ -398,7 +398,7 @@ static struct cmd_results *cmd_bindsym_or_bindcode(int argc, char **argv, list_t *split = split_string(argv[0], "+"); for (int i = 0; i < split->length; ++i) { // Check for group - if (has_prefix(split->items[i], "Group") == 0) { + if (has_prefix(split->items[i], "Group")) { if (binding->group != XKB_LAYOUT_INVALID) { free_sway_binding(binding); list_free_items_and_destroy(split); diff --git a/sway/commands/mark.c b/sway/commands/mark.c index 81bf0e38b..77c8d2394 100644 --- a/sway/commands/mark.c +++ b/sway/commands/mark.c @@ -23,7 +23,7 @@ struct cmd_results *cmd_mark(int argc, char **argv) { } bool add = false, toggle = false; - while (argc > 0 && has_prefix(*argv, "--") == 0) { + while (argc > 0 && has_prefix(*argv, "--")) { if (strcmp(*argv, "--add") == 0) { add = true; } else if (strcmp(*argv, "--replace") == 0) { From c7c0a5a1b36a5ac93baacdf0098e121817d0d642 Mon Sep 17 00:00:00 2001 From: mtvare6 Date: Wed, 8 Jan 2025 09:40:09 +0530 Subject: [PATCH 107/108] config/output: skip format checks if all are supported Fixes #8496 --- sway/config/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index f8922ea52..9ae2ee6a9 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -783,7 +783,8 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) if (needed_bits < format_bits) { continue; } - if (!wlr_drm_format_set_get(primary_formats, fmts[idx])) { + // If primary_formats is NULL, all formats are supported + if (primary_formats && !wlr_drm_format_set_get(primary_formats, fmts[idx])) { // This is not a supported format for this output continue; } From a6c0441ee060c7045b24aff943c7d0bf0f47412b Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Wed, 8 Jan 2025 08:21:58 +0100 Subject: [PATCH 108/108] config/output: don't hardcode DMA-BUF in search_render_format() We could be running with a backend which doesn't support DMA-BUFs, e.g. inside a parent Wayland compositor without GPU acceleration. --- sway/config/output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sway/config/output.c b/sway/config/output.c index 9ae2ee6a9..277084762 100644 --- a/sway/config/output.c +++ b/sway/config/output.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -773,7 +774,7 @@ static bool search_render_format(struct search_context *ctx, size_t output_idx) } const struct wlr_drm_format_set *primary_formats = - wlr_output_get_primary_formats(wlr_output, WLR_BUFFER_CAP_DMABUF); + wlr_output_get_primary_formats(wlr_output, server.allocator->buffer_caps); enum render_bit_depth needed_bits = RENDER_BIT_DEPTH_8; if (cfg->config && cfg->config->render_bit_depth != RENDER_BIT_DEPTH_DEFAULT) { needed_bits = cfg->config->render_bit_depth;