git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commit789ced2c058b765f5c15604435318b6c68731fda
parent93aa7c4dd2
authorIsaac Freund <[email protected]>
date2021-10-11 12:44
build: update to zig version 0.9.0

 .github/workflows/ci_build.yml         | 71 +++++++---------------------------
 build.zig                              | 54 +++++++++++++++++---------
 deps/zig-pixman                        |  2 +-
 deps/zig-wayland                       |  2 +-
 deps/zig-wlroots                       |  2 +-
 deps/zig-xkbcommon                     |  2 +-
 river/Config.zig                       |  6 +--
 river/Control.zig                      |  2 +-
 river/Cursor.zig                       |  6 +--
 river/Decoration.zig                   |  4 +-
 river/DragIcon.zig                     |  5 +--
 river/InputConfig.zig                  | 22 +++++------
 river/InputManager.zig                 | 17 ++++----
 river/Keyboard.zig                     | 16 ++++----
 river/LayerSurface.zig                 |  4 +-
 river/Layout.zig                       |  8 ++--
 river/LayoutDemand.zig                 |  2 +-
 river/LayoutManager.zig                | 20 ++++++----
 river/Mapping.zig                      |  2 +-
 river/Output.zig                       | 13 +++----
 river/OutputStatus.zig                 |  6 +--
 river/PointerConstraint.zig            |  3 +-
 river/Root.zig                         | 18 ++++-----
 river/Seat.zig                         |  6 +--
 river/SeatStatus.zig                   |  4 +-
 river/Server.zig                       |  5 ++-
 river/StatusManager.zig                | 20 +++++-----
 river/Subsurface.zig                   | 10 ++---
 river/View.zig                         | 11 +++---
 river/VoidView.zig                     | 18 ++++-----
 river/XdgPopup.zig                     | 10 ++---
 river/XdgToplevel.zig                  | 13 +++----
 river/XwaylandUnmanaged.zig            |  8 ++--
 river/XwaylandView.zig                 | 10 ++---
 river/command.zig                      |  4 +-
 river/command/attach_mode.zig          |  6 +--
 river/command/close.zig                |  6 +--
 river/command/config.zig               | 36 ++++++++---------
 river/command/declare_mode.zig         | 10 ++---
 river/command/enter_mode.zig           |  2 +-
 river/command/exit.zig                 |  6 +--
 river/command/filter.zig               | 29 +++++++-------
 river/command/focus_follows_cursor.zig |  6 +--
 river/command/focus_view.zig           |  4 +-
 river/command/input.zig                | 18 +++++----
 river/command/layout.zig               | 15 ++++---
 river/command/map.zig                  | 30 +++++++-------
 river/command/move.zig                 | 12 +++---
 river/command/output.zig               |  8 ++--
 river/command/set_repeat.zig           |  4 +-
 river/command/spawn.zig                | 10 ++---
 river/command/swap.zig                 |  4 +-
 river/command/tags.zig                 | 22 ++++++-----
 river/command/toggle_float.zig         |  4 +-
 river/command/toggle_fullscreen.zig    |  4 +-
 river/command/xcursor_theme.zig        |  4 +-
 river/command/zoom.zig                 |  4 +-
 river/main.zig                         | 54 +++++++++++---------------
 river/render.zig                       | 12 +++---
 river/view_stack.zig                   |  4 +-
 riverctl/main.zig                      |  8 ++--
 rivertile/main.zig                     |  4 +-
 62 files changed, 349 insertions(+), 383 deletions(-)

diff --git a/.github/workflows/ci_build.yml b/.github/workflows/ci_build.yml
index a24888a..efa0e3d 100644
--- a/.github/workflows/ci_build.yml
+++ b/.github/workflows/ci_build.yml
@@ -10,7 +10,7 @@ name: voidlinux glibc
 
 jobs:
   default_build:
-    name: default build
+    name: build/test/fmt
     runs-on: ubuntu-latest
     container: voidlinux/voidlinux:latest
 
@@ -25,7 +25,7 @@ jobs:
             libffi-devel wayland-protocols xcb-util-errors-devel xcb-util-wm-devel \
             xcb-util-renderutil-devel libxcb-devel xcb-util-cursor-devel xcb-util-devel \
             xcb-util-image-devel xcb-util-keysyms-devel xcb-util-xrm-devel \
-            xorg-server-xwayland pkg-config meson git gcc zig pkgconf scdoc
+            xorg-server-xwayland pkg-config meson git gcc pkgconf scdoc wget tar xz
 
           git clone https://gitlab.freedesktop.org/wayland/wayland.git
           cd wayland
@@ -40,6 +40,13 @@ jobs:
           meson build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
             -Dwerror=false -Db_ndebug=false --prefix /usr
           ninja -C build install
+
+          wget https://ziglang.org/download/0.9.0/zig-linux-x86_64-0.9.0.tar.xz
+          tar -xvf zig-linux-x86_64-0.9.0.tar.xz
+          mv zig-linux-x86_64-0.9.0/zig /usr/bin/
+          mv zig-linux-x86_64-0.9.0/lib /usr/lib/zig
+          zig env
+
       - name: checkout
         uses: actions/checkout@v2
         with:
@@ -53,65 +60,13 @@ jobs:
         run: |
           zig build test
 
-  xwayland_build:
-    name: xwayland build
-    runs-on: ubuntu-latest
-    container: voidlinux/voidlinux:latest
-
-    steps:
-      - name: install deps
-        run: |
-          xbps-install -Sy xbps
-          xbps-install -uy
-          # wayland/wlroots dependencies as well as we build from source
-          xbps-install -y MesaLib-devel libseat-devel eudev-libudev-devel libdrm-devel \
-            libinput-devel libxkbcommon-devel pixman-devel expat-devel libxml2-devel \
-            libffi-devel wayland-protocols xcb-util-errors-devel xcb-util-wm-devel \
-            xcb-util-renderutil-devel libxcb-devel xcb-util-cursor-devel xcb-util-devel \
-            xcb-util-image-devel xcb-util-keysyms-devel xcb-util-xrm-devel \
-            xorg-server-xwayland pkg-config meson git gcc zig pkgconf scdoc
-
-          git clone https://gitlab.freedesktop.org/wayland/wayland.git
-          cd wayland
-          git checkout 1.20.0
-          meson build -Ddocumentation=false -Dtests=false --prefix /usr
-          ninja -C build install
-          cd ..
-
-          git clone https://gitlab.freedesktop.org/wlroots/wlroots.git
-          cd wlroots
-          git checkout 0.15.0
-          meson build --auto-features=enabled -Drenderers=gles2 -Dexamples=false \
-            -Dwerror=false -Db_ndebug=false --prefix /usr
-          ninja -C build install
-      - name: checkout
-        uses: actions/checkout@v2
-        with:
-          submodules: true
-
-      - name: build
+      - name: xwayland build
         run: |
-          zig build -Dxwayland=true
+          zig build -Dxwayland
 
-      - name: test
+      - name: xwayland test
         run: |
-          zig build -Dxwayland=true test
-
-  zig_fmt:
-    name: zig fmt
-    runs-on: ubuntu-latest
-    container: voidlinux/voidlinux:latest
-
-    steps:
-      - name: install deps
-        run: |
-          xbps-install -Sy xbps
-          xbps-install -uy zig git
-
-      - name: checkout
-        uses: actions/checkout@v2
-        with:
-          submodules: true
+          zig build -Dxwayland test
 
       - name: fmt
         run: |
