diff --git a/build.zig b/build.zig
index 2e66934..264d541 100644
--- a/build.zig
+++ b/build.zig
@@ -19,39 +19,53 @@ pub fn build(b: *std.build.Builder) !void {
const scan_protocols = ScanProtocolsStep.create(b);
- const river = b.addExecutable("river", "src/river.zig");
- river.setTarget(target);
- river.setBuildMode(mode);
- river.addBuildOption(bool, "xwayland", xwayland);
- addServerDeps(river, &scan_protocols.step);
- river.install();
+ {
+ const river = b.addExecutable("river", "src/river.zig");
+ river.setTarget(target);
+ river.setBuildMode(mode);
+ river.addBuildOption(bool, "xwayland", xwayland);
- const run_cmd = river.run();
- run_cmd.step.dependOn(b.getInstallStep());
+ addProtocolDeps(river, &scan_protocols.step);
+ addServerDeps(river);
- const run_step = b.step("run", "Run the compositor");
- run_step.dependOn(&run_cmd.step);
+ river.install();
- const riverctl = b.addExecutable("riverctl", "src/riverctl.zig");
- riverctl.setTarget(target);
- riverctl.setBuildMode(mode);
- riverctl.install();
+ const run_cmd = river.run();
+ run_cmd.step.dependOn(b.getInstallStep());
- const river_test = b.addTest("src/test_main.zig");
- river_test.setTarget(target);
- river_test.setBuildMode(mode);
- river_test.addBuildOption(bool, "xwayland", xwayland);
- addServerDeps(river_test, &scan_protocols.step);
+ const run_step = b.step("run", "Run the compositor");
+ run_step.dependOn(&run_cmd.step);
+ }
- const test_step = b.step("test", "Run the tests");
- test_step.dependOn(&river_test.step);
+ {
+ const riverctl = b.addExecutable("riverctl", "src/riverctl.zig");
+ riverctl.setTarget(target);
+ riverctl.setBuildMode(mode);
+
+ addProtocolDeps(riverctl, &scan_protocols.step);
+
+ riverctl.linkLibC();
+
+ riverctl.linkSystemLibrary("wayland-client");
+
+ riverctl.install();
+ }
+
+ {
+ const river_test = b.addTest("src/test_main.zig");
+ river_test.setTarget(target);
+ river_test.setBuildMode(mode);
+ river_test.addBuildOption(bool, "xwayland", xwayland);
+
+ addProtocolDeps(river_test, &scan_protocols.step);
+ addServerDeps(river_test);
+
+ const test_step = b.step("test", "Run the tests");
+ test_step.dependOn(&river_test.step);
+ }
}
-fn addServerDeps(exe: *std.build.LibExeObjStep, protocol_step: *std.build.Step) void {
- exe.step.dependOn(protocol_step);
- exe.addIncludeDir("protocol");
- exe.addCSourceFile("protocol/river-window-management-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
-
+fn addServerDeps(exe: *std.build.LibExeObjStep) void {
exe.addCSourceFile("include/bindings.c", &[_][]const u8{"-std=c99"});
exe.addIncludeDir(".");
@@ -62,6 +76,12 @@ fn addServerDeps(exe: *std.build.LibExeObjStep, protocol_step: *std.build.Step)
exe.linkSystemLibrary("xkbcommon");
}
+fn addProtocolDeps(exe: *std.build.LibExeObjStep, protocol_step: *std.build.Step) void {
+ exe.step.dependOn(protocol_step);
+ exe.addIncludeDir("protocol");
+ exe.addCSourceFile("protocol/river-window-management-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
+}
+
const ScanProtocolsStep = struct {
builder: *std.build.Builder,
step: std.build.Step,
@@ -116,6 +136,18 @@ const ScanProtocolsStep = struct {
_ = try self.builder.exec(
&[_][]const u8{ "wayland-scanner", "private-code", xml_in_path, code_out_path },
);
+
+ // We need the client header as well for river-window-management
+ if (std.mem.eql(u8, basename_no_ext, "river-window-management-unstable-v1")) {
+ const client_header_out_path = try std.mem.concat(
+ self.builder.allocator,
+ u8,
+ &[_][]const u8{ "protocol/", basename_no_ext, "-client-protocol.h" },
+ );
+ _ = try self.builder.exec(
+ &[_][]const u8{ "wayland-scanner", "client-header", xml_in_path, client_header_out_path },
+ );
+ }
}
}
};
diff --git a/protocol/river-window-management-unstable-v1.xml b/protocol/river-window-management-unstable-v1.xml
index 3587ae5..71b37b6 100644
--- a/protocol/river-window-management-unstable-v1.xml
+++ b/protocol/river-window-management-unstable-v1.xml
@@ -27,7 +27,8 @@
A complete list of commands will be found in the man page.
TODO: write the man page.
-
+
diff --git a/src/WindowManagement.zig b/src/WindowManagement.zig
index be8d425..7c14838 100644
--- a/src/WindowManagement.zig
+++ b/src/WindowManagement.zig
@@ -36,6 +36,7 @@ wl_global: *c.wl_global,
listen_display_destroy: c.wl_listener,
pub fn init(self: *Self, server: *Server) !void {
+ self.server = server;
self.wl_global = c.wl_global_create(
server.wl_display,
&c.zriver_window_manager_v1_interface,
@@ -72,6 +73,22 @@ fn resourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
// TODO
}
-fn runCommand(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource, command: ?[*:0]const u8) callconv(.C) void {
- Log.Debug.log("command: {}", .{command});
+fn runCommand(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource, command: ?*c.wl_array) callconv(.C) void {
+ const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+ const allocator = self.server.allocator;
+
+ var args = std.ArrayList([]const u8).init(allocator);
+
+ var i: usize = 0;
+ const data = @ptrCast([*]const u8, command.?.data);
+ while (i < command.?.size) {
+ const slice = std.mem.spanZ(@ptrCast([*:0]const u8, &data[i]));
+ args.append(std.mem.dupe(allocator, u8, slice) catch unreachable) catch unreachable;
+
+ i += slice.len + 1;
+ }
+
+ for (args.items) |x| {
+ std.debug.warn("{}\n", .{x});
+ }
}
diff --git a/src/riverctl.zig b/src/riverctl.zig
index b26bcb7..5f6dd54 100644
--- a/src/riverctl.zig
+++ b/src/riverctl.zig
@@ -18,9 +18,62 @@
const std = @import("std");
const c = @cImport({
- @cInclude();
+ @cInclude("wayland-client.h");
+ @cInclude("river-window-management-unstable-v1-client-protocol.h");
});
-pub fn main() void {
- std.debug.warn("hello world\n", .{});
+const wl_registry_listener = c.wl_registry_listener{
+ .global = handleGlobal,
+ .global_remove = handleGlobalRemove,
+};
+
+var river_window_manager: ?*c.zriver_window_manager_v1 = null;
+
+pub fn main() !void {
+ const wl_display = c.wl_display_connect(null) orelse return error.CantConnectToDisplay;
+ const wl_registry = c.wl_display_get_registry(wl_display);
+
+ _ = c.wl_registry_add_listener(wl_registry, &wl_registry_listener, null);
+ if (c.wl_display_roundtrip(wl_display) == -1) return error.RoundtripFailed;
+
+ const wm = river_window_manager orelse return error.RiverWMNotAdvertised;
+
+ var command: c.wl_array = undefined;
+ c.wl_array_init(&command);
+ var it = std.process.args();
+ // Skip our name
+ _ = it.nextPosix();
+ while (it.nextPosix()) |arg| {
+ // Add one as we need to copy the null terminators as well
+ var ptr = @ptrCast([*]u8, c.wl_array_add(&command, arg.len + 1) orelse
+ return error.OutOfMemory);
+ for (arg) |ch, i| ptr[i] = ch;
+ ptr[arg.len] = 0;
+ }
+
+ c.zriver_window_manager_v1_run_command(wm, &command);
+ if (c.wl_display_roundtrip(wl_display) == -1) return error.RoundtripFailed;
}
+
+fn handleGlobal(
+ data: ?*c_void,
+ wl_registry: ?*c.wl_registry,
+ name: u32,
+ interface: ?[*:0]const u8,
+ version: u32,
+) callconv(.C) void {
+ // We only care about the river_window_manager global
+ if (std.mem.eql(
+ u8,
+ std.mem.spanZ(interface.?),
+ std.mem.spanZ(@ptrCast([*:0]const u8, c.zriver_window_manager_v1_interface.name.?)),
+ )) {
+ river_window_manager = @ptrCast(
+ *c.zriver_window_manager_v1,
+ c.wl_registry_bind(wl_registry, name, &c.zriver_window_manager_v1_interface, 1),
+ );
+ }
+}
+
+/// Ignore the event
+fn handleGlobalRemove(data: ?*c_void, wl_registry: ?*c.wl_registry, name: u32) callconv(.C) void {}