Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
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);
+}