From 2e09b66963805caccfe8534d69f2f35dd4a4c3f7 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 24 Jun 2024 19:29:19 +0200 Subject: [PATCH 1/6] Output: flag gamma as dirty on enable We can end up with stale gamma settings if we don't re-check the current gamma settings for the output on enable. --- river/Output.zig | 1 + river/Root.zig | 1 + 2 files changed, 2 insertions(+) diff --git a/river/Output.zig b/river/Output.zig index b235c1e..518bfc6 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -474,6 +474,7 @@ pub fn applyState(output: *Output, state: *wlr.Output.State) error{CommitFailed} fn handleEnableDisable(output: *Output) void { output.updateLockRenderStateOnEnableDisable(); + output.gamma_dirty = true; if (output.wlr_output.enabled) { // Add the output to root.active_outputs and the output layout if it has not diff --git a/river/Root.zig b/river/Root.zig index 1dc0cba..454c77e 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -894,6 +894,7 @@ fn handlePowerManagerSetMode( } output.updateLockRenderStateOnEnableDisable(); + output.gamma_dirty = true; } fn handleSetGamma( From ae7f4b8fcbb323e68b07eebf8dc8cca38b582940 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 25 Jun 2024 12:24:25 +0200 Subject: [PATCH 2/6] Xwayland: fix unsound cast The X11 protocol uses 16 bit integers for width/height but we use 32 bit integers everywhere else in river. Make sure that values outside the range of a 16 bit integer don't cause river to crash with an assertion failure. I think that coordinates outside the range of a 16 bit integer could theoretically be reasonable with tiled high resolution displays in the future. I doubt they ever get used in practice today but at the same time we can't allow an errant layout generator to crash river. --- river/XwaylandView.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig index 80cecc6..db91199 100644 --- a/river/XwaylandView.zig +++ b/river/XwaylandView.zig @@ -106,10 +106,10 @@ pub fn configure(xwayland_view: XwaylandView) bool { } xwayland_view.xwayland_surface.configure( - @intCast(inflight.box.x + output_box.x), - @intCast(inflight.box.y + output_box.y), - @intCast(inflight.box.width), - @intCast(inflight.box.height), + math.lossyCast(i16, inflight.box.x + output_box.x), + math.lossyCast(i16, inflight.box.y + output_box.y), + math.lossyCast(u16, inflight.box.width), + math.lossyCast(u16, inflight.box.height), ); xwayland_view.setActivated(inflight.focus != 0); From 0997fde28e1aad90a983d28061deed9fdcb972f3 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 30 Jun 2024 12:12:00 +0200 Subject: [PATCH 3/6] docs: tweak repology link wording in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9ed713..a3f7577 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ River is a dynamic tiling Wayland compositor with flexible runtime configuration. -Install from your [package manager](https://repology.org/project/river/versions) — +Check [packaging status](https://repology.org/project/river/versions) — Join us at [#river](https://web.libera.chat/?channels=#river) on irc.libera.chat — Read our man pages, [wiki](https://codeberg.org/river/wiki), and [Code of Conduct](CODE_OF_CONDUCT.md) From a80e0f7322b1f9f8b091a327aaea366b615042c8 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 1 Jul 2024 12:27:16 +0200 Subject: [PATCH 4/6] Output: fix Wayland backend support The wlroots Wayland backend does not support gamma LUT application and will currently fail to render anything if river commits a gamma LUT. To fix this, test the state when applying a gamma LUT and fall back to a state with no gamma LUT set if that fails. This problem was revealed by 2e09b66 which flags gamma as dirty on all outputs when they are enabled. --- build.zig.zon | 4 ++-- river/Output.zig | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index a1c2a92..28e5165 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -12,8 +12,8 @@ .hash = "1220687c8c47a48ba285d26a05600f8700d37fc637e223ced3aa8324f3650bf52242", }, .@"zig-wlroots" = .{ - .url = "https://codeberg.org/ifreund/zig-wlroots/archive/v0.17.1.tar.gz", - .hash = "1220c65ab884c236cc950b564c70f6cd04046d86485ee76e0cde886cef7438021b4f", + .url = "https://codeberg.org/ifreund/zig-wlroots/archive/084736cd92364b5fa7d8161611d085ce272fa707.tar.gz", + .hash = "12208383c1cf42e9b932b90f68cd4f378582cf966355a6377fd8f913852e7bc2d7c6", }, .@"zig-xkbcommon" = .{ .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.2.0.tar.gz", diff --git a/river/Output.zig b/river/Output.zig index 518bfc6..c9d8add 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -540,18 +540,23 @@ fn renderAndCommit(output: *Output, scene_output: *wlr.SceneOutput) !void { var state = wlr.Output.State.init(); defer state.finish(); - if (server.root.gamma_control_manager.getControl(output.wlr_output)) |control| { - log.info("applying gamma settings from client", .{}); - if (!control.apply(&state)) return error.OutOfMemory; - } else { - log.info("clearing gamma settings from client", .{}); + const control = server.root.gamma_control_manager.getControl(output.wlr_output); + if (!wlr.GammaControlV1.apply(control, &state)) return error.OutOfMemory; + + if (!output.wlr_output.testState(&state)) { + wlr.GammaControlV1.sendFailedAndDestroy(control); state.clearGammaLut(); + // If the backend does not support gamma LUTs it will reject any + // state with the gamma LUT committed bit set even if the state + // has a null LUT. The wayland backend for example has this behavior. + state.committed.gamma_lut = false; } if (!scene_output.buildState(&state, null)) return error.CommitFailed; if (!output.wlr_output.commitState(&state)) return error.CommitFailed; + // TODO(wlroots) remove this rotate() call when updating to wlroots 0.18 scene_output.damage_ring.rotate(); output.gamma_dirty = false; } else { From ec16f1c3753d51feb7dfc6d406dd508f4513a106 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 1 Jul 2024 12:52:31 +0200 Subject: [PATCH 5/6] XdgPopup: send configure after initial commit Currently we send the first configure for xdg popups before the popup has made its initial commit. This is incorrect according to the protocol and may confuse clients. --- river/XdgPopup.zig | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/river/XdgPopup.zig b/river/XdgPopup.zig index 5198e9b..fd82fbe 100644 --- a/river/XdgPopup.zig +++ b/river/XdgPopup.zig @@ -35,6 +35,7 @@ root: *wlr.SceneTree, tree: *wlr.SceneTree, destroy: wl.Listener(void) = wl.Listener(void).init(handleDestroy), +commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit), new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup), reposition: wl.Listener(void) = wl.Listener(void).init(handleReposition), @@ -54,22 +55,30 @@ pub fn create( }; wlr_xdg_popup.base.events.destroy.add(&xdg_popup.destroy); + wlr_xdg_popup.base.surface.events.commit.add(&xdg_popup.commit); wlr_xdg_popup.base.events.new_popup.add(&xdg_popup.new_popup); wlr_xdg_popup.events.reposition.add(&xdg_popup.reposition); - - handleReposition(&xdg_popup.reposition); } fn handleDestroy(listener: *wl.Listener(void)) void { const xdg_popup: *XdgPopup = @fieldParentPtr("destroy", listener); xdg_popup.destroy.link.remove(); + xdg_popup.commit.link.remove(); xdg_popup.new_popup.link.remove(); xdg_popup.reposition.link.remove(); util.gpa.destroy(xdg_popup); } +fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { + const xdg_popup: *XdgPopup = @fieldParentPtr("commit", listener); + + if (xdg_popup.wlr_xdg_popup.base.initial_commit) { + handleReposition(&xdg_popup.reposition); + } +} + fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void { const xdg_popup: *XdgPopup = @fieldParentPtr("new_popup", listener); From 4232d6b99f2eeede9f318aa6043128530597cc4e Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 2 Jul 2024 15:03:22 +0200 Subject: [PATCH 6/6] layer-shell: fix on_demand keyboard focus Currently keyboard focus is stolen from layer surfaces with on_demand keyboard interactivity any time Root.applyPending() is called. This commit fixes the behavior to only steal focus when explicitly focusing a different window/layer surface. --- river/Seat.zig | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/river/Seat.zig b/river/Seat.zig index ff77cce..4ac83f5 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -165,14 +165,21 @@ pub fn focus(seat: *Seat, _target: ?*View) void { // Views may not receive focus while locked. if (server.lock_manager.state != .unlocked) return; - // While a layer surface is exclusively focused, views may not receive focus + // A layer surface with exclusive focus will prevent any view from gaining + // focus if it is on the top or overlay layer. Otherwise, only steal focus + // from a focused layer surface if there is an explicit target view. if (seat.focused == .layer) { const wlr_layer_surface = seat.focused.layer.wlr_layer_surface; assert(wlr_layer_surface.surface.mapped); - if (wlr_layer_surface.current.keyboard_interactive == .exclusive and - (wlr_layer_surface.current.layer == .top or wlr_layer_surface.current.layer == .overlay)) - { - return; + switch (wlr_layer_surface.current.keyboard_interactive) { + .none => {}, + .exclusive => switch (wlr_layer_surface.current.layer) { + .top, .overlay => return, + .bottom, .background => if (target == null) return, + _ => {}, + }, + .on_demand => if (target == null) return, + _ => {}, } }