From 265461162f74b0b87443cb6828153d6b49a8bf17 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 30 Aug 2025 18:56:09 +0200 Subject: [PATCH 01/14] build: rename to river-classic --- build.zig.zon | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.zig.zon b/build.zig.zon index 009f940..e0dae29 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -1,5 +1,5 @@ .{ - .name = .river, + .name = .river_classic, // While a river release is in development, this string should contain // the version in development with the "-dev" suffix. // When a release is tagged, the "-dev" suffix should be removed for the @@ -25,5 +25,5 @@ .hash = "xkbcommon-0.3.0-VDqIe3K9AQB2fG5ZeRcMC9i7kfrp5m2rWgLrmdNn9azr", }, }, - .fingerprint = 0xf5e3672b8e8d6efc, + .fingerprint = 0x3dae7aba2ea52a3b, } From 1168a3f47adc3bf673fc54d80960bcadbea99b11 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 30 Aug 2025 18:52:39 +0200 Subject: [PATCH 02/14] doc: rename to river-classic --- PACKAGING.md | 50 +++++++++++++++++++-------------------- README.md | 67 +++++++++++++++------------------------------------- 2 files changed, 44 insertions(+), 73 deletions(-) diff --git a/PACKAGING.md b/PACKAGING.md index 315fe14..66ad60f 100644 --- a/PACKAGING.md +++ b/PACKAGING.md @@ -1,39 +1,39 @@ -# Packaging river for distribution +# Packaging river-classic for distribution -First of all, I apologize for writing river in Zig. It will likely make +First of all, I apologize for writing river-classic in Zig. It will likely make your job harder until Zig is more mature/stable. I do however believe that -writing my software in Zig allows me to deliver the best quality I can -despite the drawbacks of depending on a relatively immature language/toolchain. +writing my software in Zig allows me to deliver the best quality I can despite +the drawbacks of depending on a relatively immature language/toolchain. ## Source tarballs -Source tarballs with stable checksums and git submodule sources included may -be found on the [codeberg releases page](https://codeberg.org/river/river/releases). +Source tarballs with stable checksums may be found on the +[codeberg releases page](https://codeberg.org/river/river-classic/releases). These tarballs are signed with the PGP key available on my website at . -For the 0.1.3 release for example, the tarball and signature URLs are: +For the 0.3.12 release for example, the tarball and signature URLs are: ``` -https://codeberg.org/river/river/releases/download/v0.1.3/river-0.1.3.tar.gz -https://codeberg.org/river/river/releases/download/v0.1.3/river-0.1.3.tar.gz.sig +https://codeberg.org/river/river-classic/releases/download/v0.1.3/river-classic-0.3.12.tar.gz +https://codeberg.org/river/river-classic/releases/download/v0.1.3/river-classic-0.3.12.tar.gz.sig ``` ## Zig version Until Zig 1.0, Zig releases will often have breaking changes that prevent -river from building. River tracks the latest minor version Zig release -and is only compatible with that release and any patch releases. At the time -of writing for example river is compatible with Zig 0.9.0 and 0.9.1 but -not Zig 0.8.0 or 0.10.0. +river-classic from building. river-classic tracks the latest minor version Zig +release and is only compatible with that release and any patch releases. At the +time of writing for example river is compatible with Zig 0.9.0 and 0.9.1 but not +Zig 0.8.0 or 0.10.0. ## Zig Package Manager -River uses the built-in Zig package manager for its (few) Zig dependencies. -By default, running `zig build` will fetch river's Zig dependencies from the -internet and store them in the global zig cache before building river. Since -accessing the internet is forbidden or at least frowned upon by most distro -packaging infrastructure, there are ways to fetch the Zig dependencies in a -separate step before building river: +river-classic uses the built-in Zig package manager for its (few) Zig +dependencies. By default, running `zig build` will fetch river-classic's Zig +dependencies from the internet and store them in the global zig cache before +building river-classic. Since accessing the internet is forbidden or at least +frowned upon by most distro packaging infrastructure, there are ways to fetch +the Zig dependencies in a separate step before building river-classic: 1. Fetch step with internet access: @@ -114,10 +114,10 @@ in the first place. For greatest effect, both may be used. - `-Doptimize=ReleaseSmall`: Optimize for binary size, disable all assertions and runtime safety checks. -Please use `-Doptimize=ReleaseSafe` when building river for general -use. CPU execution speed is not the performance bottleneck for river, the -GPU is. Additionally, the increased safety is more than worth the binary -size trade-off in my opinion. +Please use `-Doptimize=ReleaseSafe` when building river-classic for general use. +CPU execution speed is not the performance bottleneck for river-classic, the GPU +is. Additionally, the increased safety is more than worth the binary size +trade-off in my opinion. ## Build prefix and DESTDIR @@ -126,7 +126,7 @@ environment variable. For example ```bash DESTDIR="/foo/bar" zig build --prefix /usr install ``` -will install river to `/foo/bar/usr/bin/river`. +will install river-classic to `/foo/bar/usr/bin/river`. The Zig build system only has a single install step, there is no way to build artifacts for a given prefix and then install those artifacts to that prefix @@ -147,7 +147,7 @@ install() { ## River specific suggestions I recommend installing the example init file found at `example/init` to -`/usr/share/examples/river/init` or similar if your distribution has such +`/usr/share/examples/river-classic/init` or similar if your distribution has such a convention. ## Examples diff --git a/README.md b/README.md index 07dbee3..b5d087c 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,35 @@ -
- -
+# river-classic -## Overview +river-classic is a dynamic tiling Wayland compositor with flexible runtime +configuration. It is a fork of [river](https://codeberg.org/river/river) 0.3 +intended for users that are happy with how river 0.3 works and do not wish to +deal with the majorly breaking changes planned for the river 0.4.0 release. -River is a dynamic tiling Wayland compositor with flexible runtime -configuration. - -Check [packaging status](https://repology.org/project/river-compositor/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) -The main repository is on [codeberg](https://codeberg.org/river/river), +The main repository is on [codeberg](https://codeberg.org/river/river-classic), which is where the issue tracker may be found and where contributions are accepted. -Read-only mirrors exist on [sourcehut](https://git.sr.ht/~ifreund/river) -and [github](https://github.com/riverwm/river). - -*Note: river has not yet seen a stable 1.0 release and it will be necessary to -make significant breaking changes before 1.0 to realize my longer term plans. -That said, I do my best to avoid gratuitous breaking changes and bugs/crashes -should be rare. If you find a bug don't hesitate to -[open an issue](https://codeberg.org/river/river/issues/new/choose).* +Read-only mirrors exist on [sourcehut](https://git.sr.ht/~ifreund/river-classic) +and [github](https://github.com/riverwm/river-classic). ## Features -Currently river's window management style is quite similar to +river-classic's window management style is quite similar to [dwm](http://dwm.suckless.org), [xmonad](https://xmonad.org), and other classic dynamic tiling X11 window managers. Windows are automatically arranged in a tiled layout and shifted around as windows are opened/closed. Rather than having the tiled layout logic built into the compositor process, -river uses a [custom Wayland -protocol](https://codeberg.org/river/river/src/branch/master/protocol/river-layout-v3.xml) +river-classic uses a [custom Wayland +protocol](https://codeberg.org/river/river-classic/src/branch/master/protocol/river-layout-v3.xml) and separate "layout generator" process. A basic layout generator, `rivertile`, is provided but users are encouraged to use community-developed [layout generators](https://codeberg.org/river/wiki/src/branch/master/pages/Community-Layouts.md) or write their own. Examples in C and Python may be found -[here](https://codeberg.org/river/river/src/branch/master/contrib). +[here](https://codeberg.org/river/river-classic/src/branch/master/contrib). Tags are used to organize windows rather than workspaces. A window may be assigned to one or more tags. Likewise, one or more tags may be displayed on a @@ -46,14 +37,14 @@ monitor at a time. River is configured at runtime using the `riverctl` tool. It can define keybindings, set the active layout generator, configure input devices, and more. -On startup, river runs a user-defined init script which usually runs `riverctl` -commands to set up the user's configuration. +On startup, river-classic runs a user-defined init script which usually runs +`riverctl` commands to set up the user's configuration. ## Building -Note: If you are packaging river for distribution, see [PACKAGING.md](PACKAGING.md). +Note: If you are packaging river-classic for distribution, see [PACKAGING.md](PACKAGING.md). -To compile river first ensure that you have the following dependencies +To compile river-classic first ensure that you have the following dependencies installed. The "development" versions are required if applicable to your distribution. @@ -79,7 +70,7 @@ Run `zig build -h` to see a list of all options. River can either be run nested in an X11/Wayland session or directly from a tty using KMS/DRM. Simply run the `river` command. -On startup river will run an executable file at `$XDG_CONFIG_HOME/river/init` +On startup river-classic will run an executable file at `$XDG_CONFIG_HOME/river/init` if such an executable exists. If `$XDG_CONFIG_HOME` is not set, `~/.config/river/init` will be used instead. @@ -93,34 +84,14 @@ in the example directory. For complete documentation see the `river(1)`, `riverctl(1)`, and `rivertile(1)` man pages. -## Future Plans - -Currently details such as how tags work across multiple monitors are not -possible for users to configure. It would be possible to extend river's source -code to allow more flexibility here but this comes at the cost of complexity and -there will always be someone who prefers something slightly different. - -My long term plan to address this is to move as much window management policy as -possible out of the river compositor process and into the "layout generator" -process which will need to be renamed to "window manager." This will give users -much more power and control over river's behavior and also enable some really -cool workflows. For example, it would be possible to write a window manager in -lisp and use hot code reloading to edit its behavior it while it is running. - -This is a non-trivial architectural change and will take a while to implement. I -plan to focus on this change for the 0.4.0 release cycle. Unfortunately, it will -almost certainly break existing river configurations as well. I think the -benefits outweigh that downside though and I will do my best to offer a -reasonable upgrade path. - ## Donate -If my work on river adds value to your life and you'd like to support me +If my work on river-classic adds value to your life and you'd like to support me financially you can find donation information [here](https://isaacfreund.com/donate/). ## Licensing -River is released under the GNU General Public License v3.0 only. +river-classic is released under the GNU General Public License v3.0 only. The protocols in the `protocol` directory are released under various licenses by various parties. You should refer to the copyright block of each protocol for From 50d5108d1fa40bc8f11b3fdb1d289db70e840203 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 30 Aug 2025 19:01:10 +0200 Subject: [PATCH 03/14] ci: rename to river-classic --- .builds/alpine.yml | 8 ++++---- .builds/archlinux.yml | 8 ++++---- .builds/freebsd.yml | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.builds/alpine.yml b/.builds/alpine.yml index d92b650..30356d9 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -23,7 +23,7 @@ packages: - wget - xz sources: - - https://codeberg.org/river/river + - https://codeberg.org/river/river-classic - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - install_deps: | @@ -42,13 +42,13 @@ tasks: sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/ sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig - build: | - cd river + cd river-classic zig build --summary all - build_xwayland: | - cd river + cd river-classic zig build --summary all -Dxwayland - fmt: | - cd river + cd river-classic zig fmt --check river/ zig fmt --check riverctl/ zig fmt --check rivertile/ diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index 7bf93d6..bf7db0a 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -20,7 +20,7 @@ packages: - wget - xz sources: - - https://codeberg.org/river/river + - https://codeberg.org/river/river-classic - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - install_deps: | @@ -39,13 +39,13 @@ tasks: sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/ sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig - build: | - cd river + cd river-classic zig build --summary all - build_xwayland: | - cd river + cd river-classic zig build --summary all -Dxwayland - fmt: | - cd river + cd river-classic zig fmt --check river/ zig fmt --check riverctl/ zig fmt --check rivertile/ diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index bb5f96d..8450545 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -26,7 +26,7 @@ packages: - scdoc - wget sources: - - https://codeberg.org/river/river + - https://codeberg.org/river/river-classic - https://gitlab.freedesktop.org/wlroots/wlroots.git tasks: - install_deps: | @@ -46,13 +46,13 @@ tasks: sudo mv zig-x86_64-freebsd-0.15.1/zig /usr/bin/ sudo mv zig-x86_64-freebsd-0.15.1/lib /usr/lib/zig - build: | - cd river + cd river-classic zig build --summary all - build_xwayland: | - cd river + cd river-classic zig build --summary all -Dxwayland - fmt: | - cd river + cd river-classic zig fmt --check river/ zig fmt --check riverctl/ zig fmt --check rivertile/ From 8e4f3f68002c9a8d6ca35a0f8ed052b3afb08458 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 30 Aug 2025 18:13:56 +0200 Subject: [PATCH 04/14] build: bump version to 0.3.12 --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index e0dae29..a96a71b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,7 +5,7 @@ // When a release is tagged, the "-dev" suffix should be removed for the // commit that gets tagged. Directly after the tagged commit, the version // should be bumped and the "-dev" suffix added. - .version = "0.3.12-dev", + .version = "0.3.12", .paths = .{""}, .dependencies = .{ .pixman = .{ From e5c7d75dccbd3696a2ff5a0dd9136265b1aea831 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 30 Aug 2025 18:31:01 +0200 Subject: [PATCH 05/14] build: bump version to 0.3.13-dev --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index a96a71b..d5a0c86 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,7 +5,7 @@ // When a release is tagged, the "-dev" suffix should be removed for the // commit that gets tagged. Directly after the tagged commit, the version // should be bumped and the "-dev" suffix added. - .version = "0.3.12", + .version = "0.3.13-dev", .paths = .{""}, .dependencies = .{ .pixman = .{ From d412c9cdd17c862f4733cdb721a0073e03d84462 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 30 Aug 2025 19:23:24 +0200 Subject: [PATCH 06/14] doc: tweak README formatting --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b5d087c..83dec09 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # river-classic river-classic is a dynamic tiling Wayland compositor with flexible runtime -configuration. It is a fork of [river](https://codeberg.org/river/river) 0.3 -intended for users that are happy with how river 0.3 works and do not wish to -deal with the majorly breaking changes planned for the river 0.4.0 release. +configuration. + +It is a fork of [river](https://codeberg.org/river/river) 0.3 intended for users +that are happy with how river 0.3 works and do not wish to deal with the majorly +breaking changes planned for the river 0.4.0 release. 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 From f0c7f57d36212d2f951e4b628c6116277601c2b6 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Mon, 1 Sep 2025 09:55:49 +0200 Subject: [PATCH 07/14] LayerSurface: fix crash on bad exclusive zone River closes layer surfaces with an unreasonably large exclusive zone. However, due to unfortunate/awkward code structure, this currently may cause a use-after-free on mapping layer surface. --- river/LayerSurface.zig | 92 +++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 32 deletions(-) diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig index da05fb7..92b62cc 100644 --- a/river/LayerSurface.zig +++ b/river/LayerSurface.zig @@ -36,6 +36,8 @@ wlr_layer_surface: *wlr.LayerSurfaceV1, scene_layer_surface: *wlr.SceneLayerSurfaceV1, popup_tree: *wlr.SceneTree, +newly_mapped: bool = false, + destroy: wl.Listener(*wlr.LayerSurfaceV1) = wl.Listener(*wlr.LayerSurfaceV1).init(handleDestroy), map: wl.Listener(void) = wl.Listener(void).init(handleMap), unmap: wl.Listener(void) = wl.Listener(void).init(handleUnmap), @@ -96,19 +98,19 @@ fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), _: *wlr.LayerSurfa fn handleMap(listener: *wl.Listener(void)) void { const layer_surface: *LayerSurface = @fieldParentPtr("map", listener); - const wlr_surface = layer_surface.wlr_layer_surface; + const wlr_layer_surface = layer_surface.wlr_layer_surface; - log.debug("layer surface '{s}' mapped", .{wlr_surface.namespace}); + log.debug("layer surface '{s}' mapped", .{wlr_layer_surface.namespace}); - layer_surface.output.arrangeLayers(); - - const consider = wlr_surface.current.keyboard_interactive == .on_demand and - (wlr_surface.current.layer == .top or wlr_surface.current.layer == .overlay); - handleKeyboardInteractiveExclusive( - layer_surface.output, - if (consider) layer_surface else null, - ); + // This is a bit of a hack, but layer surfaces are not part of the + // transaction system in river 0.3 so there's not a significantly cleaner + // way I see to do this. + layer_surface.newly_mapped = true; + // Beware: it is possible for arrangeLayers() to destroy this LayerSurface! + const output = layer_surface.output; + output.arrangeLayers(); + handleKeyboardInteractiveExclusive(output); server.root.applyPending(); } @@ -117,8 +119,10 @@ fn handleUnmap(listener: *wl.Listener(void)) void { log.debug("layer surface '{s}' unmapped", .{layer_surface.wlr_layer_surface.namespace}); - layer_surface.output.arrangeLayers(); - handleKeyboardInteractiveExclusive(layer_surface.output, null); + // Beware: it is possible for arrangeLayers() to destroy this LayerSurface! + const output = layer_surface.output; + output.arrangeLayers(); + handleKeyboardInteractiveExclusive(output); server.root.applyPending(); } @@ -137,37 +141,61 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { if (wlr_layer_surface.initial_commit or @as(u32, @bitCast(wlr_layer_surface.current.committed)) != 0) { - layer_surface.output.arrangeLayers(); - handleKeyboardInteractiveExclusive(layer_surface.output, null); + // Beware: it is possible for arrangeLayers() to destroy this LayerSurface! + const output = layer_surface.output; + output.arrangeLayers(); + handleKeyboardInteractiveExclusive(output); server.root.applyPending(); } + + layer_surface.newly_mapped = false; } -/// Focus topmost keyboard-interactivity-exclusive layer surface above normal -/// content, or if none found, focus the surface given as `consider`. -/// Requires a call to Root.applyPending() -fn handleKeyboardInteractiveExclusive(output: *Output, consider: ?*LayerSurface) void { +fn handleKeyboardInteractiveExclusive(output: *Output) void { if (server.lock_manager.state != .unlocked) return; // Find the topmost layer surface (if any) in the top or overlay layers which // requests exclusive keyboard interactivity. - const to_focus = outer: for ([_]zwlr.LayerShellV1.Layer{ .overlay, .top }) |layer| { - const tree = output.layerSurfaceTree(layer); - // Iterate in reverse to match rendering order. - var it = tree.children.iterator(.reverse); - while (it.next()) |node| { - assert(node.type == .tree); - if (@as(?*SceneNodeData, @ptrCast(@alignCast(node.data)))) |node_data| { - const layer_surface = node_data.data.layer_surface; - const wlr_layer_surface = layer_surface.wlr_layer_surface; - if (wlr_layer_surface.surface.mapped and - wlr_layer_surface.current.keyboard_interactive == .exclusive) - { - break :outer layer_surface; + // If none is found, check for a newly mapped surface in the same layers with + // on demand keyboard interactivity. + const to_focus = blk: { + for ([_]zwlr.LayerShellV1.Layer{ .overlay, .top }) |layer| { + const tree = output.layerSurfaceTree(layer); + // Iterate in reverse to match rendering order. + var it = tree.children.iterator(.reverse); + while (it.next()) |node| { + assert(node.type == .tree); + if (@as(?*SceneNodeData, @ptrCast(@alignCast(node.data)))) |node_data| { + const layer_surface = node_data.data.layer_surface; + const wlr_layer_surface = layer_surface.wlr_layer_surface; + if (wlr_layer_surface.surface.mapped and + wlr_layer_surface.current.keyboard_interactive == .exclusive) + { + break :blk layer_surface; + } } } } - } else consider; + for ([_]zwlr.LayerShellV1.Layer{ .overlay, .top }) |layer| { + const tree = output.layerSurfaceTree(layer); + // Iterate in reverse to match rendering order. + var it = tree.children.iterator(.reverse); + while (it.next()) |node| { + assert(node.type == .tree); + if (@as(?*SceneNodeData, @ptrCast(@alignCast(node.data)))) |node_data| { + const layer_surface = node_data.data.layer_surface; + const wlr_layer_surface = layer_surface.wlr_layer_surface; + if (layer_surface.newly_mapped and + wlr_layer_surface.surface.mapped and + wlr_layer_surface.current.keyboard_interactive == .on_demand) + { + break :blk layer_surface; + } + } + } + } + break :blk null; + }; if (to_focus) |s| { assert(s.wlr_layer_surface.current.keyboard_interactive != .none); From c18ed4aae18d6f7a20ab67c3a62abb31dc5de1c8 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 2 Sep 2025 09:46:46 +0200 Subject: [PATCH 08/14] river: fix logging to non-pipe Since the Zig 0.15 upgrade, if stderr is redirected to a file, river writes every log message to the beginning of the file, overwriting the previous message. This commit fixes that and also switches to the nicer new std.debug API for stderr logging. --- river/main.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/river/main.zig b/river/main.zig index 27a0bd5..d2f9481 100644 --- a/river/main.zig +++ b/river/main.zig @@ -188,8 +188,11 @@ pub fn logFn( const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; - var stderr = fs.File.stderr().writer(&.{}); - stderr.interface.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {}; + var buffer: [256]u8 = undefined; + const stderr = std.debug.lockStderrWriter(&buffer); + defer std.debug.unlockStderrWriter(); + + stderr.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {}; } /// See wlroots_log_wrapper.c From 170cac310d23c97795504c072a6f73c2ca93489a Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Tue, 2 Sep 2025 10:52:57 +0200 Subject: [PATCH 09/14] linux-dmabuf: disable fine-grained feedback again Despite the better debouncing in wlroots 0.19, this is still causing problems. In particular, certain clients (mostly Xwayland games it seems) have a bad interaction with this additional feedback, possibly related to the cursor buffer. The reported symptoms are significant framerate drops and stuttering on pointer input. Probably there's a wlroots bug here at fault but I don't have the resources to fix it myself currently and would rather not force river users to suffer. Setting WLR_SCENE_DISABLE_DIRECT_SCANOUT=1 isn't sufficient to solve these problems either, as wlr_scene still sends enough extra feedback to trigger the problems. Closes: https://codeberg.org/river/river/issues/1277 --- river/Root.zig | 2 -- 1 file changed, 2 deletions(-) diff --git a/river/Root.zig b/river/Root.zig index 8d9218f..7aec4cb 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -124,8 +124,6 @@ pub fn init(root: *Root) !void { const gamma_control_manager = try wlr.GammaControlManagerV1.create(server.wl_server); scene.setGammaControlManagerV1(gamma_control_manager); - if (server.linux_dmabuf) |linux_dmabuf| scene.setLinuxDmabufV1(linux_dmabuf); - const interactive_content = try scene.tree.createSceneTree(); const drag_icons = try scene.tree.createSceneTree(); const hidden_tree = try scene.tree.createSceneTree(); From 633f4e27330f17f5784eb964752bc6b19dddbe08 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 3 Oct 2025 11:28:01 +0200 Subject: [PATCH 10/14] river: remove dead code --- river/command/view_operations.zig | 6 ------ 1 file changed, 6 deletions(-) diff --git a/river/command/view_operations.zig b/river/command/view_operations.zig index 4f1319b..82e2516 100644 --- a/river/command/view_operations.zig +++ b/river/command/view_operations.zig @@ -79,12 +79,6 @@ fn getTarget(seat: *Seat, direction_str: []const u8, target_mode: TargetMode) !? if (target_mode == .skip_float and seat.focused.view.pending.float) return null; const output = seat.focused_output orelse return null; - // If no currently view is focused, focus the first in the stack. - if (seat.focused != .view) { - var it = output.pending.wm_stack.iterator(.forward); - return it.next(); - } - // Logical direction, based on the view stack. if (std.meta.stringToEnum(Direction, direction_str)) |direction| { switch (direction) { From 165871e0fcb82f4db77ffa7d99ed7d641e677336 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 3 Oct 2025 11:28:19 +0200 Subject: [PATCH 11/14] command: fix possible crash on focus If the currently focused view has been moved to a different output but the transaction has not yet been completed, we can hit unreachable code in this function. thread 1073 panic: reached unreachable code /home/pkaplan/Software/river/river/command/view_operations.zig:100:21: 0x11752d6 in getTarget (river) unreachable; ^ /home/pkaplan/Software/river/river/command/view_operations.zig:46:22: 0x1146f36 in focusView (river) if (try getTarget( ^ /home/pkaplan/Software/river/river/command.zig:144:16: 0x11124e3 in run (river) try impl_fn(seat, args, out); ^ /home/pkaplan/Software/river/river/Seat.zig:447:16: 0x10eb00f in runCommand (river) command.run(seat, args, &out) catch |err| { ^ /home/pkaplan/Software/river/river/Seat.zig:423:24: 0x1110350 in handleMapping (river) seat.runCommand(mapping.command_args); ^ /home/pkaplan/Software/river/river/Keyboard.zig:213:47: 0x10e83b8 in wrapper (river) if (keyboard.device.seat.handleMapping(keycode, modifiers, released, xkb_state)) { ^ ???:?:?: 0x7d8f73aa051d in ??? (libwayland-server.so.0) Unwind information for `libwayland-server.so.0:0x7d8f73aa051d` was not available, trace may be incomplete ???:?:?: 0x7d8f739fecc7 in ??? (libwlroots-0.18.so) ???:?:?: 0x7d8f739d0dbb in ??? (libwlroots-0.18.so) ???:?:?: 0x7d8f73aa2111 in ??? (libwayland-server.so.0) ???:?:?: 0x7d8f73aa41f6 in ??? (libwayland-server.so.0) /home/pkaplan/Software/river/river/main.zig:139:25: 0x10715dd in main (river) server.wl_server.run(); ^ /usr/lib/zig/std/start.zig:524:37: 0x107083e in main (river) const result = root.main() catch |err| { ^ ???:?:?: 0x7d8f736b06b4 in ??? (libc.so.6) ???:?:?: 0x7d8f736b0768 in ??? (libc.so.6) ???:?:?: 0x1070354 in ??? (???) --- river/command/view_operations.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/river/command/view_operations.zig b/river/command/view_operations.zig index 82e2516..3f8a214 100644 --- a/river/command/view_operations.zig +++ b/river/command/view_operations.zig @@ -78,6 +78,7 @@ fn getTarget(seat: *Seat, direction_str: []const u8, target_mode: TargetMode) !? if (seat.focused.view.pending.fullscreen) return null; if (target_mode == .skip_float and seat.focused.view.pending.float) return null; const output = seat.focused_output orelse return null; + if (seat.focused.view.pending.output != output) return null; // Logical direction, based on the view stack. if (std.meta.stringToEnum(Direction, direction_str)) |direction| { From 3ef7b6911274791c177a7078adec80fbb6313cb9 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 3 Oct 2025 11:35:50 +0200 Subject: [PATCH 12/14] build: bump version to 0.3.13 --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index d5a0c86..58909d7 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,7 +5,7 @@ // When a release is tagged, the "-dev" suffix should be removed for the // commit that gets tagged. Directly after the tagged commit, the version // should be bumped and the "-dev" suffix added. - .version = "0.3.13-dev", + .version = "0.3.13", .paths = .{""}, .dependencies = .{ .pixman = .{ From 9c42ed788a27f9e1bf4346d233eac49f6c55d6d1 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Fri, 3 Oct 2025 11:36:58 +0200 Subject: [PATCH 13/14] build: bump version to 0.3.14-dev --- build.zig.zon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.zig.zon b/build.zig.zon index 58909d7..3b41557 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,7 +5,7 @@ // When a release is tagged, the "-dev" suffix should be removed for the // commit that gets tagged. Directly after the tagged commit, the version // should be bumped and the "-dev" suffix added. - .version = "0.3.13", + .version = "0.3.14-dev", .paths = .{""}, .dependencies = .{ .pixman = .{ From be722b2a3f5841b00d730a0bb6223684651f8edf Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Wed, 10 Dec 2025 20:24:22 +0100 Subject: [PATCH 14/14] docs: fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83dec09..0fd029d 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Tags are used to organize windows rather than workspaces. A window may be assigned to one or more tags. Likewise, one or more tags may be displayed on a monitor at a time. -River is configured at runtime using the `riverctl` tool. It can define +river-classic is configured at runtime using the `riverctl` tool. It can define keybindings, set the active layout generator, configure input devices, and more. On startup, river-classic runs a user-defined init script which usually runs `riverctl` commands to set up the user's configuration.