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

commit63fa8d964877086a54c05ad8c5a177284ed712a8
parentc221501343
authorIsaac Freund <[email protected]>
date2024-12-21 10:58
rivercompat: initial skeleton

 build.zig                     |  25 ++++++++++
 rivercompat/WindowManager.zig |  51 ++++++++++++++++++++
 rivercompat/main.zig          | 110 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 186 insertions(+)

diff --git a/build.zig b/build.zig
index d189888..858e170 100644
--- a/build.zig
+++ b/build.zig
@@ -191,6 +191,31 @@ pub fn build(b: *Build) !void {
         b.installArtifact(river);
     }
 
+    {
+        const rivercompat = b.addExecutable(.{
+            .name = "rivercompat",
+            .root_source_file = b.path("rivercompat/main.zig"),
+            .target = target,
+            .optimize = optimize,
+            .strip = strip,
+            .use_llvm = llvm,
+            .use_lld = llvm,
+        });
+        rivercompat.root_module.addOptions("build_options", options);
+
+        rivercompat.root_module.addImport("flags", flags);
+        rivercompat.root_module.addImport("wayland", wayland);
+        rivercompat.linkLibC();
+        rivercompat.linkSystemLibrary("wayland-client");
+
+        scanner.addCSource(rivercompat);
+
+        rivercompat.pie = pie;
+        rivercompat.root_module.omit_frame_pointer = omit_frame_pointer;
+
+        b.installArtifact(rivercompat);
+    }
+
     {
         const riverctl = b.addExecutable(.{
             .name = "riverctl",
diff --git a/rivercompat/WindowManager.zig b/rivercompat/WindowManager.zig
new file mode 100644
index 0000000..44d4311
--- /dev/null
+++ b/rivercompat/WindowManager.zig
@@ -0,0 +1,51 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2024 The River Developers
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+const WindowManager = @This();
+
+const main = @import("main.zig");
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+const river = wayland.client.river;
+
+wm_v1: *river.WindowManagerV1,
+
+pub fn init(wm: *WindowManager, wm_v1: *river.WindowManagerV1) void {
+    wm.* = .{
+        .wm_v1 = wm_v1,
+    };
+
+    wm_v1.setListener(*WindowManager, handleEvent, wm);
+}
+
+fn handleEvent(wm_v1: *river.WindowManagerV1, event: river.WindowManagerV1.Event, _: *WindowManager) void {
+    switch (event) {
+        .unavailable => main.fatal("another window manager is already running", .{}),
+        .finished => unreachable, // We never send river_window_manager_v1.stop
+        .update => |args| {
+            wm_v1.ackUpdate(args.serial);
+            wm_v1.commit();
+        },
+        .session_locked => {},
+        .session_unlocked => {},
+        .window => |args| {
+            args.id.proposeDimensions(400, 400);
+        },
+        .output => |args| {
+            _ = args;
+        },
+    }
+}
diff --git a/rivercompat/main.zig b/rivercompat/main.zig
new file mode 100644
index 0000000..1d1b32c
--- /dev/null
+++ b/rivercompat/main.zig
@@ -0,0 +1,110 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2024 The River Developers
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, version 3.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+const std = @import("std");
+const fmt = std.fmt;
+const mem = std.mem;
+const math = std.math;
+const posix = std.posix;
+const assert = std.debug.assert;
+
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+const river = wayland.client.river;
+const flags = @import("flags");
+
+const WindowManager = @import("WindowManager.zig");
+
+const gpa = std.heap.c_allocator;
+
+const usage =
+    \\usage: rivercompat [options]
+    \\
+    \\  -h              Print this help message and exit.
+    \\  -version        Print the version number and exit.
+    \\
+;
+
+const Globals = struct {
+    wm_v1: ?*river.WindowManagerV1 = null,
+
+    fn handleEvent(registry: *wl.Registry, event: wl.Registry.Event, globals: *Globals) void {
+        switch (event) {
+            .global => |global| {
+                if (mem.orderZ(u8, global.interface, river.WindowManagerV1.getInterface().name) == .eq) {
+                    globals.wm_v1 = registry.bind(global.name, river.WindowManagerV1, 1) catch return;
+                }
+            },
+            .global_remove => {},
+        }
+    }
+};
+
+const Output = struct {
+    output_v1: *river.OutputV1,
+};
+
+pub fn main() !void {
+    const result = flags.parser([*:0]const u8, &[_]flags.Flag{
+        .{ .name = "h", .kind = .boolean },
+        .{ .name = "version", .kind = .boolean },
+    }).parse(std.os.argv[1..]) catch {
+        try std.io.getStdErr().writeAll(usage);
+        posix.exit(1);
+    };
+    if (result.flags.h) {
+        try std.io.getStdOut().writeAll(usage);
+        posix.exit(0);
+    }
+    if (result.args.len != 0) fatalPrintUsage("unknown option '{s}'", .{result.args[0]});
+
+    if (result.flags.version) {
+        try std.io.getStdOut().writeAll(@import("build_options").version ++ "\n");
+        posix.exit(0);
+    }
+
+    const display = wl.Display.connect(null) catch {
+        std.debug.print("Unable to connect to Wayland server.\n", .{});
+        posix.exit(1);
+    };
+    defer display.disconnect();
+
+    var globals: Globals = .{};
+    const registry = try display.getRegistry();
+    registry.setListener(*Globals, Globals.handleEvent, &globals);
+    if (display.roundtrip() != .SUCCESS) fatal("initial roundtrip failed", .{});
+
+    const wm_v1 = globals.wm_v1 orelse
+        fatal("wayland compositor does not support river-window-management-v1.\n", .{});
+
+    var wm: WindowManager = undefined;
+    wm.init(wm_v1);
+
+    while (true) {
+        if (display.dispatch() != .SUCCESS) fatal("failed to dispatch wayland events", .{});
+    }
+}
+
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
+    std.log.err(format, args);
+    posix.exit(1);
+}
+
+fn fatalPrintUsage(comptime format: []const u8, args: anytype) noreturn {
+    std.log.err(format, args);
+    std.io.getStdErr().writeAll(usage) catch {};
+    posix.exit(1);
+}