aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsaac Freund <ifreund@ifreund.xyz>2020-11-04 00:23:21 +0100
committerIsaac Freund <ifreund@ifreund.xyz>2020-12-13 22:53:33 +0100
commit20d804cdb5b4874a0ee168cd8db67caf0c841772 (patch)
tree18bf003b6d37480919bba7e1852e611c9fe8411c
parent0c5e5a7b4a4cbd56ca797698feb7ac85b22db647 (diff)
downloadriver-20d804cdb5b4874a0ee168cd8db67caf0c841772.tar.gz
river-20d804cdb5b4874a0ee168cd8db67caf0c841772.tar.xz
code: switch to custom wlroots/libwayland bindings
This is a big step up over @cImport() for ergonomics and type safety. Nearly all void pointer casts have been eliminated!
-rw-r--r--.gitmodules9
-rw-r--r--README.md2
-rw-r--r--build.zig177
m---------deps/zig-pixman0
m---------deps/zig-wayland0
m---------deps/zig-wlroots0
m---------deps/zig-xkbcommon0
-rw-r--r--example/status.zig182
-rw-r--r--include/bindings.c11
-rw-r--r--include/bindings.h14
-rw-r--r--river/Box.zig8
-rw-r--r--river/Control.zig194
-rw-r--r--river/Cursor.zig302
-rw-r--r--river/Decoration.zig51
-rw-r--r--river/DecorationManager.zig30
-rw-r--r--river/DragIcon.zig22
-rw-r--r--river/InputManager.zig106
-rw-r--r--river/Keyboard.zig168
-rw-r--r--river/LayerSurface.zig83
-rw-r--r--river/Mapping.zig11
-rw-r--r--river/Output.zig160
-rw-r--r--river/OutputManager.zig239
-rw-r--r--river/OutputStatus.zig39
-rw-r--r--river/PointerMapping.zig4
-rw-r--r--river/Root.zig57
-rw-r--r--river/Seat.zig154
-rw-r--r--river/SeatStatus.zig44
-rw-r--r--river/Server.zig181
-rw-r--r--river/StatusManager.zig176
-rw-r--r--river/View.zig123
-rw-r--r--river/VoidView.zig10
-rw-r--r--river/XdgPopup.zig43
-rw-r--r--river/XdgToplevel.zig206
-rw-r--r--river/XwaylandUnmanaged.zig86
-rw-r--r--river/XwaylandView.zig130
-rw-r--r--river/c.zig53
-rw-r--r--river/command/exit.zig4
-rw-r--r--river/command/map.zig87
-rw-r--r--river/command/set_repeat.zig4
-rw-r--r--river/main.zig16
-rw-r--r--river/render.zig134
-rw-r--r--riverctl/main.zig12
42 files changed, 1456 insertions, 1876 deletions
diff --git a/.gitmodules b/.gitmodules
index 2d523f0..bb6bc6e 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,12 @@
[submodule "deps/zig-wayland"]
path = deps/zig-wayland
url = https://github.com/ifreund/zig-wayland
+[submodule "deps/zig-pixman"]
+ path = deps/zig-pixman
+ url = https://github.com/ifreund/zig-pixman
+[submodule "deps/zig-xkbcommon"]
+ path = deps/zig-xkbcommon
+ url = https://github.com/ifreund/zig-xkbcommon
+[submodule "deps/zig-wlroots"]
+ path = deps/zig-wlroots
+ url = https://github.com/swaywm/zig-wlroots
diff --git a/README.md b/README.md
index 32a096f..b64a611 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ git submodule update --init
To compile river first ensure that you have the following dependencies
installed:
-- [zig](https://ziglang.org/download/) 0.7.0
+- [zig](https://ziglang.org/download/) 0.7.1
- wayland
- wayland-protocols
- [wlroots](https://github.com/swaywm/wlroots) 0.12.0
diff --git a/build.zig b/build.zig
index 5105227..0f46824 100644
--- a/build.zig
+++ b/build.zig
@@ -1,16 +1,10 @@
const std = @import("std");
+const zbs = std.build;
const ScanProtocolsStep = @import("deps/zig-wayland/build.zig").ScanProtocolsStep;
-pub fn build(b: *std.build.Builder) !void {
- // Standard target options allows the person running `zig build` to choose
- // what target to build for. Here we do not override the defaults, which
- // means any target is allowed, and the default is native. Other options
- // for restricting supported target set are available.
+pub fn build(b: *zbs.Builder) !void {
const target = b.standardTargetOptions(.{});
-
- // Standard release options allow the person running `zig build` to select
- // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const xwayland = b.option(
@@ -31,17 +25,14 @@ pub fn build(b: *std.build.Builder) !void {
break :scdoc_found true;
};
- const examples = b.option(
- bool,
- "examples",
- "Set to true to build examples",
- ) orelse false;
-
- // TODO: port all parts of river to zig-wayland and delete this
- const scan_protocols = OldScanProtocolsStep.create(b);
+ const examples = b.option(bool, "examples", "Set to true to build examples") orelse false;
- const scanner = ScanProtocolsStep.create(b, "deps/zig-wayland/");
+ const scanner = ScanProtocolsStep.create(b);
+ scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml");
scanner.addProtocolPath("protocol/river-control-unstable-v1.xml");
+ scanner.addProtocolPath("protocol/river-status-unstable-v1.xml");
+ scanner.addProtocolPath("protocol/wlr-layer-shell-unstable-v1.xml");
+ scanner.addProtocolPath("protocol/wlr-output-power-management-unstable-v1.xml");
{
const river = b.addExecutable("river", "river/main.zig");
@@ -49,16 +40,9 @@ pub fn build(b: *std.build.Builder) !void {
river.setBuildMode(mode);
river.addBuildOption(bool, "xwayland", xwayland);
- addProtocolDeps(river, &scan_protocols.step);
- addServerDeps(river);
+ addServerDeps(river, scanner);
river.install();
-
- const run_cmd = river.run();
- run_cmd.step.dependOn(b.getInstallStep());
-
- const run_step = b.step("run", "Run the compositor");
- run_step.dependOn(&run_cmd.step);
}
{
@@ -93,11 +77,13 @@ pub fn build(b: *std.build.Builder) !void {
status.setTarget(target);
status.setBuildMode(mode);
- addProtocolDeps(status, &scan_protocols.step);
-
+ status.step.dependOn(&scanner.step);
+ status.addPackage(scanner.getPkg());
status.linkLibC();
status.linkSystemLibrary("wayland-client");
+ scanner.addCSource(status);
+
status.install();
}
@@ -107,122 +93,43 @@ pub fn build(b: *std.build.Builder) !void {
river_test.setBuildMode(mode);
river_test.addBuildOption(bool, "xwayland", xwayland);
- addProtocolDeps(river_test, &scan_protocols.step);
- addServerDeps(river_test);
+ addServerDeps(river_test, scanner);
const test_step = b.step("test", "Run the tests");
test_step.dependOn(&river_test.step);
}
}
-fn addServerDeps(exe: *std.build.LibExeObjStep) void {
- exe.addCSourceFile("include/bindings.c", &[_][]const u8{"-std=c99"});
- exe.addIncludeDir(".");
+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 wlroots = zbs.Pkg{
+ .name = "wlroots",
+ .path = "deps/zig-wlroots/src/wlroots.zig",
+ .dependencies = &[_]zbs.Pkg{ wayland, xkbcommon, pixman },
+ };
+
+ exe.step.dependOn(&scanner.step);
exe.linkLibC();
exe.linkSystemLibrary("libevdev");
- exe.linkSystemLibrary("wayland-server");
- exe.linkSystemLibrary("wlroots");
- exe.linkSystemLibrary("xkbcommon");
- exe.linkSystemLibrary("pixman-1");
-}
-
-fn addProtocolDeps(exe: *std.build.LibExeObjStep, protocol_step: *std.build.Step) void {
- exe.step.dependOn(protocol_step);
- exe.addIncludeDir("protocol");
- exe.addCSourceFile("protocol/river-control-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
- exe.addCSourceFile("protocol/river-status-unstable-v1-protocol.c", &[_][]const u8{"-std=c99"});
-}
-
-const OldScanProtocolsStep = struct {
- builder: *std.build.Builder,
- step: std.build.Step,
-
- fn create(builder: *std.build.Builder) *OldScanProtocolsStep {
- const self = builder.allocator.create(OldScanProtocolsStep) catch @panic("out of memory");
- self.* = init(builder);
- return self;
- }
- fn init(builder: *std.build.Builder) OldScanProtocolsStep {
- return OldScanProtocolsStep{
- .builder = builder,
- .step = std.build.Step.init(.Custom, "Scan Protocols", builder.allocator, make),
- };
- }
-
- fn make(step: *std.build.Step) !void {
- const self = @fieldParentPtr(OldScanProtocolsStep, "step", step);
-
- const protocol_dir = std.mem.trim(u8, try self.builder.exec(
- &[_][]const u8{ "pkg-config", "--variable=pkgdatadir", "wayland-protocols" },
- ), &std.ascii.spaces);
-
- const protocol_dir_paths = [_][]const []const u8{
- &[_][]const u8{ protocol_dir, "stable/xdg-shell/xdg-shell.xml" },
- &[_][]const u8{ "protocol", "wlr-layer-shell-unstable-v1.xml" },
- &[_][]const u8{ "protocol", "wlr-output-power-management-unstable-v1.xml" },
- &[_][]const u8{ "protocol", "river-control-unstable-v1.xml" },
- &[_][]const u8{ "protocol", "river-status-unstable-v1.xml" },
- };
-
- const server_protocols = [_][]const u8{
- "xdg-shell",
- "wlr-layer-shell-unstable-v1",
- "wlr-output-power-management-unstable-v1",
- "river-control-unstable-v1",
- "river-status-unstable-v1",
- };
-
- const client_protocols = [_][]const u8{
- "river-control-unstable-v1",
- "river-status-unstable-v1",
- };
+ exe.addPackage(wayland);
+ exe.linkSystemLibrary("wayland-server");
- for (protocol_dir_paths) |dir_path| {
- const xml_in_path = try std.fs.path.join(self.builder.allocator, dir_path);
+ exe.addPackage(xkbcommon);
+ exe.linkSystemLibrary("xkbcommon");
- // Extension is .xml, so slice off the last 4 characters
- const basename = std.fs.path.basename(xml_in_path);
- const basename_no_ext = basename[0..(basename.len - 4)];
+ exe.addPackage(pixman);
+ exe.linkSystemLibrary("pixman-1");
- const code_out_path = try std.mem.concat(
- self.builder.allocator,
- u8,
- &[_][]const u8{ "protocol/", basename_no_ext, "-protocol.c" },
- );
- _ = try self.builder.exec(
- &[_][]const u8{ "wayland-scanner", "private-code", xml_in_path, code_out_path },
- );
+ exe.addPackage(wlroots);
+ exe.linkSystemLibrary("wlroots");
- for (server_protocols) |server_protocol| {
- if (std.mem.eql(u8, basename_no_ext, server_protocol)) {
- const header_out_path = try std.mem.concat(
- self.builder.allocator,
- u8,
- &[_][]const u8{ "protocol/", basename_no_ext, "-protocol.h" },
- );
- _ = try self.builder.exec(
- &[_][]const u8{ "wayland-scanner", "server-header", xml_in_path, header_out_path },
- );
- }
- }
-
- for (client_protocols) |client_protocol| {
- if (std.mem.eql(u8, basename_no_ext, client_protocol)) {
- const 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, header_out_path },
- );
- }
- }
- }
- }
-};
+ // TODO: remove when zig issue #131 is implemented
+ scanner.addCSource(exe);
+}
const ScdocStep = struct {
const scd_paths = [_][]const u8{
@@ -232,23 +139,23 @@ const ScdocStep = struct {
"doc/river-layouts.7.scd",
};
- builder: *std.build.Builder,
- step: std.build.Step,
+ builder: *zbs.Builder,
+ step: zbs.Step,
- fn create(builder: *std.build.Builder) *ScdocStep {
+ fn create(builder: *zbs.Builder) *ScdocStep {
const self = builder.allocator.create(ScdocStep) catch @panic("out of memory");
self.* = init(builder);
return self;
}
- fn init(builder: *std.build.Builder) ScdocStep {
+ fn init(builder: *zbs.Builder) ScdocStep {
return ScdocStep{
.builder = builder,
- .step = std.build.Step.init(.Custom, "Generate man pages", builder.allocator, make),
+ .step = zbs.Step.init(.Custom, "Generate man pages", builder.allocator, make),
};
}
- fn make(step: *std.build.Step) !void {
+ fn make(step: *zbs.Step) !void {
const self = @fieldParentPtr(ScdocStep, "step", step);
for (scd_paths) |path| {
const command = try std.fmt.allocPrint(
diff --git a/deps/zig-pixman b/deps/zig-pixman
new file mode 160000
+Subproject 7847fd1bae7021cdb572e77eac93676c551fd1e
diff --git a/deps/zig-wayland b/deps/zig-wayland
-Subproject ba49b2b6f984b788aea5e752bfeb64e3381472e
+Subproject 52326e7ee09d7acb6b55855f7a697af083ae973
diff --git a/deps/zig-wlroots b/deps/zig-wlroots
new file mode 160000
+Subproject 16d9039b5c345b2cc26118032261df9782e2494
diff --git a/deps/zig-xkbcommon b/deps/zig-xkbcommon
new file mode 160000
+Subproject 9e4d41fe9414094db31c873c2ad9cadcd8999cf
diff --git a/example/status.zig b/example/status.zig
index 76cf3d8..07f255d 100644
--- a/example/status.zig
+++ b/example/status.zig
@@ -17,145 +17,77 @@
const std = @import("std");
-const c = @cImport({
- @cInclude("wayland-client.h");
- @cInclude("river-status-unstable-v1-client-protocol.h");
-});
-
-const wl_registry_listener = c.wl_registry_listener{
- .global = handleGlobal,
- .global_remove = handleGlobalRemove,
-};
-
-const river_output_status_listener = c.zriver_output_status_v1_listener{
- .focused_tags = handleFocusedTags,
- .view_tags = handleViewTags,
+const wayland = @import("wayland");
+const wl = wayland.client.wl;
+const zriver = wayland.client.zriver;
+
+const SetupContext = struct {
+ status_manager: ?*zriver.StatusManagerV1 = null,
+ outputs: std.ArrayList(*wl.Output) = std.ArrayList(*wl.Output).init(std.heap.c_allocator),
+ seats: std.ArrayList(*wl.Seat) = std.ArrayList(*wl.Seat).init(std.heap.c_allocator),
};
-const river_seat_status_listener = c.zriver_seat_status_v1_listener{
- .focused_output = handleFocusedOutput,
- .unfocused_output = handleUnfocusedOutput,
- .focused_view = handleFocusedView,
-};
-
-var river_status_manager: ?*c.zriver_status_manager_v1 = null;
-
-var outputs = std.ArrayList(*c.wl_output).init(std.heap.c_allocator);
-var seats = std.ArrayList(*c.wl_seat).init(std.heap.c_allocator);
-
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);
-
- if (c.wl_registry_add_listener(wl_registry, &wl_registry_listener, null) < 0)
- return error.FailedToAddListener;
- if (c.wl_display_roundtrip(wl_display) < 0) return error.RoundtripFailed;
+ const display = try wl.Display.connect(null);
+ const registry = try display.getRegistry();
- if (river_status_manager == null) return error.RiverStatusManagerNotAdvertised;
+ var context = SetupContext{};
- for (outputs.items) |wl_output| createOutputStatus(wl_output);
- for (seats.items) |wl_seat| createSeatStatus(wl_seat);
- outputs.deinit();
- seats.deinit();
+ registry.setListener(*SetupContext, registryListener, &context) catch unreachable;
+ _ = try display.roundtrip();
- // Loop forever, listening for new events.
- while (true) if (c.wl_display_dispatch(wl_display) < 0) return error.DispatchFailed;
-}
+ const status_manager = context.status_manager orelse return error.RiverStatusManagerNotAdvertised;
-fn handleGlobal(
- data: ?*c_void,
- wl_registry: ?*c.wl_registry,
- name: u32,
- interface: ?[*:0]const u8,
- version: u32,
-) callconv(.C) void {
- // Global advertisement order is not defined, so save any outputs or seats
- // advertised before the river_status_manager.
- if (std.cstr.cmp(interface.?, @ptrCast([*:0]const u8, c.zriver_status_manager_v1_interface.name.?)) == 0) {
- river_status_manager = @ptrCast(
- *c.zriver_status_manager_v1,
- c.wl_registry_bind(wl_registry, name, &c.zriver_status_manager_v1_interface, version),
- );
- } else if (std.cstr.cmp(interface.?, @ptrCast([*:0]const u8, c.wl_output_interface.name.?)) == 0) {
- const wl_output = @ptrCast(
- *c.wl_output,
- c.wl_registry_bind(wl_registry, name, &c.wl_output_interface, version),
- );
- outputs.append(wl_output) catch @panic("out of memory");
- } else if (std.cstr.cmp(interface.?, @ptrCast([*:0]const u8, c.wl_seat_interface.name.?)) == 0) {
- const wl_seat = @ptrCast(
- *c.wl_seat,
- c.wl_registry_bind(wl_registry, name, &c.wl_seat_interface, version),
- );
- seats.append(wl_seat) catch @panic("out of memory");
+ for (context.outputs.items) |output| {
+ const output_status = try status_manager.getRiverOutputStatus(output);
+ output_status.setListener(?*c_void, outputStatusListener, null) catch unreachable;
}
-}
-
-fn createOutputStatus(wl_output: *c.wl_output) void {
- const river_output_status = c.zriver_status_manager_v1_get_river_output_status(
- river_status_manager.?,
- wl_output,
- );
- _ = c.zriver_output_status_v1_add_listener(
- river_output_status,
- &river_output_status_listener,
- null,
- );
-}
-
-fn createSeatStatus(wl_seat: *c.wl_seat) void {
- const river_seat_status = c.zriver_status_manager_v1_get_river_seat_status(
- river_status_manager.?,
- wl_seat,
- );
- _ = c.zriver_seat_status_v1_add_listener(river_seat_status, &river_seat_status_listener, null);
-}
-
-fn handleGlobalRemove(data: ?*c_void, wl_registry: ?*c.wl_registry, name: u32) callconv(.C) void {
- // Ignore the event
-}
+ for (context.seats.items) |seat| {
+ const seat_status = try status_manager.getRiverSeatStatus(seat);
+ seat_status.setListener(?*c_void, seatStatusListener, null) catch unreachable;
+ }
+ context.outputs.deinit();
+ context.seats.deinit();
-fn handleFocusedTags(
- data: ?*c_void,
- output_status: ?*c.zriver_output_status_v1,
- tags: u32,
-) callconv(.C) void {
- std.debug.warn("Focused tags: {b:0>10}\n", .{tags});
+ // Loop forever, listening for new events.
+ while (true) _ = try display.dispatch();
}
-fn handleViewTags(
- data: ?*c_void,
- output_status: ?*c.zriver_output_status_v1,
- tags: ?*c.wl_array,
-) callconv(.C) void {
- std.debug.warn("View tags:\n", .{});
- var offset: usize = 0;
- while (offset < tags.?.size) : (offset += @sizeOf(u32)) {
- const ptr = @ptrCast([*]u8, tags.?.data) + offset;
- std.debug.warn("{b:0>10}\n", .{std.mem.bytesToValue(u32, ptr[0..4])});
+fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *SetupContext) void {
+ switch (event) {
+ .global => |global| {
+ if (std.cstr.cmp(global.interface, zriver.StatusManagerV1.getInterface().name) == 0) {
+ context.status_manager = registry.bind(global.name, zriver.StatusManagerV1, 1) catch return;
+ } else if (std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) {
+ const seat = registry.bind(global.name, wl.Seat, 1) catch return;
+ context.seats.append(seat) catch @panic("out of memory");
+ } else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
+ const output = registry.bind(global.name, wl.Output, 1) catch return;
+ context.outputs.append(output) catch @panic("out of memory");
+ }
+ },
+ .global_remove => {},
}
}
-fn handleFocusedOutput(
- data: ?*c_void,
- seat_status: ?*c.zriver_seat_status_v1,
- wl_output: ?*c.wl_output,
-) callconv(.C) void {
- std.debug.warn("Output id {} focused\n", .{c.wl_proxy_get_id(@ptrCast(*c.wl_proxy, wl_output))});
-}
-
-fn handleUnfocusedOutput(
- data: ?*c_void,
- seat_status: ?*c.zriver_seat_status_v1,
- wl_output: ?*c.wl_output,
-) callconv(.C) void {
- std.debug.warn("Output id {} unfocused\n", .{c.wl_proxy_get_id(@ptrCast(*c.wl_proxy, wl_output))});
+fn outputStatusListener(output_status: *zriver.OutputStatusV1, event: zriver.OutputStatusV1.Event, data: ?*c_void) void {
+ switch (event) {
+ .focused_tags => |focused_tags| std.debug.warn("Focused tags: {b:0>10}\n", .{focused_tags.tags}),
+ .view_tags => |view_tags| {
+ std.debug.warn("View tags:\n", .{});
+ for (view_tags.tags.slice(u32)) |t| std.debug.warn("{b:0>10}\n", .{t});
+ },
+ }
}
-fn handleFocusedView(
- data: ?*c_void,
- seat_status: ?*c.zriver_seat_status_v1,
- title: ?[*:0]const u8,
-) callconv(.C) void {
- std.debug.warn("Focused view title: {}\n", .{title.?});
+fn seatStatusListener(seat_status: *zriver.SeatStatusV1, event: zriver.SeatStatusV1.Event, data: ?*c_void) void {
+ switch (event) {
+ .focused_output => |focused_output| std.debug.warn("Output id {} focused\n", .{
+ @ptrCast(*wl.Proxy, focused_output.output orelse return).getId(),
+ }),
+ .unfocused_output => |unfocused_output| std.debug.warn("Output id {} focused\n", .{
+ @ptrCast(*wl.Proxy, unfocused_output.output orelse return).getId(),
+ }),
+ .focused_view => |focused_view| std.debug.warn("Focused view title: {}\n", .{focused_view.title}),
+ }
}
diff --git a/include/bindings.c b/include/bindings.c
deleted file mode 100644
index 8cf8651..0000000
--- a/include/bindings.c
+++ /dev/null
@@ -1,11 +0,0 @@
-#define WLR_USE_UNSTABLE
-#include <wlr/backend.h>
-#include <wlr/render/wlr_renderer.h>
-
-struct wlr_backend *river_wlr_backend_autocreate(struct wl_display *display) {
- return wlr_backend_autocreate(display, NULL);
-}
-
-struct wlr_renderer *river_wlr_backend_get_renderer(struct wlr_backend *backend) {
- return wlr_backend_get_renderer(backend);
-}
diff --git a/include/bindings.h b/include/bindings.h
deleted file mode 100644
index ce8da0b..0000000
--- a/include/bindings.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef RIVER_BINDINGS_H
-#define RIVER_BINDINGS_H
-
-#include <wlr/backend/session.h>
-
-/*
- * This header is needed since zig cannot yet translate flexible arrays.
- * See https://github.com/ziglang/zig/issues/4775
- */
-
-struct wlr_backend *river_wlr_backend_autocreate(struct wl_display *display);
-struct wlr_renderer *river_wlr_backend_get_renderer(struct wlr_backend *backend);
-
-#endif // RIVER_BINDINGS_H
diff --git a/river/Box.zig b/river/Box.zig
index 9efe073..a7094bd 100644
--- a/river/Box.zig
+++ b/river/Box.zig
@@ -17,14 +17,14 @@
const Self = @This();
-const c = @import("c.zig");
+const wlr = @import("wlroots");
x: i32,
y: i32,
width: u32,
height: u32,
-pub fn fromWlrBox(wlr_box: c.wlr_box) Self {
+pub fn fromWlrBox(wlr_box: wlr.Box) Self {
return Self{
.x = @intCast(i32, wlr_box.x),
.y = @intCast(i32, wlr_box.y),
@@ -33,8 +33,8 @@ pub fn fromWlrBox(wlr_box: c.wlr_box) Self {
};
}
-pub fn toWlrBox(self: Self) c.wlr_box {
- return c.wlr_box{
+pub fn toWlrBox(self: Self) wlr.Box {
+ return wlr.Box{
.x = @intCast(c_int, self.x),
.y = @intCast(c_int, self.y),
.width = @intCast(c_int, self.width),
diff --git a/river/Control.zig b/river/Control.zig
index eb78f33..eca712a 100644
--- a/river/Control.zig
+++ b/river/Control.zig
@@ -18,6 +18,13 @@
const Self = @This();
const std = @import("std");
+const mem = std.mem;
+
+const wayland = @import("wayland");
+const wl = wayland.server.wl;
+const zriver = wayland.server.zriver;
+
+const wlr = @import("wlroots");
const c = @import("c.zig");
const command = @import("command.zig");
@@ -26,141 +33,106 @@ const util = @import("util.zig");
const Seat = @import("Seat.zig");
const Server = @import("Server.zig");
-const protocol_version = 1;
-
-const implementation = c.struct_zriver_control_v1_interface{
- .destroy = destroy,
- .add_argument = addArgument,
- .run_command = runCommand,
-};
-
-wl_global: *c.wl_global,
+global: *wl.Global,
args_map: std.AutoHashMap(u32, std.ArrayList([]const u8)),
-listen_display_destroy: c.wl_listener = undefined,
+server_destroy: wl.Listener(*wl.Server) = undefined,
pub fn init(self: *Self, server: *Server) !void {
self.* = .{
- .wl_global = c.wl_global_create(
- server.wl_display,
- &c.zriver_control_v1_interface,
- protocol_version,
- self,
- bind,
- ) orelse return error.OutOfMemory,
+ .global = try wl.Global.create(server.wl_server, zriver.ControlV1, 1, *Self, self, bind),
.args_map = std.AutoHashMap(u32, std.ArrayList([]const u8)).init(util.gpa),
};
- self.listen_display_destroy.notify = handleDisplayDestroy;
- c.wl_display_add_destroy_listener(server.wl_display, &self.listen_display_destroy);
+ self.server_destroy.setNotify(handleServerDestroy);
+ server.wl_server.addDestroyListener(&self.server_destroy);
}
-fn handleDisplayDestroy(wl_listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_display_destroy", wl_listener.?);
- c.wl_global_destroy(self.wl_global);
+fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server) void {
+ const self = @fieldParentPtr(Self, "server_destroy", listener);
+ self.global.destroy();
self.args_map.deinit();
}
/// Called when a client binds our global
-fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callconv(.C) void {
- const self = util.voidCast(Self, data.?);
- const wl_resource = c.wl_resource_create(
- wl_client,
- &c.zriver_control_v1_interface,
- @intCast(c_int, version),
- id,
- ) orelse {
- c.wl_client_post_no_memory(wl_client);
+fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
+ const control = zriver.ControlV1.create(client, version, id) catch {
+ client.postNoMemory();
return;
};
self.args_map.putNoClobber(id, std.ArrayList([]const u8).init(util.gpa)) catch {
- c.wl_resource_destroy(wl_resource);
- c.wl_client_post_no_memory(wl_client);
- return;
- };
- c.wl_resource_set_implementation(wl_resource, &implementation, self, handleResourceDestroy);
-}
-
-/// Remove the resource from the hash map and free all stored args
-fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
- const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
- const id = c.wl_resource_get_id(wl_resource);
- const list = self.args_map.remove(id).?.value;
- for (list.items) |arg| list.allocator.free(arg);
- list.deinit();
-}
-
-fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {
- c.wl_resource_destroy(wl_resource);
-}
-
-fn addArgument(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource, arg: ?[*:0]const u8) callconv(.C) void {
- const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
- const id = c.wl_resource_get_id(wl_resource);
-
- const owned_slice = std.mem.dupe(util.gpa, u8, std.mem.span(arg.?)) catch {
- c.wl_client_post_no_memory(wl_client);
- return;
- };
-
- self.args_map.getEntry(id).?.value.append(owned_slice) catch {
- c.wl_client_post_no_memory(wl_client);
- util.gpa.free(owned_slice);
+ control.destroy();
+ client.postNoMemory();
return;
};
+ control.setHandler(*Self, handleRequest, handleDestroy, self);
}
-fn runCommand(
- wl_client: ?*c.wl_client,
- wl_resource: ?*c.wl_resource,
- seat_wl_resource: ?*c.wl_resource,
- callback_id: u32,
-) callconv(.C) void {
- const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
- // This can be null if the seat is inert, in which case we ignore the request
- const wlr_seat_client = c.wlr_seat_client_from_resource(seat_wl_resource) orelse return;
- const seat = util.voidCast(Seat, wlr_seat_client.*.seat.*.data.?);
-
- const callback_resource = c.wl_resource_create(
- wl_client,
- &c.zriver_command_callback_v1_interface,
- protocol_version,
- callback_id,
- ) orelse {
- c.wl_client_post_no_memory(wl_client);
- return;
- };
- c.wl_resource_set_implementation(callback_resource, null, null, null);
-
- const args = self.args_map.get(c.wl_resource_get_id(wl_resource)).?.items;
+fn handleRequest(control: *zriver.ControlV1, request: zriver.ControlV1.Request, self: *Self) void {
+ switch (request) {
+ .destroy => control.destroy(),
+ .add_argument => |add_argument| {
+ const owned_slice = mem.dupe(util.gpa, u8, mem.span(add_argument.argument)) catch {
+ control.getClient().postNoMemory();
+ return;
+ };
- var out: ?[]const u8 = null;
- defer if (out) |s| util.gpa.free(s);
- command.run(util.gpa, seat, args, &out) catch |err| {
- const failure_message = switch (err) {
- command.Error.OutOfMemory => {
- c.wl_client_post_no_memory(wl_client);
+ self.args_map.getEntry(control.getId()).?.value.append(owned_slice) catch {
+ control.getClient().postNoMemory();
+ util.gpa.free(owned_slice);
return;
- },
- command.Error.Other => std.cstr.addNullByte(util.gpa, out.?) catch {
- c.wl_client_post_no_memory(wl_client);
+ };
+ },
+ .run_command => |run_command| {
+ const seat = @intToPtr(*Seat, wlr.Seat.Client.fromWlSeat(run_command.seat).?.seat.data);
+
+ const callback = zriver.CommandCallbackV1.create(
+ control.getClient(),
+ control.getVersion(),
+ run_command.callback,
+ ) catch {
+ control.getClient().postNoMemory();
return;
- },
- else => command.errToMsg(err),
- };
- defer if (err == command.Error.Other) util.gpa.free(failure_message);
- c.zriver_command_callback_v1_send_failure(callback_resource, failure_message);
- return;
- };
+ };
+
+ const args = self.args_map.get(control.getId()).?.items;
+
+ var out: ?[]const u8 = null;
+ defer if (out) |s| util.gpa.free(s);
+ command.run(util.gpa, seat, args, &out) catch |err| {
+ const failure_message = switch (err) {
+ command.Error.OutOfMemory => {
+ callback.getClient().postNoMemory();
+ return;
+ },
+ command.Error.Other => std.cstr.addNullByte(util.gpa, out.?) catch {
+ callback.getClient().postNoMemory();
+ return;
+ },
+ else => command.errToMsg(err),
+ };
+ defer if (err == command.Error.Other) util.gpa.free(failure_message);
+ callback.sendFailure(failure_message);
+ return;
+ };
+
+ const success_message = if (out) |s|
+ std.cstr.addNullByte(util.gpa, s) catch {
+ callback.getClient().postNoMemory();
+ return;
+ }
+ else
+ "";
+ defer if (out != null) util.gpa.free(success_message);
+ callback.sendSuccess(success_message);
+ },
+ }
+}
- const success_message = if (out) |s|
- std.cstr.addNullByte(util.gpa, s) catch {
- c.wl_client_post_no_memory(wl_client);
- return;
- }
- else
- "";
- defer if (out != null) util.gpa.free(success_message);
- c.zriver_command_callback_v1_send_success(callback_resource, success_message);
+/// Remove the resource from the hash map and free all stored args
+fn handleDestroy(control: *zriver.ControlV1, self: *Self) void {
+ const list = self.args_map.remove(control.getId()).?.value;
+ for (list.items) |arg| list.allocator.free(arg);
+ list.deinit();
}
diff --git a/river/Cursor.zig b/river/Cursor.zig
index ae31e51..b10695b 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -19,6 +19,10 @@ const Self = @This();
const build_options = @import("build_options");
const std = @import("std");
+const wlr = @import("wlroots");
+const wayland = @import("wayland");
+const wl = wayland.server.wl;
+const zwlr = wayland.server.zwlr;
const c = @import("c.zig");
const log = @import("log.zig");
@@ -50,34 +54,34 @@ const default_size = 24;
mode: Mode = .passthrough,
seat: *Seat,
-wlr_cursor: *c.wlr_cursor,
-wlr_xcursor_manager: *c.wlr_xcursor_manager,
+wlr_cursor: *wlr.Cursor,
+xcursor_manager: *wlr.XcursorManager,
/// Number of distinct buttons currently pressed
pressed_count: u32 = 0,
-listen_axis: c.wl_listener = undefined,
-listen_button: c.wl_listener = undefined,
-listen_frame: c.wl_listener = undefined,
-listen_motion_absolute: c.wl_listener = undefined,
-listen_motion: c.wl_listener = undefined,
-listen_request_set_cursor: c.wl_listener = undefined,
+axis: wl.Listener(*wlr.Pointer.event.Axis) = undefined,
+button: wl.Listener(*wlr.Pointer.event.Button) = undefined,
+frame: wl.Listener(*wlr.Cursor) = undefined,
+motion_absolute: wl.Listener(*wlr.Pointer.event.MotionAbsolute) = undefined,
+motion: wl.Listener(*wlr.Pointer.event.Motion) = undefined,
+request_set_cursor: wl.Listener(*wlr.Seat.event.RequestSetCursor) = undefined,
pub fn init(self: *Self, seat: *Seat) !void {
- const wlr_cursor = c.wlr_cursor_create() orelse return error.OutOfMemory;
- errdefer c.wlr_cursor_destroy(wlr_cursor);
- c.wlr_cursor_attach_output_layout(wlr_cursor, seat.input_manager.server.root.wlr_output_layout);
+ const wlr_cursor = try wlr.Cursor.create();
+ errdefer wlr_cursor.destroy();
+ wlr_cursor.attachOutputLayout(seat.input_manager.server.root.output_layout);
- // This is here so that self.wlr_xcursor_manager doesn't need to be an
+ // This is here so that self.xcursor_manager doesn't need to be an
// optional pointer. This isn't optimal as it does a needless allocation,
// but this is not a hot path.
- const wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, default_size) orelse return error.OutOfMemory;
- errdefer c.wlr_xcursor_manager_destroy(wlr_xcursor_manager);
+ const xcursor_manager = try wlr.XcursorManager.create(null, default_size);
+ errdefer xcursor_manager.destroy();
self.* = .{
.seat = seat,
.wlr_cursor = wlr_cursor,
- .wlr_xcursor_manager = wlr_xcursor_manager,
+ .xcursor_manager = xcursor_manager,
};
try self.setTheme(null, null);
@@ -87,28 +91,28 @@ pub fn init(self: *Self, seat: *Seat) !void {
// can choose how we want to process them, forwarding them to clients and
// moving the cursor around. See following post for more detail:
// https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
- self.listen_axis.notify = handleAxis;
- c.wl_signal_add(&self.wlr_cursor.events.axis, &self.listen_axis);
+ self.axis.setNotify(handleAxis);
+ self.wlr_cursor.events.axis.add(&self.axis);
- self.listen_button.notify = handleButton;
- c.wl_signal_add(&self.wlr_cursor.events.button, &self.listen_button);
+ self.button.setNotify(handleButton);
+ self.wlr_cursor.events.button.add(&self.button);
- self.listen_frame.notify = handleFrame;
- c.wl_signal_add(&self.wlr_cursor.events.frame, &self.listen_frame);
+ self.frame.setNotify(handleFrame);
+ self.wlr_cursor.events.frame.add(&self.frame);
- self.listen_motion_absolute.notify = handleMotionAbsolute;
- c.wl_signal_add(&self.wlr_cursor.events.motion_absolute, &self.listen_motion_absolute);
+ self.motion_absolute.setNotify(handleMotionAbsolute);
+ self.wlr_cursor.events.motion_absolute.add(&self.motion_absolute);
- self.listen_motion.notify = handleMotion;
- c.wl_signal_add(&self.wlr_cursor.events.motion, &self.listen_motion);
+ self.motion.setNotify(handleMotion);
+ self.wlr_cursor.events.motion.add(&self.motion);
- self.listen_request_set_cursor.notify = handleRequestSetCursor;
- c.wl_signal_add(&self.seat.wlr_seat.events.request_set_cursor, &self.listen_request_set_cursor);
+ self.request_set_cursor.setNotify(handleRequestSetCursor);
+ self.seat.wlr_seat.events.request_set_cursor.add(&self.request_set_cursor);
}
pub fn deinit(self: *Self) void {
- c.wlr_xcursor_manager_destroy(self.wlr_xcursor_manager);
- c.wlr_cursor_destroy(self.wlr_cursor);
+ self.xcursor_manager.destroy();
+ self.wlr_cursor.destroy();
}
/// Set the cursor theme for the given seat, as well as the xwayland theme if
@@ -118,15 +122,14 @@ pub fn setTheme(self: *Self, theme: ?[*:0]const u8, _size: ?u32) !void {
const server = self.seat.input_manager.server;
const size = _size orelse default_size;
- c.wlr_xcursor_manager_destroy(self.wlr_xcursor_manager);
- self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(theme, size) orelse
- return error.OutOfMemory;
+ self.xcursor_manager.destroy();
+ self.xcursor_manager = try wlr.XcursorManager.create(theme, size);
// For each output, ensure a theme of the proper scale is loaded
var it = server.root.outputs.first;
while (it) |node| : (it = node.next) {
const wlr_output = node.data.wlr_output;
- if (!c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, wlr_output.scale))
+ self.xcursor_manager.load(wlr_output.scale) catch
log.err(.cursor, "failed to load xcursor theme '{}' at scale {}", .{ theme, wlr_output.scale });
}
@@ -139,19 +142,20 @@ pub fn setTheme(self: *Self, theme: ?[*:0]const u8, _size: ?u32) !void {
if (theme) |t| if (c.setenv("XCURSOR_THEME", t, 1) < 0) return error.OutOfMemory;
if (build_options.xwayland) {
- if (c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, 1)) {
- const wlr_xcursor = c.wlr_xcursor_manager_get_xcursor(self.wlr_xcursor_manager, "left_ptr", 1).?;
- const image: *c.wlr_xcursor_image = wlr_xcursor.*.images[0];
- c.wlr_xwayland_set_cursor(
- server.wlr_xwayland,
- image.buffer,
- image.width * 4,
- image.width,
- image.height,
- @intCast(i32, image.hotspot_x),
- @intCast(i32, image.hotspot_y),
- );
- } else log.err(.cursor, "failed to load xcursor theme '{}' at scale 1", .{theme});
+ self.xcursor_manager.load(1) catch {
+ log.err(.cursor, "failed to load xcursor theme '{}' at scale 1", .{theme});
+ return;
+ };
+ const wlr_xcursor = self.xcursor_manager.getXcursor("left_ptr", 1).?;
+ const image = wlr_xcursor.images[0];
+ server.xwayland.setCursor(
+ image.buffer,
+ image.width * 4,
+ image.width,
+ image.height,
+ @intCast(i32, image.hotspot_x),
+ @intCast(i32, image.hotspot_y),
+ );
}
}
}
@@ -173,25 +177,18 @@ pub fn handleViewUnmap(self: *Self, view: *View) void {
}
fn clearFocus(self: Self) void {
- c.wlr_xcursor_manager_set_cursor_image(
- self.wlr_xcursor_manager,
- "left_ptr",
- self.wlr_cursor,
- );
- c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
+ self.xcursor_manager.setCursorImage("left_ptr", self.wlr_cursor);
+ self.seat.wlr_seat.pointerNotifyClearFocus();
}
-fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits an axis event,
- // for example when you move the scroll wheel.
- const self = @fieldParentPtr(Self, "listen_axis", listener.?);
- const event = util.voidCast(c.wlr_event_pointer_axis, data.?);
+/// Axis event is a scroll wheel or similiar
+fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Pointer.event.Axis) void {
+ const self = @fieldParentPtr(Self, "axis", listener);
self.seat.handleActivity();
// Notify the client with pointer focus of the axis event.
- c.wlr_seat_pointer_notify_axis(
- self.seat.wlr_seat,
+ self.seat.wlr_seat.pointerNotifyAxis(
event.time_msec,
event.orientation,
event.delta,
@@ -200,15 +197,12 @@ fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
);
}
-fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits a button
- // event.
- const self = @fieldParentPtr(Self, "listen_button", listener.?);
- const event = util.voidCast(c.wlr_event_pointer_button, data.?);
+fn handleButton(listener: *wl.Listener(*wlr.Pointer.event.Button), event: *wlr.Pointer.event.Button) void {
+ const self = @fieldParentPtr(Self, "button", listener);
self.seat.handleActivity();
- if (event.state == .WLR_BUTTON_PRESSED) {
+ if (event.state == .pressed) {
self.pressed_count += 1;
} else {
std.debug.assert(self.pressed_count > 0);
@@ -221,21 +215,21 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
var sx: f64 = undefined;
var sy: f64 = undefined;
- if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
+ if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |surface| {
// If the found surface is a keyboard inteactive layer surface,
// give it keyboard focus.
- if (c.wlr_surface_is_layer_surface(wlr_surface)) {
- const wlr_layer_surface = c.wlr_layer_surface_v1_from_wlr_surface(wlr_surface);
- if (wlr_layer_surface.*.current.keyboard_interactive) {
- const layer_surface = util.voidCast(LayerSurface, wlr_layer_surface.*.data.?);
+ if (surface.isLayerSurface()) {
+ const wlr_layer_surface = wlr.LayerSurfaceV1.fromWlrSurface(surface);
+ if (wlr_layer_surface.current.keyboard_interactive) {
+ const layer_surface = @intToPtr(*LayerSurface, wlr_layer_surface.data);
self.seat.setFocusRaw(.{ .layer = layer_surface });
}
}
// If the target surface has a view, give that view keyboard focus and
// perhaps enter move/resize mode.
- if (View.fromWlrSurface(wlr_surface)) |view| {
- if (event.state == .WLR_BUTTON_PRESSED and self.pressed_count == 1) {
+ if (View.fromWlrSurface(surface)) |view| {
+ if (event.state == .pressed and self.pressed_count == 1) {
// If there is an active mapping for this button which is
// handled we are done here
if (self.handlePointerMapping(event, view)) return;
@@ -244,26 +238,21 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
}
- _ = c.wlr_seat_pointer_notify_button(
- self.seat.wlr_seat,
- event.time_msec,
- event.button,
- event.state,
- );
+ _ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
}
}
/// Handle the mapping for the passed button if any. Returns true if there
/// was a mapping and the button was handled.
-fn handlePointerMapping(self: *Self, event: *c.wlr_event_pointer_button, view: *View) bool {
- const wlr_keyboard = c.wlr_seat_get_keyboard(self.seat.wlr_seat);
- const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
+fn handlePointerMapping(self: *Self, event: *wlr.Pointer.event.Button, view: *View) bool {
+ const wlr_keyboard = self.seat.wlr_seat.getKeyboard() orelse return false;
+ const modifiers = wlr_keyboard.getModifiers();
const fullscreen = view.current.fullscreen or view.pending.fullscreen;
const config = self.seat.input_manager.server.config;
return for (config.modes.items[self.seat.mode_id].pointer_mappings.items) |mapping| {
- if (event.button == mapping.event_code and modifiers == mapping.modifiers) {
+ if (event.button == mapping.event_code and std.meta.eql(modifiers, mapping.modifiers)) {
switch (mapping.action) {
.move => if (!fullscreen) self.enterMode(.move, view),
.resize => if (!fullscreen) self.enterMode(.resize, view),
@@ -273,50 +262,54 @@ fn handlePointerMapping(self: *Self, event: *c.wlr_event_pointer_button, view: *
} else false;
}
-fn handleFrame(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits an frame
- // event. 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.
- const self = @fieldParentPtr(Self, "listen_frame", listener.?);
- // Notify the client with pointer focus of the frame event.
- c.wlr_seat_pointer_notify_frame(self.seat.wlr_seat);
+/// 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 {
+ const self = @fieldParentPtr(Self, "frame", listener);
+ self.seat.wlr_seat.pointerNotifyFrame();
}
-fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits an _absolute_
- // motion event, from 0..1 on each axis. This happens, for example, when
- // wlroots is running under a Wayland window rather than KMS+DRM, and you
- // move the mouse over the window. You could enter the window from any edge,
- // so we have to warp the mouse there. There is also some hardware which
- // emits these events.
- const self = @fieldParentPtr(Self, "listen_motion_absolute", listener.?);
- const event = util.voidCast(c.wlr_event_pointer_motion_absolute, data.?);
+/// This event is forwarded by the cursor when a pointer emits an _absolute_
+/// motion event, from 0..1 on each axis. This happens, for example, when
+/// wlroots is running under a Wayland window rather than KMS+DRM, and you
+/// move the mouse over the window. You could enter the window from any edge,
+/// so we have to warp the mouse there. There is also some hardware which
+/// emits these events.
+fn handleMotionAbsolute(
+ listener: *wl.Listener(*wlr.Pointer.event.MotionAbsolute),
+ event: *wlr.Pointer.event.MotionAbsolute,
+) void {
+ const self = @fieldParentPtr(Self, "motion_absolute", listener);
self.seat.handleActivity();
var lx: f64 = undefined;
var ly: f64 = undefined;
- c.wlr_cursor_absolute_to_layout_coords(self.wlr_cursor, event.device, event.x, event.y, &lx, &ly);
+ self.wlr_cursor.absoluteToLayoutCoords(event.device, event.x, event.y, &lx, &ly);
self.processMotion(event.device, event.time_msec, lx - self.wlr_cursor.x, ly - self.wlr_cursor.y);
}
-fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits a _relative_
- // pointer motion event (i.e. a delta)
- const self = @fieldParentPtr(Self, "listen_motion", listener.?);
- const event = util.voidCast(c.wlr_event_pointer_motion, data.?);
+/// This event is forwarded by the cursor when a pointer emits a _relative_
+/// pointer motion event (i.e. a delta)
+fn handleMotion(
+ listener: *wl.Listener(*wlr.Pointer.event.Motion),
+ event: *wlr.Pointer.event.Motion,
+) void {
+ const self = @fieldParentPtr(Self, "motion", listener);
self.seat.handleActivity();
self.processMotion(event.device, event.time_msec, event.delta_x, event.delta_y);
}
-fn handleRequestSetCursor(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+fn handleRequestSetCursor(
+ listener: *wl.Listener(*wlr.Seat.event.RequestSetCursor),
+ event: *wlr.Seat.event.RequestSetCursor,
+) void {
// This event is rasied by the seat when a client provides a cursor image
- const self = @fieldParentPtr(Self, "listen_request_set_cursor", listener.?);
- const event = util.voidCast(c.wlr_seat_pointer_request_set_cursor_event, data.?);
+ const self = @fieldParentPtr(Self, "request_set_cursor", listener);
const focused_client = self.seat.wlr_seat.pointer_state.focused_client;
// This can be sent by any client, so we check to make sure this one is
@@ -327,53 +320,40 @@ fn handleRequestSetCursor(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C
// on the output that it's currently on and continue to do so as the
// cursor moves between outputs.
log.debug(.cursor, "focused client set cursor", .{});
- c.wlr_cursor_set_surface(
- self.wlr_cursor,
- event.surface,
- event.hotspot_x,
- event.hotspot_y,
- );
+ self.wlr_cursor.setSurface(event.surface, event.hotspot_x, event.hotspot_y);
}
}
/// Find the topmost surface under the output layout coordinates lx/ly
/// returns the surface if found and sets the sx/sy parametes to the
/// surface coordinates.
-fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
+fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
// Find the output to check
const root = self.seat.input_manager.server.root;
- const wlr_output = c.wlr_output_layout_output_at(root.wlr_output_layout, lx, ly) orelse return null;
- const output = util.voidCast(Output, wlr_output.*.data orelse return null);
+ const wlr_output = root.output_layout.outputAt(lx, ly) orelse return null;
+ const output = @intToPtr(*Output, wlr_output.data);
// Get output-local coords from the layout coords
var ox = lx;
var oy = ly;
- c.wlr_output_layout_output_coords(root.wlr_output_layout, wlr_output, &ox, &oy);
-
- // Check layers and views from top to bottom
- const layer_idxs = [_]usize{
- c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
- c.ZWLR_LAYER_SHELL_V1_LAYER_TOP,
- c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM,
- c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
- };
+ root.output_layout.outputCoords(wlr_output, &ox, &oy);
// Check overlay layer incl. popups
- if (layerSurfaceAt(output.*, output.layers[layer_idxs[0]], ox, oy, sx, sy, false)) |s| return s;
+ if (layerSurfaceAt(output.*, output.getLayer(.overlay).*, ox, oy, sx, sy, false)) |s| return s;
// Check top-background popups only
- for (layer_idxs[1..4]) |idx|
- if (layerSurfaceAt(output.*, output.layers[idx], ox, oy, sx, sy, true)) |s| return s;
+ for ([_]zwlr.LayerShellV1.Layer{ .top, .bottom, .background }) |layer|
+ if (layerSurfaceAt(output.*, output.getLayer(layer).*, ox, oy, sx, sy, true)) |s| return s;
// Check top layer
- if (layerSurfaceAt(output.*, output.layers[layer_idxs[1]], ox, oy, sx, sy, false)) |s| return s;
+ if (layerSurfaceAt(output.*, output.getLayer(.top).*, ox, oy, sx, sy, false)) |s| return s;
// Check views
if (viewSurfaceAt(output.*, ox, oy, sx, sy)) |s| return s;
// Check the bottom-background layers
- for (layer_idxs[2..4]) |idx|
- if (layerSurfaceAt(output.*, output.layers[idx], ox, oy, sx, sy, false)) |s| return s;
+ for ([_]zwlr.LayerShellV1.Layer{ .bottom, .background }) |layer|
+ if (layerSurfaceAt(output.*, output.getLayer(layer).*, ox, oy, sx, sy, false)) |s| return s;
return null;
}
@@ -388,25 +368,20 @@ fn layerSurfaceAt(
sx: *f64,
sy: *f64,
popups_only: bool,
-) ?*c.wlr_surface {
+) ?*wlr.Surface {
var it = layer.first;
while (it) |node| : (it = node.next) {
const layer_surface = &node.data;
- const surface = c.wlr_layer_surface_v1_surface_at(
- layer_surface.wlr_layer_surface,
+ if (layer_surface.wlr_layer_surface.surfaceAt(
ox - @intToFloat(f64, layer_surface.box.x),
oy - @intToFloat(f64, layer_surface.box.y),
sx,
sy,
- );
- if (surface) |found| {
+ )) |found| {
if (!popups_only) {
return found;
- } else if (c.wlr_surface_is_xdg_surface(found)) {
- const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(found);
- if (wlr_xdg_surface.*.role == .WLR_XDG_SURFACE_ROLE_POPUP) {
- return found;
- }
+ } else if (found.isXdgSurface() and wlr.XdgSurface.fromWlrSurface(found).role == .popup) {
+ return found;
}
}
}
@@ -414,7 +389,7 @@ fn layerSurfaceAt(
}
/// Find the topmost visible view surface (incl. popups) at ox,oy.
-fn viewSurfaceAt(output: Output, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
+fn viewSurfaceAt(output: Output, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
// Focused views are rendered on top, so look for them first.
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, surfaceAtFilter);
while (it.next()) |view| {
@@ -468,10 +443,9 @@ pub fn enterMode(self: *Self, mode: @TagType(Mode), view: *View) void {
}
// Clear cursor focus, so that the surface does not receive events
- c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
+ self.seat.wlr_seat.pointerNotifyClearFocus();
- c.wlr_xcursor_manager_set_cursor_image(
- self.wlr_xcursor_manager,
+ self.xcursor_manager.setCursorImage(
if (mode == .move) "move" else "se-resize",
self.wlr_cursor,
);
@@ -480,36 +454,30 @@ pub fn enterMode(self: *Self, mode: @TagType(Mode), view: *View) void {
}
/// Return from down/move/resize to passthrough
-fn leaveMode(self: *Self, event: *c.wlr_event_pointer_button) void {
+fn leaveMode(self: *Self, event: *wlr.Pointer.event.Button) void {
std.debug.assert(self.mode != .passthrough);
log.debug(.cursor, "leave {} mode", .{@tagName(self.mode)});
// If we were in down mode, we need pass along the release event
if (self.mode == .down)
- _ = c.wlr_seat_pointer_notify_button(
- self.seat.wlr_seat,
- event.time_msec,
- event.button,
- event.state,
- );
+ _ = self.seat.wlr_seat.pointerNotifyButton(event.time_msec, event.button, event.state);
self.mode = .passthrough;
self.passthrough(event.time_msec);
}
-fn processMotion(self: *Self, device: *c.wlr_input_device, time: u32, delta_x: f64, delta_y: f64) void {
+fn processMotion(self: *Self, device: *wlr.InputDevice, time: u32, delta_x: f64, delta_y: f64) void {
const config = self.seat.input_manager.server.config;
switch (self.mode) {
.passthrough => {
- c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
+ self.wlr_cursor.move(device, delta_x, delta_y);
self.passthrough(time);
},
.down => |view| {
- c.wlr_cursor_move(self.wlr_cursor, device, delta_x, delta_y);
- c.wlr_seat_pointer_notify_motion(
- self.seat.wlr_seat,
+ self.wlr_cursor.move(device, delta_x, delta_y);
+ self.seat.wlr_seat.pointerNotifyMotion(
time,
self.wlr_cursor.x - @intToFloat(f64, view.current.box.x),
self.wlr_cursor.y - @intToFloat(f64, view.current.box.y),
@@ -531,8 +499,7 @@ fn processMotion(self: *Self, device: *c.wlr_input_device, time: u32, delta_x: f
@intCast(i32, output_resolution.height - view.pending.box.height - border_width),
);
- c.wlr_cursor_move(
- self.wlr_cursor,
+ self.wlr_cursor.move(
device,
@intToFloat(f64, view.pending.box.x - view.current.box.x),
@intToFloat(f64, view.pending.box.y - view.current.box.y),
@@ -557,8 +524,7 @@ fn processMotion(self: *Self, device: *c.wlr_input_device, time: u32, delta_x: f
data.view.applyPending();
// Keep cursor locked to the original offset from the bottom right corner
- c.wlr_cursor_warp_closest(
- self.wlr_cursor,
+ self.wlr_cursor.warpClosest(
device,
@intToFloat(f64, box.x + @intCast(i32, box.width) - data.offset_x),
@intToFloat(f64, box.y + @intCast(i32, box.height) - data.offset_y),
@@ -574,20 +540,20 @@ fn passthrough(self: *Self, time: u32) void {
var sx: f64 = undefined;
var sy: f64 = undefined;
- if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
+ if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |surface| {
// If input is allowed on the surface, send pointer enter and motion
// events. Note that wlroots won't actually send an enter event if
// the surface has already been entered.
- if (self.seat.input_manager.inputAllowed(wlr_surface)) {
+ if (self.seat.input_manager.inputAllowed(surface)) {
// The focus change must be checked before sending enter events
- const focus_change = self.seat.wlr_seat.pointer_state.focused_surface != wlr_surface;
+ const focus_change = self.seat.wlr_seat.pointer_state.focused_surface != surface;
- c.wlr_seat_pointer_notify_enter(self.seat.wlr_seat, wlr_surface, sx, sy);
- c.wlr_seat_pointer_notify_motion(self.seat.wlr_seat, time, sx, sy);
+ self.seat.wlr_seat.pointerNotifyEnter(surface, sx, sy);
+ self.seat.wlr_seat.pointerNotifyMotion(time, sx, sy);
const follow_mode = config.focus_follows_cursor;
if (follow_mode == .strict or (follow_mode == .normal and focus_change)) {
- if (View.fromWlrSurface(wlr_surface)) |view| {
+ if (View.fromWlrSurface(surface)) |view| {
self.seat.focus(view);
self.seat.focusOutput(view.output);
root.startTransaction();
diff --git a/river/Decoration.zig b/river/Decoration.zig
index f1d06ab..70cd138 100644
--- a/river/Decoration.zig
+++ b/river/Decoration.zig
@@ -18,53 +18,56 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const util = @import("util.zig");
const Server = @import("Server.zig");
server: *Server,
-wlr_xdg_toplevel_decoration: *c.wlr_xdg_toplevel_decoration_v1,
+xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
-listen_destroy: c.wl_listener = undefined,
-listen_request_mode: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.XdgToplevelDecorationV1) = undefined,
+request_mode: wl.Listener(*wlr.XdgToplevelDecorationV1) = undefined,
pub fn init(
self: *Self,
server: *Server,
- wlr_xdg_toplevel_decoration: *c.wlr_xdg_toplevel_decoration_v1,
+ xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
) void {
- self.* = .{ .server = server, .wlr_xdg_toplevel_decoration = wlr_xdg_toplevel_decoration };
+ self.* = .{ .server = server, .xdg_toplevel_decoration = xdg_toplevel_decoration };
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&self.wlr_xdg_toplevel_decoration.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ self.xdg_toplevel_decoration.events.destroy.add(&self.destroy);
- self.listen_request_mode.notify = handleRequestMode;
- c.wl_signal_add(&self.wlr_xdg_toplevel_decoration.events.request_mode, &self.listen_request_mode);
+ self.request_mode.setNotify(handleRequestMode);
+ self.xdg_toplevel_decoration.events.request_mode.add(&self.request_mode);
- handleRequestMode(&self.listen_request_mode, self.wlr_xdg_toplevel_decoration);
+ handleRequestMode(&self.request_mode, self.xdg_toplevel_decoration);
}
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(
+ listener: *wl.Listener(*wlr.XdgToplevelDecorationV1),
+ xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
+) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
util.gpa.destroy(self);
}
-fn handleRequestMode(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_mode", listener.?);
+fn handleRequestMode(
+ listener: *wl.Listener(*wlr.XdgToplevelDecorationV1),
+ xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
+) void {
+ const self = @fieldParentPtr(Self, "request_mode", listener);
- const wlr_xdg_surface: *c.wlr_xdg_surface = self.wlr_xdg_toplevel_decoration.surface;
- const wlr_xdg_toplevel: *c.wlr_xdg_toplevel = @field(wlr_xdg_surface, c.wlr_xdg_surface_union).toplevel;
- const app_id: [*:0]const u8 = if (wlr_xdg_toplevel.app_id) |id| id else "NULL";
+ const toplevel = self.xdg_toplevel_decoration.surface.role_data.toplevel;
+ const app_id: [*:0]const u8 = if (toplevel.app_id) |id| id else "NULL";
- _ = c.wlr_xdg_toplevel_decoration_v1_set_mode(
- self.wlr_xdg_toplevel_decoration,
+ _ = self.xdg_toplevel_decoration.setMode(
for (self.server.config.csd_filter.items) |filter_app_id| {
- if (std.mem.eql(u8, std.mem.span(app_id), filter_app_id)) {
- break .WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
- }
- } else .WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE,
+ if (std.mem.eql(u8, std.mem.span(app_id), filter_app_id)) break .client_side;
+ } else .server_side,
);
}
diff --git a/river/DecorationManager.zig b/river/DecorationManager.zig
index 22998b5..b4d6383 100644
--- a/river/DecorationManager.zig
+++ b/river/DecorationManager.zig
@@ -18,8 +18,9 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const util = @import("util.zig");
const Decoration = @import("Decoration.zig");
@@ -27,31 +28,28 @@ const Server = @import("Server.zig");
server: *Server,
-wlr_xdg_decoration_manager: *c.wlr_xdg_decoration_manager_v1,
+xdg_decoration_manager: *wlr.XdgDecorationManagerV1,
-listen_new_toplevel_decoration: c.wl_listener = undefined,
+new_toplevel_decoration: wl.Listener(*wlr.XdgToplevelDecorationV1) = undefined,
pub fn init(self: *Self, server: *Server) !void {
self.* = .{
.server = server,
- .wlr_xdg_decoration_manager = c.wlr_xdg_decoration_manager_v1_create(server.wl_display) orelse
- return error.OutOfMemory,
+ .xdg_decoration_manager = try wlr.XdgDecorationManagerV1.create(server.wl_server),
};
- self.listen_new_toplevel_decoration.notify = handleNewToplevelDecoration;
- c.wl_signal_add(
- &self.wlr_xdg_decoration_manager.events.new_toplevel_decoration,
- &self.listen_new_toplevel_decoration,
- );
+ self.new_toplevel_decoration.setNotify(handleNewToplevelDecoration);
+ self.xdg_decoration_manager.events.new_toplevel_decoration.add(&self.new_toplevel_decoration);
}
-fn handleNewToplevelDecoration(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_toplevel_decoration", listener.?);
- const wlr_xdg_toplevel_decoration = util.voidCast(c.wlr_xdg_toplevel_decoration_v1, data.?);
-
+fn handleNewToplevelDecoration(
+ listener: *wl.Listener(*wlr.XdgToplevelDecorationV1),
+ xdg_toplevel_decoration: *wlr.XdgToplevelDecorationV1,
+) void {
+ const self = @fieldParentPtr(Self, "new_toplevel_decoration", listener);
const decoration = util.gpa.create(Decoration) catch {
- c.wl_resource_post_no_memory(wlr_xdg_toplevel_decoration.resource);
+ xdg_toplevel_decoration.resource.postNoMemory();
return;
};
- decoration.init(self.server, wlr_xdg_toplevel_decoration);
+ decoration.init(self.server, xdg_toplevel_decoration);
}
diff --git a/river/DragIcon.zig b/river/DragIcon.zig
index 22b9d19..7c77ff2 100644
--- a/river/DragIcon.zig
+++ b/river/DragIcon.zig
@@ -18,29 +18,27 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const util = @import("util.zig");
const Seat = @import("Seat.zig");
seat: *Seat,
-wlr_drag_icon: *c.wlr_drag_icon,
+wlr_drag_icon: *wlr.Drag.Icon,
-listen_destroy: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.Drag.Icon) = undefined,
-pub fn init(self: *Self, seat: *Seat, wlr_drag_icon: *c.wlr_drag_icon) void {
- self.* = .{
- .seat = seat,
- .wlr_drag_icon = wlr_drag_icon,
- };
+pub fn init(self: *Self, seat: *Seat, wlr_drag_icon: *wlr.Drag.Icon) void {
+ self.* = .{ .seat = seat, .wlr_drag_icon = wlr_drag_icon };
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&wlr_drag_icon.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ wlr_drag_icon.events.destroy.add(&self.destroy);
}
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(listener: *wl.Listener(*wlr.Drag.Icon), wlr_drag_icon: *wlr.Drag.Icon) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
const root = &self.seat.input_manager.server.root;
const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
root.drag_icons.remove(node);
diff --git a/river/InputManager.zig b/river/InputManager.zig
index 026732c..a67f07d 100644
--- a/river/InputManager.zig
+++ b/river/InputManager.zig
@@ -19,8 +19,9 @@ const Self = @This();
const build_options = @import("build_options");
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -32,56 +33,53 @@ const default_seat_name = "default";
server: *Server,
-wlr_idle: *c.wlr_idle,
-wlr_input_inhibit_manager: *c.wlr_input_inhibit_manager,
-wlr_virtual_pointer_manager: *c.wlr_virtual_pointer_manager_v1,
-wlr_virtual_keyboard_manager: *c.wlr_virtual_keyboard_manager_v1,
+idle: *wlr.Idle,
+input_inhibit_manager: *wlr.InputInhibitManager,
+virtual_pointer_manager: *wlr.VirtualPointerManagerV1,
+virtual_keyboard_manager: *wlr.VirtualKeyboardManagerV1,
seats: std.TailQueue(Seat) = .{},
-exclusive_client: ?*c.wl_client = null,
+exclusive_client: ?*wl.Client = null,
-listen_inhibit_activate: c.wl_listener = undefined,
-listen_inhibit_deactivate: c.wl_listener = undefined,
-listen_new_input: c.wl_listener = undefined,
-listen_new_virtual_pointer: c.wl_listener = undefined,
-listen_new_virtual_keyboard: c.wl_listener = undefined,
+inhibit_activate: wl.Listener(*wlr.InputInhibitManager) = undefined,
+inhibit_deactivate: wl.Listener(*wlr.InputInhibitManager) = undefined,
+new_input: wl.Listener(*wlr.InputDevice) = undefined,
+new_virtual_pointer: wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer) = undefined,
+new_virtual_keyboard: wl.Listener(*wlr.VirtualKeyboardV1) = undefined,
pub fn init(self: *Self, server: *Server) !void {
const seat_node = try util.gpa.create(std.TailQueue(Seat).Node);
+ errdefer util.gpa.destroy(seat_node);
self.* = .{
.server = server,
// These are automatically freed when the display is destroyed
- .wlr_idle = c.wlr_idle_create(server.wl_display) orelse return error.OutOfMemory,
- .wlr_input_inhibit_manager = c.wlr_input_inhibit_manager_create(server.wl_display) orelse
- return error.OutOfMemory,
- .wlr_virtual_pointer_manager = c.wlr_virtual_pointer_manager_v1_create(server.wl_display) orelse
- return error.OutOfMemory,
- .wlr_virtual_keyboard_manager = c.wlr_virtual_keyboard_manager_v1_create(server.wl_display) orelse
- return error.OutOfMemory,
+ .idle = try wlr.Idle.create(server.wl_server),
+ .input_inhibit_manager = try wlr.InputInhibitManager.create(server.wl_server),
+ .virtual_pointer_manager = try wlr.VirtualPointerManagerV1.create(server.wl_server),
+ .virtual_keyboard_manager = try wlr.VirtualKeyboardManagerV1.create(server.wl_server),
};
self.seats.prepend(seat_node);
try seat_node.data.init(self, default_seat_name);
- if (build_options.xwayland) c.wlr_xwayland_set_seat(server.wlr_xwayland, self.defaultSeat().wlr_seat);
+ if (build_options.xwayland) server.xwayland.setSeat(self.defaultSeat().wlr_seat);
- // Set up all listeners
- self.listen_inhibit_activate.notify = handleInhibitActivate;
- c.wl_signal_add(&self.wlr_input_inhibit_manager.events.activate, &self.listen_inhibit_activate);
+ self.inhibit_activate.setNotify(handleInhibitActivate);
+ self.input_inhibit_manager.events.activate.add(&self.inhibit_activate);
- self.listen_inhibit_deactivate.notify = handleInhibitDeactivate;
- c.wl_signal_add(&self.wlr_input_inhibit_manager.events.deactivate, &self.listen_inhibit_deactivate);
+ self.inhibit_deactivate.setNotify(handleInhibitDeactivate);
+ self.input_inhibit_manager.events.deactivate.add(&self.inhibit_deactivate);
- self.listen_new_input.notify = handleNewInput;
- c.wl_signal_add(&self.server.wlr_backend.events.new_input, &self.listen_new_input);
+ self.new_input.setNotify(handleNewInput);
+ self.server.backend.events.new_input.add(&self.new_input);
- self.listen_new_virtual_pointer.notify = handleNewVirtualPointer;
- c.wl_signal_add(&self.wlr_virtual_pointer_manager.events.new_virtual_pointer, &self.listen_new_virtual_pointer);
+ self.new_virtual_pointer.setNotify(handleNewVirtualPointer);
+ self.virtual_pointer_manager.events.new_virtual_pointer.add(&self.new_virtual_pointer);
- self.listen_new_virtual_keyboard.notify = handleNewVirtualKeyboard;
- c.wl_signal_add(&self.wlr_virtual_keyboard_manager.events.new_virtual_keyboard, &self.listen_new_virtual_keyboard);
+ self.new_virtual_keyboard.setNotify(handleNewVirtualKeyboard);
+ self.virtual_keyboard_manager.events.new_virtual_keyboard.add(&self.new_virtual_keyboard);
}
pub fn deinit(self: *Self) void {
@@ -105,9 +103,9 @@ pub fn handleViewUnmap(self: Self, view: *View) void {
}
/// Returns true if input is currently allowed on the passed surface.
-pub fn inputAllowed(self: Self, wlr_surface: *c.wlr_surface) bool {
+pub fn inputAllowed(self: Self, wlr_surface: *wlr.Surface) bool {
return if (self.exclusive_client) |exclusive_client|
- exclusive_client == c.wl_resource_get_client(wlr_surface.resource)
+ exclusive_client == wlr_surface.resource.getClient()
else
true;
}
@@ -119,8 +117,11 @@ pub fn isCursorActionTarget(self: Self, view: *View) bool {
} else false;
}
-fn handleInhibitActivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_inhibit_activate", listener.?);
+fn handleInhibitActivate(
+ listener: *wl.Listener(*wlr.InputInhibitManager),
+ input_inhibit_manager: *wlr.InputInhibitManager,
+) void {
+ const self = @fieldParentPtr(Self, "inhibit_activate", listener);
log.debug(.input_manager, "input inhibitor activated", .{});
@@ -134,11 +135,14 @@ fn handleInhibitActivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C)
seat_node.data.mode_id = 1;
}
- self.exclusive_client = self.wlr_input_inhibit_manager.active_client;
+ self.exclusive_client = self.input_inhibit_manager.active_client;
}
-fn handleInhibitDeactivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_inhibit_deactivate", listener.?);
+fn handleInhibitDeactivate(
+ listener: *wl.Listener(*wlr.InputInhibitManager),
+ input_inhibit_manager: *wlr.InputInhibitManager,
+) void {
+ const self = @fieldParentPtr(Self, "inhibit_deactivate", listener);
log.debug(.input_manager, "input inhibitor deactivated", .{});
@@ -163,17 +167,17 @@ fn handleInhibitDeactivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.
}
/// This event is raised by the backend when a new input device becomes available.
-fn handleNewInput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_input", listener.?);
- const device = util.voidCast(c.wlr_input_device, data.?);
-
+fn handleNewInput(listener: *wl.Listener(*wlr.InputDevice), device: *wlr.InputDevice) void {
+ const self = @fieldParentPtr(Self, "new_input", listener);
// TODO: suport multiple seats
self.defaultSeat().addDevice(device);
}
-fn handleNewVirtualPointer(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_virtual_pointer", listener.?);
- const event = util.voidCast(c.wlr_virtual_pointer_v1_new_pointer_event, data.?);
+fn handleNewVirtualPointer(
+ listener: *wl.Listener(*wlr.VirtualPointerManagerV1.event.NewPointer),
+ event: *wlr.VirtualPointerManagerV1.event.NewPointer,
+) void {
+ const self = @fieldParentPtr(Self, "new_virtual_pointer", listener);
// TODO Support multiple seats and don't ignore
if (event.suggested_seat != null) {
@@ -184,14 +188,14 @@ fn handleNewVirtualPointer(listener: ?*c.wl_listener, data: ?*c_void) callconv(.
log.debug(.input_manager, "Ignoring output suggestion from virtual pointer", .{});
}
- const new_pointer: *c.wlr_virtual_pointer_v1 = event.new_pointer;
- self.defaultSeat().addDevice(&new_pointer.input_device);
+ self.defaultSeat().addDevice(&event.new_pointer.input_device);
}
-fn handleNewVirtualKeyboard(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_virtual_keyboard", listener.?);
- const virtual_keyboard = util.voidCast(c.wlr_virtual_keyboard_v1, data.?);
- const seat = util.voidCast(Seat, @as(*c.wlr_seat, virtual_keyboard.seat).data.?);
-
+fn handleNewVirtualKeyboard(
+ listener: *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 9fe876d..2c3c4d5 100644
--- a/river/Keyboard.zig
+++ b/river/Keyboard.zig
@@ -18,122 +18,102 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
+const xkb = @import("xkbcommon");
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
const Seat = @import("Seat.zig");
seat: *Seat,
-wlr_input_device: *c.wlr_input_device,
-wlr_keyboard: *c.wlr_keyboard,
+input_device: *wlr.InputDevice,
-listen_key: c.wl_listener = undefined,
-listen_modifiers: c.wl_listener = undefined,
-listen_destroy: c.wl_listener = undefined,
+key: wl.Listener(*wlr.Keyboard.event.Key) = undefined,
+modifiers: wl.Listener(*wlr.Keyboard) = undefined,
+destroy: wl.Listener(*wlr.Keyboard) = undefined,
-pub fn init(self: *Self, seat: *Seat, wlr_input_device: *c.wlr_input_device) !void {
+pub fn init(self: *Self, seat: *Seat, input_device: *wlr.InputDevice) !void {
self.* = .{
.seat = seat,
- .wlr_input_device = wlr_input_device,
- .wlr_keyboard = @field(wlr_input_device, c.wlr_input_device_union).keyboard,
+ .input_device = input_device,
};
// We need to prepare an XKB keymap and assign it to the keyboard. This
// assumes the defaults (e.g. layout = "us").
- const rules = c.xkb_rule_names{
+ const rules = xkb.RuleNames{
.rules = null,
.model = null,
.layout = null,
.variant = null,
.options = null,
};
- const context = c.xkb_context_new(.XKB_CONTEXT_NO_FLAGS) orelse return error.XkbContextFailed;
- defer c.xkb_context_unref(context);
+ const context = xkb.Context.new(.no_flags) orelse return error.XkbContextFailed;
+ defer context.unref();
- const keymap = c.xkb_keymap_new_from_names(
- context,
- &rules,
- .XKB_KEYMAP_COMPILE_NO_FLAGS,
- ) orelse return error.XkbKeymapFailed;
- defer c.xkb_keymap_unref(keymap);
+ const keymap = xkb.Keymap.newFromNames(context, &rules, .no_flags) orelse return error.XkbKeymapFailed;
+ defer keymap.unref();
- if (!c.wlr_keyboard_set_keymap(self.wlr_keyboard, keymap)) return error.SetKeymapFailed;
- c.wlr_keyboard_set_repeat_info(self.wlr_keyboard, 25, 600);
+ const wlr_keyboard = self.input_device.device.keyboard;
- // Setup listeners for keyboard events
- self.listen_key.notify = handleKey;
- c.wl_signal_add(&self.wlr_keyboard.events.key, &self.listen_key);
+ if (!wlr_keyboard.setKeymap(keymap)) return error.SetKeymapFailed;
+ wlr_keyboard.setRepeatInfo(25, 600);
- self.listen_modifiers.notify = handleModifiers;
- c.wl_signal_add(&self.wlr_keyboard.events.modifiers, &self.listen_modifiers);
+ self.key.setNotify(handleKey);
+ wlr_keyboard.events.key.add(&self.key);
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&self.wlr_keyboard.events.destroy, &self.listen_destroy);
+ self.modifiers.setNotify(handleModifiers);
+ wlr_keyboard.events.modifiers.add(&self.modifiers);
+
+ self.destroy.setNotify(handleDestroy);
+ wlr_keyboard.events.destroy.add(&self.destroy);
}
pub fn deinit(self: *Self) void {
- c.wl_list_remove(&self.listen_key.link);
- c.wl_list_remove(&self.listen_modifiers.link);
- c.wl_list_remove(&self.listen_destroy.link);
+ self.key.link.remove();
+ self.modifiers.link.remove();
+ self.destroy.link.remove();
}
-fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void {
// This event is raised when a key is pressed or released.
- const self = @fieldParentPtr(Self, "listen_key", listener.?);
- const event = util.voidCast(c.wlr_event_keyboard_key, data.?);
- const wlr_keyboard = self.wlr_keyboard;
+ const self = @fieldParentPtr(Self, "key", listener);
+ const wlr_keyboard = self.input_device.device.keyboard;
self.seat.handleActivity();
// Translate libinput keycode -> xkbcommon
const keycode = event.keycode + 8;
- // Get a list of keysyms as xkb reports them
- var translated_keysyms: ?[*]c.xkb_keysym_t = undefined;
- const translated_keysyms_len = c.xkb_state_key_get_syms(
- wlr_keyboard.xkb_state,
- keycode,
- &translated_keysyms,
- );
-
- // Get a list of keysyms ignoring modifiers (e.g. 1 instead of !)
- // Important for mappings like Mod+Shift+1
- var raw_keysyms: ?[*]c.xkb_keysym_t = undefined;
- const layout_index = c.xkb_state_key_get_layout(wlr_keyboard.xkb_state, keycode);
- const raw_keysyms_len = c.xkb_keymap_key_get_syms_by_level(
- wlr_keyboard.keymap,
- keycode,
- layout_index,
- 0,
- &raw_keysyms,
- );
+ // TODO: These modifiers aren't properly handled, see sway's code
+ const modifiers = wlr_keyboard.getModifiers();
+ const released = event.state == .released;
var handled = false;
- // TODO: These modifiers aren't properly handled, see sway's code
- const modifiers = c.wlr_keyboard_get_modifiers(wlr_keyboard);
- const released = event.state == .WLR_KEY_RELEASED;
- var i: usize = 0;
- while (i < translated_keysyms_len) : (i += 1) {
+ // 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(translated_keysyms.?[i])) {
+ if (!released and self.handleBuiltinMapping(sym)) {
handled = true;
break;
- } else if (self.seat.handleMapping(translated_keysyms.?[i], modifiers, released)) {
+ } else if (self.seat.handleMapping(sym, modifiers, released)) {
handled = true;
break;
}
}
+
+ // If not yet handled, check keysyms ignoring modifiers (e.g. 1 instead of !)
+ // Important for mappings like Mod+Shift+1
if (!handled) {
- i = 0;
- while (i < raw_keysyms_len) : (i += 1) {
+ 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(raw_keysyms.?[i])) {
+ if (!released and self.handleBuiltinMapping(sym)) {
handled = true;
break;
- } else if (self.seat.handleMapping(raw_keysyms.?[i], modifiers, released)) {
+ } else if (self.seat.handleMapping(sym, modifiers, released)) {
handled = true;
break;
}
@@ -143,52 +123,44 @@ fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
if (!handled) {
// Otherwise, we pass it along to the client.
const wlr_seat = self.seat.wlr_seat;
- c.wlr_seat_set_keyboard(wlr_seat, self.wlr_input_device);
- c.wlr_seat_keyboard_notify_key(
- wlr_seat,
- event.time_msec,
- event.keycode,
- @intCast(u32, @enumToInt(event.state)),
- );
+ wlr_seat.setKeyboard(self.input_device);
+ wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state);
}
}
-fn handleModifiers(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is raised when a modifier key, such as shift or alt, is
- // pressed. We simply communicate this to the client. */
- const self = @fieldParentPtr(Self, "listen_modifiers", listener.?);
+/// Simply pass modifiers along to the client
+fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void {
+ const self = @fieldParentPtr(Self, "modifiers", listener);
- // A seat can only have one keyboard, but this is a limitation of the
- // Wayland protocol - not wlroots. We assign all connected keyboards to the
- // same seat. You can swap out the underlying wlr_keyboard like this and
- // wlr_seat handles this transparently.
- c.wlr_seat_set_keyboard(self.seat.wlr_seat, self.wlr_input_device);
-
- // Send modifiers to the client.
- c.wlr_seat_keyboard_notify_modifiers(self.seat.wlr_seat, &self.wlr_keyboard.modifiers);
+ self.seat.wlr_seat.setKeyboard(self.input_device);
+ self.seat.wlr_seat.keyboardNotifyModifiers(&self.input_device.device.keyboard.modifiers);
}
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
- self.deinit();
+
+fn handleDestroy(listener: *wl.Listener(*wlr.Keyboard), wlr_keyboard: *wlr.Keyboard) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
+
self.seat.keyboards.remove(node);
+ self.deinit();
util.gpa.destroy(node);
}
/// Handle any builtin, harcoded compsitor mappings such as VT switching.
/// Returns true if the keysym was handled.
-fn handleBuiltinMapping(self: Self, keysym: c.xkb_keysym_t) bool {
- if (keysym >= c.XKB_KEY_XF86Switch_VT_1 and keysym <= c.XKB_KEY_XF86Switch_VT_12) {
- log.debug(.keyboard, "switch VT keysym received", .{});
- const wlr_backend = self.seat.input_manager.server.wlr_backend;
- if (c.wlr_backend_is_multi(wlr_backend)) {
- if (c.wlr_backend_get_session(wlr_backend)) |session| {
- const vt = keysym - c.XKB_KEY_XF86Switch_VT_1 + 1;
- log.notice(.server, "switching to VT {}", .{vt});
- _ = c.wlr_session_change_vt(session, vt);
+fn handleBuiltinMapping(self: Self, keysym: xkb.Keysym) bool {
+ switch (@enumToInt(keysym)) {
+ @enumToInt(xkb.Keysym.XF86Switch_VT_1)...@enumToInt(xkb.Keysym.XF86Switch_VT_12) => {
+ log.debug(.keyboard, "switch VT keysym received", .{});
+ const backend = self.seat.input_manager.server.backend;
+ if (backend.isMulti()) {
+ if (backend.getSession()) |session| {
+ const vt = @enumToInt(keysym) - @enumToInt(xkb.Keysym.XF86Switch_VT_1) + 1;
+ log.notice(.server, "switching to VT {}", .{vt});
+ session.changeVt(vt) catch log.err(.server, "changing VT failed", .{});
+ }
}
- }
- return true;
+ return true;
+ },
+ else => return false,
}
- return false;
}
diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig
index d2af692..341a060 100644
--- a/river/LayerSurface.zig
+++ b/river/LayerSurface.zig
@@ -18,8 +18,9 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -28,27 +29,27 @@ const Output = @import("Output.zig");
const XdgPopup = @import("XdgPopup.zig");
output: *Output,
-wlr_layer_surface: *c.wlr_layer_surface_v1,
+wlr_layer_surface: *wlr.LayerSurfaceV1,
box: Box = undefined,
-state: c.wlr_layer_surface_v1_state,
+state: wlr.LayerSurfaceV1.State,
// Listeners active the entire lifetime of the layser surface
-listen_destroy: c.wl_listener = undefined,
-listen_map: c.wl_listener = undefined,
-listen_unmap: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.LayerSurfaceV1) = undefined,
+map: wl.Listener(*wlr.LayerSurfaceV1) = undefined,
+unmap: wl.Listener(*wlr.LayerSurfaceV1) = undefined,
// Listeners only active while the layer surface is mapped
-listen_commit: c.wl_listener = undefined,
-listen_new_popup: c.wl_listener = undefined,
+commit: wl.Listener(*wlr.Surface) = undefined,
+new_popup: wl.Listener(*wlr.XdgPopup) = undefined,
-pub fn init(self: *Self, output: *Output, wlr_layer_surface: *c.wlr_layer_surface_v1) void {
+pub fn init(self: *Self, output: *Output, wlr_layer_surface: *wlr.LayerSurfaceV1) void {
self.* = .{
.output = output,
.wlr_layer_surface = wlr_layer_surface,
.state = wlr_layer_surface.current,
};
- wlr_layer_surface.data = self;
+ wlr_layer_surface.data = @ptrToInt(self);
// Temporarily add to the output's list to allow for inital arrangement
// which sends the first configure.
@@ -59,61 +60,56 @@ pub fn init(self: *Self, output: *Output, wlr_layer_surface: *c.wlr_layer_surfac
list.remove(node);
// Set up listeners that are active for the entire lifetime of the layer surface
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&self.wlr_layer_surface.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ self.wlr_layer_surface.events.destroy.add(&self.destroy);
- self.listen_map.notify = handleMap;
- c.wl_signal_add(&self.wlr_layer_surface.events.map, &self.listen_map);
+ self.map.setNotify(handleMap);
+ self.wlr_layer_surface.events.map.add(&self.map);
- self.listen_unmap.notify = handleUnmap;
- c.wl_signal_add(&self.wlr_layer_surface.events.unmap, &self.listen_unmap);
+ self.unmap.setNotify(handleUnmap);
+ self.wlr_layer_surface.events.unmap.add(&self.unmap);
}
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
- const output = self.output;
+fn handleDestroy(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
log.debug(.layer_shell, "layer surface '{}' destroyed", .{self.wlr_layer_surface.namespace});
// Remove listeners active the entire lifetime of the layer surface
- c.wl_list_remove(&self.listen_destroy.link);
- c.wl_list_remove(&self.listen_map.link);
- c.wl_list_remove(&self.listen_unmap.link);
+ self.destroy.link.remove();
+ self.map.link.remove();
+ self.unmap.link.remove();
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
util.gpa.destroy(node);
}
-fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_map", listener.?);
- const wlr_layer_surface = self.wlr_layer_surface;
+fn handleMap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
+ const self = @fieldParentPtr(Self, "map", listener);
log.debug(.layer_shell, "layer surface '{}' mapped", .{wlr_layer_surface.namespace});
// Add listeners that are only active while mapped
- self.listen_commit.notify = handleCommit;
- c.wl_signal_add(&wlr_layer_surface.surface.*.events.commit, &self.listen_commit);
+ self.commit.setNotify(handleCommit);
+ wlr_layer_surface.surface.events.commit.add(&self.commit);
- self.listen_new_popup.notify = handleNewPopup;
- c.wl_signal_add(&wlr_layer_surface.events.new_popup, &self.listen_new_popup);
+ self.new_popup.setNotify(handleNewPopup);
+ wlr_layer_surface.events.new_popup.add(&self.new_popup);
- c.wlr_surface_send_enter(
- wlr_layer_surface.surface,
- wlr_layer_surface.output,
- );
+ wlr_layer_surface.surface.sendEnter(wlr_layer_surface.output.?);
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
self.output.layers[@intCast(usize, @enumToInt(self.state.layer))].append(node);
}
-fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_unmap", listener.?);
+fn handleUnmap(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
+ const self = @fieldParentPtr(Self, "unmap", listener);
log.debug(.layer_shell, "layer surface '{}' unmapped", .{self.wlr_layer_surface.namespace});
// remove listeners only active while the layer surface is mapped
- c.wl_list_remove(&self.listen_commit.link);
- c.wl_list_remove(&self.listen_new_popup.link);
+ self.commit.link.remove();
+ self.new_popup.link.remove();
// Remove from the output's list of layer surfaces
const self_node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
@@ -142,8 +138,8 @@ fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
self.output.root.startTransaction();
}
-fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_commit", listener.?);
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), wlr_surface: *wlr.Surface) void {
+ const self = @fieldParentPtr(Self, "commit", listener);
if (self.wlr_layer_surface.output == null) {
log.err(.layer_shell, "layer surface committed with null output", .{});
@@ -166,13 +162,12 @@ fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
}
-fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_popup", listener.?);
- const wlr_xdg_popup = util.voidCast(c.wlr_xdg_popup, data.?);
+fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void {
+ const self = @fieldParentPtr(Self, "new_popup", listener);
// This will free itself on destroy
- var xdg_popup = util.gpa.create(XdgPopup) catch {
- c.wl_resource_post_no_memory(wlr_xdg_popup.resource);
+ const xdg_popup = util.gpa.create(XdgPopup) catch {
+ wlr_xdg_popup.resource.postNoMemory();
return;
};
xdg_popup.init(self.output, &self.box, wlr_xdg_popup);
diff --git a/river/Mapping.zig b/river/Mapping.zig
index 1518f85..03a9b45 100644
--- a/river/Mapping.zig
+++ b/river/Mapping.zig
@@ -18,20 +18,21 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const xkb = @import("xkbcommon");
-const c = @import("c.zig");
const util = @import("util.zig");
-keysym: c.xkb_keysym_t,
-modifiers: u32,
+keysym: xkb.Keysym,
+modifiers: wlr.Keyboard.ModifierMask,
command_args: []const []const u8,
/// When set to true the mapping will be executed on key release rather than on press
release: bool,
pub fn init(
- keysym: c.xkb_keysym_t,
- modifiers: u32,
+ keysym: xkb.Keysym,
+ modifiers: wlr.Keyboard.ModifierMask,
release: bool,
command_args: []const []const u8,
) !Self {
diff --git a/river/Output.zig b/river/Output.zig
index 244c1a5..94501db 100644
--- a/river/Output.zig
+++ b/river/Output.zig
@@ -18,6 +18,10 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wayland = @import("wayland");
+const wl = wayland.server.wl;
+const zwlr = wayland.server.zwlr;
const c = @import("c.zig");
const log = @import("log.zig");
@@ -38,7 +42,7 @@ const State = struct {
};
root: *Root,
-wlr_output: *c.wlr_output,
+wlr_output: *wlr.Output,
/// All layer surfaces on the output, indexed by the layer enum.
layers: [4]std.TailQueue(LayerSurface) = [1]std.TailQueue(LayerSurface){.{}} ** 4,
@@ -76,22 +80,21 @@ status_trackers: std.SinglyLinkedList(OutputStatus) = .{},
/// An active output can have focus (e.g. an output turned off by dpms is active)
active: bool = false,
-// All listeners for this output, in alphabetical order
-listen_destroy: c.wl_listener = undefined,
-listen_enable: c.wl_listener = undefined,
-listen_frame: c.wl_listener = undefined,
-listen_mode: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.Output) = undefined,
+enable: wl.Listener(*wlr.Output) = undefined,
+frame: wl.Listener(*wlr.Output) = undefined,
+mode: wl.Listener(*wlr.Output) = undefined,
-pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
+pub fn init(self: *Self, root: *Root, wlr_output: *wlr.Output) !void {
// Some backends don't have modes. DRM+KMS does, and we need to set a mode
// before we can use the output. The mode is a tuple of (width, height,
// refresh rate), and each monitor supports only a specific set of modes. We
// just pick the monitor's preferred mode, a more sophisticated compositor
// would let the user configure it.
- if (c.wlr_output_preferred_mode(wlr_output)) |mode| {
- c.wlr_output_set_mode(wlr_output, mode);
- c.wlr_output_enable(wlr_output, true);
- if (!c.wlr_output_commit(wlr_output)) return error.OutputCommitFailed;
+ if (wlr_output.preferredMode()) |mode| {
+ wlr_output.setMode(mode);
+ wlr_output.enable(true);
+ try wlr_output.commit();
}
const layout = try std.mem.dupe(util.gpa, u8, "full");
@@ -103,22 +106,21 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
.layout = layout,
.usable_box = undefined,
};
- wlr_output.data = self;
+ wlr_output.data = @ptrToInt(self);
- // Set up listeners
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&wlr_output.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ wlr_output.events.destroy.add(&self.destroy);
- self.listen_enable.notify = handleEnable;
- c.wl_signal_add(&wlr_output.events.enable, &self.listen_enable);
+ self.enable.setNotify(handleEnable);
+ wlr_output.events.enable.add(&self.enable);
- self.listen_frame.notify = handleFrame;
- c.wl_signal_add(&wlr_output.events.frame, &self.listen_frame);
+ self.frame.setNotify(handleFrame);
+ wlr_output.events.frame.add(&self.frame);
- self.listen_mode.notify = handleMode;
- c.wl_signal_add(&wlr_output.events.mode, &self.listen_mode);
+ self.mode.setNotify(handleMode);
+ wlr_output.events.mode.add(&self.mode);
- if (c.wlr_output_is_noop(wlr_output)) {
+ if (wlr_output.isNoop()) {
// A noop output is always 0 x 0
self.usable_box = .{
.x = 0,
@@ -132,7 +134,7 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
var it = root.server.input_manager.seats.first;
while (it) |node| : (it = node.next) {
const seat = &node.data;
- if (!c.wlr_xcursor_manager_load(seat.cursor.wlr_xcursor_manager, wlr_output.scale))
+ seat.cursor.xcursor_manager.load(wlr_output.scale) catch
log.err(.cursor, "failed to load xcursor theme at scale {}", .{wlr_output.scale});
}
@@ -146,8 +148,8 @@ pub fn init(self: *Self, root: *Root, wlr_output: *c.wlr_output) !void {
}
}
-pub fn getRenderer(self: Self) *c.wlr_renderer {
- return c.wlr_backend_get_renderer(self.wlr_output.backend);
+pub fn getLayer(self: *Self, layer: zwlr.LayerShellV1.Layer) *std.TailQueue(LayerSurface) {
+ return &self.layers[@intCast(usize, @enumToInt(layer))];
}
pub fn sendViewTags(self: Self) void {
@@ -321,16 +323,11 @@ pub fn arrangeLayers(self: *Self) void {
// This box is modified as exclusive zones are applied
var usable_box = full_box;
- const layer_idxs = [_]usize{
- c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
- c.ZWLR_LAYER_SHELL_V1_LAYER_TOP,
- c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM,
- c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
- };
+ const layers = [_]zwlr.LayerShellV1.Layer{ .overlay, .top, .bottom, .background };
// Arrange all layer surfaces with exclusive zones, applying them to the
// usable box along the way.
- for (layer_idxs) |layer| self.arrangeLayer(self.layers[layer], full_box, &usable_box, true);
+ for (layers) |layer| self.arrangeLayer(self.getLayer(layer).*, full_box, &usable_box, true);
// If the the usable_box has changed, we need to rearrange the output
if (!std.meta.eql(self.usable_box, usable_box)) {
@@ -339,13 +336,13 @@ pub fn arrangeLayers(self: *Self) void {
}
// Arrange the layers without exclusive zones
- for (layer_idxs) |layer| self.arrangeLayer(self.layers[layer], full_box, &usable_box, false);
+ for (layers) |layer| self.arrangeLayer(self.getLayer(layer).*, full_box, &usable_box, false);
// Find the topmost layer surface in the top or overlay layers which
// requests keyboard interactivity if any.
- const topmost_surface = outer: for (layer_idxs[0..2]) |layer| {
+ const topmost_surface = outer: for (layers[0..2]) |layer| {
// Iterate in reverse order since the last layer is rendered on top
- var it = self.layers[layer].last;
+ var it = self.getLayer(layer).last;
while (it) |node| : (it = node.prev) {
const layer_surface = &node.data;
if (layer_surface.wlr_layer_surface.current.keyboard_interactive) {
@@ -400,19 +397,14 @@ fn arrangeLayer(
var new_box: Box = undefined;
// Horizontal alignment
- const anchor_left = @as(u32, c.ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT);
- const anchor_right = @as(u32, c.ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
if (current_state.desired_width == 0) {
- const anchor_left_right = anchor_left | anchor_right;
- if (current_state.anchor & anchor_left_right == anchor_left_right) {
- new_box.x = bounds.x + @intCast(i32, current_state.margin.left);
- new_box.width = bounds.width -
- (current_state.margin.left + current_state.margin.right);
- }
- } else if (current_state.anchor & anchor_left != 0) {
+ std.debug.assert(current_state.anchor.right and current_state.anchor.left);
+ new_box.x = bounds.x + @intCast(i32, current_state.margin.left);
+ new_box.width = bounds.width - (current_state.margin.left + current_state.margin.right);
+ } else if (current_state.anchor.left) {
new_box.x = bounds.x + @intCast(i32, current_state.margin.left);
new_box.width = current_state.desired_width;
- } else if (current_state.anchor & anchor_right != 0) {
+ } else if (current_state.anchor.right) {
new_box.x = bounds.x + @intCast(i32, bounds.width - current_state.desired_width -
current_state.margin.right);
new_box.width = current_state.desired_width;
@@ -422,19 +414,14 @@ fn arrangeLayer(
}
// Vertical alignment
- const anchor_top = @as(u32, c.ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP);
- const anchor_bottom = @as(u32, c.ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM);
if (current_state.desired_height == 0) {
- const anchor_top_bottom = anchor_top | anchor_bottom;
- if (current_state.anchor & anchor_top_bottom == anchor_top_bottom) {
- new_box.y = bounds.y + @intCast(i32, current_state.margin.top);
- new_box.height = bounds.height -
- (current_state.margin.top + current_state.margin.bottom);
- }
- } else if (current_state.anchor & anchor_top != 0) {
+ std.debug.assert(current_state.anchor.top and current_state.anchor.bottom);
+ new_box.y = bounds.y + @intCast(i32, current_state.margin.top);
+ new_box.height = bounds.height - (current_state.margin.top + current_state.margin.bottom);
+ } else if (current_state.anchor.top) {
new_box.y = bounds.y + @intCast(i32, current_state.margin.top);
new_box.height = current_state.desired_height;
- } else if (current_state.anchor & anchor_bottom != 0) {
+ } else if (current_state.anchor.bottom) {
new_box.y = bounds.y + @intCast(i32, bounds.height - current_state.desired_height -
current_state.margin.bottom);
new_box.height = current_state.desired_height;
@@ -447,36 +434,36 @@ fn arrangeLayer(
// Apply the exclusive zone to the current bounds
const edges = [4]struct {
- single: u32,
- triple: u32,
+ single: zwlr.LayerSurfaceV1.Anchor,
+ triple: zwlr.LayerSurfaceV1.Anchor,
to_increase: ?*i32,
to_decrease: *u32,
margin: u32,
}{
.{
- .single = anchor_top,
- .triple = anchor_top | anchor_left | anchor_right,
+ .single = .{ .top = true },
+ .triple = .{ .top = true, .left = true, .right = true },
.to_increase = &usable_box.y,
.to_decrease = &usable_box.height,
.margin = current_state.margin.top,
},
.{
- .single = anchor_bottom,
- .triple = anchor_bottom | anchor_left | anchor_right,
+ .single = .{ .bottom = true },
+ .triple = .{ .bottom = true, .left = true, .right = true },
.to_increase = null,
.to_decrease = &usable_box.height,
.margin = current_state.margin.bottom,
},
.{
- .single = anchor_left,
- .triple = anchor_left | anchor_top | anchor_bottom,
+ .single = .{ .left = true },
+ .triple = .{ .left = true, .top = true, .bottom = true },
.to_increase = &usable_box.x,
.to_decrease = &usable_box.width,
.margin = current_state.margin.left,
},
.{
- .single = anchor_right,
- .triple = anchor_right | anchor_top | anchor_bottom,
+ .single = .{ .right = true },
+ .triple = .{ .right = true, .top = true, .bottom = true },
.to_increase = null,
.to_decrease = &usable_box.width,
.margin = current_state.margin.right,
@@ -484,7 +471,7 @@ fn arrangeLayer(
};
for (edges) |edge| {
- if ((current_state.anchor == edge.single or current_state.anchor == edge.triple) and
+ if ((std.meta.eql(current_state.anchor, edge.single) or std.meta.eql(current_state.anchor, edge.triple)) and
current_state.exclusive_zone + @intCast(i32, edge.margin) > 0)
{
const delta = current_state.exclusive_zone + @intCast(i32, edge.margin);
@@ -496,21 +483,18 @@ fn arrangeLayer(
// Tell the client to assume the new size
log.debug(.layer_shell, "send configure, {} x {}", .{ layer_surface.box.width, layer_surface.box.height });
- c.wlr_layer_surface_v1_configure(
- layer_surface.wlr_layer_surface,
- layer_surface.box.width,
- layer_surface.box.height,
- );
+ layer_surface.wlr_layer_surface.configure(layer_surface.box.width, layer_surface.box.height);
}
}
/// Called when the output is destroyed. Evacuate all views from the output
/// and then remove it from the list of outputs.
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
const root = self.root;
log.debug(.server, "output '{}' destroyed", .{self.wlr_output.name});
+
// Remove the destroyed output from root if it wasn't already removed
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
if (self.active) {
@@ -526,37 +510,35 @@ fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
// Remove all listeners
- c.wl_list_remove(&self.listen_destroy.link);
- c.wl_list_remove(&self.listen_enable.link);
- c.wl_list_remove(&self.listen_frame.link);
- c.wl_list_remove(&self.listen_mode.link);
+ self.destroy.link.remove();
+ self.enable.link.remove();
+ self.frame.link.remove();
+ self.mode.link.remove();
- // Free the layout command
+ // Free all memory and clean up the wlr.Output
+ self.wlr_output.data = undefined;
util.gpa.free(self.layout);
-
- // Clean up the wlr_output
- self.wlr_output.data = null;
util.gpa.destroy(node);
}
-fn handleEnable(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_enable", listener.?);
+fn handleEnable(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+ const self = @fieldParentPtr(Self, "enable", listener);
- if (self.wlr_output.enabled and !self.active) {
+ if (wlr_output.enabled and !self.active) {
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
self.root.addOutput(node);
}
}
-fn handleFrame(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+fn handleFrame(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) 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, "listen_frame", listener.?);
+ const self = @fieldParentPtr(Self, "frame", listener);
render.renderOutput(self);
}
-fn handleMode(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_mode", listener.?);
+fn handleMode(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+ const self = @fieldParentPtr(Self, "mode", listener);
self.arrangeLayers();
self.arrangeViews();
self.root.startTransaction();
@@ -565,7 +547,7 @@ fn handleMode(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
pub fn getEffectiveResolution(self: *Self) struct { width: u32, height: u32 } {
var width: c_int = undefined;
var height: c_int = undefined;
- c.wlr_output_effective_resolution(self.wlr_output, &width, &height);
+ self.wlr_output.effectiveResolution(&width, &height);
return .{
.width = @intCast(u32, width),
.height = @intCast(u32, height),
diff --git a/river/OutputManager.zig b/river/OutputManager.zig
index 3500547..352b631 100644
--- a/river/OutputManager.zig
+++ b/river/OutputManager.zig
@@ -19,8 +19,9 @@ const Self = @This();
const build_options = @import("build_options");
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -38,65 +39,60 @@ const min_size = 50;
root: *Root,
-listen_new_output: c.wl_listener = undefined,
-listen_output_layout_change: c.wl_listener = undefined,
+new_output: wl.Listener(*wlr.Output) = undefined,
-wlr_output_manager: *c.wlr_output_manager_v1,
-listen_output_manager_apply: c.wl_listener = undefined,
-listen_output_manager_test: c.wl_listener = undefined,
+wlr_output_manager: *wlr.OutputManagerV1,
+manager_apply: wl.Listener(*wlr.OutputConfigurationV1) = undefined,
+manager_test: wl.Listener(*wlr.OutputConfigurationV1) = undefined,
+layout_change: wl.Listener(*wlr.OutputLayout) = undefined,
-wlr_output_power_manager: *c.wlr_output_power_manager_v1,
-listen_output_power_manager_set_mode: c.wl_listener = undefined,
+power_manager: *wlr.OutputPowerManagerV1,
+power_manager_set_mode: wl.Listener(*wlr.OutputPowerManagerV1.event.SetMode) = undefined,
/// True if and only if we are currently applying an output config
output_config_pending: bool = false,
pub fn init(self: *Self, server: *Server) !void {
self.* = .{
- .wlr_output_manager = c.wlr_output_manager_v1_create(server.wl_display) orelse
- return error.OutOfMemory,
- .wlr_output_power_manager = c.wlr_output_power_manager_v1_create(server.wl_display) orelse
- return error.OutOfMemory,
.root = &server.root,
+ .wlr_output_manager = try wlr.OutputManagerV1.create(server.wl_server),
+ .power_manager = try wlr.OutputPowerManagerV1.create(server.wl_server),
};
- self.listen_new_output.notify = handleNewOutput;
- c.wl_signal_add(&server.wlr_backend.events.new_output, &self.listen_new_output);
+ self.new_output.setNotify(handleNewOutput);
+ server.backend.events.new_output.add(&self.new_output);
- // Set up wlr_output_management
- self.listen_output_manager_apply.notify = handleOutputManagerApply;
- c.wl_signal_add(&self.wlr_output_manager.events.apply, &self.listen_output_manager_apply);
- self.listen_output_manager_test.notify = handleOutputManagerTest;
- c.wl_signal_add(&self.wlr_output_manager.events.@"test", &self.listen_output_manager_test);
+ self.manager_apply.setNotify(handleOutputManagerApply);
+ self.wlr_output_manager.events.apply.add(&self.manager_apply);
- // Listen for changes in the output layout to send them to the clients of wlr_output_manager
- self.listen_output_layout_change.notify = handleOutputLayoutChange;
- c.wl_signal_add(&self.root.wlr_output_layout.events.change, &self.listen_output_layout_change);
+ self.manager_test.setNotify(handleOutputManagerTest);
+ self.wlr_output_manager.events.@"test".add(&self.manager_test);
- // Set up output power manager
- self.listen_output_power_manager_set_mode.notify = handleOutputPowerManagementSetMode;
- c.wl_signal_add(&self.wlr_output_power_manager.events.set_mode, &self.listen_output_power_manager_set_mode);
+ self.layout_change.setNotify(handleOutputLayoutChange);
+ self.root.output_layout.events.change.add(&self.layout_change);
- _ = c.wlr_xdg_output_manager_v1_create(server.wl_display, server.root.wlr_output_layout) orelse
- return error.OutOfMemory;
+ self.power_manager_set_mode.setNotify(handleOutputPowerManagementSetMode);
+ self.power_manager.events.set_mode.add(&self.power_manager_set_mode);
+
+ _ = try wlr.XdgOutputManagerV1.create(server.wl_server, self.root.output_layout);
}
-fn handleNewOutput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_output", listener.?);
- const wlr_output = util.voidCast(c.wlr_output, data.?);
+fn handleNewOutput(listener: *wl.Listener(*wlr.Output), wlr_output: *wlr.Output) void {
+ const self = @fieldParentPtr(Self, "new_output", listener);
log.debug(.output_manager, "new output {}", .{wlr_output.name});
const node = util.gpa.create(std.TailQueue(Output).Node) catch {
- c.wlr_output_destroy(wlr_output);
+ wlr_output.destroy();
return;
};
node.data.init(self.root, wlr_output) catch {
- c.wlr_output_destroy(wlr_output);
+ wlr_output.destroy();
+ util.gpa.destroy(node);
return;
};
const ptr_node = util.gpa.create(std.TailQueue(*Output).Node) catch {
+ wlr_output.destroy();
util.gpa.destroy(node);
- c.wlr_output_destroy(wlr_output);
return;
};
ptr_node.data = &node.data;
@@ -105,107 +101,108 @@ fn handleNewOutput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void
self.root.addOutput(node);
}
-/// Sends the new output configuration to all clients of wlr_output_manager
-fn handleOutputLayoutChange(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_output_layout_change", listener.?);
- // Dont do anything if the layout change is coming from applying a config
+/// Send the new output configuration to all wlr-output-manager clients
+fn handleOutputLayoutChange(
+ listener: *wl.Listener(*wlr.OutputLayout),
+ output_layout: *wlr.OutputLayout,
+) void {
+ const self = @fieldParentPtr(Self, "layout_change", listener);
+ // Ignore if the layout change is from applying a config
if (self.output_config_pending) return;
- const config = self.createOutputConfigurationFromCurrent() catch {
- log.err(.output_manager, "Could not create output configuration", .{});
+ const config = self.ouputConfigFromCurrent() catch {
+ log.crit(.output_manager, "out of memory", .{});
return;
};
- c.wlr_output_manager_v1_set_configuration(self.wlr_output_manager, config);
+ self.wlr_output_manager.setConfiguration(config);
}
-fn handleOutputManagerApply(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_output_manager_apply", listener.?);
- const config = util.voidCast(c.wlr_output_configuration_v1, data.?);
- defer c.wlr_output_configuration_v1_destroy(config);
+fn handleOutputManagerApply(
+ listener: *wl.Listener(*wlr.OutputConfigurationV1),
+ config: *wlr.OutputConfigurationV1,
+) void {
+ const self = @fieldParentPtr(Self, "manager_apply", listener);
+ defer config.destroy();
if (self.applyOutputConfig(config)) {
- c.wlr_output_configuration_v1_send_succeeded(config);
+ config.sendSucceeded();
} else {
- c.wlr_output_configuration_v1_send_failed(config);
+ config.sendFailed();
}
- // Now send the config that actually was applied
- const actualConfig = self.createOutputConfigurationFromCurrent() catch {
- log.err(.output_manager, "Could not create output configuration", .{});
+ // Send the config that was actually applied
+ const applied_config = self.ouputConfigFromCurrent() catch {
+ log.crit(.output_manager, "out of memory", .{});
return;
};
- c.wlr_output_manager_v1_set_configuration(self.wlr_output_manager, actualConfig);
+ self.wlr_output_manager.setConfiguration(applied_config);
}
-fn handleOutputManagerTest(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_output_manager_test", listener.?);
- const config = util.voidCast(c.wlr_output_configuration_v1, data.?);
- defer c.wlr_output_configuration_v1_destroy(config);
+fn handleOutputManagerTest(
+ listener: *wl.Listener(*wlr.OutputConfigurationV1),
+ config: *wlr.OutputConfigurationV1,
+) void {
+ const self = @fieldParentPtr(Self, "manager_test", listener);
+ defer config.destroy();
if (testOutputConfig(config, true)) {
- c.wlr_output_configuration_v1_send_succeeded(config);
+ config.sendSucceeded();
} else {
- c.wlr_output_configuration_v1_send_failed(config);
+ config.sendFailed();
}
}
-fn handleOutputPowerManagementSetMode(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_output_power_manager_set_mode", listener.?);
- const mode_event = util.voidCast(c.wlr_output_power_v1_set_mode_event, data.?);
- const wlr_output: *c.wlr_output = mode_event.output;
+fn handleOutputPowerManagementSetMode(
+ listener: *wl.Listener(*wlr.OutputPowerManagerV1.event.SetMode),
+ event: *wlr.OutputPowerManagerV1.event.SetMode,
+) void {
+ const self = @fieldParentPtr(Self, "power_manager_set_mode", listener);
- const enable = mode_event.mode == .ZWLR_OUTPUT_POWER_V1_MODE_ON;
+ const enable = event.mode == .on;
const log_text = if (enable) "Enabling" else "Disabling";
log.debug(
.output_manager,
"{} dpms for output {}",
- .{ log_text, wlr_output.name },
+ .{ log_text, event.output.name },
);
- c.wlr_output_enable(wlr_output, enable);
- if (!c.wlr_output_commit(wlr_output)) {
- log.err(
- .output_manager,
- "wlr_output_commit failed for {}",
- .{wlr_output.name},
- );
- }
+ event.output.enable(enable);
+ event.output.commit() catch
+ log.err(.server, "output commit failed for {}", .{event.output.name});
}
-/// Applies an output config
-fn applyOutputConfig(self: *Self, config: *c.wlr_output_configuration_v1) bool {
- // We need to store whether a config is pending because we listen to wlr_output_layout.change
- // and this event can be triggered by applying the config
+/// Apply the given config, return false on faliure
+fn applyOutputConfig(self: *Self, config: *wlr.OutputConfigurationV1) bool {
+ // Store whether a config is pending so we can tell if the
+ // wlr_output_layout.change event was triggered by applying the config
self.output_config_pending = true;
defer self.output_config_pending = false;
// Test if the config should apply cleanly
if (!testOutputConfig(config, false)) return false;
- const list_head: *c.wl_list = &config.heads;
- var it: *c.wl_list = list_head.next;
- while (it != list_head) : (it = it.next) {
- const head = @fieldParentPtr(c.wlr_output_configuration_head_v1, "link", it);
- const output = util.voidCast(Output, @as(*c.wlr_output, head.state.output).data.?);
+ var it = config.heads.iterator(.forward);
+ while (it.next()) |head| {
+ const output = @intToPtr(*Output, head.state.output.data);
const disable = output.wlr_output.enabled and !head.state.enabled;
- // This commit will only fail due to runtime errors.
- // We choose to ignore this error
- if (!c.wlr_output_commit(output.wlr_output)) {
- log.err(.output_manager, "wlr_output_commit failed for {}", .{output.wlr_output.name});
- }
+ // Since we have done a successful test commit, this will only fail
+ // due to error in the output's backend implementation.
+ output.wlr_output.commit() catch
+ log.err(.output_manager, "output commit failed for {}", .{output.wlr_output.name});
if (output.wlr_output.enabled) {
// Moves the output if it is already in the layout
- c.wlr_output_layout_add(self.root.wlr_output_layout, output.wlr_output, head.state.x, head.state.y);
+ self.root.output_layout.add(output.wlr_output, head.state.x, head.state.y);
}
if (disable) {
const node = @fieldParentPtr(std.TailQueue(Output).Node, "data", output);
self.root.removeOutput(node);
- c.wlr_output_layout_remove(self.root.wlr_output_layout, output.wlr_output);
+ self.root.output_layout.remove(output.wlr_output);
}
+
// Arrange layers to adjust the usable_box
// We dont need to call arrangeViews() since arrangeLayers() will call
// it for us because the usable_box changed
@@ -218,16 +215,14 @@ fn applyOutputConfig(self: *Self, config: *c.wlr_output_configuration_v1) bool {
/// Tests the output configuration.
/// If rollback is false all changes are applied to the pending state of the affected outputs.
-fn testOutputConfig(config: *c.wlr_output_configuration_v1, rollback: bool) bool {
+fn testOutputConfig(config: *wlr.OutputConfigurationV1, rollback: bool) bool {
var ok = true;
- const list_head: *c.wl_list = &config.heads;
- var it: *c.wl_list = list_head.next;
- while (it != list_head) : (it = it.next) {
- const head = @fieldParentPtr(c.wlr_output_configuration_head_v1, "link", it);
- const wlr_output = @as(*c.wlr_output, head.state.output);
-
- const width = if (@as(?*c.wlr_output_mode, head.state.mode)) |m| m.width else head.state.custom_mode.width;
- const height = if (@as(?*c.wlr_output_mode, head.state.mode)) |m| m.height else head.state.custom_mode.height;
+ var it = config.heads.iterator(.forward);
+ while (it.next()) |head| {
+ const wlr_output = head.state.output;
+
+ const width = if (head.state.mode) |m| m.width else head.state.custom_mode.width;
+ const height = if (head.state.mode) |m| m.height else head.state.custom_mode.height;
const scale = head.state.scale;
const too_small = (@intToFloat(f32, width) / scale < min_size) or
@@ -242,24 +237,20 @@ fn testOutputConfig(config: *c.wlr_output_configuration_v1, rollback: bool) bool
}
applyHeadToOutput(head, wlr_output);
- ok = ok and !too_small and c.wlr_output_test(wlr_output);
+ ok = ok and !too_small and wlr_output.testCommit();
}
if (rollback or !ok) {
// Rollback all changes
- it = list_head.next;
- while (it != list_head) : (it = it.next) {
- const head = @fieldParentPtr(c.wlr_output_configuration_head_v1, "link", it);
- const wlr_output = @as(*c.wlr_output, head.state.output);
- c.wlr_output_rollback(wlr_output);
- }
+ it = config.heads.iterator(.forward);
+ while (it.next()) |head| head.state.output.rollback();
}
return ok;
}
-fn applyHeadToOutput(head: *c.wlr_output_configuration_head_v1, wlr_output: *c.wlr_output) void {
- c.wlr_output_enable(wlr_output, head.state.enabled);
+fn applyHeadToOutput(head: *wlr.OutputConfigurationV1.Head, wlr_output: *wlr.Output) void {
+ wlr_output.enable(head.state.enabled);
// The output must be enabled for the following properties to apply
if (head.state.enabled) {
// TODO(wlroots) Somehow on the drm backend setting the mode causes
@@ -268,43 +259,41 @@ fn applyHeadToOutput(head: *c.wlr_output_configuration_head_v1, wlr_output: *c.w
// We can just ignore this because nothing bad happens but it
// should be fixed in the future
// See https://github.com/swaywm/wlroots/issues/2492
- if (head.state.mode != null) {
- c.wlr_output_set_mode(wlr_output, head.state.mode);
+ if (head.state.mode) |mode| {
+ wlr_output.setMode(mode);
} else {
log.info(.output_manager, "custom modes are not supported until the next wlroots release: ignoring", .{});
// TODO(wlroots) uncomment the following lines when wlroots 0.13.0 is released
// See https://github.com/swaywm/wlroots/pull/2517
//const custom_mode = &head.state.custom_mode;
- //c.wlr_output_set_custom_mode(wlr_output, custom_mode.width, custom_mode.height, custom_mode.refresh);
+ //wlr_output.setCustomMode(custom_mode.width, custom_mode.height, custom_mode.refresh);
}
// TODO(wlroots) Figure out if this conversion is needed or if that is a bug in wlroots
- c.wlr_output_set_scale(wlr_output, @floatCast(f32, head.state.scale));
- c.wlr_output_set_transform(wlr_output, head.state.transform);
+ wlr_output.setScale(@floatCast(f32, head.state.scale));
+ wlr_output.setTransform(head.state.transform);
}
}
-/// Creates an wlr_output_configuration from the current configuration
-fn createOutputConfigurationFromCurrent(self: *Self) !*c.wlr_output_configuration_v1 {
- var config = c.wlr_output_configuration_v1_create() orelse return error.OutOfMemory;
- errdefer c.wlr_output_configuration_v1_destroy(config);
+/// Create the config describing the current configuration
+fn ouputConfigFromCurrent(self: *Self) !*wlr.OutputConfigurationV1 {
+ const config = try wlr.OutputConfigurationV1.create();
+ // this destroys all associated config heads as well
+ errdefer config.destroy();
var it = self.root.all_outputs.first;
- while (it) |node| : (it = node.next) {
- try self.createHead(node.data, config);
- }
+ while (it) |node| : (it = node.next) try self.createHead(node.data, config);
return config;
}
-fn createHead(self: *Self, output: *Output, config: *c.wlr_output_configuration_v1) !void {
+fn createHead(self: *Self, output: *Output, config: *wlr.OutputConfigurationV1) !void {
const wlr_output = output.wlr_output;
- const head: *c.wlr_output_configuration_head_v1 = c.wlr_output_configuration_head_v1_create(config, wlr_output) orelse
- return error.OutOfMemory;
-
- // If the output is not part of the layout (and thus disabled) we dont care about the position
- const box = @as(?*c.wlr_box, c.wlr_output_layout_get_box(self.root.wlr_output_layout, wlr_output));
- if (box) |b| {
- head.state.x = b.x;
- head.state.y = b.y;
+ const head = try wlr.OutputConfigurationV1.Head.create(config, wlr_output);
+
+ // If the output is not part of the layout (and thus disabled) we dont care
+ // about the position
+ if (output.root.output_layout.getBox(wlr_output)) |box| {
+ head.state.x = box.x;
+ head.state.y = box.y;
}
}
diff --git a/river/OutputStatus.zig b/river/OutputStatus.zig
index 298a9d1..4b8e22c 100644
--- a/river/OutputStatus.zig
+++ b/river/OutputStatus.zig
@@ -18,8 +18,10 @@
const Self = @This();
const std = @import("std");
+const wayland = @import("wayland");
+const wl = wayland.server.wl;
+const zriver = wayland.server.zriver;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -27,29 +29,28 @@ const Output = @import("Output.zig");
const View = @import("View.zig");
const ViewStack = @import("view_stack.zig").ViewStack;
-const implementation = c.struct_zriver_output_status_v1_interface{ .destroy = destroy };
-
output: *Output,
-wl_resource: *c.wl_resource,
+output_status: *zriver.OutputStatusV1,
-pub fn init(self: *Self, output: *Output, wl_resource: *c.wl_resource) void {
- self.* = .{ .output = output, .wl_resource = wl_resource };
+pub fn init(self: *Self, output: *Output, output_status: *zriver.OutputStatusV1) void {
+ self.* = .{ .output = output, .output_status = output_status };
- c.wl_resource_set_implementation(wl_resource, &implementation, self, handleResourceDestroy);
+ output_status.setHandler(*Self, handleRequest, handleDestroy, self);
// Send view/focused tags once on bind.
self.sendViewTags();
self.sendFocusedTags();
}
-fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
- const self = util.voidCast(Self, @ptrCast(*c_void, c.wl_resource_get_user_data(wl_resource)));
- const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
- self.output.status_trackers.remove(node);
+fn handleRequest(output_status: *zriver.OutputStatusV1, request: zriver.OutputStatusV1.Request, self: *Self) void {
+ switch (request) {
+ .destroy => output_status.destroy(),
+ }
}
-fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {
- c.wl_resource_destroy(wl_resource);
+fn handleDestroy(output_status: *zriver.OutputStatusV1, self: *Self) void {
+ const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
+ self.output.status_trackers.remove(node);
}
/// Send the current tags of each view on the output to the client.
@@ -61,21 +62,17 @@ pub fn sendViewTags(self: Self) void {
while (it) |node| : (it = node.next) {
if (node.view.destroying) continue;
view_tags.append(node.view.current.tags) catch {
- c.wl_resource_post_no_memory(self.wl_resource);
+ self.output_status.postNoMemory();
log.crit(.river_status, "out of memory", .{});
return;
};
}
- var wl_array = c.wl_array{
- .size = view_tags.items.len * @sizeOf(u32),
- .alloc = view_tags.capacity * @sizeOf(u32),
- .data = view_tags.items.ptr,
- };
- c.zriver_output_status_v1_send_view_tags(self.wl_resource, &wl_array);
+ var wl_array = wl.Array.fromArrayList(u32, view_tags);
+ self.output_status.sendViewTags(&wl_array);
}
/// Send the currently focused tags of the output to the client.
pub fn sendFocusedTags(self: Self) void {
- c.zriver_output_status_v1_send_focused_tags(self.wl_resource, self.output.current.tags);
+ self.output_status.sendFocusedTags(self.output.current.tags);
}
diff --git a/river/PointerMapping.zig b/river/PointerMapping.zig
index cc41cc9..270e3a8 100644
--- a/river/PointerMapping.zig
+++ b/river/PointerMapping.zig
@@ -15,11 +15,13 @@
// 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 wlr = @import("wlroots");
+
pub const Action = enum {
move,
resize,
};
event_code: u32,
-modifiers: u32,
+modifiers: wlr.Keyboard.ModifierMask,
action: Action,
diff --git a/river/Root.zig b/river/Root.zig
index b707143..4218276 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -17,10 +17,11 @@
const Self = @This();
-const std = @import("std");
const build_options = @import("build_options");
+const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -31,10 +32,9 @@ const ViewStack = @import("view_stack.zig").ViewStack;
const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
const DragIcon = @import("DragIcon.zig");
-/// Responsible for all windowing operations
server: *Server,
-wlr_output_layout: *c.wlr_output_layout,
+output_layout: *wlr.OutputLayout,
/// A list of all outputs
all_outputs: std.TailQueue(*Output) = .{},
@@ -60,24 +60,20 @@ else
pending_configures: u32 = 0,
/// Handles timeout of transactions
-transaction_timer: *c.wl_event_source,
+transaction_timer: *wl.EventSource,
pub fn init(self: *Self, server: *Server) !void {
- // Create an output layout, which a wlroots utility for working with an
- // arrangement of screens in a physical layout.
- errdefer c.wlr_output_layout_destroy(self.wlr_output_layout);
+ const output_layout = try wlr.OutputLayout.create();
+ errdefer output_layout.destroy();
+
self.* = .{
.server = server,
- .wlr_output_layout = c.wlr_output_layout_create() orelse return error.OutOfMemory,
- .transaction_timer = c.wl_event_loop_add_timer(
- c.wl_display_get_event_loop(self.server.wl_display),
- handleTimeout,
- self,
- ) orelse return error.AddTimerError,
+ .output_layout = output_layout,
+ .transaction_timer = try self.server.wl_server.getEventLoop().addTimer(*Self, handleTimeout, self),
.noop_output = undefined,
};
- const noop_wlr_output = c.wlr_noop_add_output(server.noop_backend) orelse return error.OutOfMemory;
+ const noop_wlr_output = try server.noop_backend.noopAddOutput();
try self.noop_output.init(self, noop_wlr_output);
}
@@ -86,14 +82,12 @@ pub fn deinit(self: *Self) void {
// the noop backend triggering the destroy event. However,
// Output.handleDestroy is not intended to handle the noop output being
// destroyed.
- c.wl_list_remove(&self.noop_output.listen_destroy.link);
- c.wl_list_remove(&self.noop_output.listen_frame.link);
- c.wl_list_remove(&self.noop_output.listen_mode.link);
-
- c.wlr_output_layout_destroy(self.wlr_output_layout);
+ self.noop_output.destroy.link.remove();
+ self.noop_output.frame.link.remove();
+ self.noop_output.mode.link.remove();
- // This literally cannot fail, but for some reason returns 0
- if (c.wl_event_source_remove(self.transaction_timer) < 0) unreachable;
+ self.output_layout.destroy();
+ self.transaction_timer.remove();
}
/// Removes the output in node.data from self.outputs
@@ -124,7 +118,7 @@ pub fn removeOutput(self: *Self, node: *std.TailQueue(Output).Node) void {
// handle them.
self.noop_output.layers[layer_idx].prepend(layer_node);
layer_surface.output = &self.noop_output;
- c.wlr_layer_surface_v1_close(layer_surface.wlr_layer_surface);
+ layer_surface.wlr_layer_surface.close();
}
}
@@ -153,7 +147,7 @@ pub fn addOutput(self: *Self, node: *std.TailQueue(Output).Node) void {
// from left-to-right in the order they appear. A more sophisticated
// compositor would let the user configure the arrangement of outputs in the
// layout. This automatically creates an output global on the wl_display.
- c.wlr_output_layout_add_auto(self.wlr_output_layout, node.data.wlr_output);
+ self.output_layout.addAuto(node.data.wlr_output);
// if we previously had no real outputs, move focus from the noop output
// to the new one.
@@ -222,26 +216,22 @@ pub fn startTransaction(self: *Self) void {
);
// Set timeout to 200ms
- if (c.wl_event_source_timer_update(self.transaction_timer, 200) < 0) {
+ self.transaction_timer.timerUpdate(200) catch {
log.err(.transaction, "failed to update timer", .{});
self.commitTransaction();
- }
+ };
} else {
// No views need configures, clear the current timer in case we are
// interrupting another transaction and commit.
- if (c.wl_event_source_timer_update(self.transaction_timer, 0) < 0)
- log.err(.transaction, "error disarming timer", .{});
+ self.transaction_timer.timerUpdate(0) catch log.err(.transaction, "error disarming timer", .{});
self.commitTransaction();
}
}
-fn handleTimeout(data: ?*c_void) callconv(.C) c_int {
- const self = util.voidCast(Self, data.?);
-
+fn handleTimeout(self: *Self) callconv(.C) c_int {
log.err(.transaction, "timeout occurred, some imperfect frames may be shown", .{});
self.pending_configures = 0;
-
self.commitTransaction();
return 0;
@@ -251,8 +241,7 @@ pub fn notifyConfigured(self: *Self) void {
self.pending_configures -= 1;
if (self.pending_configures == 0) {
// Disarm the timer, as we didn't timeout
- if (c.wl_event_source_timer_update(self.transaction_timer, 0) == -1)
- log.err(.transaction, "error disarming timer", .{});
+ self.transaction_timer.timerUpdate(0) catch log.err(.transaction, "error disarming timer", .{});
self.commitTransaction();
}
}
diff --git a/river/Seat.zig b/river/Seat.zig
index d2184d9..4a00d1a 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -19,8 +19,10 @@ const Self = @This();
const build_options = @import("build_options");
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
+const xkb = @import("xkbcommon");
-const c = @import("c.zig");
const command = @import("command.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -42,7 +44,7 @@ const FocusTarget = union(enum) {
};
input_manager: *InputManager,
-wlr_seat: *c.wlr_seat,
+wlr_seat: *wlr.Seat,
/// Multiple mice are handled by the same Cursor
cursor: Cursor = undefined,
@@ -56,7 +58,8 @@ mode_id: usize = 0,
/// ID of previous keymap mode, used when returning from "locked" mode
prev_mode_id: usize = 0,
-/// Currently focused output, may be the noop output if no
+/// Currently focused output, may be the noop output if no real output
+/// is currently available for focus.
focused_output: *Output,
/// Currently focused view/layer surface if any
@@ -69,33 +72,33 @@ focus_stack: ViewStack(*View) = .{},
/// List of status tracking objects relaying changes to this seat to clients.
status_trackers: std.SinglyLinkedList(SeatStatus) = .{},
-listen_request_set_selection: c.wl_listener = undefined,
-listen_request_start_drag: c.wl_listener = undefined,
-listen_start_drag: c.wl_listener = undefined,
-listen_request_set_primary_selection: c.wl_listener = undefined,
+request_set_selection: wl.Listener(*wlr.Seat.event.RequestSetSelection) = undefined,
+request_start_drag: wl.Listener(*wlr.Seat.event.RequestStartDrag) = undefined,
+start_drag: wl.Listener(*wlr.Drag) = undefined,
+request_set_primary_selection: wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection) = undefined,
pub fn init(self: *Self, input_manager: *InputManager, name: [*:0]const u8) !void {
self.* = .{
.input_manager = input_manager,
// This will be automatically destroyed when the display is destroyed
- .wlr_seat = c.wlr_seat_create(input_manager.server.wl_display, name) orelse return error.OutOfMemory,
+ .wlr_seat = try wlr.Seat.create(input_manager.server.wl_server, name),
.focused_output = &self.input_manager.server.root.noop_output,
};
- self.wlr_seat.data = self;
+ self.wlr_seat.data = @ptrToInt(self);
try self.cursor.init(self);
- self.listen_request_set_selection.notify = handleRequestSetSelection;
- c.wl_signal_add(&self.wlr_seat.events.request_set_selection, &self.listen_request_set_selection);
+ self.request_set_selection.setNotify(handleRequestSetSelection);
+ self.wlr_seat.events.request_set_selection.add(&self.request_set_selection);
- self.listen_request_start_drag.notify = handleRequestStartDrag;
- c.wl_signal_add(&self.wlr_seat.events.request_start_drag, &self.listen_request_start_drag);
+ self.request_start_drag.setNotify(handleRequestStartDrag);
+ self.wlr_seat.events.request_start_drag.add(&self.request_start_drag);
- self.listen_start_drag.notify = handleStartDrag;
- c.wl_signal_add(&self.wlr_seat.events.start_drag, &self.listen_start_drag);
+ self.start_drag.setNotify(handleStartDrag);
+ self.wlr_seat.events.start_drag.add(&self.start_drag);
- self.listen_request_set_primary_selection.notify = handleRequestPrimarySelection;
- c.wl_signal_add(&self.wlr_seat.events.request_set_primary_selection, &self.listen_request_set_primary_selection);
+ self.request_set_primary_selection.setNotify(handleRequestPrimarySelection);
+ self.wlr_seat.events.request_set_primary_selection.add(&self.request_set_primary_selection);
}
pub fn deinit(self: *Self) void {
@@ -180,29 +183,28 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
// If the target is already focused, do nothing
if (std.meta.eql(new_focus, self.focused)) return;
- // Obtain the target wlr_surface
- const target_wlr_surface = switch (new_focus) {
- .view => |target_view| target_view.wlr_surface.?,
- .layer => |target_layer| target_layer.wlr_layer_surface.surface.?,
+ // Obtain the target surface
+ const target_surface = switch (new_focus) {
+ .view => |target_view| target_view.surface.?,
+ .layer => |target_layer| target_layer.wlr_layer_surface.surface,
.none => null,
};
// If input is not allowed on the target surface (e.g. due to an active
// input inhibitor) do not set focus. If there is no target surface we
// still clear the focus.
- if (if (target_wlr_surface) |wlr_surface| self.input_manager.inputAllowed(wlr_surface) else true) {
+ if (if (target_surface) |wlr_surface| self.input_manager.inputAllowed(wlr_surface) else true) {
// First clear the current focus
if (self.focused == .view) {
self.focused.view.pending.focus -= 1;
// This is needed because xwayland views don't double buffer
// activated state.
if (build_options.xwayland and self.focused.view.impl == .xwayland_view)
- c.wlr_xwayland_surface_activate(self.focused.view.impl.xwayland_view.wlr_xwayland_surface, false);
+ self.focused.view.impl.xwayland_view.xwayland_surface.activate(false);
if (self.focused.view.pending.focus == 0 and !self.focused.view.pending.fullscreen) {
self.focused.view.pending.target_opacity = self.input_manager.server.config.view_opacity_unfocused;
}
}
- c.wlr_seat_keyboard_clear_focus(self.wlr_seat);
// Set the new focus
switch (new_focus) {
@@ -212,7 +214,7 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
// This is needed because xwayland views don't double buffer
// activated state.
if (build_options.xwayland and target_view.impl == .xwayland_view)
- c.wlr_xwayland_surface_activate(target_view.impl.xwayland_view.wlr_xwayland_surface, true);
+ target_view.impl.xwayland_view.xwayland_surface.activate(true);
if (!target_view.pending.fullscreen) {
target_view.pending.target_opacity = self.input_manager.server.config.view_opacity_focused;
}
@@ -222,16 +224,20 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void {
}
self.focused = new_focus;
- // Tell wlroots to send the new keyboard focus if we have a target
- if (target_wlr_surface) |wlr_surface| {
- const keyboard: *c.wlr_keyboard = c.wlr_seat_get_keyboard(self.wlr_seat);
- c.wlr_seat_keyboard_notify_enter(
- self.wlr_seat,
- wlr_surface,
- &keyboard.keycodes,
- keyboard.num_keycodes,
- &keyboard.modifiers,
- );
+ // Send surface enter/leave events
+ if (target_surface) |wlr_surface| {
+ if (self.wlr_seat.getKeyboard()) |keyboard| {
+ self.wlr_seat.keyboardNotifyEnter(
+ wlr_surface,
+ &keyboard.keycodes,
+ keyboard.num_keycodes,
+ &keyboard.modifiers,
+ );
+ } else {
+ self.wlr_seat.keyboardNotifyEnter(wlr_surface, null, 0, null);
+ }
+ } else {
+ self.wlr_seat.keyboardClearFocus();
}
}
@@ -256,7 +262,7 @@ pub fn focusOutput(self: *Self, output: *Output) void {
}
pub fn handleActivity(self: Self) void {
- c.wlr_idle_notify_activity(self.input_manager.wlr_idle, self.wlr_seat);
+ self.input_manager.idle.notifyActivity(self.wlr_seat);
}
/// Handle the unmapping of a view, removing it from the focus stack and
@@ -280,10 +286,15 @@ pub fn handleViewUnmap(self: *Self, view: *View) void {
/// Handle any user-defined mapping for the passed keysym and modifiers
/// Returns true if the key was handled
-pub fn handleMapping(self: *Self, keysym: c.xkb_keysym_t, modifiers: u32, released: bool) bool {
+pub fn handleMapping(
+ self: *Self,
+ keysym: xkb.Keysym,
+ modifiers: wlr.Keyboard.ModifierMask,
+ released: bool,
+) bool {
const modes = &self.input_manager.server.config.modes;
for (modes.items[self.mode_id].mappings.items) |mapping| {
- if (modifiers == mapping.modifiers and keysym == mapping.keysym and released == mapping.release) {
+ if (std.meta.eql(modifiers, mapping.modifiers) and keysym == mapping.keysym and released == mapping.release) {
// Execute the bound command
const args = mapping.command_args;
var out: ?[]const u8 = null;
@@ -309,22 +320,23 @@ pub fn handleMapping(self: *Self, keysym: c.xkb_keysym_t, modifiers: u32, releas
/// Add a newly created input device to the seat and update the reported
/// capabilities.
-pub fn addDevice(self: *Self, device: *c.wlr_input_device) void {
+pub fn addDevice(self: *Self, device: *wlr.InputDevice) void {
switch (device.type) {
- .WLR_INPUT_DEVICE_KEYBOARD => self.addKeyboard(device) catch return,
- .WLR_INPUT_DEVICE_POINTER => self.addPointer(device),
+ .keyboard => self.addKeyboard(device) catch return,
+ .pointer => self.addPointer(device),
else => return,
}
// We need to let the wlr_seat know what our capabilities are, which is
// communiciated to the client. We always have a cursor, even if
// there are no pointer devices, so we always include that capability.
- var caps = @intCast(u32, c.WL_SEAT_CAPABILITY_POINTER);
- if (self.keyboards.len > 0) caps |= @intCast(u32, c.WL_SEAT_CAPABILITY_KEYBOARD);
- c.wlr_seat_set_capabilities(self.wlr_seat, caps);
+ self.wlr_seat.setCapabilities(.{
+ .pointer = true,
+ .keyboard = self.keyboards.len > 0,
+ });
}
-fn addKeyboard(self: *Self, device: *c.wlr_input_device) !void {
+fn addKeyboard(self: *Self, device: *wlr.InputDevice) !void {
const node = try util.gpa.create(std.TailQueue(Keyboard).Node);
node.data.init(self, device) catch |err| {
switch (err) {
@@ -335,40 +347,46 @@ fn addKeyboard(self: *Self, device: *c.wlr_input_device) !void {
return;
};
self.keyboards.append(node);
- c.wlr_seat_set_keyboard(self.wlr_seat, device);
+ self.wlr_seat.setKeyboard(device);
}
-fn addPointer(self: Self, device: *c.struct_wlr_input_device) void {
+fn addPointer(self: Self, device: *wlr.InputDevice) void {
// We don't do anything special with pointers. All of our pointer handling
// is proxied through wlr_cursor. On another compositor, you might take this
// opportunity to do libinput configuration on the device to set
// acceleration, etc.
- c.wlr_cursor_attach_input_device(self.cursor.wlr_cursor, device);
+ self.cursor.wlr_cursor.attachInputDevice(device);
}
-fn handleRequestSetSelection(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_set_selection", listener.?);
- const event = util.voidCast(c.wlr_seat_request_set_selection_event, data.?);
- c.wlr_seat_set_selection(self.wlr_seat, event.source, event.serial);
+fn handleRequestSetSelection(
+ listener: *wl.Listener(*wlr.Seat.event.RequestSetSelection),
+ event: *wlr.Seat.event.RequestSetSelection,
+) void {
+ const self = @fieldParentPtr(Self, "request_set_selection", listener);
+ self.wlr_seat.setSelection(event.source, event.serial);
}
-fn handleRequestStartDrag(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_start_drag", listener.?);
- const event = util.voidCast(c.wlr_seat_request_start_drag_event, data.?);
+fn handleRequestStartDrag(
+ listener: *wl.Listener(*wlr.Seat.event.RequestStartDrag),
+ event: *wlr.Seat.event.RequestStartDrag,
+) void {
+ const self = @fieldParentPtr(Self, "request_start_drag", listener);
- if (c.wlr_seat_validate_pointer_grab_serial(self.wlr_seat, event.origin, event.serial)) {
- log.debug(.seat, "starting pointer drag", .{});
- c.wlr_seat_start_pointer_drag(self.wlr_seat, event.drag, event.serial);
+ if (!self.wlr_seat.validatePointerGrabSerial(event.origin, event.serial)) {
+ log.debug(.seat, "ignoring request to start drag, failed to validate serial {}", .{event.serial});
+ if (event.drag.source) |source| source.destroy();
return;
}
- log.debug(.seat, "ignoring request to start drag, failed to validate serial {}", .{event.serial});
- c.wlr_data_source_destroy(event.drag.*.source);
+ log.debug(.seat, "starting pointer drag", .{});
+ self.wlr_seat.startPointerDrag(event.drag, event.serial);
}
-fn handleStartDrag(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_start_drag", listener.?);
- const wlr_drag = util.voidCast(c.wlr_drag, data.?);
+fn handleStartDrag(
+ listener: *wl.Listener(*wlr.Drag),
+ wlr_drag: *wlr.Drag,
+) void {
+ const self = @fieldParentPtr(Self, "start_drag", listener);
if (wlr_drag.icon) |wlr_drag_icon| {
const node = util.gpa.create(std.SinglyLinkedList(DragIcon).Node) catch {
@@ -381,8 +399,10 @@ fn handleStartDrag(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void
self.cursor.mode = .passthrough;
}
-fn handleRequestPrimarySelection(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_set_primary_selection", listener.?);
- const event = util.voidCast(c.wlr_seat_request_set_primary_selection_event, data.?);
- c.wlr_seat_set_primary_selection(self.wlr_seat, event.source, event.serial);
+fn handleRequestPrimarySelection(
+ listener: *wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection),
+ event: *wlr.Seat.event.RequestSetPrimarySelection,
+) void {
+ const self = @fieldParentPtr(Self, "request_set_primary_selection", listener);
+ self.wlr_seat.setPrimarySelection(event.source, event.serial);
}
diff --git a/river/SeatStatus.zig b/river/SeatStatus.zig
index 44f3ae3..a6c0d46 100644
--- a/river/SeatStatus.zig
+++ b/river/SeatStatus.zig
@@ -18,55 +18,53 @@
const Self = @This();
const std = @import("std");
+const wayland = @import("wayland");
+const wl = wayland.server.wl;
+const zriver = wayland.server.zriver;
-const c = @import("c.zig");
const util = @import("util.zig");
const Seat = @import("Seat.zig");
const Output = @import("Output.zig");
const View = @import("View.zig");
-const implementation = c.struct_zriver_seat_status_v1_interface{ .destroy = destroy };
-
seat: *Seat,
-wl_resource: *c.wl_resource,
+seat_status: *zriver.SeatStatusV1,
-pub fn init(self: *Self, seat: *Seat, wl_resource: *c.wl_resource) void {
- self.* = .{ .seat = seat, .wl_resource = wl_resource };
+pub fn init(self: *Self, seat: *Seat, seat_status: *zriver.SeatStatusV1) void {
+ self.* = .{ .seat = seat, .seat_status = seat_status };
- c.wl_resource_set_implementation(wl_resource, &implementation, self, handleResourceDestroy);
+ seat_status.setHandler(*Self, handleRequest, handleDestroy, self);
// Send focused output/view once on bind
self.sendOutput(.focused);
self.sendFocusedView();
}
-fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
- const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
+fn handleRequest(seat_status: *zriver.SeatStatusV1, request: zriver.SeatStatusV1.Request, self: *Self) void {
+ switch (request) {
+ .destroy => seat_status.destroy(),
+ }
+}
+
+fn handleDestroy(seat_status: *zriver.SeatStatusV1, self: *Self) void {
const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
self.seat.status_trackers.remove(node);
util.gpa.destroy(node);
}
-fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {
- c.wl_resource_destroy(wl_resource);
-}
-
pub fn sendOutput(self: Self, state: enum { focused, unfocused }) void {
- const wl_client = c.wl_resource_get_client(self.wl_resource);
- const output_resources = &self.seat.focused_output.wlr_output.resources;
- var output_resource = c.wl_resource_from_link(output_resources.next);
- while (c.wl_resource_get_link(output_resource) != output_resources) : (output_resource =
- c.wl_resource_from_link(c.wl_resource_get_link(output_resource).*.next))
- {
- if (c.wl_resource_get_client(output_resource) == wl_client) switch (state) {
- .focused => c.zriver_seat_status_v1_send_focused_output(self.wl_resource, output_resource),
- .unfocused => c.zriver_seat_status_v1_send_unfocused_output(self.wl_resource, output_resource),
+ const client = self.seat_status.getClient();
+ var it = self.seat.focused_output.wlr_output.resources.iterator(.forward);
+ while (it.next()) |wl_output| {
+ if (wl_output.getClient() == client) switch (state) {
+ .focused => self.seat_status.sendFocusedOutput(wl_output),
+ .unfocused => self.seat_status.sendUnfocusedOutput(wl_output),
};
}
}
pub fn sendFocusedView(self: Self) void {
const title: [*:0]const u8 = if (self.seat.focused == .view) self.seat.focused.view.getTitle() else "";
- c.zriver_seat_status_v1_send_focused_view(self.wl_resource, title);
+ self.seat_status.sendFocusedView(title);
}
diff --git a/river/Server.zig b/river/Server.zig
index b9b88b8..1d91061 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -19,6 +19,8 @@ const Self = @This();
const build_options = @import("build_options");
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
const c = @import("c.zig");
const log = @import("log.zig");
@@ -37,22 +39,22 @@ const View = @import("View.zig");
const ViewStack = @import("view_stack.zig").ViewStack;
const XwaylandUnmanaged = @import("XwaylandUnmanaged.zig");
-wl_display: *c.wl_display,
+wl_server: *wl.Server,
-sigint_source: *c.wl_event_source,
-sigterm_source: *c.wl_event_source,
+sigint_source: *wl.EventSource,
+sigterm_source: *wl.EventSource,
-wlr_backend: *c.wlr_backend,
-noop_backend: *c.wlr_backend,
+backend: *wlr.Backend,
+noop_backend: *wlr.Backend,
-wlr_xdg_shell: *c.wlr_xdg_shell,
-listen_new_xdg_surface: c.wl_listener,
+xdg_shell: *wlr.XdgShell,
+new_xdg_surface: wl.Listener(*wlr.XdgSurface),
-wlr_layer_shell: *c.wlr_layer_shell_v1,
-listen_new_layer_surface: c.wl_listener,
+layer_shell: *wlr.LayerShellV1,
+new_layer_surface: wl.Listener(*wlr.LayerSurfaceV1),
-wlr_xwayland: if (build_options.xwayland) *c.wlr_xwayland else void,
-listen_new_xwayland_surface: if (build_options.xwayland) c.wl_listener else void,
+xwayland: if (build_options.xwayland) *wlr.Xwayland else void,
+new_xwayland_surface: if (build_options.xwayland) wl.Listener(*wlr.XwaylandSurface) else void,
decoration_manager: DecorationManager,
input_manager: InputManager,
@@ -63,61 +65,46 @@ control: Control,
status_manager: StatusManager,
pub fn init(self: *Self) !void {
- // The Wayland display is managed by libwayland. It handles accepting
- // clients from the Unix socket, managing Wayland globals, and so on.
- self.wl_display = c.wl_display_create() orelse return error.CreateDisplayError;
- errdefer c.wl_display_destroy(self.wl_display);
-
- // Never returns null if the display was created successfully
- const wl_event_loop = c.wl_display_get_event_loop(self.wl_display);
- self.sigint_source = c.wl_event_loop_add_signal(wl_event_loop, std.os.SIGINT, terminate, self.wl_display) orelse
- return error.AddEventSourceFailed;
- errdefer _ = c.wl_event_source_remove(self.sigint_source);
- self.sigterm_source = c.wl_event_loop_add_signal(wl_event_loop, std.os.SIGTERM, terminate, self.wl_display) orelse
- return error.AddEventSourceFailed;
- errdefer _ = c.wl_event_source_remove(self.sigterm_source);
-
- // The wlr_backend abstracts the input/output hardware. Autocreate chooses
- // the best option based on the environment, for example DRM when run from
- // a tty or wayland if WAYLAND_DISPLAY is set. This frees itself when the
- // wl_display is destroyed.
- self.wlr_backend = c.river_wlr_backend_autocreate(self.wl_display) orelse
- return error.CreateBackendError;
+ self.wl_server = try wl.Server.create();
+ 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);
+ errdefer self.sigint_source.remove();
+ self.sigterm_source = try loop.addSignal(*wl.Server, std.os.SIGTERM, terminate, self.wl_server);
+ errdefer self.sigterm_source.remove();
+
+ // This frees itself when the wl.Server is destroyed
+ self.backend = try wlr.Backend.autocreate(self.wl_server, null);
// This backend is used to create a noop output for use when no actual
- // outputs are available. This frees itself when the wl_display is destroyed.
- self.noop_backend = c.wlr_noop_backend_create(self.wl_display) orelse
- return error.OutOfMemory;
+ // outputs are available. This frees itself when the wl.Server is destroyed.
+ self.noop_backend = try wlr.Backend.createNoop(self.wl_server);
- // If we don't provide a renderer, autocreate makes a GLES2 renderer for us.
- // The renderer is responsible for defining the various pixel formats it
- // supports for shared memory, this configures that for clients.
- const wlr_renderer = c.wlr_backend_get_renderer(self.wlr_backend).?;
- if (!c.wlr_renderer_init_wl_display(wlr_renderer, self.wl_display)) return error.DisplayInitFailed;
+ // This will never be null for the non-custom backends in wlroots
+ const renderer = self.backend.getRenderer().?;
+ try renderer.initServer(self.wl_server);
- const wlr_compositor = c.wlr_compositor_create(self.wl_display, wlr_renderer) orelse
- return error.OutOfMemory;
+ const compositor = try wlr.Compositor.create(self.wl_server, renderer);
// Set up xdg shell
- self.wlr_xdg_shell = c.wlr_xdg_shell_create(self.wl_display) orelse return error.OutOfMemory;
- self.listen_new_xdg_surface.notify = handleNewXdgSurface;
- c.wl_signal_add(&self.wlr_xdg_shell.events.new_surface, &self.listen_new_xdg_surface);
+ self.xdg_shell = try wlr.XdgShell.create(self.wl_server);
+ self.new_xdg_surface.setNotify(handleNewXdgSurface);
+ self.xdg_shell.events.new_surface.add(&self.new_xdg_surface);
// Set up layer shell
- self.wlr_layer_shell = c.wlr_layer_shell_v1_create(self.wl_display) orelse return error.OutOfMemory;
- self.listen_new_layer_surface.notify = handleNewLayerSurface;
- c.wl_signal_add(&self.wlr_layer_shell.events.new_surface, &self.listen_new_layer_surface);
+ self.layer_shell = try wlr.LayerShellV1.create(self.wl_server);
+ self.new_layer_surface.setNotify(handleNewLayerSurface);
+ self.layer_shell.events.new_surface.add(&self.new_layer_surface);
// Set up xwayland if built with support
if (build_options.xwayland) {
- self.wlr_xwayland = c.wlr_xwayland_create(self.wl_display, wlr_compositor, false) orelse
- return error.CreateXwaylandError;
- self.listen_new_xwayland_surface.notify = handleNewXwaylandSurface;
- c.wl_signal_add(&self.wlr_xwayland.events.new_surface, &self.listen_new_xwayland_surface);
+ self.xwayland = try wlr.Xwayland.create(self.wl_server, compositor, false);
+ self.new_xwayland_surface.setNotify(handleNewXwaylandSurface);
+ self.xwayland.events.new_surface.add(&self.new_xwayland_surface);
}
- // Set up primary selection
- _ = c.wlr_primary_selection_v1_device_manager_create(self.wl_display);
+ _ = try wlr.PrimarySelectionDeviceManagerV1.create(self.wl_server);
self.config = try Config.init();
try self.decoration_manager.init(self);
@@ -128,31 +115,28 @@ pub fn init(self: *Self) !void {
try self.status_manager.init(self);
try self.output_manager.init(self);
- // These all free themselves when the wl_display is destroyed
- _ = c.wlr_data_device_manager_create(self.wl_display) orelse return error.OutOfMemory;
- _ = c.wlr_data_control_manager_v1_create(self.wl_display) orelse return error.OutOfMemory;
- _ = c.wlr_export_dmabuf_manager_v1_create(self.wl_display) orelse return error.OutOfMemory;
- _ = c.wlr_gamma_control_manager_v1_create(self.wl_display) orelse return error.OutOfMemory;
- _ = c.wlr_screencopy_manager_v1_create(self.wl_display) orelse return error.OutOfMemory;
- _ = c.wlr_viewporter_create(self.wl_display) orelse return error.OutOfMemory;
- _ = c.wlr_xdg_output_manager_v1_create(self.wl_display, self.root.wlr_output_layout) orelse
- return error.OutOfMemory;
+ // These all free themselves when the wl_server is destroyed
+ _ = try wlr.DataDeviceManager.create(self.wl_server);
+ _ = try wlr.DataControlManagerV1.create(self.wl_server);
+ _ = try wlr.ExportDmabufManagerV1.create(self.wl_server);
+ _ = try wlr.GammaControlManagerV1.create(self.wl_server);
+ _ = try wlr.ScreencopyManagerV1.create(self.wl_server);
+ _ = try wlr.Viewporter.create(self.wl_server);
}
-/// Free allocated memory and clean up
+/// Free allocated memory and clean up. Note: order is important here
pub fn deinit(self: *Self) void {
- // Note: order is important here
- _ = c.wl_event_source_remove(self.sigint_source);
- _ = c.wl_event_source_remove(self.sigterm_source);
+ self.sigint_source.remove();
+ self.sigterm_source.remove();
- if (build_options.xwayland) c.wlr_xwayland_destroy(self.wlr_xwayland);
+ if (build_options.xwayland) self.xwayland.destroy();
- c.wl_display_destroy_clients(self.wl_display);
+ self.wl_server.destroyClients();
self.root.deinit();
- c.wl_display_destroy(self.wl_display);
- c.wlr_backend_destroy(self.noop_backend);
+ self.wl_server.destroy();
+ self.noop_backend.destroy();
self.input_manager.deinit();
self.config.deinit();
@@ -160,33 +144,26 @@ pub fn deinit(self: *Self) void {
/// Create the socket, start the backend, and setup the environment
pub fn start(self: Self) !void {
- const socket = c.wl_display_add_socket_auto(self.wl_display) orelse return error.AddSocketError;
- if (!c.wlr_backend_start(self.wlr_backend)) return error.StartBackendError;
+ var buf: [11]u8 = undefined;
+ const socket = try self.wl_server.addSocketAuto(&buf);
+ try self.backend.start();
+ // TODO: don't use libc's setenv
if (c.setenv("WAYLAND_DISPLAY", socket, 1) < 0) return error.SetenvError;
if (build_options.xwayland) {
- if (c.setenv("DISPLAY", self.wlr_xwayland.display_name, 1) < 0) return error.SetenvError;
+ if (c.setenv("DISPLAY", self.xwayland.display_name, 1) < 0) return error.SetenvError;
}
}
-/// Enter the wayland event loop and block until the compositor is exited
-pub fn run(self: Self) void {
- c.wl_display_run(self.wl_display);
-}
-
/// Handle SIGINT and SIGTERM by gracefully stopping the server
-fn terminate(signal: c_int, data: ?*c_void) callconv(.C) c_int {
- const wl_display = util.voidCast(c.wl_display, data.?);
- c.wl_display_terminate(wl_display);
+fn terminate(signal: c_int, wl_server: *wl.Server) callconv(.C) c_int {
+ wl_server.terminate();
return 0;
}
-fn handleNewXdgSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is raised when wlr_xdg_shell receives a new xdg surface from a
- // client, either a toplevel (application window) or popup.
- const self = @fieldParentPtr(Self, "listen_new_xdg_surface", listener.?);
- const wlr_xdg_surface = util.voidCast(c.wlr_xdg_surface, data.?);
+fn handleNewXdgSurface(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+ const self = @fieldParentPtr(Self, "new_xdg_surface", listener);
- if (wlr_xdg_surface.role == .WLR_XDG_SURFACE_ROLE_POPUP) {
+ if (xdg_surface.role == .popup) {
log.debug(.server, "new xdg_popup", .{});
return;
}
@@ -196,16 +173,15 @@ fn handleNewXdgSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) v
// The View will add itself to the output's view stack on map
const output = self.input_manager.defaultSeat().focused_output;
const node = util.gpa.create(ViewStack(View).Node) catch {
- c.wl_resource_post_no_memory(wlr_xdg_surface.resource);
+ xdg_surface.resource.postNoMemory();
return;
};
- node.view.init(output, output.current.tags, wlr_xdg_surface);
+ node.view.init(output, output.current.tags, xdg_surface);
}
/// This event is raised when the layer_shell recieves a new surface from a client.
-fn handleNewLayerSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_layer_surface", listener.?);
- const wlr_layer_surface = util.voidCast(c.wlr_layer_surface_v1, data.?);
+fn handleNewLayerSurface(listener: *wl.Listener(*wlr.LayerSurfaceV1), wlr_layer_surface: *wlr.LayerSurfaceV1) void {
+ const self = @fieldParentPtr(Self, "new_layer_surface", listener);
log.debug(
.server,
@@ -229,31 +205,28 @@ fn handleNewLayerSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C)
if (wlr_layer_surface.output == null) {
const output = self.input_manager.defaultSeat().focused_output;
if (output == &self.root.noop_output) {
- log.err(.server, "no output available for layer surface '{s}'", .{wlr_layer_surface.namespace});
- c.wlr_layer_surface_v1_close(wlr_layer_surface);
+ log.err(.server, "no output available for layer surface '{}'", .{wlr_layer_surface.namespace});
+ wlr_layer_surface.close();
return;
}
- log.debug(
- .server,
- "new layer surface had null output, assigning it to output '{s}'",
- .{output.wlr_output.name},
- );
+ log.debug(.server, "new layer surface had null output, assigning it to output '{}'", .{
+ output.wlr_output.name,
+ });
wlr_layer_surface.output = output.wlr_output;
}
// The layer surface will add itself to the proper list of the output on map
- const output = util.voidCast(Output, wlr_layer_surface.output.*.data.?);
+ const output = @intToPtr(*Output, wlr_layer_surface.output.?.data);
const node = util.gpa.create(std.TailQueue(LayerSurface).Node) catch {
- c.wl_resource_post_no_memory(wlr_layer_surface.resource);
+ wlr_layer_surface.resource.postNoMemory();
return;
};
node.data.init(output, wlr_layer_surface);
}
-fn handleNewXwaylandSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_xwayland_surface", listener.?);
- const wlr_xwayland_surface = util.voidCast(c.wlr_xwayland_surface, data.?);
+fn handleNewXwaylandSurface(listener: *wl.Listener(*wlr.XwaylandSurface), wlr_xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "new_xwayland_surface", listener);
if (wlr_xwayland_surface.override_redirect) {
log.debug(.server, "new unmanaged xwayland surface", .{});
diff --git a/river/StatusManager.zig b/river/StatusManager.zig
index bd4d48b..f630dde 100644
--- a/river/StatusManager.zig
+++ b/river/StatusManager.zig
@@ -18,8 +18,11 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wayland = @import("wayland");
+const wl = wayland.server.wl;
+const zriver = wayland.server.zriver;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -29,120 +32,89 @@ const Seat = @import("Seat.zig");
const SeatStatus = @import("SeatStatus.zig");
const Server = @import("Server.zig");
-const protocol_version = 1;
+global: *wl.Global,
-const implementation = c.struct_zriver_status_manager_v1_interface{
- .destroy = destroy,
- .get_river_output_status = getRiverOutputStatus,
- .get_river_seat_status = getRiverSeatStatus,
-};
-
-wl_global: *c.wl_global,
-
-listen_display_destroy: c.wl_listener = undefined,
+server_destroy: wl.Listener(*wl.Server) = undefined,
pub fn init(self: *Self, server: *Server) !void {
self.* = .{
- .wl_global = c.wl_global_create(
- server.wl_display,
- &c.zriver_status_manager_v1_interface,
- protocol_version,
- self,
- bind,
- ) orelse return error.OutOfMemory,
+ .global = try wl.Global.create(server.wl_server, zriver.StatusManagerV1, 1, *Self, self, bind),
};
- self.listen_display_destroy.notify = handleDisplayDestroy;
- c.wl_display_add_destroy_listener(server.wl_display, &self.listen_display_destroy);
+ self.server_destroy.setNotify(handleServerDestroy);
+ server.wl_server.addDestroyListener(&self.server_destroy);
}
-fn handleDisplayDestroy(wl_listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_display_destroy", wl_listener.?);
- c.wl_global_destroy(self.wl_global);
+fn handleServerDestroy(listener: *wl.Listener(*wl.Server), wl_server: *wl.Server) void {
+ const self = @fieldParentPtr(Self, "server_destroy", listener);
+ self.global.destroy();
}
-/// Called when a client binds our global
-fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callconv(.C) void {
- const self = util.voidCast(Self, data.?);
- const wl_resource = c.wl_resource_create(
- wl_client,
- &c.zriver_status_manager_v1_interface,
- @intCast(c_int, version),
- id,
- ) orelse {
- c.wl_client_post_no_memory(wl_client);
- log.crit(.river_status, "out of memory", .{});
- return;
- };
- c.wl_resource_set_implementation(wl_resource, &implementation, self, null);
-}
-
-fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C) void {
- c.wl_resource_destroy(wl_resource);
-}
-
-fn getRiverOutputStatus(
- wl_client: ?*c.wl_client,
- wl_resource: ?*c.wl_resource,
- new_id: u32,
- output_wl_resource: ?*c.wl_resource,
-) callconv(.C) void {
- const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
- // This can be null if the output is inert, in which case we ignore the request
- const wlr_output = c.wlr_output_from_resource(output_wl_resource) orelse return;
- const output = util.voidCast(Output, wlr_output.*.data.?);
-
- const node = util.gpa.create(std.SinglyLinkedList(OutputStatus).Node) catch {
- c.wl_client_post_no_memory(wl_client);
- log.crit(.river_status, "out of memory", .{});
- return;
- };
-
- const output_status_resource = c.wl_resource_create(
- wl_client,
- &c.zriver_output_status_v1_interface,
- protocol_version,
- new_id,
- ) orelse {
- c.wl_client_post_no_memory(wl_client);
- util.gpa.destroy(node);
+fn bind(client: *wl.Client, self: *Self, version: u32, id: u32) callconv(.C) void {
+ const status_manager = zriver.StatusManagerV1.create(client, version, id) catch {
+ client.postNoMemory();
log.crit(.river_status, "out of memory", .{});
return;
};
-
- node.data.init(output, output_status_resource);
- output.status_trackers.prepend(node);
+ status_manager.setHandler(*Self, handleRequest, null, self);
}
-fn getRiverSeatStatus(
- wl_client: ?*c.wl_client,
- wl_resource: ?*c.wl_resource,
- new_id: u32,
- seat_wl_resource: ?*c.wl_resource,
-) callconv(.C) void {
- const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
- // This can be null if the seat is inert, in which case we ignore the request
- const wlr_seat_client = c.wlr_seat_client_from_resource(seat_wl_resource) orelse return;
- const seat = util.voidCast(Seat, wlr_seat_client.*.seat.*.data.?);
-
- const node = util.gpa.create(std.SinglyLinkedList(SeatStatus).Node) catch {
- c.wl_client_post_no_memory(wl_client);
- log.crit(.river_status, "out of memory", .{});
- return;
- };
-
- const seat_status_resource = c.wl_resource_create(
- wl_client,
- &c.zriver_seat_status_v1_interface,
- protocol_version,
- new_id,
- ) orelse {
- c.wl_client_post_no_memory(wl_client);
- util.gpa.destroy(node);
- log.crit(.river_status, "out of memory", .{});
- return;
- };
-
- node.data.init(seat, seat_status_resource);
- seat.status_trackers.prepend(node);
+fn handleRequest(
+ status_manager: *zriver.StatusManagerV1,
+ request: zriver.StatusManagerV1.Request,
+ self: *Self,
+) void {
+ switch (request) {
+ .destroy => status_manager.destroy(),
+ .get_river_output_status => |req| {
+ // ignore if the output is inert
+ const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return;
+ const output = @intToPtr(*Output, wlr_output.data);
+
+ const node = util.gpa.create(std.SinglyLinkedList(OutputStatus).Node) catch {
+ status_manager.getClient().postNoMemory();
+ log.crit(.river_status, "out of memory", .{});
+ return;
+ };
+
+ const output_status = zriver.OutputStatusV1.create(
+ status_manager.getClient(),
+ status_manager.getVersion(),
+ req.id,
+ ) catch {
+ status_manager.getClient().postNoMemory();
+ util.gpa.destroy(node);
+ log.crit(.river_status, "out of memory", .{});
+ return;
+ };
+
+ node.data.init(output, output_status);
+ output.status_trackers.prepend(node);
+ },
+ .get_river_seat_status => |req| {
+ // ignore if the seat is inert
+ const wlr_seat = wlr.Seat.Client.fromWlSeat(req.seat) orelse return;
+ const seat = @intToPtr(*Seat, wlr_seat.seat.data);
+
+ const node = util.gpa.create(std.SinglyLinkedList(SeatStatus).Node) catch {
+ status_manager.getClient().postNoMemory();
+ log.crit(.river_status, "out of memory", .{});
+ return;
+ };
+
+ const seat_status = zriver.SeatStatusV1.create(
+ status_manager.getClient(),
+ status_manager.getVersion(),
+ req.id,
+ ) catch {
+ status_manager.getClient().postNoMemory();
+ util.gpa.destroy(node);
+ log.crit(.river_status, "out of memory", .{});
+ return;
+ };
+
+ node.data.init(seat, seat_status);
+ seat.status_trackers.prepend(node);
+ },
+ }
}
diff --git a/river/View.zig b/river/View.zig
index f049895..ddde6a4 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -19,8 +19,10 @@ const Self = @This();
const build_options = @import("build_options");
const std = @import("std");
+const os = std.os;
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -71,9 +73,9 @@ const State = struct {
};
const SavedBuffer = struct {
- wlr_client_buffer: *c.wlr_client_buffer,
+ client_buffer: *wlr.ClientBuffer,
box: Box,
- transform: c.wl_output_transform,
+ transform: wl.Output.Transform,
};
/// The implementation of this view
@@ -84,10 +86,10 @@ output: *Output,
/// This is from the point where the view is mapped until the surface
/// is destroyed by wlroots.
-wlr_surface: ?*c.wlr_surface = null,
+surface: ?*wlr.Surface = null,
/// This View struct outlasts the wlroots object it wraps. This bool is set to
-/// true when the backing wlr_xdg_toplevel or equivalent has been destroyed.
+/// true when the backing wlr.XdgToplevel or equivalent has been destroyed.
destroying: bool = false,
/// The double-buffered state of the view
@@ -116,7 +118,7 @@ float_box: Box = undefined,
opacity: f32,
/// Opacity change timer event source
-opacity_timer: ?*c.wl_event_source = null,
+opacity_timer: ?*wl.EventSource = null,
draw_borders: bool = true,
@@ -135,10 +137,10 @@ pub fn init(self: *Self, output: *Output, tags: u32, surface: anytype) void {
.opacity = output.root.server.config.view_opacity_initial,
};
- if (@TypeOf(surface) == *c.wlr_xdg_surface) {
+ if (@TypeOf(surface) == *wlr.XdgSurface) {
self.impl = .{ .xdg_toplevel = undefined };
self.impl.xdg_toplevel.init(self, surface);
- } else if (build_options.xwayland and @TypeOf(surface) == *c.wlr_xwayland_surface) {
+ } else if (build_options.xwayland and @TypeOf(surface) == *wlr.XwaylandSurface) {
self.impl = .{ .xwayland_view = undefined };
self.impl.xwayland_view.init(self, surface);
} else unreachable;
@@ -146,7 +148,7 @@ pub fn init(self: *Self, output: *Output, tags: u32, surface: anytype) void {
/// Deinit the view, remove it from the view stack and free the memory.
pub fn destroy(self: *Self) void {
- for (self.saved_buffers.items) |buffer| c.wlr_buffer_unlock(&buffer.wlr_client_buffer.*.base);
+ self.dropSavedBuffers();
self.saved_buffers.deinit();
switch (self.impl) {
.xdg_toplevel => |*xdg_toplevel| xdg_toplevel.deinit(),
@@ -183,12 +185,12 @@ pub fn applyPending(self: *Self) void {
// and turn the view fully opaque
if (!self.current.fullscreen and self.pending.fullscreen) {
self.pending.target_opacity = 1.0;
- const layout_box = c.wlr_output_layout_get_box(self.output.root.wlr_output_layout, self.output.wlr_output);
+ const layout_box = self.output.root.output_layout.getBox(self.output.wlr_output).?;
self.pending.box = .{
.x = 0,
.y = 0,
- .width = @intCast(u32, layout_box.*.width),
- .height = @intCast(u32, layout_box.*.height),
+ .width = @intCast(u32, layout_box.width),
+ .height = @intCast(u32, layout_box.height),
};
}
@@ -225,20 +227,20 @@ pub fn configure(self: Self) void {
}
pub fn sendFrameDone(self: Self) void {
- var now: c.timespec = undefined;
- _ = c.clock_gettime(c.CLOCK_MONOTONIC, &now);
- c.wlr_surface_send_frame_done(self.wlr_surface.?, &now);
+ var now: os.timespec = undefined;
+ os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch @panic("CLOCK_MONOTONIC not supported");
+ self.surface.?.sendFrameDone(&now);
}
pub fn dropSavedBuffers(self: *Self) void {
- for (self.saved_buffers.items) |buffer| c.wlr_buffer_unlock(&buffer.wlr_client_buffer.*.base);
+ for (self.saved_buffers.items) |buffer| buffer.client_buffer.base.unlock();
self.saved_buffers.items.len = 0;
}
pub fn saveBuffers(self: *Self) void {
std.debug.assert(self.saved_buffers.items.len == 0);
self.saved_surface_box = self.surface_box;
- self.forEachSurface(saveBuffersIterator, &self.saved_buffers);
+ self.forEachSurface(*std.ArrayList(SavedBuffer), saveBuffersIterator, &self.saved_buffers);
}
/// If this commit is in response to our configure and the
@@ -257,26 +259,23 @@ pub fn notifyConfiguredOrApplyPending(self: *Self) void {
}
fn saveBuffersIterator(
- wlr_surface: ?*c.wlr_surface,
+ surface: *wlr.Surface,
surface_x: c_int,
surface_y: c_int,
- data: ?*c_void,
+ saved_buffers: *std.ArrayList(SavedBuffer),
) callconv(.C) void {
- const saved_buffers = util.voidCast(std.ArrayList(SavedBuffer), data.?);
- if (wlr_surface) |surface| {
- if (c.wlr_surface_has_buffer(surface)) {
- saved_buffers.append(.{
- .wlr_client_buffer = surface.buffer,
- .box = Box{
- .x = surface_x,
- .y = surface_y,
- .width = @intCast(u32, surface.current.width),
- .height = @intCast(u32, surface.current.height),
- },
- .transform = surface.current.transform,
- }) catch return;
- _ = c.wlr_buffer_lock(&surface.buffer.*.base);
- }
+ if (surface.buffer) |buffer| {
+ saved_buffers.append(.{
+ .client_buffer = buffer,
+ .box = Box{
+ .x = surface_x,
+ .y = surface_y,
+ .width = @intCast(u32, surface.current.width),
+ .height = @intCast(u32, surface.current.height),
+ },
+ .transform = surface.current.transform,
+ }) catch return;
+ _ = buffer.base.lock();
}
}
@@ -291,8 +290,8 @@ pub fn sendToOutput(self: *Self, destination_output: *Output) void {
self.output.sendViewTags();
destination_output.sendViewTags();
- c.wlr_surface_send_leave(self.wlr_surface, self.output.wlr_output);
- c.wlr_surface_send_enter(self.wlr_surface, destination_output.wlr_output);
+ self.surface.?.sendLeave(self.output.wlr_output);
+ self.surface.?.sendEnter(destination_output.wlr_output);
self.output = destination_output;
}
@@ -304,20 +303,21 @@ pub fn close(self: Self) void {
}
}
-pub fn forEachSurface(
+pub inline fn forEachSurface(
self: Self,
- iterator: c.wlr_surface_iterator_func_t,
- user_data: ?*c_void,
+ comptime T: type,
+ iterator: fn (surface: *wlr.Surface, sx: c_int, sy: c_int, data: T) callconv(.C) void,
+ user_data: T,
) void {
switch (self.impl) {
- .xdg_toplevel => |xdg_toplevel| xdg_toplevel.forEachSurface(iterator, user_data),
- .xwayland_view => |xwayland_view| xwayland_view.forEachSurface(iterator, user_data),
+ .xdg_toplevel => |xdg_toplevel| xdg_toplevel.forEachSurface(T, iterator, user_data),
+ .xwayland_view => |xwayland_view| xwayland_view.forEachSurface(T, iterator, user_data),
}
}
/// Return the surface at output coordinates ox, oy and set sx, sy to the
/// corresponding surface-relative coordinates, if there is a surface.
-pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
+pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
return switch (self.impl) {
.xdg_toplevel => |xdg_toplevel| xdg_toplevel.surfaceAt(ox, oy, sx, sy),
.xwayland_view => |xwayland_view| xwayland_view.surfaceAt(ox, oy, sx, sy),
@@ -348,18 +348,18 @@ pub fn getConstraints(self: Self) Constraints {
};
}
-/// Find and return the view corresponding to a given wlr_surface, if any
-pub fn fromWlrSurface(wlr_surface: *c.wlr_surface) ?*Self {
- if (c.wlr_surface_is_xdg_surface(wlr_surface)) {
- const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(wlr_surface);
- if (wlr_xdg_surface.*.role == .WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
- return util.voidCast(Self, wlr_xdg_surface.*.data.?);
+/// Find and return the view corresponding to a given surface, if any
+pub fn fromWlrSurface(surface: *wlr.Surface) ?*Self {
+ if (surface.isXdgSurface()) {
+ const xdg_surface = wlr.XdgSurface.fromWlrSurface(surface);
+ if (xdg_surface.role == .toplevel) {
+ return @intToPtr(*Self, xdg_surface.data);
}
}
if (build_options.xwayland) {
- if (c.wlr_surface_is_xwayland_surface(wlr_surface)) {
- const wlr_xwayland_surface = c.wlr_xwayland_surface_from_wlr_surface(wlr_surface);
- return util.voidCast(Self, wlr_xwayland_surface.*.data.?);
+ if (surface.isXWaylandSurface()) {
+ const xwayland_surface = wlr.XwaylandSurface.fromWlrSurface(surface);
+ return @intToPtr(*Self, xwayland_surface.data);
}
}
return null;
@@ -392,7 +392,7 @@ pub fn map(self: *Self) void {
var it = root.server.input_manager.seats.first;
while (it) |seat_node| : (it = seat_node.next) seat_node.data.focus(self);
- c.wlr_surface_send_enter(self.wlr_surface.?, self.output.wlr_output);
+ self.surface.?.sendEnter(self.output.wlr_output);
self.output.sendViewTags();
@@ -446,22 +446,21 @@ fn incrementOpacity(self: *Self) bool {
/// Destroy a views opacity timer
fn killOpacityTimer(self: *Self) void {
- if (c.wl_event_source_remove(self.opacity_timer) < 0) unreachable;
+ self.opacity_timer.?.remove();
self.opacity_timer = null;
}
/// Set the timeout on a views opacity timer
fn armOpacityTimer(self: *Self) void {
const delta_t = self.output.root.server.config.view_opacity_delta_t;
- if (c.wl_event_source_timer_update(self.opacity_timer, delta_t) < 0) {
- log.err(.view, "failed to update opacity timer", .{});
+ self.opacity_timer.?.timerUpdate(delta_t) catch |err| {
+ log.err(.view, "failed to update opacity timer: {}", .{err});
self.killOpacityTimer();
- }
+ };
}
/// Called by the opacity timer
-fn handleOpacityTimer(data: ?*c_void) callconv(.C) c_int {
- const self = util.voidCast(Self, data.?);
+fn handleOpacityTimer(self: *Self) callconv(.C) c_int {
if (self.incrementOpacity()) {
self.killOpacityTimer();
} else {
@@ -472,12 +471,8 @@ fn handleOpacityTimer(data: ?*c_void) callconv(.C) c_int {
/// Create an opacity timer for a view and arm it
fn attachOpacityTimer(self: *Self) void {
- const server = self.output.root.server;
- self.opacity_timer = c.wl_event_loop_add_timer(
- c.wl_display_get_event_loop(server.wl_display),
- handleOpacityTimer,
- self,
- ) orelse {
+ const event_loop = self.output.root.server.wl_server.getEventLoop();
+ self.opacity_timer = event_loop.addTimer(*Self, handleOpacityTimer, self) catch {
log.err(.view, "failed to create opacity timer for view '{}'", .{self.getTitle()});
return;
};
diff --git a/river/VoidView.zig b/river/VoidView.zig
index 033286d..ff29d99 100644
--- a/river/VoidView.zig
+++ b/river/VoidView.zig
@@ -18,8 +18,7 @@
const Self = @This();
const std = @import("std");
-
-const c = @import("c.zig");
+const wlr = @import("wlroots");
const Box = @import("Box.zig");
const View = @import("View.zig");
@@ -42,13 +41,14 @@ pub fn close(self: Self) void {
pub fn forEachSurface(
self: Self,
- iterator: c.wlr_surface_iterator_func_t,
- user_data: ?*c_void,
+ comptime T: type,
+ iterator: fn (surface: *wlr.Surface, sx: c_int, sy: c_int, data: T) callconv(.C) void,
+ user_data: T,
) void {
unreachable;
}
-pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
+pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
unreachable;
}
diff --git a/river/XdgPopup.zig b/river/XdgPopup.zig
index 7f73582..abdced9 100644
--- a/river/XdgPopup.zig
+++ b/river/XdgPopup.zig
@@ -18,8 +18,10 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
+const log = @import("log.zig");
const util = @import("util.zig");
const Box = @import("Box.zig");
@@ -32,12 +34,12 @@ output: *Output,
parent_box: *const Box,
/// The corresponding wlroots object
-wlr_xdg_popup: *c.wlr_xdg_popup,
+wlr_xdg_popup: *wlr.XdgPopup,
-listen_destroy: c.wl_listener = undefined,
-listen_new_popup: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.XdgSurface) = undefined,
+new_popup: wl.Listener(*wlr.XdgPopup) = undefined,
-pub fn init(self: *Self, output: *Output, parent_box: *const Box, wlr_xdg_popup: *c.wlr_xdg_popup) void {
+pub fn init(self: *Self, output: *Output, parent_box: *const Box, wlr_xdg_popup: *wlr.XdgPopup) void {
self.* = .{
.output = output,
.parent_box = parent_box,
@@ -45,36 +47,35 @@ pub fn init(self: *Self, output: *Output, parent_box: *const Box, wlr_xdg_popup:
};
// The output box relative to the parent of the popup
- var box = c.wlr_output_layout_get_box(output.root.wlr_output_layout, output.wlr_output).*;
+ var box = output.root.output_layout.getBox(output.wlr_output).?.*;
box.x -= parent_box.x;
box.y -= parent_box.y;
- c.wlr_xdg_popup_unconstrain_from_box(wlr_xdg_popup, &box);
+ wlr_xdg_popup.unconstrainFromBox(&box);
- // Setup listeners
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&wlr_xdg_popup.base.*.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ wlr_xdg_popup.base.events.destroy.add(&self.destroy);
- self.listen_new_popup.notify = handleNewPopup;
- c.wl_signal_add(&wlr_xdg_popup.base.*.events.new_popup, &self.listen_new_popup);
+ self.new_popup.setNotify(handleNewPopup);
+ wlr_xdg_popup.base.events.new_popup.add(&self.new_popup);
}
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), wlr_xdg_surface: *wlr.XdgSurface) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
- c.wl_list_remove(&self.listen_destroy.link);
- c.wl_list_remove(&self.listen_new_popup.link);
+ self.destroy.link.remove();
+ self.new_popup.link.remove();
util.gpa.destroy(self);
}
/// Called when a new xdg popup is requested by the client
-fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_popup", listener.?);
- const wlr_xdg_popup = util.voidCast(c.wlr_xdg_popup, data.?);
+fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void {
+ const self = @fieldParentPtr(Self, "new_popup", listener);
// This will free itself on destroy
- var xdg_popup = util.gpa.create(Self) catch {
- c.wl_resource_post_no_memory(wlr_xdg_popup.resource);
+ const xdg_popup = util.gpa.create(Self) catch {
+ wlr_xdg_popup.resource.postNoMemory();
+ log.crit(.server, "out of memory", .{});
return;
};
xdg_popup.init(self.output, self.parent_box, wlr_xdg_popup);
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index 6f28874..8017be9 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -18,8 +18,9 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -33,52 +34,49 @@ const XdgPopup = @import("XdgPopup.zig");
view: *View,
/// The corresponding wlroots object
-wlr_xdg_surface: *c.wlr_xdg_surface,
+xdg_surface: *wlr.XdgSurface,
// Listeners that are always active over the view's lifetime
-listen_destroy: c.wl_listener = undefined,
-listen_map: c.wl_listener = undefined,
-listen_unmap: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.XdgSurface) = undefined,
+map: wl.Listener(*wlr.XdgSurface) = undefined,
+unmap: wl.Listener(*wlr.XdgSurface) = undefined,
// Listeners that are only active while the view is mapped
-listen_commit: c.wl_listener = undefined,
-listen_new_popup: c.wl_listener = undefined,
-listen_request_fullscreen: c.wl_listener = undefined,
-listen_request_move: c.wl_listener = undefined,
-listen_request_resize: c.wl_listener = undefined,
-listen_set_title: c.wl_listener = undefined,
+commit: wl.Listener(*wlr.Surface) = undefined,
+new_popup: wl.Listener(*wlr.XdgPopup) = undefined,
+request_fullscreen: wl.Listener(*wlr.XdgToplevel.event.SetFullscreen) = undefined,
+request_move: wl.Listener(*wlr.XdgToplevel.event.Move) = undefined,
+request_resize: wl.Listener(*wlr.XdgToplevel.event.Resize) = undefined,
+set_title: wl.Listener(*wlr.XdgSurface) = undefined,
-pub fn init(self: *Self, view: *View, wlr_xdg_surface: *c.wlr_xdg_surface) void {
- self.* = .{ .view = view, .wlr_xdg_surface = wlr_xdg_surface };
- wlr_xdg_surface.data = self;
+pub fn init(self: *Self, view: *View, xdg_surface: *wlr.XdgSurface) void {
+ self.* = .{ .view = view, .xdg_surface = xdg_surface };
+ xdg_surface.data = @ptrToInt(self);
// Add listeners that are active over the view's entire lifetime
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&self.wlr_xdg_surface.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ self.xdg_surface.events.destroy.add(&self.destroy);
- self.listen_map.notify = handleMap;
- c.wl_signal_add(&self.wlr_xdg_surface.events.map, &self.listen_map);
+ self.map.setNotify(handleMap);
+ self.xdg_surface.events.map.add(&self.map);
- self.listen_unmap.notify = handleUnmap;
- c.wl_signal_add(&self.wlr_xdg_surface.events.unmap, &self.listen_unmap);
+ self.unmap.setNotify(handleUnmap);
+ self.xdg_surface.events.unmap.add(&self.unmap);
}
pub fn deinit(self: *Self) void {
- if (self.view.wlr_surface != null) {
+ if (self.view.surface != null) {
// Remove listeners that are active for the entire lifetime of the view
- c.wl_list_remove(&self.listen_destroy.link);
- c.wl_list_remove(&self.listen_map.link);
- c.wl_list_remove(&self.listen_unmap.link);
+ self.destroy.link.remove();
+ self.map.link.remove();
+ self.unmap.link.remove();
}
}
/// Returns true if a configure must be sent to ensure the dimensions of the
/// pending_box are applied.
pub fn needsConfigure(self: Self) bool {
- const server_pending = &@field(
- self.wlr_xdg_surface,
- c.wlr_xdg_surface_union,
- ).toplevel.*.server_pending;
+ const server_pending = &self.xdg_surface.role_data.toplevel.server_pending;
const state = &self.view.pending;
// Checking server_pending is sufficient here since it will be either in
@@ -92,35 +90,32 @@ pub fn needsConfigure(self: Self) bool {
/// Send a configure event, applying the pending state of the view.
pub fn configure(self: Self) void {
+ const toplevel = self.xdg_surface.role_data.toplevel;
const state = &self.view.pending;
- _ = c.wlr_xdg_toplevel_set_activated(self.wlr_xdg_surface, state.focus != 0);
- _ = c.wlr_xdg_toplevel_set_fullscreen(self.wlr_xdg_surface, state.fullscreen);
- self.view.pending_serial = c.wlr_xdg_toplevel_set_size(
- self.wlr_xdg_surface,
- state.box.width,
- state.box.height,
- );
+ _ = toplevel.setActivated(state.focus != 0);
+ _ = toplevel.setFullscreen(state.fullscreen);
+ self.view.pending_serial = toplevel.setSize(state.box.width, state.box.height);
}
/// Close the view. This will lead to the unmap and destroy events being sent
pub fn close(self: Self) void {
- c.wlr_xdg_toplevel_send_close(self.wlr_xdg_surface);
+ self.xdg_surface.role_data.toplevel.sendClose();
}
-pub fn forEachSurface(
+pub inline fn forEachSurface(
self: Self,
- iterator: c.wlr_surface_iterator_func_t,
- user_data: ?*c_void,
+ comptime T: type,
+ iterator: fn (surface: *wlr.Surface, sx: c_int, sy: c_int, data: T) callconv(.C) void,
+ user_data: T,
) void {
- c.wlr_xdg_surface_for_each_surface(self.wlr_xdg_surface, iterator, user_data);
+ self.xdg_surface.forEachSurface(T, iterator, user_data);
}
/// Return the surface at output coordinates ox, oy and set sx, sy to the
/// corresponding surface-relative coordinates, if there is a surface.
-pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
+pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
const view = self.view;
- return c.wlr_xdg_surface_surface_at(
- self.wlr_xdg_surface,
+ return self.xdg_surface.surfaceAt(
ox - @intToFloat(f64, view.current.box.x - view.surface_box.x),
oy - @intToFloat(f64, view.current.box.y - view.surface_box.y),
sx,
@@ -130,16 +125,12 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
/// Return the current title of the toplevel. May be an empty string.
pub fn getTitle(self: Self) [*:0]const u8 {
- const wlr_xdg_toplevel: *c.wlr_xdg_toplevel = @field(
- self.wlr_xdg_surface,
- c.wlr_xdg_surface_union,
- ).toplevel;
- return wlr_xdg_toplevel.title orelse "NULL";
+ return self.xdg_surface.role_data.toplevel.title orelse "NULL";
}
/// Return bounds on the dimensions of the toplevel.
pub fn getConstraints(self: Self) View.Constraints {
- const state = @field(self.wlr_xdg_surface, c.wlr_xdg_surface_union).toplevel.*.current;
+ const state = &self.xdg_surface.role_data.toplevel.current;
return .{
.min_width = std.math.max(state.min_width, View.min_size),
.max_width = if (state.max_width > 0) state.max_width else std.math.maxInt(u32),
@@ -149,55 +140,55 @@ pub fn getConstraints(self: Self) View.Constraints {
}
/// Called when the xdg surface is destroyed
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
self.deinit();
- self.view.wlr_surface = null;
+ self.view.surface = null;
}
/// Called when the xdg surface is mapped, or ready to display on-screen.
-fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_map", listener.?);
+fn handleMap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+ const self = @fieldParentPtr(Self, "map", listener);
const view = self.view;
const root = view.output.root;
- const wlr_xdg_toplevel: *c.wlr_xdg_toplevel = @field(self.wlr_xdg_surface, c.wlr_xdg_surface_union).toplevel;
+ const toplevel = self.xdg_surface.role_data.toplevel;
// Add listeners that are only active while mapped
- self.listen_commit.notify = handleCommit;
- c.wl_signal_add(&self.wlr_xdg_surface.surface.*.events.commit, &self.listen_commit);
+ self.commit.setNotify(handleCommit);
+ self.xdg_surface.surface.events.commit.add(&self.commit);
- self.listen_new_popup.notify = handleNewPopup;
- c.wl_signal_add(&self.wlr_xdg_surface.events.new_popup, &self.listen_new_popup);
+ self.new_popup.setNotify(handleNewPopup);
+ self.xdg_surface.events.new_popup.add(&self.new_popup);
- self.listen_request_fullscreen.notify = handleRequestFullscreen;
- c.wl_signal_add(&wlr_xdg_toplevel.events.request_fullscreen, &self.listen_request_fullscreen);
+ self.request_fullscreen.setNotify(handleRequestFullscreen);
+ toplevel.events.request_fullscreen.add(&self.request_fullscreen);
- self.listen_request_move.notify = handleRequestMove;
- c.wl_signal_add(&wlr_xdg_toplevel.events.request_move, &self.listen_request_move);
+ self.request_move.setNotify(handleRequestMove);
+ toplevel.events.request_move.add(&self.request_move);
- self.listen_request_resize.notify = handleRequestResize;
- c.wl_signal_add(&wlr_xdg_toplevel.events.request_resize, &self.listen_request_resize);
+ self.request_resize.setNotify(handleRequestResize);
+ toplevel.events.request_resize.add(&self.request_resize);
- self.listen_set_title.notify = handleSetTitle;
- c.wl_signal_add(&wlr_xdg_toplevel.events.set_title, &self.listen_set_title);
+ self.set_title.setNotify(handleSetTitle);
+ toplevel.events.set_title.add(&self.set_title);
- view.wlr_surface = self.wlr_xdg_surface.surface;
+ view.surface = self.xdg_surface.surface;
// Use the view's "natural" size centered on the output as the default
// floating dimensions
- view.float_box.width = @intCast(u32, self.wlr_xdg_surface.geometry.width);
- view.float_box.height = @intCast(u32, self.wlr_xdg_surface.geometry.height);
+ view.float_box.width = @intCast(u32, self.xdg_surface.geometry.width);
+ view.float_box.height = @intCast(u32, self.xdg_surface.geometry.height);
view.float_box.x = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) -
@intCast(i32, view.float_box.width), 2));
view.float_box.y = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) -
@intCast(i32, view.float_box.height), 2));
- const state = &wlr_xdg_toplevel.current;
+ const state = &toplevel.current;
const has_fixed_size = state.min_width != 0 and state.min_height != 0 and
(state.min_width == state.max_width or state.min_height == state.max_height);
- const app_id: [*:0]const u8 = if (wlr_xdg_toplevel.app_id) |id| id else "NULL";
+ const app_id: [*:0]const u8 = if (toplevel.app_id) |id| id else "NULL";
- if (wlr_xdg_toplevel.parent != null or has_fixed_size) {
+ if (toplevel.parent != null or has_fixed_size) {
// If the toplevel has a parent or has a fixed size make it float
view.current.float = true;
view.pending.float = true;
@@ -222,38 +213,35 @@ fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
break;
}
} else {
- _ = c.wlr_xdg_toplevel_set_tiled(
- self.wlr_xdg_surface,
- c.WLR_EDGE_LEFT | c.WLR_EDGE_RIGHT | c.WLR_EDGE_TOP | c.WLR_EDGE_BOTTOM,
- );
+ _ = toplevel.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true });
}
view.map();
}
/// Called when the surface is unmapped and will no longer be displayed.
-fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_unmap", listener.?);
+fn handleUnmap(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+ const self = @fieldParentPtr(Self, "unmap", listener);
const root = self.view.output.root;
self.view.unmap();
// Remove listeners that are only active while mapped
- c.wl_list_remove(&self.listen_commit.link);
- c.wl_list_remove(&self.listen_new_popup.link);
- c.wl_list_remove(&self.listen_request_fullscreen.link);
- c.wl_list_remove(&self.listen_request_move.link);
- c.wl_list_remove(&self.listen_request_resize.link);
- c.wl_list_remove(&self.listen_set_title.link);
+ self.commit.link.remove();
+ self.new_popup.link.remove();
+ self.request_fullscreen.link.remove();
+ self.request_move.link.remove();
+ self.request_resize.link.remove();
+ self.set_title.link.remove();
}
/// Called when the surface is comitted
-fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_commit", listener.?);
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+ const self = @fieldParentPtr(Self, "commit", listener);
const view = self.view;
- var wlr_box: c.wlr_box = undefined;
- c.wlr_xdg_surface_get_geometry(self.wlr_xdg_surface, &wlr_box);
+ var wlr_box: wlr.Box = undefined;
+ self.xdg_surface.getGeometry(&wlr_box);
const new_box = Box.fromWlrBox(wlr_box);
// If we have sent a configure changing the size
@@ -261,7 +249,7 @@ fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
// Update the stored dimensions of the surface
view.surface_box = new_box;
- if (s == self.wlr_xdg_surface.configure_serial) {
+ if (s == self.xdg_surface.configure_serial) {
view.notifyConfiguredOrApplyPending();
} else {
// If the client has not yet acked our configure, we need to send a
@@ -279,13 +267,12 @@ fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
/// Called when a new xdg popup is requested by the client
-fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_new_popup", listener.?);
- const wlr_xdg_popup = util.voidCast(c.wlr_xdg_popup, data.?);
+fn handleNewPopup(listener: *wl.Listener(*wlr.XdgPopup), wlr_xdg_popup: *wlr.XdgPopup) void {
+ const self = @fieldParentPtr(Self, "new_popup", listener);
// This will free itself on destroy
- var xdg_popup = util.gpa.create(XdgPopup) catch {
- c.wl_resource_post_no_memory(wlr_xdg_popup.resource);
+ const xdg_popup = util.gpa.create(XdgPopup) catch {
+ wlr_xdg_popup.resource.postNoMemory();
return;
};
xdg_popup.init(self.view.output, &self.view.current.box, wlr_xdg_popup);
@@ -293,33 +280,36 @@ fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
/// Called when the client asks to be fullscreened. We always honor the request
/// for now, perhaps it should be denied in some cases in the future.
-fn handleRequestFullscreen(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_fullscreen", listener.?);
- const event = util.voidCast(c.wlr_xdg_toplevel_set_fullscreen_event, data.?);
+fn handleRequestFullscreen(
+ listener: *wl.Listener(*wlr.XdgToplevel.event.SetFullscreen),
+ event: *wlr.XdgToplevel.event.SetFullscreen,
+) void {
+ const self = @fieldParentPtr(Self, "request_fullscreen", listener);
self.view.pending.fullscreen = event.fullscreen;
self.view.applyPending();
}
/// Called when the client asks to be moved via the cursor, for example when the
/// user drags CSD titlebars.
-fn handleRequestMove(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_move", listener.?);
- const event = util.voidCast(c.wlr_xdg_toplevel_move_event, data.?);
- const seat = util.voidCast(Seat, event.seat.*.seat.*.data.?);
+fn handleRequestMove(
+ listener: *wl.Listener(*wlr.XdgToplevel.event.Move),
+ event: *wlr.XdgToplevel.event.Move,
+) void {
+ const self = @fieldParentPtr(Self, "request_move", listener);
+ const seat = @intToPtr(*Seat, event.seat.seat.data);
seat.cursor.enterMode(.move, self.view);
}
/// Called when the client asks to be resized via the cursor.
-fn handleRequestResize(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_request_resize", listener.?);
- const event = util.voidCast(c.wlr_xdg_toplevel_resize_event, data.?);
- const seat = util.voidCast(Seat, event.seat.*.seat.*.data.?);
+fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), event: *wlr.XdgToplevel.event.Resize) void {
+ const self = @fieldParentPtr(Self, "request_resize", listener);
+ const seat = @intToPtr(*Seat, event.seat.seat.data);
seat.cursor.enterMode(.resize, self.view);
}
/// Called when the client sets / updates its title
-fn handleSetTitle(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_set_title", listener.?);
+fn handleSetTitle(listener: *wl.Listener(*wlr.XdgSurface), xdg_surface: *wlr.XdgSurface) void {
+ const self = @fieldParentPtr(Self, "set_title", listener);
// Send title to all status listeners attached to a seat which focuses this view
var seat_it = self.view.output.root.server.input_manager.seats.first;
diff --git a/river/XwaylandUnmanaged.zig b/river/XwaylandUnmanaged.zig
index 746e962..003693d 100644
--- a/river/XwaylandUnmanaged.zig
+++ b/river/XwaylandUnmanaged.zig
@@ -18,8 +18,9 @@
const Self = @This();
const std = @import("std");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
-const c = @import("c.zig");
const util = @import("util.zig");
const Box = @import("Box.zig");
@@ -28,39 +29,38 @@ const Root = @import("Root.zig");
root: *Root,
/// The corresponding wlroots object
-wlr_xwayland_surface: *c.wlr_xwayland_surface,
+xwayland_surface: *wlr.XwaylandSurface,
// Listeners that are always active over the view's lifetime
-liseten_request_configure: c.wl_listener = undefined,
-listen_destroy: c.wl_listener = undefined,
-listen_map: c.wl_listener = undefined,
-listen_unmap: c.wl_listener = undefined,
+request_configure: wl.Listener(*wlr.XwaylandSurface.event.Configure) = undefined,
+destroy: wl.Listener(*wlr.XwaylandSurface) = undefined,
+map: wl.Listener(*wlr.XwaylandSurface) = undefined,
+unmap: wl.Listener(*wlr.XwaylandSurface) = undefined,
// Listeners that are only active while the view is mapped
-listen_commit: c.wl_listener = undefined,
+commit: wl.Listener(*wlr.Surface) = undefined,
-pub fn init(self: *Self, root: *Root, wlr_xwayland_surface: *c.wlr_xwayland_surface) void {
- self.* = .{ .root = root, .wlr_xwayland_surface = wlr_xwayland_surface };
+pub fn init(self: *Self, root: *Root, xwayland_surface: *wlr.XwaylandSurface) void {
+ self.* = .{ .root = root, .xwayland_surface = xwayland_surface };
// Add listeners that are active over the view's entire lifetime
- self.liseten_request_configure.notify = handleRequestConfigure;
- c.wl_signal_add(&wlr_xwayland_surface.events.request_configure, &self.liseten_request_configure);
+ self.request_configure.setNotify(handleRequestConfigure);
+ xwayland_surface.events.request_configure.add(&self.request_configure);
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&wlr_xwayland_surface.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ xwayland_surface.events.destroy.add(&self.destroy);
- self.listen_map.notify = handleMap;
- c.wl_signal_add(&wlr_xwayland_surface.events.map, &self.listen_map);
+ self.map.setNotify(handleMap);
+ xwayland_surface.events.map.add(&self.map);
- self.listen_unmap.notify = handleUnmap;
- c.wl_signal_add(&wlr_xwayland_surface.events.unmap, &self.listen_unmap);
+ self.unmap.setNotify(handleUnmap);
+ xwayland_surface.events.unmap.add(&self.unmap);
}
/// Return the surface at output coordinates ox, oy and set sx, sy to the
/// corresponding surface-relative coordinates, if there is a surface.
-pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
- return c.wlr_surface_surface_at(
- self.wlr_xwayland_surface.surface,
+pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
+ return self.xwayland_surface.surface.?.surfaceAt(
ox - @intToFloat(f64, self.view.current_box.x),
oy - @intToFloat(f64, self.view.current_box.y),
sx,
@@ -68,26 +68,22 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
);
}
-fn handleRequestConfigure(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "liseten_request_configure", listener.?);
- const wlr_xwayland_surface_configure_event = util.voidCast(c.wlr_xwayland_surface_configure_event, data.?);
- c.wlr_xwayland_surface_configure(
- self.wlr_xwayland_surface,
- wlr_xwayland_surface_configure_event.x,
- wlr_xwayland_surface_configure_event.y,
- wlr_xwayland_surface_configure_event.width,
- wlr_xwayland_surface_configure_event.height,
- );
+fn handleRequestConfigure(
+ listener: *wl.Listener(*wlr.XwaylandSurface.event.Configure),
+ event: *wlr.XwaylandSurface.event.Configure,
+) void {
+ const self = @fieldParentPtr(Self, "request_configure", listener);
+ self.xwayland_surface.configure(event.x, event.y, event.width, event.height);
}
/// Called when the xwayland surface is destroyed
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
// Remove listeners that are active for the entire lifetime of the view
- c.wl_list_remove(&self.listen_destroy.link);
- c.wl_list_remove(&self.listen_map.link);
- c.wl_list_remove(&self.listen_unmap.link);
+ self.destroy.link.remove();
+ self.map.link.remove();
+ self.unmap.link.remove();
// Deallocate the node
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
@@ -95,8 +91,8 @@ fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
/// Called when the xwayland surface is mapped, or ready to display on-screen.
-fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_map", listener.?);
+fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "map", listener);
const root = self.root;
// Add self to the list of unmanaged views in the root
@@ -104,29 +100,29 @@ fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
root.xwayland_unmanaged_views.prepend(node);
// Add listeners that are only active while mapped
- self.listen_commit.notify = handleCommit;
- c.wl_signal_add(&self.wlr_xwayland_surface.surface.*.events.commit, &self.listen_commit);
+ self.commit.setNotify(handleCommit);
+ xwayland_surface.surface.?.events.commit.add(&self.commit);
// TODO: handle keyboard focus
- // if (wlr_xwayland_or_surface_wants_focus(self.wlr_xwayland_surface)) { ...
+ // if (wlr_xwayland_or_surface_wants_focus(self.xwayland_surface)) { ...
}
/// Called when the surface is unmapped and will no longer be displayed.
-fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_unmap", listener.?);
+fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "unmap", listener);
// Remove self from the list of unmanged views in the root
const node = @fieldParentPtr(std.TailQueue(Self).Node, "data", self);
self.root.xwayland_unmanaged_views.remove(node);
// Remove listeners that are only active while mapped
- c.wl_list_remove(&self.listen_commit.link);
+ self.commit.link.remove();
// TODO: return focus
}
/// Called when the surface is comitted
-fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_commit", listener.?);
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+ const self = @fieldParentPtr(Self, "commit", listener);
// TODO: check if the surface has moved for damage tracking
}
diff --git a/river/XwaylandView.zig b/river/XwaylandView.zig
index 04a6b60..ab665a8 100644
--- a/river/XwaylandView.zig
+++ b/river/XwaylandView.zig
@@ -18,8 +18,8 @@
const Self = @This();
const std = @import("std");
-
-const c = @import("c.zig");
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
const Box = @import("Box.zig");
const View = @import("View.zig");
@@ -30,58 +30,57 @@ const XdgPopup = @import("XdgPopup.zig");
view: *View,
/// The corresponding wlroots object
-wlr_xwayland_surface: *c.wlr_xwayland_surface,
+xwayland_surface: *wlr.XwaylandSurface,
// Listeners that are always active over the view's lifetime
-listen_destroy: c.wl_listener = undefined,
-listen_map: c.wl_listener = undefined,
-listen_unmap: c.wl_listener = undefined,
-listen_title: c.wl_listener = undefined,
+destroy: wl.Listener(*wlr.XwaylandSurface) = undefined,
+map: wl.Listener(*wlr.XwaylandSurface) = undefined,
+unmap: wl.Listener(*wlr.XwaylandSurface) = undefined,
+title: wl.Listener(*wlr.XwaylandSurface) = undefined,
// Listeners that are only active while the view is mapped
-listen_commit: c.wl_listener = undefined,
+commit: wl.Listener(*wlr.Surface) = undefined,
-pub fn init(self: *Self, view: *View, wlr_xwayland_surface: *c.wlr_xwayland_surface) void {
- self.* = .{ .view = view, .wlr_xwayland_surface = wlr_xwayland_surface };
- wlr_xwayland_surface.data = self;
+pub fn init(self: *Self, view: *View, xwayland_surface: *wlr.XwaylandSurface) void {
+ self.* = .{ .view = view, .xwayland_surface = xwayland_surface };
+ xwayland_surface.data = @ptrToInt(self);
// Add listeners that are active over the view's entire lifetime
- self.listen_destroy.notify = handleDestroy;
- c.wl_signal_add(&self.wlr_xwayland_surface.events.destroy, &self.listen_destroy);
+ self.destroy.setNotify(handleDestroy);
+ self.xwayland_surface.events.destroy.add(&self.destroy);
- self.listen_map.notify = handleMap;
- c.wl_signal_add(&self.wlr_xwayland_surface.events.map, &self.listen_map);
+ self.map.setNotify(handleMap);
+ self.xwayland_surface.events.map.add(&self.map);
- self.listen_unmap.notify = handleUnmap;
- c.wl_signal_add(&self.wlr_xwayland_surface.events.unmap, &self.listen_unmap);
+ self.unmap.setNotify(handleUnmap);
+ self.xwayland_surface.events.unmap.add(&self.unmap);
- self.listen_title.notify = handleTitle;
- c.wl_signal_add(&self.wlr_xwayland_surface.events.set_title, &self.listen_title);
+ self.title.setNotify(handleTitle);
+ self.xwayland_surface.events.set_title.add(&self.title);
}
pub fn deinit(self: *Self) void {
- if (self.view.wlr_surface != null) {
+ if (self.view.surface != null) {
// Remove listeners that are active for the entire lifetime of the view
- c.wl_list_remove(&self.listen_destroy.link);
- c.wl_list_remove(&self.listen_map.link);
- c.wl_list_remove(&self.listen_unmap.link);
- c.wl_list_remove(&self.listen_title.link);
+ self.destroy.link.remove();
+ self.map.link.remove();
+ self.unmap.link.remove();
+ self.title.link.remove();
}
}
pub fn needsConfigure(self: Self) bool {
- return self.wlr_xwayland_surface.x != self.view.pending.box.x or
- self.wlr_xwayland_surface.y != self.view.pending.box.y or
- self.wlr_xwayland_surface.width != self.view.pending.box.width or
- self.wlr_xwayland_surface.height != self.view.pending.box.height;
+ return self.xwayland_surface.x != self.view.pending.box.x or
+ self.xwayland_surface.y != self.view.pending.box.y or
+ self.xwayland_surface.width != self.view.pending.box.width or
+ self.xwayland_surface.height != self.view.pending.box.height;
}
/// Apply pending state
pub fn configure(self: Self) void {
const state = &self.view.pending;
- c.wlr_xwayland_surface_set_fullscreen(self.wlr_xwayland_surface, state.fullscreen);
- c.wlr_xwayland_surface_configure(
- self.wlr_xwayland_surface,
+ self.xwayland_surface.setFullscreen(state.fullscreen);
+ self.xwayland_surface.configure(
@intCast(i16, state.box.x),
@intCast(i16, state.box.y),
@intCast(u16, state.box.width),
@@ -97,23 +96,23 @@ pub fn configure(self: Self) void {
/// Close the view. This will lead to the unmap and destroy events being sent
pub fn close(self: Self) void {
- c.wlr_xwayland_surface_close(self.wlr_xwayland_surface);
+ self.xwayland_surface.close();
}
/// Iterate over all surfaces of the xwayland view.
pub fn forEachSurface(
self: Self,
- iterator: c.wlr_surface_iterator_func_t,
- user_data: ?*c_void,
+ comptime T: type,
+ iterator: fn (surface: *wlr.Surface, sx: c_int, sy: c_int, data: T) callconv(.C) void,
+ data: T,
) void {
- c.wlr_surface_for_each_surface(self.wlr_xwayland_surface.surface, iterator, user_data);
+ self.xwayland_surface.surface.?.forEachSurface(T, iterator, data);
}
/// Return the surface at output coordinates ox, oy and set sx, sy to the
/// corresponding surface-relative coordinates, if there is a surface.
-pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
- return c.wlr_surface_surface_at(
- self.wlr_xwayland_surface.surface,
+pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*wlr.Surface {
+ return self.xwayland_surface.surface.?.surfaceAt(
ox - @intToFloat(f64, self.view.current.box.x),
oy - @intToFloat(f64, self.view.current.box.y),
sx,
@@ -123,12 +122,12 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
/// Get the current title of the xwayland surface. May be an empty string
pub fn getTitle(self: Self) [*:0]const u8 {
- return self.wlr_xwayland_surface.title orelse "";
+ return self.xwayland_surface.title orelse "";
}
/// Return bounds on the dimensions of the view
pub fn getConstraints(self: Self) View.Constraints {
- const hints: *c.wlr_xwayland_surface_size_hints = self.wlr_xwayland_surface.size_hints orelse return .{
+ const hints = self.xwayland_surface.size_hints orelse return .{
.min_width = View.min_size,
.max_width = std.math.maxInt(u32),
.min_height = View.min_size,
@@ -143,39 +142,42 @@ pub fn getConstraints(self: Self) View.Constraints {
}
/// Called when the xwayland surface is destroyed
-fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_destroy", listener.?);
+fn handleDestroy(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "destroy", listener);
self.deinit();
- self.view.wlr_surface = null;
+ self.view.surface = null;
}
/// Called when the xwayland surface is mapped, or ready to display on-screen.
-fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_map", listener.?);
+fn handleMap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "map", listener);
const view = self.view;
const root = view.output.root;
// Add listeners that are only active while mapped
- self.listen_commit.notify = handleCommit;
- c.wl_signal_add(&self.wlr_xwayland_surface.surface.*.events.commit, &self.listen_commit);
+ self.commit.setNotify(handleCommit);
+ self.xwayland_surface.surface.?.events.commit.add(&self.commit);
- view.wlr_surface = self.wlr_xwayland_surface.surface;
+ view.surface = self.xwayland_surface.surface;
// Use the view's "natural" size centered on the output as the default
// floating dimensions
- view.float_box.width = self.wlr_xwayland_surface.width;
- view.float_box.height = self.wlr_xwayland_surface.height;
+ view.float_box.width = self.xwayland_surface.width;
+ view.float_box.height = self.xwayland_surface.height;
view.float_box.x = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.width) -
@intCast(i32, view.float_box.width), 2));
view.float_box.y = std.math.max(0, @divTrunc(@intCast(i32, view.output.usable_box.height) -
@intCast(i32, view.float_box.height), 2));
- const size_hints = self.wlr_xwayland_surface.size_hints;
- const has_fixed_size = size_hints.*.min_width != 0 and size_hints.*.min_height != 0 and
- (size_hints.*.min_width == size_hints.*.max_width or size_hints.*.min_height == size_hints.*.max_height);
- const app_id: [*:0]const u8 = if (self.wlr_xwayland_surface.class) |id| id else "NULL";
+ const has_fixed_size = if (self.xwayland_surface.size_hints) |size_hints|
+ size_hints.min_width != 0 and size_hints.min_height != 0 and
+ (size_hints.min_width == size_hints.max_width or size_hints.min_height == size_hints.max_height)
+ else
+ false;
+
+ const app_id: [*:0]const u8 = if (self.xwayland_surface.class) |id| id else "NULL";
- if (self.wlr_xwayland_surface.parent != null or has_fixed_size) {
+ if (self.xwayland_surface.parent != null or has_fixed_size) {
// If the toplevel has a parent or has a fixed size make it float
view.current.float = true;
view.pending.float = true;
@@ -196,26 +198,26 @@ fn handleMap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
/// Called when the surface is unmapped and will no longer be displayed.
-fn handleUnmap(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_unmap", listener.?);
+fn handleUnmap(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "unmap", listener);
self.view.unmap();
// Remove listeners that are only active while mapped
- c.wl_list_remove(&self.listen_commit.link);
+ self.commit.link.remove();
}
/// Called when the surface is comitted
/// TODO: check for unexpected change in size and react as needed
-fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_commit", listener.?);
+fn handleCommit(listener: *wl.Listener(*wlr.Surface), surface: *wlr.Surface) void {
+ const self = @fieldParentPtr(Self, "commit", listener);
const view = self.view;
view.surface_box = Box{
.x = 0,
.y = 0,
- .width = @intCast(u32, self.wlr_xwayland_surface.surface.*.current.width),
- .height = @intCast(u32, self.wlr_xwayland_surface.surface.*.current.height),
+ .width = @intCast(u32, surface.current.width),
+ .height = @intCast(u32, surface.current.height),
};
// See comment in XwaylandView.configure()
@@ -225,8 +227,8 @@ fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
}
/// Called then the window updates its title
-fn handleTitle(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- const self = @fieldParentPtr(Self, "listen_title", listener.?);
+fn handleTitle(listener: *wl.Listener(*wlr.XwaylandSurface), xwayland_surface: *wlr.XwaylandSurface) void {
+ const self = @fieldParentPtr(Self, "title", listener);
// Send title to all status listeners attached to a seat which focuses this view
var seat_it = self.view.output.root.server.input_manager.seats.first;
diff --git a/river/c.zig b/river/c.zig
index 306ae25..76b97e2 100644
--- a/river/c.zig
+++ b/river/c.zig
@@ -17,63 +17,10 @@
pub usingnamespace @cImport({
@cDefine("_POSIX_C_SOURCE", "200809L");
- @cDefine("WLR_USE_UNSTABLE", {});
@cInclude("stdlib.h");
- @cInclude("time.h");
@cInclude("unistd.h");
@cInclude("linux/input-event-codes.h");
@cInclude("libevdev/libevdev.h");
-
- @cInclude("wayland-server-core.h");
- @cInclude("wlr/backend.h");
- @cInclude("wlr/backend/multi.h");
- @cInclude("wlr/backend/noop.h");
- //@cInclude("wlr/render/wlr_renderer.h");
- @cInclude("wlr/types/wlr_buffer.h");
- @cInclude("wlr/types/wlr_compositor.h");
- @cInclude("wlr/types/wlr_cursor.h");
- @cInclude("wlr/types/wlr_data_control_v1.h");
- @cInclude("wlr/types/wlr_data_device.h");
- @cInclude("wlr/types/wlr_export_dmabuf_v1.h");
- @cInclude("wlr/types/wlr_gamma_control_v1.h");
- @cInclude("wlr/types/wlr_idle.h");
- @cInclude("wlr/types/wlr_input_device.h");
- @cInclude("wlr/types/wlr_input_inhibitor.h");
- @cInclude("wlr/types/wlr_keyboard.h");
- @cInclude("wlr/types/wlr_layer_shell_v1.h");
- @cInclude("wlr/types/wlr_matrix.h");
- @cInclude("wlr/types/wlr_output.h");
- @cInclude("wlr/types/wlr_output_layout.h");
- @cInclude("wlr/types/wlr_output_management_v1.h");
- @cInclude("wlr/types/wlr_output_power_management_v1.h");
- @cInclude("wlr/types/wlr_pointer.h");
- @cInclude("wlr/types/wlr_primary_selection.h");
- @cInclude("wlr/types/wlr_primary_selection_v1.h");
- @cInclude("wlr/types/wlr_screencopy_v1.h");
- @cInclude("wlr/types/wlr_seat.h");
- @cInclude("wlr/types/wlr_viewporter.h");
- @cInclude("wlr/types/wlr_virtual_pointer_v1.h");
- @cInclude("wlr/types/wlr_virtual_keyboard_v1.h");
- @cInclude("wlr/types/wlr_xcursor_manager.h");
- @cInclude("wlr/types/wlr_xdg_decoration_v1.h");
- @cInclude("wlr/types/wlr_xdg_output_v1.h");
- @cInclude("wlr/types/wlr_xdg_shell.h");
- if (@import("build_options").xwayland) @cInclude("wlr/xwayland.h");
- @cInclude("wlr/util/log.h");
- @cInclude("xkbcommon/xkbcommon.h");
-
- // Contains a subset of functions from wlr/backend.h and wlr/render/wlr_renderer.h
- // that can be automatically imported
- @cInclude("include/bindings.h");
-
- @cInclude("river-control-unstable-v1-protocol.h");
- @cInclude("river-status-unstable-v1-protocol.h");
});
-
-// These are needed because zig currently names translated anonymous unions
-// with a global counter, which makes code unportable.
-// See https://github.com/ifreund/river/issues/17
-pub const wlr_xdg_surface_union = @typeInfo(wlr_xdg_surface).Struct.fields[5].name;
-pub const wlr_input_device_union = @typeInfo(wlr_input_device).Struct.fields[8].name;
diff --git a/river/command/exit.zig b/river/command/exit.zig
index db378fc..129a247 100644
--- a/river/command/exit.zig
+++ b/river/command/exit.zig
@@ -17,8 +17,6 @@
const std = @import("std");
-const c = @import("../c.zig");
-
const Error = @import("../command.zig").Error;
const Seat = @import("../Seat.zig");
@@ -30,5 +28,5 @@ pub fn exit(
out: *?[]const u8,
) Error!void {
if (args.len > 1) return Error.TooManyArguments;
- c.wl_display_terminate(seat.input_manager.server.wl_display);
+ seat.input_manager.server.wl_server.terminate();
}
diff --git a/river/command/map.zig b/river/command/map.zig
index a7b95f9..b2ee3c2 100644
--- a/river/command/map.zig
+++ b/river/command/map.zig
@@ -16,6 +16,9 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
+const mem = std.mem;
+const wlr = @import("wlroots");
+const xkb = @import("xkbcommon");
const c = @import("../c.zig");
const util = @import("../util.zig");
@@ -25,21 +28,6 @@ const Mapping = @import("../Mapping.zig");
const PointerMapping = @import("../PointerMapping.zig");
const Seat = @import("../Seat.zig");
-const modifier_names = [_]struct {
- name: []const u8,
- modifier: u32,
-}{
- .{ .name = "None", .modifier = 0 },
- .{ .name = "Shift", .modifier = c.WLR_MODIFIER_SHIFT },
- .{ .name = "Lock", .modifier = c.WLR_MODIFIER_CAPS },
- .{ .name = "Control", .modifier = c.WLR_MODIFIER_CTRL },
- .{ .name = "Mod1", .modifier = c.WLR_MODIFIER_ALT },
- .{ .name = "Mod2", .modifier = c.WLR_MODIFIER_MOD2 },
- .{ .name = "Mod3", .modifier = c.WLR_MODIFIER_MOD3 },
- .{ .name = "Mod4", .modifier = c.WLR_MODIFIER_LOGO },
- .{ .name = "Mod5", .modifier = c.WLR_MODIFIER_MOD5 },
-};
-
/// Create a new mapping for a given mode
///
/// Example:
@@ -133,9 +121,14 @@ fn modeNameToId(allocator: *std.mem.Allocator, seat: *Seat, mode_name: []const u
}
/// Returns the index of the Mapping with matching modifiers, keysym and release, if any.
-fn mappingExists(mappings: *std.ArrayList(Mapping), modifiers: u32, keysym: u32, release: bool) ?usize {
+fn mappingExists(
+ mappings: *std.ArrayList(Mapping),
+ modifiers: wlr.Keyboard.ModifierMask,
+ keysym: xkb.Keysym,
+ release: bool,
+) ?usize {
for (mappings.items) |mapping, i| {
- if (mapping.modifiers == modifiers and mapping.keysym == keysym and mapping.release == release) {
+ if (std.meta.eql(mapping.modifiers, modifiers) and mapping.keysym == keysym and mapping.release == release) {
return i;
}
}
@@ -144,9 +137,13 @@ fn mappingExists(mappings: *std.ArrayList(Mapping), modifiers: u32, keysym: u32,
}
/// Returns the index of the PointerMapping with matching modifiers and event code, if any.
-fn pointerMappingExists(pointer_mappings: *std.ArrayList(PointerMapping), modifiers: u32, event_code: u32) ?usize {
+fn pointerMappingExists(
+ pointer_mappings: *std.ArrayList(PointerMapping),
+ modifiers: wlr.Keyboard.ModifierMask,
+ event_code: u32,
+) ?usize {
for (pointer_mappings.items) |mapping, i| {
- if (mapping.modifiers == modifiers and mapping.event_code == event_code) {
+ if (std.meta.eql(mapping.modifiers, modifiers) and mapping.event_code == event_code) {
return i;
}
}
@@ -166,39 +163,47 @@ fn parseEventCode(allocator: *std.mem.Allocator, event_code_str: []const u8, out
return @intCast(u32, ret);
}
-fn parseKeysym(allocator: *std.mem.Allocator, keysym_str: []const u8, out: *?[]const u8) !u32 {
+fn parseKeysym(allocator: *std.mem.Allocator, keysym_str: []const u8, out: *?[]const u8) !xkb.Keysym {
const keysym_name = try std.cstr.addNullByte(allocator, keysym_str);
defer allocator.free(keysym_name);
- const keysym = c.xkb_keysym_from_name(keysym_name, .XKB_KEYSYM_CASE_INSENSITIVE);
- if (keysym == c.XKB_KEY_NoSymbol) {
- out.* = try std.fmt.allocPrint(
- allocator,
- "invalid keysym '{}'",
- .{keysym_str},
- );
+ const keysym = xkb.Keysym.fromName(keysym_name, .case_insensitive);
+ if (keysym == .NoSymbol) {
+ out.* = try std.fmt.allocPrint(allocator, "invalid keysym '{}'", .{keysym_str});
return Error.Other;
}
-
return keysym;
}
-fn parseModifiers(allocator: *std.mem.Allocator, modifiers_str: []const u8, out: *?[]const u8) !u32 {
+fn parseModifiers(
+ allocator: *std.mem.Allocator,
+ modifiers_str: []const u8,
+ out: *?[]const u8,
+) !wlr.Keyboard.ModifierMask {
var it = std.mem.split(modifiers_str, "+");
- var modifiers: u32 = 0;
- while (it.next()) |mod_name| {
- for (modifier_names) |def| {
+ var modifiers = wlr.Keyboard.ModifierMask{};
+ outer: while (it.next()) |mod_name| {
+ if (mem.eql(u8, mod_name, "None")) continue;
+ inline for ([_]struct { name: []const u8, field_name: []const u8 }{
+ .{ .name = "Shift", .field_name = "shift" },
+ .{ .name = "Lock", .field_name = "caps" },
+ .{ .name = "Control", .field_name = "ctrl" },
+ .{ .name = "Mod1", .field_name = "alt" },
+ .{ .name = "Mod2", .field_name = "mod2" },
+ .{ .name = "Mod3", .field_name = "mod3" },
+ .{ .name = "Mod4", .field_name = "logo" },
+ .{ .name = "Mod5", .field_name = "mod5" },
+ }) |def| {
if (std.mem.eql(u8, def.name, mod_name)) {
- modifiers |= def.modifier;
- break;
+ @field(modifiers, def.field_name) = true;
+ continue :outer;
}
- } else {
- out.* = try std.fmt.allocPrint(
- allocator,
- "invalid modifier '{}'",
- .{mod_name},
- );
- return Error.Other;
}
+ out.* = try std.fmt.allocPrint(
+ allocator,
+ "invalid modifier '{}'",
+ .{mod_name},
+ );
+ return Error.Other;
}
return modifiers;
}
diff --git a/river/command/set_repeat.zig b/river/command/set_repeat.zig
index 32b9f78..8dec594 100644
--- a/river/command/set_repeat.zig
+++ b/river/command/set_repeat.zig
@@ -17,8 +17,6 @@
const std = @import("std");
-const c = @import("../c.zig");
-
const Error = @import("../command.zig").Error;
const Seat = @import("../Seat.zig");
@@ -37,6 +35,6 @@ pub fn setRepeat(
var it = seat.keyboards.first;
while (it) |node| : (it = node.next) {
- c.wlr_keyboard_set_repeat_info(node.data.wlr_keyboard, rate, delay);
+ node.data.input_device.device.keyboard.setRepeatInfo(rate, delay);
}
}
diff --git a/river/main.zig b/river/main.zig
index df03b54..d576bb2 100644
--- a/river/main.zig
+++ b/river/main.zig
@@ -16,6 +16,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std");
+const wlr = @import("wlroots");
const c = @import("c.zig");
const log = @import("log.zig");
@@ -93,14 +94,11 @@ pub fn main() anyerror!void {
}
}
- c.wlr_log_init(
- switch (log.level) {
- .debug => .WLR_DEBUG,
- .notice, .info => .WLR_INFO,
- .warn, .err, .crit, .alert, .emerg => .WLR_ERROR,
- },
- null,
- );
+ wlr.log.init(switch (log.level) {
+ .debug => .debug,
+ .notice, .info => .info,
+ .warn, .err, .crit, .alert, .emerg => .err,
+ });
log.info(.server, "initializing", .{});
@@ -136,7 +134,7 @@ pub fn main() anyerror!void {
log.info(.server, "running...", .{});
- server.run();
+ server.wl_server.run();
log.info(.server, "shutting down", .{});
}
diff --git a/river/render.zig b/river/render.zig
index 0bb65ab..15cc005 100644
--- a/river/render.zig
+++ b/river/render.zig
@@ -17,8 +17,11 @@
const build_options = @import("build_options");
const std = @import("std");
+const os = std.os;
+const wlr = @import("wlroots");
+const wl = @import("wayland").server.wl;
+const pixman = @import("pixman");
-const c = @import("c.zig");
const log = @import("log.zig");
const util = @import("util.zig");
@@ -36,24 +39,21 @@ const SurfaceRenderData = struct {
output_x: i32,
output_y: i32,
- when: *c.timespec,
+ when: *os.timespec,
opacity: f32,
};
pub fn renderOutput(output: *Output) void {
const config = &output.root.server.config;
- const wlr_renderer = output.getRenderer();
+ const renderer = output.wlr_output.backend.getRenderer().?;
- var now: c.timespec = undefined;
- _ = c.clock_gettime(c.CLOCK_MONOTONIC, &now);
+ var now: os.timespec = undefined;
+ os.clock_gettime(os.CLOCK_MONOTONIC, &now) catch unreachable;
- // wlr_output_attach_render makes the OpenGL context current.
- if (!c.wlr_output_attach_render(output.wlr_output, null)) return;
+ output.wlr_output.attachRender(null) catch return;
- // Begin the renderer (calls glViewport and some other GL sanity checks)
- // Here we don't want the output_effective_resolution since we want to render the whole output
- c.wlr_renderer_begin(wlr_renderer, output.wlr_output.width, output.wlr_output.height);
+ renderer.begin(output.wlr_output.width, output.wlr_output.height);
// Find the first visible fullscreen view in the stack if there is one
var it = ViewStack(View).iter(output.views.first, .forward, output.current.tags, renderFilter);
@@ -64,15 +64,15 @@ pub fn renderOutput(output: *Output) void {
// If we have a fullscreen view to render, render it.
if (fullscreen_view) |view| {
// Always clear with solid black for fullscreen
- c.wlr_renderer_clear(wlr_renderer, &[_]f32{ 0, 0, 0, 1 });
+ renderer.clear(&[_]f32{ 0, 0, 0, 1 });
renderView(output.*, view, &now);
if (build_options.xwayland) renderXwaylandUnmanaged(output.*, &now);
} else {
// No fullscreen view, so render normal layers/views
- c.wlr_renderer_clear(wlr_renderer, &config.background_color);
+ renderer.clear(&config.background_color);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now, .toplevels);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now, .toplevels);
+ renderLayer(output.*, output.getLayer(.background).*, &now, .toplevels);
+ renderLayer(output.*, output.getLayer(.bottom).*, &now, .toplevels);
// The first view in the list is "on top" so iterate in reverse.
it = ViewStack(View).iter(output.views.last, .reverse, output.current.tags, renderFilter);
@@ -96,16 +96,16 @@ pub fn renderOutput(output: *Output) void {
if (build_options.xwayland) renderXwaylandUnmanaged(output.*, &now);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_TOP], &now, .toplevels);
+ renderLayer(output.*, output.getLayer(.top).*, &now, .toplevels);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &now, .popups);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &now, .popups);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_TOP], &now, .popups);
+ renderLayer(output.*, output.getLayer(.background).*, &now, .popups);
+ renderLayer(output.*, output.getLayer(.bottom).*, &now, .popups);
+ renderLayer(output.*, output.getLayer(.top).*, &now, .popups);
}
// The overlay layer is rendered in both fullscreen and normal cases
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &now, .toplevels);
- renderLayer(output.*, output.layers[c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &now, .popups);
+ renderLayer(output.*, output.getLayer(.overlay).*, &now, .toplevels);
+ renderLayer(output.*, output.getLayer(.overlay).*, &now, .popups);
renderDragIcons(output.*, &now);
@@ -115,28 +115,27 @@ pub fn renderOutput(output: *Output) void {
// reason, wlroots provides a software fallback, which we ask it to render
// here. wlr_cursor handles configuring hardware vs software cursors for you,
// and this function is a no-op when hardware cursors are in use.
- c.wlr_output_render_software_cursors(output.wlr_output, null);
+ output.wlr_output.renderSoftwareCursors(null);
// Conclude rendering and swap the buffers, showing the final frame
// on-screen.
- c.wlr_renderer_end(wlr_renderer);
+ renderer.end();
// TODO(wlroots): remove this with the next release. It is here due to
// a wlroots bug in the screencopy damage implementation
{
var w: c_int = undefined;
var h: c_int = undefined;
- c.wlr_output_transformed_resolution(output.wlr_output, &w, &h);
- var damage: c.pixman_region32_t = undefined;
- c.pixman_region32_init(&damage);
- _ = c.pixman_region32_union_rect(&damage, &damage, 0, 0, @intCast(c_uint, w), @intCast(c_uint, h));
- c.wlr_output_set_damage(output.wlr_output, &damage);
+ output.wlr_output.transformedResolution(&w, &h);
+ var damage: pixman.Region32 = undefined;
+ damage.init();
+ _ = damage.unionRect(&damage, 0, 0, @intCast(c_uint, w), @intCast(c_uint, h));
+ output.wlr_output.setDamage(&damage);
}
// TODO: handle failure
- if (!c.wlr_output_commit(output.wlr_output)) {
- log.err(.render, "wlr_output_commit failed for {}", .{output.wlr_output.name});
- }
+ output.wlr_output.commit() catch
+ log.err(.render, "output commit failed for {}", .{output.wlr_output.name});
}
fn renderFilter(view: *View, filter_tags: u32) bool {
@@ -151,7 +150,7 @@ fn renderFilter(view: *View, filter_tags: u32) bool {
fn renderLayer(
output: Output,
layer: std.TailQueue(LayerSurface),
- now: *c.timespec,
+ now: *os.timespec,
role: enum { toplevels, popups },
) void {
var it = layer.first;
@@ -165,13 +164,13 @@ fn renderLayer(
.opacity = 1.0,
};
switch (role) {
- .toplevels => c.wlr_surface_for_each_surface(
- layer_surface.wlr_layer_surface.surface,
+ .toplevels => layer_surface.wlr_layer_surface.surface.forEachSurface(
+ *SurfaceRenderData,
renderSurfaceIterator,
&rdata,
),
- .popups => c.wlr_layer_surface_v1_for_each_popup(
- layer_surface.wlr_layer_surface,
+ .popups => layer_surface.wlr_layer_surface.forEachPopup(
+ *SurfaceRenderData,
renderSurfaceIterator,
&rdata,
),
@@ -179,14 +178,14 @@ fn renderLayer(
}
}
-fn renderView(output: Output, view: *View, now: *c.timespec) void {
+fn renderView(output: Output, view: *View, now: *os.timespec) void {
// If we have saved buffers, we are in the middle of a transaction
// and need to render those buffers until the transaction is complete.
if (view.saved_buffers.items.len != 0) {
for (view.saved_buffers.items) |saved_buffer|
renderTexture(
output,
- saved_buffer.wlr_client_buffer.texture,
+ saved_buffer.client_buffer.texture orelse continue,
.{
.x = saved_buffer.box.x + view.current.box.x - view.saved_surface_box.x,
.y = saved_buffer.box.y + view.current.box.y - view.saved_surface_box.y,
@@ -207,12 +206,12 @@ fn renderView(output: Output, view: *View, now: *c.timespec) void {
.opacity = view.opacity,
};
- view.forEachSurface(renderSurfaceIterator, &rdata);
+ view.forEachSurface(*SurfaceRenderData, renderSurfaceIterator, &rdata);
}
}
-fn renderDragIcons(output: Output, now: *c.timespec) void {
- const output_box = c.wlr_output_layout_get_box(output.root.wlr_output_layout, output.wlr_output);
+fn renderDragIcons(output: Output, now: *os.timespec) void {
+ const output_box = output.root.output_layout.getBox(output.wlr_output).?;
var it = output.root.drag_icons.first;
while (it) |node| : (it = node.next) {
@@ -221,70 +220,67 @@ fn renderDragIcons(output: Output, now: *c.timespec) void {
var rdata = SurfaceRenderData{
.output = &output,
.output_x = @floatToInt(i32, drag_icon.seat.cursor.wlr_cursor.x) +
- drag_icon.wlr_drag_icon.surface.*.sx - output_box.*.x,
+ drag_icon.wlr_drag_icon.surface.sx - output_box.x,
.output_y = @floatToInt(i32, drag_icon.seat.cursor.wlr_cursor.y) +
- drag_icon.wlr_drag_icon.surface.*.sy - output_box.*.y,
+ drag_icon.wlr_drag_icon.surface.sy - output_box.y,
.when = now,
.opacity = 1.0,
};
- c.wlr_surface_for_each_surface(drag_icon.wlr_drag_icon.surface, renderSurfaceIterator, &rdata);
+ drag_icon.wlr_drag_icon.surface.forEachSurface(*SurfaceRenderData, renderSurfaceIterator, &rdata);
}
}
/// Render all xwayland unmanaged windows that appear on the output
-fn renderXwaylandUnmanaged(output: Output, now: *c.timespec) void {
- const output_box = c.wlr_output_layout_get_box(output.root.wlr_output_layout, output.wlr_output);
+fn renderXwaylandUnmanaged(output: Output, now: *os.timespec) void {
+ const output_box = output.root.output_layout.getBox(output.wlr_output).?;
var it = output.root.xwayland_unmanaged_views.first;
while (it) |node| : (it = node.next) {
- const wlr_xwayland_surface = node.data.wlr_xwayland_surface;
+ const xwayland_surface = node.data.xwayland_surface;
var rdata = SurfaceRenderData{
.output = &output,
- .output_x = wlr_xwayland_surface.x - output_box.*.x,
- .output_y = wlr_xwayland_surface.y - output_box.*.y,
+ .output_x = xwayland_surface.x - output_box.x,
+ .output_y = xwayland_surface.y - output_box.y,
.when = now,
.opacity = 1.0,
};
- c.wlr_surface_for_each_surface(wlr_xwayland_surface.surface, renderSurfaceIterator, &rdata);
+ xwayland_surface.surface.?.forEachSurface(*SurfaceRenderData, renderSurfaceIterator, &rdata);
}
}
/// This function is passed to wlroots to render each surface during iteration
fn renderSurfaceIterator(
- surface: ?*c.wlr_surface,
+ surface: *wlr.Surface,
surface_x: c_int,
surface_y: c_int,
- data: ?*c_void,
+ rdata: *SurfaceRenderData,
) callconv(.C) void {
- const rdata = util.voidCast(SurfaceRenderData, data.?);
-
renderTexture(
rdata.output.*,
- c.wlr_surface_get_texture(surface),
+ surface.getTexture() orelse return,
.{
.x = rdata.output_x + surface_x,
.y = rdata.output_y + surface_y,
- .width = surface.?.current.width,
- .height = surface.?.current.height,
+ .width = surface.current.width,
+ .height = surface.current.height,
},
- surface.?.current.transform,
+ surface.current.transform,
rdata.opacity,
);
- c.wlr_surface_send_frame_done(surface, rdata.when);
+ surface.sendFrameDone(rdata.when);
}
/// Render the given texture at the given box, taking the scale and transform
/// of the output into account.
fn renderTexture(
output: Output,
- wlr_texture: ?*c.wlr_texture,
- wlr_box: c.wlr_box,
- transform: c.wl_output_transform,
+ texture: *wlr.Texture,
+ wlr_box: wlr.Box,
+ transform: wl.Output.Transform,
opacity: f32,
) void {
- const texture = wlr_texture orelse return;
var box = wlr_box;
// Scale the box to the output's current scaling factor
@@ -295,15 +291,16 @@ fn renderTexture(
// prepares an orthographic projection and multiplies the necessary
// transforms to produce a model-view-projection matrix.
var matrix: [9]f32 = undefined;
- const inverted = c.wlr_output_transform_invert(transform);
- c.wlr_matrix_project_box(&matrix, &box, inverted, 0.0, &output.wlr_output.transform_matrix);
+ const inverted = wlr.Output.transformInvert(transform);
+ wlr.matrix.projectBox(&matrix, &box, inverted, 0.0, &output.wlr_output.transform_matrix);
// This takes our matrix, the texture, and an alpha, and performs the actual
// rendering on the GPU.
- _ = c.wlr_render_texture_with_matrix(output.getRenderer(), texture, &matrix, opacity);
+ const renderer = output.wlr_output.backend.getRenderer().?;
+ renderer.renderTextureWithMatrix(texture, &matrix, opacity) catch return;
}
-fn renderBorders(output: Output, view: *View, now: *c.timespec) void {
+fn renderBorders(output: Output, view: *View, now: *os.timespec) void {
const config = &output.root.server.config;
const color = if (view.current.focus != 0) &config.border_color_focused else &config.border_color_unfocused;
const border_width = config.border_width;
@@ -341,8 +338,7 @@ fn renderBorders(output: Output, view: *View, now: *c.timespec) void {
fn renderRect(output: Output, box: Box, color: *const [4]f32) void {
var wlr_box = box.toWlrBox();
scaleBox(&wlr_box, output.wlr_output.scale);
- c.wlr_render_rect(
- output.getRenderer(),
+ output.wlr_output.backend.getRenderer().?.renderRect(
&wlr_box,
color,
&output.wlr_output.transform_matrix,
@@ -350,7 +346,7 @@ fn renderRect(output: Output, box: Box, color: *const [4]f32) void {
}
/// Scale a wlr_box, taking the possibility of fractional scaling into account.
-fn scaleBox(box: *c.wlr_box, scale: f64) void {
+fn scaleBox(box: *wlr.Box, scale: f64) void {
box.x = @floatToInt(c_int, @round(@intToFloat(f64, box.x) * scale));
box.y = @floatToInt(c_int, @round(@intToFloat(f64, box.y) * scale));
box.width = scaleLength(box.width, box.x, scale);
diff --git a/riverctl/main.zig b/riverctl/main.zig
index e3453eb..dad41ea 100644
--- a/riverctl/main.zig
+++ b/riverctl/main.zig
@@ -19,10 +19,10 @@ const std = @import("std");
const wayland = @import("wayland");
const wl = wayland.client.wl;
-const river = wayland.client.river;
+const zriver = wayland.client.zriver;
const SetupContext = struct {
- river_control: ?*river.ControlV1 = null,
+ river_control: ?*zriver.ControlV1 = null,
seat: ?*wl.Seat = null,
};
@@ -54,17 +54,17 @@ pub fn main() !void {
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *SetupContext) void {
switch (event) {
.global => |global| {
- if (context.seat == null and std.cstr.cmp(global.interface, wl.Seat.interface().name) == 0) {
+ if (context.seat == null and std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) {
context.seat = registry.bind(global.name, wl.Seat, 1) catch return;
- } else if (std.cstr.cmp(global.interface, river.ControlV1.interface().name) == 0) {
- context.river_control = registry.bind(global.name, river.ControlV1, 1) catch return;
+ } else if (std.cstr.cmp(global.interface, zriver.ControlV1.getInterface().name) == 0) {
+ context.river_control = registry.bind(global.name, zriver.ControlV1, 1) catch return;
}
},
.global_remove => {},
}
}
-fn callbackListener(callback: *river.CommandCallbackV1, event: river.CommandCallbackV1.Event, _: ?*c_void) void {
+fn callbackListener(callback: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV1.Event, _: ?*c_void) void {
switch (event) {
.success => |success| {
if (std.mem.len(success.output) > 0) {