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 {}