diff --git a/build.zig b/build.zig
index 2324760..600d00f 100644
--- a/build.zig
+++ b/build.zig
@@ -16,12 +16,6 @@ pub fn build(b: *zbs.Builder) !void {
     const target = b.standardTargetOptions(.{});
     const mode = b.standardReleaseOptions();
 
-    const xwayland = b.option(
-        bool,
-        "xwayland",
-        "Set to true to enable xwayland support",
-    ) orelse false;
-
     const man_pages = b.option(
         bool,
         "man-pages",
@@ -52,6 +46,12 @@ pub fn build(b: *zbs.Builder) !void {
         "Set to true to install fish completion for riverctl. Defaults to true.",
     ) orelse true;
 
+    const xwayland = b.option(
+        bool,
+        "xwayland",
+        "Set to true to enable xwayland support",
+    ) orelse false;
+
     const full_version = blk: {
         if (mem.endsWith(u8, version, "-dev")) {
             var ret: u8 = undefined;
@@ -70,6 +70,10 @@ pub fn build(b: *zbs.Builder) !void {
         }
     };
 
+    const options = b.addOptions();
+    options.addOption(bool, "xwayland", xwayland);
+    options.addOption([]const u8, "version", full_version);
+
     const scanner = ScanProtocolsStep.create(b);
     scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
     scanner.addSystemProtocol("unstable/pointer-gestures/pointer-gestures-unstable-v1.xml");
@@ -85,8 +89,7 @@ pub fn build(b: *zbs.Builder) !void {
         const river = b.addExecutable("river", "river/main.zig");
         river.setTarget(target);
         river.setBuildMode(mode);
-        river.addBuildOption(bool, "xwayland", xwayland);
-        river.addBuildOption([:0]const u8, "version", full_version);
+        river.addOptions("build_options", options);
 
         addServerDeps(river, scanner);
 
@@ -97,11 +100,14 @@ pub fn build(b: *zbs.Builder) !void {
         const riverctl = b.addExecutable("riverctl", "riverctl/main.zig");
         riverctl.setTarget(target);
         riverctl.setBuildMode(mode);
-        riverctl.addBuildOption([:0]const u8, "version", full_version);
+        riverctl.addOptions("build_options", options);
 
         riverctl.step.dependOn(&scanner.step);
-        riverctl.addPackage(scanner.getPkg());
         riverctl.addPackagePath("flags", "common/flags.zig");
+        riverctl.addPackage(.{
+            .name = "wayland",
+            .path = .{ .generated = &scanner.result },
+        });
         riverctl.linkLibC();
         riverctl.linkSystemLibrary("wayland-client");
 
@@ -114,11 +120,14 @@ pub fn build(b: *zbs.Builder) !void {
         const rivertile = b.addExecutable("rivertile", "rivertile/main.zig");
         rivertile.setTarget(target);
         rivertile.setBuildMode(mode);
-        rivertile.addBuildOption([:0]const u8, "version", full_version);
+        rivertile.addOptions("build_options", options);
 
         rivertile.step.dependOn(&scanner.step);
-        rivertile.addPackage(scanner.getPkg());
         rivertile.addPackagePath("flags", "common/flags.zig");
+        rivertile.addPackage(.{
+            .name = "wayland",
+            .path = .{ .generated = &scanner.result },
+        });
         rivertile.linkLibC();
         rivertile.linkSystemLibrary("wayland-client");
 
@@ -169,7 +178,7 @@ pub fn build(b: *zbs.Builder) !void {
         const river_test = b.addTest("river/test_main.zig");
         river_test.setTarget(target);
         river_test.setBuildMode(mode);
-        river_test.addBuildOption(bool, "xwayland", xwayland);
+        river_test.addOptions("build_options", options);
 
         addServerDeps(river_test, scanner);
 
@@ -179,12 +188,21 @@ pub fn build(b: *zbs.Builder) !void {
 }
 
 fn addServerDeps(exe: *zbs.LibExeObjStep, scanner: *ScanProtocolsStep) void {
-    const wayland = scanner.getPkg();
-    const xkbcommon = zbs.Pkg{ .name = "xkbcommon", .path = "deps/zig-xkbcommon/src/xkbcommon.zig" };
-    const pixman = zbs.Pkg{ .name = "pixman", .path = "deps/zig-pixman/pixman.zig" };
+    const wayland = zbs.Pkg{
+        .name = "wayland",
+        .path = .{ .generated = &scanner.result },
+    };
+    const xkbcommon = zbs.Pkg{
+        .name = "xkbcommon",
+        .path = .{ .path = "deps/zig-xkbcommon/src/xkbcommon.zig" },
+    };
+    const pixman = zbs.Pkg{
+        .name = "pixman",
+        .path = .{ .path = "deps/zig-pixman/pixman.zig" },
+    };
     const wlroots = zbs.Pkg{
         .name = "wlroots",
-        .path = "deps/zig-wlroots/src/wlroots.zig",
+        .path = .{ .path = "deps/zig-wlroots/src/wlroots.zig" },
         .dependencies = &[_]zbs.Pkg{ wayland, xkbcommon, pixman },
     };
 
@@ -232,7 +250,7 @@ const ScdocStep = struct {
     fn init(builder: *zbs.Builder) ScdocStep {
         return ScdocStep{
             .builder = builder,
-            .step = zbs.Step.init(.Custom, "Generate man pages", builder.allocator, make),
+            .step = zbs.Step.init(.custom, "Generate man pages", builder.allocator, make),
         };
     }
 
diff --git a/deps/zig-pixman b/deps/zig-pixman
index 88520f2..d381567 160000
--- a/deps/zig-pixman
+++ b/deps/zig-pixman
@@ -1 +1 @@
-Subproject commit 88520f264cd65153fe77ad2e5a17d22eee5e1af4
+Subproject commit d381567de9b6e40dd7f4c6e0b5740f94ebd8c9d7
diff --git a/deps/zig-wayland b/deps/zig-wayland
index 38b5de8..80166ba 160000
--- a/deps/zig-wayland
+++ b/deps/zig-wayland
@@ -1 +1 @@
-Subproject commit 38b5de89b3e3de670bb2496de56e5173d626fad5
+Subproject commit 80166ba1d5b4f94c7972d6922587ba769be93f8a
diff --git a/deps/zig-wlroots b/deps/zig-wlroots
index 0bb3207..417c103 160000
--- a/deps/zig-wlroots
+++ b/deps/zig-wlroots
@@ -1 +1 @@
-Subproject commit 0bb32072a435561cd2310901f8ee0c8e41b150db
+Subproject commit 417c10339e253a978135f48d5573932a340a6992
diff --git a/deps/zig-xkbcommon b/deps/zig-xkbcommon
index 10cdca6..60dde05 160000
--- a/deps/zig-xkbcommon
+++ b/deps/zig-xkbcommon
@@ -1 +1 @@
-Subproject commit 10cdca686dacb91d6cfe37f1839b6aedfb53cc4c
+Subproject commit 60dde0523907df672ec9ca8b9efb25a1c7ca4d82
diff --git a/river/Config.zig b/river/Config.zig
index 56d2bbe..00c259d 100644
--- a/river/Config.zig
+++ b/river/Config.zig
@@ -94,16 +94,16 @@ pub fn init() !Self {
     errdefer self.deinit();
 
     // Start with two empty modes, "normal" and "locked"
-    try self.modes.ensureCapacity(2);
+    try self.modes.ensureTotalCapacity(2);
     {
         // Normal mode, id 0
-        const owned_slice = try std.mem.dupe(util.gpa, u8, "normal");
+        const owned_slice = try util.gpa.dupe(u8, "normal");
         try self.mode_to_id.putNoClobber(owned_slice, 0);
         self.modes.appendAssumeCapacity(Mode.init());
     }
     {
         // Locked mode, id 1
-        const owned_slice = try std.mem.dupe(util.gpa, u8, "locked");
+        const owned_slice = try util.gpa.dupe(u8, "locked");
         try self.mode_to_id.putNoClobber(owned_slice, 1);
         self.modes.appendAssumeCapacity(Mode.init());
     }
diff --git a/river/Control.zig b/river/Control.zig
index 0865fac..48dd4f9 100644
--- a/river/Control.zig
+++ b/river/Control.zig
@@ -48,7 +48,7 @@ pub fn init(self: *Self) !void {
     server.wl_server.addDestroyListener(&self.server_destroy);
 }
 
-fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server) void {
+fn handleServerDestroy(listener: *wl.Listener(*wl.Server), _: *wl.Server) void {
     const self = @fieldParentPtr(Self, "server_destroy", listener);
     self.global.destroy();
     self.args_map.deinit();
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 30bd826..95bf7d6 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -178,7 +178,7 @@ pub fn setTheme(self: *Self, theme: ?[*:0]const u8, _size: ?u32) !void {
     // If this cursor belongs to the default seat, set the xcursor environment
     // variables and the xwayland cursor theme.
     if (self.seat == server.input_manager.defaultSeat()) {
-        const size_str = try std.fmt.allocPrint0(util.gpa, "{}", .{size});
+        const size_str = try std.fmt.allocPrintZ(util.gpa, "{}", .{size});
         defer util.gpa.free(size_str);
         if (c.setenv("XCURSOR_SIZE", size_str, 1) < 0) return error.OutOfMemory;
         if (theme) |t| if (c.setenv("XCURSOR_THEME", t, 1) < 0) return error.OutOfMemory;
@@ -400,7 +400,7 @@ fn handlePointerMapping(self: *Self, event: *wlr.Pointer.event.Button, view: *Vi
 /// Frame events are sent after regular pointer events to group multiple
 /// events together. For instance, two axis events may happen at the same
 /// time, in which case a frame event won't be sent in between.
-fn handleFrame(listener: *wl.Listener(*wlr.Cursor), wlr_cursor: *wlr.Cursor) void {
+fn handleFrame(listener: *wl.Listener(*wlr.Cursor), _: *wlr.Cursor) void {
     const self = @fieldParentPtr(Self, "frame", listener);
     self.seat.wlr_seat.pointerNotifyFrame();
 }
@@ -867,7 +867,7 @@ pub fn updateState(self: *Self) void {
     if (self.shouldPassthrough()) {
         self.mode = .passthrough;
         var now: os.timespec = undefined;
-        os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
+        os.clock_gettime(os.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
         const msec = @intCast(u32, now.tv_sec * std.time.ms_per_s +
             @divFloor(now.tv_nsec, std.time.ns_per_ms));
         self.passthrough(msec);
diff --git a/river/Decoration.zig b/river/Decoration.zig
index 45263ac..d18c09a 100644
--- a/river/Decoration.zig
+++ b/river/Decoration.zig
@@ -46,7 +46,7 @@ pub fn init(self: *Self, xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1)
 
 fn handleDestroy(
     listener: *wl.Listener(*wlr.XdgToplevelDecorationV1),
-    xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
+    _: *wlr.XdgToplevelDecorationV1,
 ) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
     self.destroy.link.remove();
@@ -59,7 +59,7 @@ fn handleDestroy(
 
 fn handleRequestMode(
     listener: *wl.Listener(*wlr.XdgToplevelDecorationV1),
-    xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
+    _: *wlr.XdgToplevelDecorationV1,
 ) void {
     const self = @fieldParentPtr(Self, "request_mode", listener);
 
diff --git a/river/DragIcon.zig b/river/DragIcon.zig
index bb05161..5d88c2e 100644
--- a/river/DragIcon.zig
+++ b/river/DragIcon.zig
@@ -74,16 +74,15 @@ fn handleMap(listener: *wl.Listener(*wlr.Drag.Icon), wlr_drag_icon: *wlr.Drag.Ic
     while (it) |node| : (it = node.next) node.data.damage.addWhole();
 }
 
-fn handleUnmap(listener: *wl.Listener(*wlr.Drag.Icon), wlr_drag_icon: *wlr.Drag.Icon) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.Drag.Icon), _: *wlr.Drag.Icon) void {
     const drag_icon = @fieldParentPtr(DragIcon, "unmap", listener);
 
     drag_icon.commit.link.remove();
     var it = server.root.outputs.first;
     while (it) |node| : (it = node.next) node.data.damage.addWhole();
 }
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
-    const drag_icon = @fieldParentPtr(DragIcon, "commit", listener);
 
+fn handleCommit(_: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     var it = server.root.outputs.first;
     while (it) |node| : (it = node.next) node.data.damage.addWhole();
 }
diff --git a/river/InputConfig.zig b/river/InputConfig.zig
index 2f9b7cc..ca124f4 100644
--- a/river/InputConfig.zig
+++ b/river/InputConfig.zig
@@ -59,7 +59,7 @@ pub const AccelProfile = enum {
     adaptive,
 
     pub fn apply(accel_profile: AccelProfile, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_accel_profile, switch (accel_profile) {
+        const want = @as(c_uint, switch (accel_profile) {
             .none => c.LIBINPUT_CONFIG_ACCEL_PROFILE_NONE,
             .flat => c.LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT,
             .adaptive => c.LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE,
@@ -78,13 +78,13 @@ pub const ClickMethod = enum {
     clickfinger,
 
     pub fn apply(click_method: ClickMethod, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_click_method, switch (click_method) {
+        const want = @as(c_uint, switch (click_method) {
             .none => c.LIBINPUT_CONFIG_CLICK_METHOD_NONE,
             .@"button-areas" => c.LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS,
             .clickfinger => c.LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER,
         });
         const supports = c.libinput_device_config_click_get_methods(device);
-        if (supports & @intCast(u32, @enumToInt(want)) == 0) return;
+        if (supports & want == 0) return;
         _ = c.libinput_device_config_click_set_method(device, want);
     }
 };
@@ -94,7 +94,7 @@ pub const DragState = enum {
     enabled,
 
     pub fn apply(drag_state: DragState, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_drag_state, switch (drag_state) {
+        const want = @as(c_uint, switch (drag_state) {
             .disabled => c.LIBINPUT_CONFIG_DRAG_DISABLED,
             .enabled => c.LIBINPUT_CONFIG_DRAG_ENABLED,
         });
@@ -111,7 +111,7 @@ pub const DragLock = enum {
     enabled,
 
     pub fn apply(drag_lock: DragLock, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_drag_lock_state, switch (drag_lock) {
+        const want = @as(c_uint, switch (drag_lock) {
             .disabled => c.LIBINPUT_CONFIG_DRAG_LOCK_DISABLED,
             .enabled => c.LIBINPUT_CONFIG_DRAG_LOCK_ENABLED,
         });
@@ -128,7 +128,7 @@ pub const DwtState = enum {
     enabled,
 
     pub fn apply(dwt_state: DwtState, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_dwt_state, switch (dwt_state) {
+        const want = @as(c_uint, switch (dwt_state) {
             .disabled => c.LIBINPUT_CONFIG_DWT_DISABLED,
             .enabled => c.LIBINPUT_CONFIG_DWT_ENABLED,
         });
@@ -145,7 +145,7 @@ pub const MiddleEmulation = enum {
     enabled,
 
     pub fn apply(middle_emulation: MiddleEmulation, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_middle_emulation_state, switch (middle_emulation) {
+        const want = @as(c_uint, switch (middle_emulation) {
             .disabled => c.LIBINPUT_CONFIG_MIDDLE_EMULATION_DISABLED,
             .enabled => c.LIBINPUT_CONFIG_MIDDLE_EMULATION_ENABLED,
         });
@@ -196,7 +196,7 @@ pub const TapState = enum {
     enabled,
 
     pub fn apply(tap_state: TapState, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_tap_state, switch (tap_state) {
+        const want = @as(c_uint, switch (tap_state) {
             .disabled => c.LIBINPUT_CONFIG_TAP_DISABLED,
             .enabled => c.LIBINPUT_CONFIG_TAP_ENABLED,
         });
@@ -213,7 +213,7 @@ pub const TapButtonMap = enum {
     @"left-right-middle",
 
     pub fn apply(tap_button_map: TapButtonMap, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_tap_button_map, switch (tap_button_map) {
+        const want = @as(c_uint, switch (tap_button_map) {
             .@"left-right-middle" => c.LIBINPUT_CONFIG_TAP_MAP_LRM,
             .@"left-middle-right" => c.LIBINPUT_CONFIG_TAP_MAP_LMR,
         });
@@ -243,14 +243,14 @@ pub const ScrollMethod = enum {
     button,
 
     pub fn apply(scroll_method: ScrollMethod, device: *c.libinput_device) void {
-        const want = @intToEnum(c.libinput_config_scroll_method, switch (scroll_method) {
+        const want = @as(c_uint, switch (scroll_method) {
             .none => c.LIBINPUT_CONFIG_SCROLL_NO_SCROLL,
             .@"two-finger" => c.LIBINPUT_CONFIG_SCROLL_2FG,
             .edge => c.LIBINPUT_CONFIG_SCROLL_EDGE,
             .button => c.LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN,
         });
         const supports = c.libinput_device_config_scroll_get_methods(device);
-        if (supports & @intCast(u32, @enumToInt(want)) == 0) return;
+        if (supports & want == 0) return;
         _ = c.libinput_device_config_scroll_set_method(device, want);
     }
 };
diff --git a/river/InputManager.zig b/river/InputManager.zig
index d6a554f..7061ac4 100644
--- a/river/InputManager.zig
+++ b/river/InputManager.zig
@@ -71,7 +71,7 @@ pub const InputDevice = struct {
         self.destroy.link.remove();
     }
 
-    fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDevice) void {
+    fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) void {
         const self = @fieldParentPtr(InputDevice, "destroy", listener);
         log.debug("removed input device: {s}", .{self.identifier});
         self.deinit();
@@ -172,7 +172,7 @@ pub fn updateCursorState(self: Self) void {
 
 fn handleInhibitActivate(
     listener: *wl.Listener(*wlr.InputInhibitManager),
-    input_inhibit_manager: *wlr.InputInhibitManager,
+    _: *wlr.InputInhibitManager,
 ) void {
     const self = @fieldParentPtr(Self, "inhibit_activate", listener);
 
@@ -193,7 +193,7 @@ fn handleInhibitActivate(
 
 fn handleInhibitDeactivate(
     listener: *wl.Listener(*wlr.InputInhibitManager),
-    input_inhibit_manager: *wlr.InputInhibitManager,
+    _: *wlr.InputInhibitManager,
 ) void {
     const self = @fieldParentPtr(Self, "inhibit_deactivate", listener);
 
@@ -241,9 +241,13 @@ fn handleNewInput(listener: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDe
     }
 }
 
-fn handleNewPointerConstraint(listener: *wl.Listener(*wlr.PointerConstraintV1), constraint: *wlr.PointerConstraintV1) void {
+fn handleNewPointerConstraint(
+    _: *wl.Listener(*wlr.PointerConstraintV1),
+    constraint: *wlr.PointerConstraintV1,
+) void {
     const pointer_constraint = util.gpa.create(PointerConstraint) catch {
-        log.crit("out of memory", .{});
+        constraint.resource.getClient().postNoMemory();
+        log.err("out of memory", .{});
         return;
     };
 
@@ -269,10 +273,9 @@ fn handleNewVirtualPointer(
 }
 
 fn handleNewVirtualKeyboard(
-    listener: *wl.Listener(*wlr.VirtualKeyboardV1),
+    _: *wl.Listener(*wlr.VirtualKeyboardV1),
     virtual_keyboard: *wlr.VirtualKeyboardV1,
 ) void {
-    const self = @fieldParentPtr(Self, "new_virtual_keyboard", listener);
     const seat = @intToPtr(*Seat, virtual_keyboard.seat.data);
     seat.addDevice(&virtual_keyboard.input_device);
 }
diff --git a/river/Keyboard.zig b/river/Keyboard.zig
index ba82180..6a7cc82 100644
--- a/river/Keyboard.zig
+++ b/river/Keyboard.zig
@@ -95,7 +95,7 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa
     // First check translated keysyms as xkb reports them
     for (wlr_keyboard.xkb_state.?.keyGetSyms(keycode)) |sym| {
         // Handle builtin mapping only when keys are pressed
-        if (!released and self.handleBuiltinMapping(sym)) {
+        if (!released and handleBuiltinMapping(sym)) {
             handled = true;
             break;
         } else if (self.seat.handleMapping(sym, modifiers, released)) {
@@ -110,7 +110,7 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa
         const layout_index = wlr_keyboard.xkb_state.?.keyGetLayout(keycode);
         for (wlr_keyboard.keymap.?.keyGetSymsByLevel(keycode, layout_index, 0)) |sym| {
             // Handle builtin mapping only when keys are pressed
-            if (!released and self.handleBuiltinMapping(sym)) {
+            if (!released and handleBuiltinMapping(sym)) {
                 handled = true;
                 break;
             } else if (self.seat.handleMapping(sym, modifiers, released)) {
@@ -129,14 +129,14 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa
 }
 
 /// Simply pass modifiers along to the client
-fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void {
+fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void {
     const self = @fieldParentPtr(Self, "modifiers", listener);
 
     self.seat.wlr_seat.setKeyboard(self.input_device);
     self.seat.wlr_seat.keyboardNotifyModifiers(&self.input_device.device.keyboard.modifiers);
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
     const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
 
@@ -147,15 +147,15 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keybo
 
 /// Handle any builtin, harcoded compsitor mappings such as VT switching.
 /// Returns true if the keysym was handled.
-fn handleBuiltinMapping(self: Self, keysym: xkb.Keysym) bool {
+fn handleBuiltinMapping(keysym: xkb.Keysym) bool {
     switch (@enumToInt(keysym)) {
-        @enumToInt(xkb.Keysym.XF86Switch_VT_1)...@enumToInt(xkb.Keysym.XF86Switch_VT_12) => {
+        xkb.Keysym.XF86Switch_VT_1...xkb.Keysym.XF86Switch_VT_12 => {
             log.debug("switch VT keysym received", .{});
             if (server.backend.isMulti()) {
                 if (server.backend.getSession()) |session| {
-                    const vt = @enumToInt(keysym) - @enumToInt(xkb.Keysym.XF86Switch_VT_1) + 1;
+                    const vt = @enumToInt(keysym) - xkb.Keysym.XF86Switch_VT_1 + 1;
                     const log_server = std.log.scoped(.server);
-                    log_server.notice("switching to VT {}", .{vt});
+                    log_server.info("switching to VT {}", .{vt});
                     session.changeVt(vt) catch log_server.err("changing VT failed", .{});
                 }
             }
diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig
index 1a94f08..d2de457 100644
--- a/river/LayerSurface.zig
+++ b/river/LayerSurface.zig
@@ -105,7 +105,7 @@ fn handleMap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wl
     self.output.arrangeLayers(.mapped);
 }
 
-fn handleUnmap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.LayerSurfaceV1), _: *wlr.LayerSurfaceV1) void {
     const self = @fieldParentPtr(Self, "unmap", listener);
 
     log.debug("layer surface '{s}' unmapped", .{self.wlr_layer_surface.namespace});
@@ -137,7 +137,7 @@ fn handleUnmap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *
     server.root.startTransaction();
 }
 
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), wlr_surface: *wlr.Surface) void {
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     const self = @fieldParentPtr(Self, "commit", listener);
 
     assert(self.wlr_layer_surface.output != null);
diff --git a/river/Layout.zig b/river/Layout.zig
index 04e8ef6..f42671e 100644
--- a/river/Layout.zig
+++ b/river/Layout.zig
@@ -45,7 +45,7 @@ pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namesp
 
     if (namespaceInUse(namespace, output, client)) {
         layout.sendNamespaceInUse();
-        layout.setHandler(?*c_void, handleRequestInert, null, null);
+        layout.setHandler(?*anyopaque, handleRequestInert, null, null);
         return;
     }
 
@@ -92,7 +92,7 @@ fn namespaceInUse(namespace: []const u8, output: *Output, client: *wl.Client) bo
 
 /// This exists to handle layouts that have been rendered inert (due to the
 /// namespace already being in use) until the client destroys them.
-fn handleRequestInert(layout: *river.LayoutV3, request: river.LayoutV3.Request, _: ?*c_void) void {
+fn handleRequestInert(layout: *river.LayoutV3, request: river.LayoutV3.Request, _: ?*anyopaque) void {
     if (request == .destroy) layout.destroy();
 }
 
@@ -159,7 +159,7 @@ fn handleRequest(layout: *river.LayoutV3, request: river.LayoutV3.Request, self:
     }
 }
 
-fn handleDestroy(layout: *river.LayoutV3, self: *Self) void {
+fn handleDestroy(_: *river.LayoutV3, self: *Self) void {
     self.destroy();
 }
 
@@ -183,7 +183,7 @@ pub fn destroy(self: *Self) void {
         }
     }
 
-    self.layout.setHandler(?*c_void, handleRequestInert, null, null);
+    self.layout.setHandler(?*anyopaque, handleRequestInert, null, null);
 
     util.gpa.free(self.namespace);
     util.gpa.destroy(node);
diff --git a/river/LayoutDemand.zig b/river/LayoutDemand.zig
index eda4ba7..1b3ffd1 100644
--- a/river/LayoutDemand.zig
+++ b/river/LayoutDemand.zig
@@ -70,7 +70,7 @@ pub fn deinit(self: *const Self) void {
 /// Destroy the LayoutDemand on timeout.
 /// All further responses to the event will simply be ignored.
 fn handleTimeout(layout: *Layout) callconv(.C) c_int {
-    log.notice(
+    log.info(
         "layout demand for layout '{s}' on output '{s}' timed out",
         .{ layout.namespace, layout.output.wlr_output.name },
     );
diff --git a/river/LayoutManager.zig b/river/LayoutManager.zig
index b77ace6..b46f3f2 100644
--- a/river/LayoutManager.zig
+++ b/river/LayoutManager.zig
@@ -38,27 +38,31 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer
 
 pub fn init(self: *Self) !void {
     self.* = .{
-        .global = try wl.Global.create(server.wl_server, river.LayoutManagerV3, 1, *Self, self, bind),
+        .global = try wl.Global.create(server.wl_server, river.LayoutManagerV3, 1, ?*anyopaque, null, bind),
     };
 
     server.wl_server.addDestroyListener(&self.server_destroy);
 }
 
-fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server) void {
+fn handleServerDestroy(listener: *wl.Listener(*wl.Server), _: *wl.Server) void {
     const self = @fieldParentPtr(Self, "server_destroy", listener);
     self.global.destroy();
 }
 
-fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
-    const layout_manager = river.LayoutManagerV3.create(client, 1, id) catch {
+fn bind(client: *wl.Client, _: ?*anyopaque, version: u32, id: u32) callconv(.C) void {
+    const layout_manager = river.LayoutManagerV3.create(client, version, id) catch {
         client.postNoMemory();
-        log.crit("out of memory", .{});
+        log.err("out of memory", .{});
         return;
     };
-    layout_manager.setHandler(*Self, handleRequest, null, self);
+    layout_manager.setHandler(?*anyopaque, handleRequest, null, null);
 }
 
-fn handleRequest(layout_manager: *river.LayoutManagerV3, request: river.LayoutManagerV3.Request, self: *Self) void {
+fn handleRequest(
+    layout_manager: *river.LayoutManagerV3,
+    request: river.LayoutManagerV3.Request,
+    _: ?*anyopaque,
+) void {
     switch (request) {
         .destroy => layout_manager.destroy(),
 
@@ -77,7 +81,7 @@ fn handleRequest(layout_manager: *river.LayoutManagerV3, request: river.LayoutMa
                 mem.span(req.namespace),
             ) catch {
                 layout_manager.getClient().postNoMemory();
-                log.crit("out of memory", .{});
+                log.err("out of memory", .{});
                 return;
             };
         },
diff --git a/river/Mapping.zig b/river/Mapping.zig
index bb284e1..2aded2f 100644
--- a/river/Mapping.zig
+++ b/river/Mapping.zig
@@ -44,7 +44,7 @@ pub fn init(
     errdefer util.gpa.free(owned_args);
     for (command_args) |arg, i| {
         errdefer for (owned_args[0..i]) |a| util.gpa.free(a);
-        owned_args[i] = try std.mem.dupeZ(util.gpa, u8, arg);
+        owned_args[i] = try util.gpa.dupeZ(u8, arg);
     }
     return Self{
         .keysym = keysym,
diff --git a/river/Output.zig b/river/Output.zig
index 8a5459a..c0bcae0 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -236,7 +236,7 @@ pub fn arrangeLayers(self: *Self, target: ArrangeLayersTarget) void {
 
     // Arrange all layer surfaces with exclusive zones, applying them to the
     // usable box along the way.
-    for (layers) |layer| self.arrangeLayer(self.getLayer(layer).*, full_box, &usable_box, true, target);
+    for (layers) |layer| arrangeLayer(self.getLayer(layer).*, full_box, &usable_box, true, target);
 
     // If the the usable_box has changed, we need to rearrange the output
     if (target == .mapped and !std.meta.eql(self.usable_box, usable_box)) {
@@ -245,7 +245,7 @@ pub fn arrangeLayers(self: *Self, target: ArrangeLayersTarget) void {
     }
 
     // Arrange the layers without exclusive zones
-    for (layers) |layer| self.arrangeLayer(self.getLayer(layer).*, full_box, &usable_box, false, target);
+    for (layers) |layer| arrangeLayer(self.getLayer(layer).*, full_box, &usable_box, false, target);
 
     if (target == .unmapped) return;
 
@@ -286,7 +286,6 @@ pub fn arrangeLayers(self: *Self, target: ArrangeLayersTarget) void {
 
 /// Arrange the layer surfaces of a given layer
 fn arrangeLayer(
-    self: *Self,
     layer: std.TailQueue(LayerSurface),
     full_box: Box,
     usable_box: *Box,
@@ -424,7 +423,7 @@ fn arrangeLayer(
     }
 }
 
-fn handleDamageDestroy(listener: *wl.Listener(*wlr.OutputDamage), wlr_output: *wlr.OutputDamage) void {
+fn handleDamageDestroy(listener: *wl.Listener(*wlr.OutputDamage), _: *wlr.OutputDamage) void {
     const self = @fieldParentPtr(Self, "damage_destroy", listener);
     // The wlr.OutputDamage is only destroyed by wlroots when the output is
     // destroyed and is never destroyed manually by river.
@@ -433,7 +432,7 @@ fn handleDamageDestroy(listener: *wl.Listener(*wlr.OutputDamage), wlr_output: *w
     self.frame.link = .{ .prev = &self.frame.link, .next = &self.frame.link };
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
 
     std.log.scoped(.server).debug("output '{s}' destroyed", .{self.wlr_output.name});
@@ -476,14 +475,14 @@ fn handleEnable(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) vo
     if (wlr_output.enabled) server.root.addOutput(self);
 }
 
-fn handleFrame(listener: *wl.Listener(*wlr.OutputDamage), wlr_output: *wlr.OutputDamage) void {
+fn handleFrame(listener: *wl.Listener(*wlr.OutputDamage), _: *wlr.OutputDamage) void {
     // This function is called every time an output is ready to display a frame,
     // generally at the output's refresh rate (e.g. 60Hz).
     const self = @fieldParentPtr(Self, "frame", listener);
     render.renderOutput(self);
 }
 
-fn handleMode(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+fn handleMode(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void {
     const self = @fieldParentPtr(Self, "mode", listener);
     self.arrangeLayers(.mapped);
     self.arrangeViews();
diff --git a/river/OutputStatus.zig b/river/OutputStatus.zig
index 0120ebd..c42b432 100644
--- a/river/OutputStatus.zig
+++ b/river/OutputStatus.zig
@@ -57,13 +57,13 @@ pub fn destroy(self: *Self) void {
     util.gpa.destroy(node);
 }
 
-fn handleRequest(output_status: *zriver.OutputStatusV1, request: zriver.OutputStatusV1.Request, self: *Self) void {
+fn handleRequest(output_status: *zriver.OutputStatusV1, request: zriver.OutputStatusV1.Request, _: *Self) void {
     switch (request) {
         .destroy => output_status.destroy(),
     }
 }
 
-fn handleDestroy(output_status: *zriver.OutputStatusV1, self: *Self) void {
+fn handleDestroy(_: *zriver.OutputStatusV1, self: *Self) void {
     self.destroy();
 }
 
@@ -77,7 +77,7 @@ pub fn sendViewTags(self: Self) void {
         if (node.view.surface == null) continue;
         view_tags.append(node.view.current.tags) catch {
             self.output_status.postNoMemory();
-            log.crit("out of memory", .{});
+            log.err("out of memory", .{});
             return;
         };
     }
diff --git a/river/PointerConstraint.zig b/river/PointerConstraint.zig
index 5e632e6..21640d7 100644
--- a/river/PointerConstraint.zig
+++ b/river/PointerConstraint.zig
@@ -23,7 +23,6 @@ const wlr = @import("wlroots");
 const wl = @import("wayland").server.wl;
 const pixman = @import("pixman");
 
-const log = @import("log.zig");
 const server = &@import("main.zig").server;
 const util = @import("util.zig");
 
@@ -103,7 +102,7 @@ fn constrainToRegion(self: *Self) void {
     }
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), constraint: *wlr.PointerConstraintV1) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.PointerConstraintV1) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
 
     self.destroy.link.remove();
diff --git a/river/Root.zig b/river/Root.zig
index 020a73a..15be993 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -408,14 +408,11 @@ fn commitTransaction(self: *Self) void {
 }
 
 /// Send the new output configuration to all wlr-output-manager clients
-fn handleLayoutChange(
-    listener: *wl.Listener(*wlr.OutputLayout),
-    output_layout: *wlr.OutputLayout,
-) void {
+fn handleLayoutChange(listener: *wl.Listener(*wlr.OutputLayout), _: *wlr.OutputLayout) void {
     const self = @fieldParentPtr(Self, "layout_change", listener);
 
     const config = self.outputConfigFromCurrent() catch {
-        std.log.scoped(.output_manager).crit("out of memory", .{});
+        std.log.scoped(.output_manager).err("out of memory", .{});
         return;
     };
     self.output_manager.setConfiguration(config);
@@ -436,17 +433,16 @@ fn handleManagerApply(
 
     // Send the config that was actually applied
     const applied_config = self.outputConfigFromCurrent() catch {
-        std.log.scoped(.output_manager).crit("out of memory", .{});
+        std.log.scoped(.output_manager).err("out of memory", .{});
         return;
     };
     self.output_manager.setConfiguration(applied_config);
 }
 
 fn handleManagerTest(
-    listener: *wl.Listener(*wlr.OutputConfigurationV1),
+    _: *wl.Listener(*wlr.OutputConfigurationV1),
     config: *wlr.OutputConfigurationV1,
 ) void {
-    const self = @fieldParentPtr(Self, "manager_test", listener);
     defer config.destroy();
 
     if (testOutputConfig(config, true)) {
@@ -553,6 +549,8 @@ fn applyHeadToOutput(head: *wlr.OutputConfigurationV1.Head, wlr_output: *wlr.Out
 
 /// Create the config describing the current configuration
 fn outputConfigFromCurrent(self: *Self) !*wlr.OutputConfigurationV1 {
+    // TODO there no real reason this needs to allocate memory every time it is called.
+    // consider improving this wlroots api or reimplementing in zig-wlroots/river.
     const config = try wlr.OutputConfigurationV1.create();
     // this destroys all associated config heads as well
     errdefer config.destroy();
@@ -575,11 +573,9 @@ fn createHead(self: *Self, output: *Output, config: *wlr.OutputConfigurationV1)
 }
 
 fn handlePowerManagerSetMode(
-    listener: *wl.Listener(*wlr.OutputPowerManagerV1.event.SetMode),
+    _: *wl.Listener(*wlr.OutputPowerManagerV1.event.SetMode),
     event: *wlr.OutputPowerManagerV1.event.SetMode,
 ) void {
-    const self = @fieldParentPtr(Self, "power_manager_set_mode", listener);
-
     const enable = event.mode == .on;
 
     const log_text = if (enable) "Enabling" else "Disabling";
diff --git a/river/Seat.zig b/river/Seat.zig
index d7d8d97..b029c72 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -173,7 +173,7 @@ pub fn focus(self: *Self, _target: ?*View) void {
         var it = self.focus_stack.first;
         while (it) |node| : (it = node.next) {
             if (node.view == view) {
-                const new_focus_node = self.focus_stack.remove(node);
+                self.focus_stack.remove(node);
                 self.focus_stack.push(node);
                 break;
             }
@@ -474,7 +474,7 @@ fn handleStartDrag(listener: *wl.Listener(*wlr.Drag), wlr_drag: *wlr.Drag) void
 
     if (wlr_drag.icon) |wlr_drag_icon| {
         const node = util.gpa.create(std.SinglyLinkedList(DragIcon).Node) catch {
-            log.crit("out of memory", .{});
+            log.err("out of memory", .{});
             wlr_drag.seat_client.client.postNoMemory();
             return;
         };
@@ -484,7 +484,7 @@ fn handleStartDrag(listener: *wl.Listener(*wlr.Drag), wlr_drag: *wlr.Drag) void
     self.cursor.mode = .passthrough;
 }
 
-fn handlePointerDragDestroy(listener: *wl.Listener(*wlr.Drag), wlr_drag: *wlr.Drag) void {
+fn handlePointerDragDestroy(listener: *wl.Listener(*wlr.Drag), _: *wlr.Drag) void {
     const self = @fieldParentPtr(Self, "pointer_drag_destroy", listener);
     self.pointer_drag_destroy.link.remove();
 
diff --git a/river/SeatStatus.zig b/river/SeatStatus.zig
index c83a9db..3d4db80 100644
--- a/river/SeatStatus.zig
+++ b/river/SeatStatus.zig
@@ -41,13 +41,13 @@ pub fn init(self: *Self, seat: *Seat, seat_status: *zriver.SeatStatusV1) void {
     self.sendFocusedView();
 }
 
-fn handleRequest(seat_status: *zriver.SeatStatusV1, request: zriver.SeatStatusV1.Request, self: *Self) void {
+fn handleRequest(seat_status: *zriver.SeatStatusV1, request: zriver.SeatStatusV1.Request, _: *Self) void {
     switch (request) {
         .destroy => seat_status.destroy(),
     }
 }
 
-fn handleDestroy(seat_status: *zriver.SeatStatusV1, self: *Self) void {
+fn handleDestroy(_: *zriver.SeatStatusV1, self: *Self) void {
     const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
     self.seat.status_trackers.remove(node);
     util.gpa.destroy(node);
diff --git a/river/Server.zig b/river/Server.zig
index bff5d58..5429667 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -77,9 +77,9 @@ pub fn init(self: *Self) !void {
     errdefer self.wl_server.destroy();
 
     const loop = self.wl_server.getEventLoop();
-    self.sigint_source = try loop.addSignal(*wl.Server, std.os.SIGINT, terminate, self.wl_server);
+    self.sigint_source = try loop.addSignal(*wl.Server, std.os.SIG.INT, terminate, self.wl_server);
     errdefer self.sigint_source.remove();
-    self.sigterm_source = try loop.addSignal(*wl.Server, std.os.SIGTERM, terminate, self.wl_server);
+    self.sigterm_source = try loop.addSignal(*wl.Server, std.os.SIG.TERM, terminate, self.wl_server);
     errdefer self.sigterm_source.remove();
 
     // This frees itself when the wl.Server is destroyed
@@ -170,6 +170,7 @@ pub fn start(self: Self) !void {
 
 /// Handle SIGINT and SIGTERM by gracefully stopping the server
 fn terminate(signal: c_int, wl_server: *wl.Server) callconv(.C) c_int {
+    _ = signal;
     wl_server.terminate();
     return 0;
 }
diff --git a/river/StatusManager.zig b/river/StatusManager.zig
index f8ea03e..ebb7076 100644
--- a/river/StatusManager.zig
+++ b/river/StatusManager.zig
@@ -40,30 +40,30 @@ server_destroy: wl.Listener(*wl.Server) = wl.Listener(*wl.Server).init(handleSer
 
 pub fn init(self: *Self) !void {
     self.* = .{
-        .global = try wl.Global.create(server.wl_server, zriver.StatusManagerV1, 2, *Self, self, bind),
+        .global = try wl.Global.create(server.wl_server, zriver.StatusManagerV1, 2, ?*anyopaque, null, bind),
     };
 
     server.wl_server.addDestroyListener(&self.server_destroy);
 }
 
-fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server) void {
+fn handleServerDestroy(listener: *wl.Listener(*wl.Server), _: *wl.Server) void {
     const self = @fieldParentPtr(Self, "server_destroy", listener);
     self.global.destroy();
 }
 
-fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
+fn bind(client: *wl.Client, _: ?*anyopaque, version: u32, id: u32) callconv(.C) void {
     const status_manager = zriver.StatusManagerV1.create(client, version, id) catch {
         client.postNoMemory();
-        log.crit("out of memory", .{});
+        log.err("out of memory", .{});
         return;
     };
-    status_manager.setHandler(*Self, handleRequest, null, self);
+    status_manager.setHandler(?*anyopaque, handleRequest, null, null);
 }
 
 fn handleRequest(
     status_manager: *zriver.StatusManagerV1,
     request: zriver.StatusManagerV1.Request,
-    self: *Self,
+    _: ?*anyopaque,
 ) void {
     switch (request) {
         .destroy => status_manager.destroy(),
@@ -74,7 +74,7 @@ fn handleRequest(
 
             const node = util.gpa.create(std.SinglyLinkedList(OutputStatus).Node) catch {
                 status_manager.getClient().postNoMemory();
-                log.crit("out of memory", .{});
+                log.err("out of memory", .{});
                 return;
             };
 
@@ -85,7 +85,7 @@ fn handleRequest(
             ) catch {
                 status_manager.getClient().postNoMemory();
                 util.gpa.destroy(node);
-                log.crit("out of memory", .{});
+                log.err("out of memory", .{});
                 return;
             };
 
@@ -99,7 +99,7 @@ fn handleRequest(
 
             const node = util.gpa.create(std.SinglyLinkedList(SeatStatus).Node) catch {
                 status_manager.getClient().postNoMemory();
-                log.crit("out of memory", .{});
+                log.err("out of memory", .{});
                 return;
             };
 
@@ -110,7 +110,7 @@ fn handleRequest(
             ) catch {
                 status_manager.getClient().postNoMemory();
                 util.gpa.destroy(node);
-                log.crit("out of memory", .{});
+                log.err("out of memory", .{});
                 return;
             };
 
diff --git a/river/Subsurface.zig b/river/Subsurface.zig
index edbd80f..d715efd 100644
--- a/river/Subsurface.zig
+++ b/river/Subsurface.zig
@@ -38,7 +38,7 @@ pub const Parent = union(enum) {
         switch (parent) {
             .xdg_toplevel => |xdg_toplevel| xdg_toplevel.view.output.damage.addWhole(),
             .layer_surface => |layer_surface| layer_surface.output.damage.addWhole(),
-            .drag_icon => |drag_icon| {
+            .drag_icon => |_| {
                 var it = server.root.outputs.first;
                 while (it) |node| : (it = node.next) node.data.damage.addWhole();
             },
@@ -61,7 +61,7 @@ commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit)
 
 pub fn create(wlr_subsurface: *wlr.Subsurface, parent: Parent) void {
     const subsurface = util.gpa.create(Subsurface) catch {
-        std.log.crit("out of memory", .{});
+        std.log.err("out of memory", .{});
         wlr_subsurface.resource.getClient().postNoMemory();
         return;
     };
@@ -125,7 +125,7 @@ pub fn destroySubsurfaces(surface: *wlr.Surface) void {
     }
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.Subsurface), wlr_subsurface: *wlr.Subsurface) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.Subsurface), _: *wlr.Subsurface) void {
     const subsurface = @fieldParentPtr(Subsurface, "subsurface_destroy", listener);
 
     subsurface.destroy();
@@ -138,14 +138,14 @@ fn handleMap(listener: *wl.Listener(*wlr.Subsurface), wlr_subsurface: *wlr.Subsu
     subsurface.parent.damageWholeOutput();
 }
 
-fn handleUnmap(listener: *wl.Listener(*wlr.Subsurface), wlr_subsurface: *wlr.Subsurface) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.Subsurface), _: *wlr.Subsurface) void {
     const subsurface = @fieldParentPtr(Subsurface, "unmap", listener);
 
     subsurface.commit.link.remove();
     subsurface.parent.damageWholeOutput();
 }
 
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     const subsurface = @fieldParentPtr(Subsurface, "commit", listener);
 
     subsurface.parent.damageWholeOutput();
diff --git a/river/View.zig b/river/View.zig
index d882791..5ffe4dd 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -231,7 +231,7 @@ pub fn configure(self: *Self) void {
 
 pub fn sendFrameDone(self: Self) void {
     var now: os.timespec = undefined;
-    os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
+    os.clock_gettime(os.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
     self.surface.?.sendFrameDone(&now);
 }
 
@@ -313,7 +313,7 @@ fn sendEnter(self: *Self, output: *Output) void {
     self.forEachSurface(*wlr.Output, sendEnterIterator, output.wlr_output);
 }
 
-fn sendEnterIterator(surface: *wlr.Surface, sx: c_int, sy: c_int, wlr_output: *wlr.Output) callconv(.C) void {
+fn sendEnterIterator(surface: *wlr.Surface, _: c_int, _: c_int, wlr_output: *wlr.Output) callconv(.C) void {
     surface.sendEnter(wlr_output);
 }
 
@@ -321,7 +321,7 @@ fn sendLeave(self: *Self, output: *Output) void {
     self.forEachSurface(*wlr.Output, sendLeaveIterator, output.wlr_output);
 }
 
-fn sendLeaveIterator(surface: *wlr.Surface, sx: c_int, sy: c_int, wlr_output: *wlr.Output) callconv(.C) void {
+fn sendLeaveIterator(surface: *wlr.Surface, _: c_int, _: c_int, wlr_output: *wlr.Output) callconv(.C) void {
     surface.sendLeave(wlr_output);
 }
 
@@ -577,17 +577,16 @@ fn handleForeignFullscreen(
 
 fn handleForeignClose(
     listener: *wl.Listener(*wlr.ForeignToplevelHandleV1),
-    event: *wlr.ForeignToplevelHandleV1,
+    _: *wlr.ForeignToplevelHandleV1,
 ) void {
     const self = @fieldParentPtr(Self, "foreign_close", listener);
     self.close();
 }
 
 fn handleRequestActivate(
-    listener: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
+    _: *wl.Listener(*wlr.XdgActivationV1.event.RequestActivate),
     event: *wlr.XdgActivationV1.event.RequestActivate,
 ) void {
-    const self = @fieldParentPtr(Self, "request_activate", listener);
     if (fromWlrSurface(event.surface)) |view| {
         if (view.current.focus == 0) {
             view.pending.urgent = true;
diff --git a/river/VoidView.zig b/river/VoidView.zig
index 641382a..1bab6d3 100644
--- a/river/VoidView.zig
+++ b/river/VoidView.zig
@@ -23,38 +23,38 @@ const wlr = @import("wlroots");
 const Box = @import("Box.zig");
 const View = @import("View.zig");
 
-pub fn needsConfigure(self: Self) bool {
+pub fn needsConfigure(_: Self) bool {
     unreachable;
 }
 
-pub fn configure(self: Self) void {
+pub fn configure(_: Self) void {
     unreachable;
 }
 
-pub fn close(self: Self) void {
+pub fn setActivated(_: Self, _: bool) void {
     unreachable;
 }
 
-pub fn setActivated(self: Self, activated: bool) void {
+pub fn setFullscreen(_: Self, _: bool) void {
     unreachable;
 }
 
-pub fn setFullscreen(self: Self, fullscreen: bool) void {
+pub fn close(_: Self) void {
     unreachable;
 }
 
-pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
+pub fn surfaceAt(_: Self, _: f64, _: f64, _: *f64, _: *f64) ?*wlr.Surface {
     unreachable;
 }
 
-pub fn getTitle(self: Self) ?[*:0]const u8 {
+pub fn getTitle(_: Self) ?[*:0]const u8 {
     unreachable;
 }
 
-pub fn getAppId(self: Self) ?[*:0]const u8 {
+pub fn getAppId(_: Self) ?[*:0]const u8 {
     unreachable;
 }
 
-pub fn getConstraints(self: Self) View.Constraints {
+pub fn getConstraints(_: Self) View.Constraints {
     unreachable;
 }
diff --git a/river/XdgPopup.zig b/river/XdgPopup.zig
index dce546b..864d69d 100644
--- a/river/XdgPopup.zig
+++ b/river/XdgPopup.zig
@@ -43,7 +43,7 @@ commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit)
 
 pub fn create(wlr_xdg_popup: *wlr.XdgPopup, parent: Parent) void {
     const xdg_popup = util.gpa.create(XdgPopup) catch {
-        std.log.crit("out of memory", .{});
+        std.log.err("out of memory", .{});
         wlr_xdg_popup.resource.postNoMemory();
         return;
     };
@@ -104,26 +104,26 @@ pub fn destroyPopups(wlr_xdg_surface: *wlr.XdgSurface) void {
     }
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), wlr_xdg_surface: *wlr.XdgSurface) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const xdg_popup = @fieldParentPtr(XdgPopup, "surface_destroy", listener);
     xdg_popup.destroy();
 }
 
-fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const xdg_popup = @fieldParentPtr(XdgPopup, "map", listener);
 
     xdg_popup.wlr_xdg_popup.base.surface.events.commit.add(&xdg_popup.commit);
     xdg_popup.parent.damageWholeOutput();
 }
 
-fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const xdg_popup = @fieldParentPtr(XdgPopup, "unmap", listener);
 
     xdg_popup.commit.link.remove();
     xdg_popup.parent.damageWholeOutput();
 }
 
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     const xdg_popup = @fieldParentPtr(XdgPopup, "commit", listener);
 
     xdg_popup.parent.damageWholeOutput();
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 986f881..96594ef 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -147,7 +147,7 @@ pub fn getConstraints(self: Self) View.Constraints {
     };
 }
 
-fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
 
     // Remove listeners that are active for the entire lifetime of the view
@@ -163,7 +163,6 @@ fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgS
     self.view.destroy();
 }
 
-/// Called when the xdg surface is mapped, or ready to display on-screen.
 fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
     const self = @fieldParentPtr(Self, "map", listener);
     const view = self.view;
@@ -225,13 +224,13 @@ fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurfa
     }
 
     view.map() catch {
-        log.crit("out of memory", .{});
+        log.err("out of memory", .{});
         xdg_surface.resource.getClient().postNoMemory();
     };
 }
 
 /// Called when the surface is unmapped and will no longer be displayed.
-fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const self = @fieldParentPtr(Self, "unmap", listener);
 
     // Remove listeners that are only active while mapped
@@ -258,7 +257,7 @@ fn handleAckConfigure(
     }
 }
 
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     const self = @fieldParentPtr(Self, "commit", listener);
     const view = self.view;
 
@@ -356,13 +355,13 @@ fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), ev
 }
 
 /// Called when the client sets / updates its title
-fn handleSetTitle(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+fn handleSetTitle(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const self = @fieldParentPtr(Self, "set_title", listener);
     self.view.notifyTitle();
 }
 
 /// Called when the client sets / updates its app_id
-fn handleSetAppId(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+fn handleSetAppId(listener: *wl.Listener(*wlr.XdgSurface), _: *wlr.XdgSurface) void {
     const self = @fieldParentPtr(Self, "set_app_id", listener);
     self.view.notifyAppId();
 }
diff --git a/river/XwaylandUnmanaged.zig b/river/XwaylandUnmanaged.zig
index 88f4f07..2fa977a 100644
--- a/river/XwaylandUnmanaged.zig
+++ b/river/XwaylandUnmanaged.zig
@@ -48,14 +48,14 @@ pub fn init(self: *Self, xwayland_surface: *wlr.XwaylandSurface) void {
 }
 
 fn handleRequestConfigure(
-    listener: *wl.Listener(*wlr.XwaylandSurface.event.Configure),
+    _: *wl.Listener(*wlr.XwaylandSurface.event.Configure),
     event: *wlr.XwaylandSurface.event.Configure,
 ) void {
     event.surface.configure(event.x, event.y, event.width, event.height);
 }
 
 /// Called when the xwayland surface is destroyed
-fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
 
     // Remove listeners that are active for the entire lifetime
@@ -84,7 +84,7 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
 }
 
 /// Called when the surface is unmapped and will no longer be displayed.
-fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "unmap", listener);
 
     // Remove self from the list of unmanged views in the root
@@ -94,7 +94,7 @@ fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *
     self.commit.link.remove();
 }
 
-fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+fn handleCommit(_: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
     var it = server.root.outputs.first;
     while (it) |node| : (it = node.next) node.data.damage.addWhole();
 }
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index aac9af6..8bc27bf 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -143,7 +143,7 @@ pub fn getConstraints(self: Self) View.Constraints {
 }
 
 /// Called when the xwayland surface is destroyed
-fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "destroy", listener);
 
     // Remove listeners that are active for the entire lifetime of the view
@@ -202,13 +202,13 @@ fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wl
     }
 
     view.map() catch {
-        std.log.crit("out of memory", .{});
+        std.log.err("out of memory", .{});
         surface.resource.getClient().postNoMemory();
     };
 }
 
 /// Called when the surface is unmapped and will no longer be displayed.
-fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "unmap", listener);
 
     // Remove listeners that are only active while mapped
@@ -253,12 +253,12 @@ fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) voi
     };
 }
 
-fn handleSetTitle(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+fn handleSetTitle(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "set_title", listener);
     self.view.notifyTitle();
 }
 
-fn handleSetClass(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+fn handleSetClass(listener: *wl.Listener(*wlr.XwaylandSurface), _: *wlr.XwaylandSurface) void {
     const self = @fieldParentPtr(Self, "set_class", listener);
     self.view.notifyAppId();
 }
diff --git a/river/command.zig b/river/command.zig
index a2ce47c..c428ade 100644
--- a/river/command.zig
+++ b/river/command.zig
@@ -38,7 +38,7 @@ pub const Orientation = enum {
 
 // zig fmt: off
 const command_impls = std.ComptimeStringMap(
-    fn (*std.mem.Allocator, *Seat, []const [:0]const u8, *?[]const u8) Error!void,
+    fn (std.mem.Allocator, *Seat, []const [:0]const u8, *?[]const u8) Error!void,
     .{
         .{ "attach-mode",               @import("command/attach_mode.zig").attachMode },
         .{ "background-color",          @import("command/config.zig").backgroundColor },
@@ -117,7 +117,7 @@ pub const Error = error{
 /// The caller is then responsible for freeing that slice, which will be
 /// allocated using the provided allocator.
 pub fn run(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
diff --git a/river/command/attach_mode.zig b/river/command/attach_mode.zig
index 8fc0dbb..4649c55 100644
--- a/river/command/attach_mode.zig
+++ b/river/command/attach_mode.zig
@@ -25,10 +25,10 @@ const Error = @import("../command.zig").Error;
 const Seat = @import("../Seat.zig");
 
 pub fn attachMode(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
diff --git a/river/command/close.zig b/river/command/close.zig
index ff527a3..d05d7cc 100644
--- a/river/command/close.zig
+++ b/river/command/close.zig
@@ -22,10 +22,10 @@ const Seat = @import("../Seat.zig");
 
 /// Close the focused view, if any.
 pub fn close(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
-    args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: []const [:0]const u8,
+    _: *?[]const u8,
 ) Error!void {
     // Note: we don't call arrange() here as it will be called
     // automatically when the view is unmapped.
diff --git a/river/command/config.zig b/river/command/config.zig
index f2046ce..9e0307c 100644
--- a/river/command/config.zig
+++ b/river/command/config.zig
@@ -25,10 +25,10 @@ const Seat = @import("../Seat.zig");
 const Config = @import("../Config.zig");
 
 pub fn borderWidth(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -39,10 +39,10 @@ pub fn borderWidth(
 }
 
 pub fn backgroundColor(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -54,10 +54,10 @@ pub fn backgroundColor(
 }
 
 pub fn borderColorFocused(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -69,10 +69,10 @@ pub fn borderColorFocused(
 }
 
 pub fn borderColorUnfocused(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -84,10 +84,10 @@ pub fn borderColorUnfocused(
 }
 
 pub fn borderColorUrgent(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -99,10 +99,10 @@ pub fn borderColorUrgent(
 }
 
 pub fn setCursorWarp(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
diff --git a/river/command/declare_mode.zig b/river/command/declare_mode.zig
index 964d97c..aabc01d 100644
--- a/river/command/declare_mode.zig
+++ b/river/command/declare_mode.zig
@@ -27,10 +27,10 @@ const Seat = @import("../Seat.zig");
 
 /// Declare a new keymap mode
 pub fn declareMode(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -40,8 +40,8 @@ pub fn declareMode(
 
     if (config.mode_to_id.get(new_mode_name) != null) return;
 
-    try config.modes.ensureCapacity(config.modes.items.len + 1);
-    const owned_name = try std.mem.dupe(util.gpa, u8, new_mode_name);
+    try config.modes.ensureUnusedCapacity(1);
+    const owned_name = try util.gpa.dupe(u8, new_mode_name);
     errdefer util.gpa.free(owned_name);
     try config.mode_to_id.putNoClobber(owned_name, config.modes.items.len);
     config.modes.appendAssumeCapacity(Mode.init());
diff --git a/river/command/enter_mode.zig b/river/command/enter_mode.zig
index 00f1bcd..cf4b649 100644
--- a/river/command/enter_mode.zig
+++ b/river/command/enter_mode.zig
@@ -24,7 +24,7 @@ const Seat = @import("../Seat.zig");
 
 /// Switch to the given mode
 pub fn enterMode(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
diff --git a/river/command/exit.zig b/river/command/exit.zig
index 6c694f4..d092351 100644
--- a/river/command/exit.zig
+++ b/river/command/exit.zig
@@ -24,10 +24,10 @@ const Seat = @import("../Seat.zig");
 
 /// Exit the compositor, terminating the wayland session.
 pub fn exit(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
     server.wl_server.terminate();
diff --git a/river/command/filter.zig b/river/command/filter.zig
index 298b56d..e10b722 100644
--- a/river/command/filter.zig
+++ b/river/command/filter.zig
@@ -23,7 +23,6 @@ const server = &@import("../main.zig").server;
 const util = @import("../util.zig");
 
 const View = @import("../View.zig");
-const ViewStack = @import("view_stack.zig").ViewStack;
 const Error = @import("../command.zig").Error;
 const Seat = @import("../Seat.zig");
 
@@ -33,10 +32,10 @@ const FilterKind = enum {
 };
 
 pub fn floatFilterAdd(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    _: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
@@ -51,14 +50,14 @@ pub fn floatFilterAdd(
     const gop = try map.getOrPut(util.gpa, key);
     if (gop.found_existing) return;
     errdefer assert(map.remove(key));
-    gop.key_ptr.* = try std.mem.dupe(util.gpa, u8, key);
+    gop.key_ptr.* = try util.gpa.dupe(u8, key);
 }
 
 pub fn floatFilterRemove(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    _: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
@@ -74,10 +73,10 @@ pub fn floatFilterRemove(
 }
 
 pub fn csdFilterAdd(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    _: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
@@ -92,16 +91,16 @@ pub fn csdFilterAdd(
     const gop = try map.getOrPut(util.gpa, key);
     if (gop.found_existing) return;
     errdefer assert(map.remove(key));
-    gop.key_ptr.* = try std.mem.dupe(util.gpa, u8, key);
+    gop.key_ptr.* = try util.gpa.dupe(u8, key);
 
     csdFilterUpdateViews(kind, key, .add);
 }
 
 pub fn csdFilterRemove(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    _: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
diff --git a/river/command/focus_follows_cursor.zig b/river/command/focus_follows_cursor.zig
index c8494ac..73c6e81 100644
--- a/river/command/focus_follows_cursor.zig
+++ b/river/command/focus_follows_cursor.zig
@@ -25,10 +25,10 @@ const Error = @import("../command.zig").Error;
 const Seat = @import("../Seat.zig");
 
 pub fn focusFollowsCursor(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
diff --git a/river/command/focus_view.zig b/river/command/focus_view.zig
index 654ac9c..96adc69 100644
--- a/river/command/focus_view.zig
+++ b/river/command/focus_view.zig
@@ -28,10 +28,10 @@ const ViewStack = @import("../view_stack.zig").ViewStack;
 /// Focus either the next or the previous visible view, depending on the enum
 /// passed. Does nothing if there are 1 or 0 views in the stack.
 pub fn focusView(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
diff --git a/river/command/input.zig b/river/command/input.zig
index 02c96bd..d472555 100644
--- a/river/command/input.zig
+++ b/river/command/input.zig
@@ -28,11 +28,13 @@ const InputConfig = @import("../InputConfig.zig");
 const InputManager = @import("../InputManager.zig");
 
 pub fn listInputs(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    allocator: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
+    if (args.len > 1) return error.TooManyArguments;
+
     var input_list = std.ArrayList(u8).init(allocator);
     const writer = input_list.writer();
     var prev = false;
@@ -59,11 +61,13 @@ pub fn listInputs(
 }
 
 pub fn listInputConfigs(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    allocator: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
+    if (args.len > 1) return error.TooManyArguments;
+
     var input_list = std.ArrayList(u8).init(allocator);
     const writer = input_list.writer();
 
@@ -122,10 +126,10 @@ pub fn listInputConfigs(
 }
 
 pub fn input(
-    allocator: *mem.Allocator,
-    seat: *Seat,
+    _: mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 4) return Error.NotEnoughArguments;
     if (args.len > 4) return Error.TooManyArguments;
diff --git a/river/command/layout.zig b/river/command/layout.zig
index f75a7ab..15399f3 100644
--- a/river/command/layout.zig
+++ b/river/command/layout.zig
@@ -26,10 +26,10 @@ const Error = @import("../command.zig").Error;
 const Seat = @import("../Seat.zig");
 
 pub fn outputLayout(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -40,10 +40,10 @@ pub fn outputLayout(
 }
 
 pub fn defaultLayout(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    _: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -63,10 +63,10 @@ pub fn defaultLayout(
 /// riverctl send-layout-cmd rivertile "mod-main-factor -0.1"
 /// riverctl send-layout-cmd rivertile "main-location top"
 pub fn sendLayoutCmd(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
@@ -81,6 +81,5 @@ pub fn sendLayoutCmd(
     } else return;
 
     layout.layout.sendUserCommand(args[2]);
-
     output.arrangeViews();
 }
diff --git a/river/command/map.zig b/river/command/map.zig
index 0ef834a..13d2ff9 100644
--- a/river/command/map.zig
+++ b/river/command/map.zig
@@ -34,7 +34,7 @@ const Seat = @import("../Seat.zig");
 /// Example:
 /// map normal Mod4+Shift Return spawn foot
 pub fn map(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -46,7 +46,7 @@ pub fn map(
 
     if (optionals.release and optionals.repeat) return Error.ConflictingOptions;
 
-    const mode_id = try modeNameToId(allocator, seat, args[1 + offset], out);
+    const mode_id = try modeNameToId(allocator, args[1 + offset], out);
     const modifiers = try parseModifiers(allocator, args[2 + offset], out);
     const keysym = try parseKeysym(allocator, args[3 + offset], out);
 
@@ -72,15 +72,15 @@ pub fn map(
 /// Example:
 /// map-pointer normal Mod4 BTN_LEFT move-view
 pub fn mapPointer(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    allocator: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 5) return Error.NotEnoughArguments;
     if (args.len > 5) return Error.TooManyArguments;
 
-    const mode_id = try modeNameToId(allocator, seat, args[1], out);
+    const mode_id = try modeNameToId(allocator, args[1], out);
     const modifiers = try parseModifiers(allocator, args[2], out);
     const event_code = try parseEventCode(allocator, args[3], out);
 
@@ -111,7 +111,7 @@ pub fn mapPointer(
     }
 }
 
-fn modeNameToId(allocator: *std.mem.Allocator, seat: *Seat, mode_name: []const u8, out: *?[]const u8) !usize {
+fn modeNameToId(allocator: std.mem.Allocator, mode_name: []const u8, out: *?[]const u8) !usize {
     const config = &server.config;
     return config.mode_to_id.get(mode_name) orelse {
         out.* = try std.fmt.allocPrint(
@@ -154,7 +154,7 @@ fn pointerMappingExists(
     return null;
 }
 
-fn parseEventCode(allocator: *std.mem.Allocator, name: [:0]const u8, out: *?[]const u8) !u32 {
+fn parseEventCode(allocator: std.mem.Allocator, name: [:0]const u8, out: *?[]const u8) !u32 {
     const event_code = c.libevdev_event_code_from_name(c.EV_KEY, name);
     if (event_code < 1) {
         out.* = try std.fmt.allocPrint(allocator, "unknown button {s}", .{name});
@@ -164,7 +164,7 @@ fn parseEventCode(allocator: *std.mem.Allocator, name: [:0]const u8, out: *?[]co
     return @intCast(u32, event_code);
 }
 
-fn parseKeysym(allocator: *std.mem.Allocator, name: [:0]const u8, out: *?[]const u8) !xkb.Keysym {
+fn parseKeysym(allocator: std.mem.Allocator, name: [:0]const u8, out: *?[]const u8) !xkb.Keysym {
     const keysym = xkb.Keysym.fromName(name, .case_insensitive);
     if (keysym == .NoSymbol) {
         out.* = try std.fmt.allocPrint(allocator, "invalid keysym '{s}'", .{name});
@@ -174,11 +174,11 @@ fn parseKeysym(allocator: *std.mem.Allocator, name: [:0]const u8, out: *?[]const
 }
 
 fn parseModifiers(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     modifiers_str: []const u8,
     out: *?[]const u8,
 ) !wlr.Keyboard.ModifierMask {
-    var it = std.mem.split(modifiers_str, "+");
+    var it = std.mem.split(u8, modifiers_str, "+");
     var modifiers = wlr.Keyboard.ModifierMask{};
     outer: while (it.next()) |mod_name| {
         if (mem.eql(u8, mod_name, "None")) continue;
@@ -245,7 +245,7 @@ fn parseOptionalArgs(args: []const []const u8) OptionalArgsContainer {
 /// Example:
 /// unmap normal Mod4+Shift Return
 pub fn unmap(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -255,7 +255,7 @@ pub fn unmap(
     const offset = optionals.i;
     if (args.len - offset < 4) return Error.NotEnoughArguments;
 
-    const mode_id = try modeNameToId(allocator, seat, args[1 + offset], out);
+    const mode_id = try modeNameToId(allocator, args[1 + offset], out);
     const modifiers = try parseModifiers(allocator, args[2 + offset], out);
     const keysym = try parseKeysym(allocator, args[3 + offset], out);
 
@@ -276,15 +276,15 @@ pub fn unmap(
 /// Example:
 /// unmap-pointer normal Mod4 BTN_LEFT
 pub fn unmapPointer(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    allocator: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
     if (args.len < 4) return Error.NotEnoughArguments;
     if (args.len > 4) return Error.TooManyArguments;
 
-    const mode_id = try modeNameToId(allocator, seat, args[1], out);
+    const mode_id = try modeNameToId(allocator, args[1], out);
     const modifiers = try parseModifiers(allocator, args[2], out);
     const event_code = try parseEventCode(allocator, args[3], out);
 
diff --git a/river/command/move.zig b/river/command/move.zig
index d8589f5..02a153e 100644
--- a/river/command/move.zig
+++ b/river/command/move.zig
@@ -27,10 +27,10 @@ const View = @import("../View.zig");
 const Box = @import("../Box.zig");
 
 pub fn move(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
@@ -51,10 +51,10 @@ pub fn move(
 }
 
 pub fn snap(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -78,10 +78,10 @@ pub fn snap(
 }
 
 pub fn resize(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
diff --git a/river/command/output.zig b/river/command/output.zig
index 564c478..113db28 100644
--- a/river/command/output.zig
+++ b/river/command/output.zig
@@ -28,10 +28,10 @@ const Output = @import("../Output.zig");
 const Seat = @import("../Seat.zig");
 
 pub fn focusOutput(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
@@ -48,10 +48,10 @@ pub fn focusOutput(
 }
 
 pub fn sendToOutput(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
diff --git a/river/command/set_repeat.zig b/river/command/set_repeat.zig
index e7e3519..8288ade 100644
--- a/river/command/set_repeat.zig
+++ b/river/command/set_repeat.zig
@@ -24,10 +24,10 @@ const Seat = @import("../Seat.zig");
 
 /// Set the repeat rate and delay for all keyboards.
 pub fn setRepeat(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 3) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
diff --git a/river/command/spawn.zig b/river/command/spawn.zig
index 6ad3936..b50f854 100644
--- a/river/command/spawn.zig
+++ b/river/command/spawn.zig
@@ -24,8 +24,8 @@ const Seat = @import("../Seat.zig");
 
 /// Spawn a program.
 pub fn spawn(
-    allocator: *std.mem.Allocator,
-    seat: *Seat,
+    allocator: std.mem.Allocator,
+    _: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!void {
@@ -42,7 +42,7 @@ pub fn spawn(
     if (pid == 0) {
         // Clean things up for the child in an intermediate fork
         if (c.setsid() < 0) unreachable;
-        if (std.os.system.sigprocmask(std.os.SIG_SETMASK, &std.os.empty_sigset, null) < 0) unreachable;
+        if (std.os.system.sigprocmask(std.os.SIG.SETMASK, &std.os.empty_sigset, null) < 0) unreachable;
 
         const pid2 = std.os.fork() catch c._exit(1);
         if (pid2 == 0) std.os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
@@ -52,8 +52,8 @@ pub fn spawn(
 
     // Wait the intermediate child.
     const ret = std.os.waitpid(pid, 0);
-    if (!std.os.WIFEXITED(ret.status) or
-        (std.os.WIFEXITED(ret.status) and std.os.WEXITSTATUS(ret.status) != 0))
+    if (!std.os.W.IFEXITED(ret.status) or
+        (std.os.W.IFEXITED(ret.status) and std.os.W.EXITSTATUS(ret.status) != 0))
     {
         out.* = try std.fmt.allocPrint(allocator, "fork/execve failed", .{});
         return Error.Other;
diff --git a/river/command/swap.zig b/river/command/swap.zig
index f0db519..109c969 100644
--- a/river/command/swap.zig
+++ b/river/command/swap.zig
@@ -27,10 +27,10 @@ const ViewStack = @import("../view_stack.zig").ViewStack;
 
 /// Swap the currently focused view with either the view higher or lower in the visible stack
 pub fn swap(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 2) return Error.TooManyArguments;
diff --git a/river/command/tags.zig b/river/command/tags.zig
index 7551b67..0cb8880 100644
--- a/river/command/tags.zig
+++ b/river/command/tags.zig
@@ -24,7 +24,7 @@ const Seat = @import("../Seat.zig");
 
 /// Switch focus to the passed tags.
 pub fn setFocusedTags(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -41,7 +41,7 @@ pub fn setFocusedTags(
 
 /// Set the spawn tagmask
 pub fn spawnTagmask(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -52,7 +52,7 @@ pub fn spawnTagmask(
 
 /// Set the tags of the focused view.
 pub fn setViewTags(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -68,7 +68,7 @@ pub fn setViewTags(
 
 /// Toggle focus of the passsed tags.
 pub fn toggleFocusedTags(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -87,7 +87,7 @@ pub fn toggleFocusedTags(
 
 /// Toggle the passed tags of the focused view
 pub fn toggleViewTags(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
     out: *?[]const u8,
@@ -106,11 +106,12 @@ pub fn toggleViewTags(
 
 /// Switch focus to tags that were selected previously
 pub fn focusPreviousTags(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const []const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
+    if (args.len > 1) return error.TooManyArguments;
     const previous_tags = seat.focused_output.previous_tags;
     if (seat.focused_output.pending.tags != previous_tags) {
         seat.focused_output.previous_tags = seat.focused_output.pending.tags;
@@ -123,11 +124,12 @@ pub fn focusPreviousTags(
 
 /// Set the tags of the focused view to the tags that were selected previously
 pub fn sendToPreviousTags(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const []const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
+    if (args.len > 1) return error.TooManyArguments;
     const previous_tags = seat.focused_output.previous_tags;
     if (seat.focused == .view) {
         const view = seat.focused.view;
@@ -138,7 +140,7 @@ pub fn sendToPreviousTags(
 }
 
 fn parseTags(
-    allocator: *std.mem.Allocator,
+    allocator: std.mem.Allocator,
     args: []const [:0]const u8,
     out: *?[]const u8,
 ) Error!u32 {
diff --git a/river/command/toggle_float.zig b/river/command/toggle_float.zig
index 6b36374..83b3aff 100644
--- a/river/command/toggle_float.zig
+++ b/river/command/toggle_float.zig
@@ -25,10 +25,10 @@ const Seat = @import("../Seat.zig");
 /// Make the focused view float or stop floating, depending on its current
 /// state.
 pub fn toggleFloat(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
 
diff --git a/river/command/toggle_fullscreen.zig b/river/command/toggle_fullscreen.zig
index 63c1c86..ee32d4b 100644
--- a/river/command/toggle_fullscreen.zig
+++ b/river/command/toggle_fullscreen.zig
@@ -25,10 +25,10 @@ const Seat = @import("../Seat.zig");
 
 /// Toggle fullscreen state of the currently focused view
 pub fn toggleFullscreen(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
 
diff --git a/river/command/xcursor_theme.zig b/river/command/xcursor_theme.zig
index b224990..410983b 100644
--- a/river/command/xcursor_theme.zig
+++ b/river/command/xcursor_theme.zig
@@ -21,10 +21,10 @@ const Error = @import("../command.zig").Error;
 const Seat = @import("../Seat.zig");
 
 pub fn xcursorTheme(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len < 2) return Error.NotEnoughArguments;
     if (args.len > 3) return Error.TooManyArguments;
diff --git a/river/command/zoom.zig b/river/command/zoom.zig
index 9ca87d4..898e1da 100644
--- a/river/command/zoom.zig
+++ b/river/command/zoom.zig
@@ -27,10 +27,10 @@ const ViewStack = @import("../view_stack.zig").ViewStack;
 /// Bump the focused view to the top of the stack. If the view on the top of
 /// the stack is focused, bump the second view to the top.
 pub fn zoom(
-    allocator: *std.mem.Allocator,
+    _: std.mem.Allocator,
     seat: *Seat,
     args: []const [:0]const u8,
-    out: *?[]const u8,
+    _: *?[]const u8,
 ) Error!void {
     if (args.len > 1) return Error.TooManyArguments;
 
diff --git a/river/main.zig b/river/main.zig
index cef7852..62eaabc 100644
--- a/river/main.zig
+++ b/river/main.zig
@@ -17,9 +17,11 @@
 
 const build_options = @import("build_options");
 const std = @import("std");
+const mem = std.mem;
 const fs = std.fs;
 const io = std.io;
 const os = std.os;
+const builtin = @import("builtin");
 const wlr = @import("wlroots");
 const flags = @import("flags");
 
@@ -66,18 +68,20 @@ pub fn main() anyerror!void {
         try io.getStdOut().writeAll(build_options.version ++ "\n");
         os.exit(0);
     }
-    if (result.argFlag("-log-level")) |level_str| {
-        const level = std.meta.stringToEnum(LogLevel, level_str) orelse {
-            std.log.err("invalid log level '{s}'", .{level_str});
+    if (result.argFlag("-log-level")) |level| {
+        if (mem.eql(u8, level, std.log.Level.err.asText())) {
+            runtime_log_level = .err;
+        } else if (mem.eql(u8, level, std.log.Level.warn.asText())) {
+            runtime_log_level = .warn;
+        } else if (mem.eql(u8, level, std.log.Level.info.asText())) {
+            runtime_log_level = .info;
+        } else if (mem.eql(u8, level, std.log.Level.debug.asText())) {
+            runtime_log_level = .debug;
+        } else {
+            std.log.err("invalid log level '{s}'", .{level});
             try io.getStdErr().writeAll(usage);
             os.exit(1);
-        };
-        runtime_log_level = switch (level) {
-            .@"error" => .err,
-            .warning => .warn,
-            .info => .info,
-            .debug => .debug,
-        };
+        }
     }
     const startup_command = blk: {
         if (result.argFlag("-c")) |command| {
@@ -89,8 +93,8 @@ pub fn main() anyerror!void {
 
     river_init_wlroots_log(switch (runtime_log_level) {
         .debug => .debug,
-        .notice, .info => .info,
-        .warn, .err, .crit, .alert, .emerg => .err,
+        .info => .info,
+        .warn, .err => .err,
     });
 
     std.log.info("initializing server", .{});
@@ -107,14 +111,14 @@ pub fn main() anyerror!void {
         const pid = try os.fork();
         if (pid == 0) {
             if (c.setsid() < 0) unreachable;
-            if (os.system.sigprocmask(os.SIG_SETMASK, &os.empty_sigset, null) < 0) unreachable;
+            if (os.system.sigprocmask(os.SIG.SETMASK, &os.empty_sigset, null) < 0) unreachable;
             os.execveZ("/bin/sh", &child_args, std.c.environ) catch c._exit(1);
         }
         util.gpa.free(cmd);
         // Since the child has called setsid, the pid is the pgid
         break :blk pid;
     } else null;
-    defer if (child_pgid) |pgid| os.kill(-pgid, os.SIGTERM) catch |err| {
+    defer if (child_pgid) |pgid| os.kill(-pgid, os.SIG.TERM) catch |err| {
         std.log.err("failed to kill init process group: {s}", .{@errorName(err)});
     };
 
@@ -149,37 +153,23 @@ fn defaultInitPath() !?[:0]const u8 {
 pub const log_level: std.log.Level = .debug;
 
 /// Set the default log level based on the build mode.
-var runtime_log_level: std.log.Level = switch (std.builtin.mode) {
+var runtime_log_level: std.log.Level = switch (builtin.mode) {
     .Debug => .debug,
     .ReleaseSafe, .ReleaseFast, .ReleaseSmall => .info,
 };
 
-/// River only exposes these 4 log levels to the user for simplicity
-const LogLevel = enum {
-    @"error",
-    warning,
-    info,
-    debug,
-};
-
 pub fn log(
-    comptime message_level: std.log.Level,
+    comptime level: std.log.Level,
     comptime scope: @TypeOf(.EnumLiteral),
     comptime format: []const u8,
     args: anytype,
 ) void {
-    if (@enumToInt(message_level) > @enumToInt(runtime_log_level)) return;
+    if (@enumToInt(level) > @enumToInt(runtime_log_level)) return;
 
-    const river_level: LogLevel = switch (message_level) {
-        .emerg, .alert, .crit, .err => .@"error",
-        .warn => .warning,
-        .notice, .info => .info,
-        .debug => .debug,
-    };
     const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
 
     const stderr = std.io.getStdErr().writer();
-    stderr.print(@tagName(river_level) ++ scope_prefix ++ format ++ "\n", args) catch {};
+    stderr.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {};
 }
 
 /// See wlroots_log_wrapper.c
diff --git a/river/render.zig b/river/render.zig
index b6885d4..2850d82 100644
--- a/river/render.zig
+++ b/river/render.zig
@@ -48,7 +48,7 @@ const SurfaceRenderData = struct {
 /// The rendering order in this function must be kept in sync with Cursor.surfaceAt()
 pub fn renderOutput(output: *Output) void {
     var now: os.timespec = undefined;
-    os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
+    os.clock_gettime(os.CLOCK.MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
 
     var needs_frame: bool = undefined;
     var damage_region: pixman.Region32 = undefined;
@@ -91,7 +91,7 @@ pub fn renderOutput(output: *Output) void {
         it = ViewStack(View).iter(output.views.last, .reverse, output.current.tags, renderFilter);
         while (it.next()) |view| {
             if (view.current.focus != 0 or view.current.float) continue;
-            if (view.draw_borders) renderBorders(output, view, &now);
+            if (view.draw_borders) renderBorders(output, view);
             renderView(output, view, &now);
         }
 
@@ -99,7 +99,7 @@ pub fn renderOutput(output: *Output) void {
         it = ViewStack(View).iter(output.views.last, .reverse, output.current.tags, renderFilter);
         while (it.next()) |view| {
             if (view.current.focus == 0 or view.current.float) continue;
-            if (view.draw_borders) renderBorders(output, view, &now);
+            if (view.draw_borders) renderBorders(output, view);
             renderView(output, view, &now);
         }
 
@@ -107,7 +107,7 @@ pub fn renderOutput(output: *Output) void {
         it = ViewStack(View).iter(output.views.last, .reverse, output.current.tags, renderFilter);
         while (it.next()) |view| {
             if (view.current.focus != 0 or !view.current.float) continue;
-            if (view.draw_borders) renderBorders(output, view, &now);
+            if (view.draw_borders) renderBorders(output, view);
             renderView(output, view, &now);
         }
 
@@ -115,7 +115,7 @@ pub fn renderOutput(output: *Output) void {
         it = ViewStack(View).iter(output.views.last, .reverse, output.current.tags, renderFilter);
         while (it.next()) |view| {
             if (view.current.focus == 0 or !view.current.float) continue;
-            if (view.draw_borders) renderBorders(output, view, &now);
+            if (view.draw_borders) renderBorders(output, view);
             renderView(output, view, &now);
         }
 
@@ -316,7 +316,7 @@ fn renderTexture(
     server.renderer.renderSubtextureWithMatrix(texture, source_box, &matrix, 1.0) catch return;
 }
 
-fn renderBorders(output: *const Output, view: *View, now: *os.timespec) void {
+fn renderBorders(output: *const Output, view: *View) void {
     const config = &server.config;
     const color = blk: {
         if (view.current.urgent) break :blk &config.border_color_urgent;
diff --git a/river/view_stack.zig b/river/view_stack.zig
index 23ceab1..a573618 100644
--- a/river/view_stack.zig
+++ b/river/view_stack.zig
@@ -328,11 +328,11 @@ test "iteration (View)" {
     const allocator = testing.allocator;
 
     const filters = struct {
-        fn all(view: *View, context: void) bool {
+        fn all(_: *View, _: void) bool {
             return true;
         }
 
-        fn none(view: *View, context: void) bool {
+        fn none(_: *View, _: void) bool {
             return false;
         }
 
diff --git a/riverctl/main.zig b/riverctl/main.zig
index 63ec331..7c429dc 100644
--- a/riverctl/main.zig
+++ b/riverctl/main.zig
@@ -20,6 +20,7 @@ const mem = std.mem;
 const io = std.io;
 const os = std.os;
 const assert = std.debug.assert;
+const builtin = @import("builtin");
 
 const wayland = @import("wayland");
 const wl = wayland.client.wl;
@@ -47,8 +48,7 @@ pub const Globals = struct {
 
 pub fn main() !void {
     _main() catch |err| {
-        if (std.builtin.mode == .Debug)
-            return err;
+        if (builtin.mode == .Debug) return err;
 
         switch (err) {
             error.RiverControlNotAdvertised => fatal(
@@ -96,7 +96,7 @@ fn _main() !void {
     for (result.args) |arg| control.addArgument(arg);
 
     const callback = try control.runCommand(seat);
-    callback.setListener(?*c_void, callbackListener, null);
+    callback.setListener(?*anyopaque, callbackListener, null);
 
     // Loop until our callback is called and we exit.
     while (true) _ = try display.dispatch();
@@ -116,7 +116,7 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *
     }
 }
 
-fn callbackListener(callback: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV1.Event, _: ?*c_void) void {
+fn callbackListener(_: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV1.Event, _: ?*anyopaque) void {
     switch (event) {
         .success => |success| {
             if (mem.len(success.output) > 0) {
diff --git a/rivertile/main.zig b/rivertile/main.zig
index ed794da..07b9c38 100644
--- a/rivertile/main.zig
+++ b/rivertile/main.zig
@@ -140,7 +140,7 @@ const Output = struct {
             .namespace_in_use => fatal("namespace 'rivertile' already in use.", .{}),
 
             .user_command => |ev| {
-                var it = mem.tokenize(mem.span(ev.command), " ");
+                var it = mem.tokenize(u8, mem.span(ev.command), " ");
                 const raw_cmd = it.next() orelse {
                     std.log.err("not enough arguments", .{});
                     return;
@@ -357,7 +357,7 @@ pub fn main() !void {
     }
 
     const display = wl.Display.connect(null) catch {
-        std.debug.warn("Unable to connect to Wayland server.\n", .{});
+        std.debug.print("Unable to connect to Wayland server.\n", .{});
         os.exit(1);
     };
     defer display.disconnect();