diff --git a/.builds/alpine.yml b/.builds/alpine.yml index e2cff37..d92b650 100644 --- a/.builds/alpine.yml +++ b/.builds/alpine.yml @@ -37,10 +37,10 @@ tasks: cd .. # Eat Github's resources rather than the Zig Software Foundation's resources! - wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz - tar xf zig-linux-x86_64-0.14.0.tar.xz - sudo mv zig-linux-x86_64-0.14.0/zig /usr/bin/ - sudo mv zig-linux-x86_64-0.14.0/lib /usr/lib/zig + wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz + tar xf zig-x86_64-linux-0.15.1.tar.xz + sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/ + sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig - build: | cd river zig build --summary all diff --git a/.builds/archlinux.yml b/.builds/archlinux.yml index b1c0eaf..7bf93d6 100644 --- a/.builds/archlinux.yml +++ b/.builds/archlinux.yml @@ -34,10 +34,10 @@ tasks: cd .. # Eat Github's resources rather than the Zig Software Foundation's resources! - wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.14.0/zig-linux-x86_64-0.14.0.tar.xz - tar xf zig-linux-x86_64-0.14.0.tar.xz - sudo mv zig-linux-x86_64-0.14.0/zig /usr/bin/ - sudo mv zig-linux-x86_64-0.14.0/lib /usr/lib/zig + wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-linux-0.15.1.tar.xz + tar xf zig-x86_64-linux-0.15.1.tar.xz + sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/ + sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig - build: | cd river zig build --summary all diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index d160aca..bb5f96d 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -41,10 +41,10 @@ tasks: cd .. # Eat Github's resources rather than the Zig Software Foundation's resources! - wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.14.0/zig-freebsd-x86_64-0.14.0-unofficial.tar.xz - tar xf zig-freebsd-x86_64-0.14.0-unofficial.tar.xz - sudo mv zig-freebsd-x86_64-0.14.0-unofficial/zig /usr/bin/ - sudo mv zig-freebsd-x86_64-0.14.0-unofficial/lib /usr/lib/zig + wget -nv https://github.com/ifreund/zig-tarball-mirror/releases/download/0.15.1/zig-x86_64-freebsd-0.15.1.tar.xz + tar xf zig-x86_64-freebsd-0.15.1.tar.xz + sudo mv zig-x86_64-freebsd-0.15.1/zig /usr/bin/ + sudo mv zig-x86_64-freebsd-0.15.1/lib /usr/lib/zig - build: | cd river zig build --summary all diff --git a/README.md b/README.md index 4c5b12a..07dbee3 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ To compile river first ensure that you have the following dependencies installed. The "development" versions are required if applicable to your distribution. -- [zig](https://ziglang.org/download/) 0.14 +- [zig](https://ziglang.org/download/) 0.15 - wayland - wayland-protocols - [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.19 diff --git a/build.zig b/build.zig index 6280f65..bdbd49c 100644 --- a/build.zig +++ b/build.zig @@ -12,7 +12,6 @@ pub fn build(b: *Build) !void { const strip = b.option(bool, "strip", "Omit debug information") orelse false; const pie = b.option(bool, "pie", "Build a Position Independent Executable") orelse false; - const llvm = !(b.option(bool, "no-llvm", "(expirimental) Use non-LLVM x86 Zig backend") orelse false); const omit_frame_pointer = switch (optimize) { .Debug, .ReleaseSafe => false, @@ -150,12 +149,12 @@ pub fn build(b: *Build) !void { { const river = b.addExecutable(.{ .name = "river", - .root_source_file = b.path("river/main.zig"), - .target = target, - .optimize = optimize, - .strip = strip, - .use_llvm = llvm, - .use_lld = llvm, + .root_module = b.createModule(.{ + .root_source_file = b.path("river/main.zig"), + .target = target, + .optimize = optimize, + .strip = strip, + }), }); river.root_module.addOptions("build_options", options); @@ -188,12 +187,12 @@ pub fn build(b: *Build) !void { { const riverctl = b.addExecutable(.{ .name = "riverctl", - .root_source_file = b.path("riverctl/main.zig"), - .target = target, - .optimize = optimize, - .strip = strip, - .use_llvm = llvm, - .use_lld = llvm, + .root_module = b.createModule(.{ + .root_source_file = b.path("riverctl/main.zig"), + .target = target, + .optimize = optimize, + .strip = strip, + }), }); riverctl.root_module.addOptions("build_options", options); @@ -211,12 +210,12 @@ pub fn build(b: *Build) !void { { const rivertile = b.addExecutable(.{ .name = "rivertile", - .root_source_file = b.path("rivertile/main.zig"), - .target = target, - .optimize = optimize, - .strip = strip, - .use_llvm = llvm, - .use_lld = llvm, + .root_module = b.createModule(.{ + .root_source_file = b.path("rivertile/main.zig"), + .target = target, + .optimize = optimize, + .strip = strip, + }), }); rivertile.root_module.addOptions("build_options", options); @@ -275,9 +274,11 @@ pub fn build(b: *Build) !void { { const globber_test = b.addTest(.{ - .root_source_file = b.path("common/globber.zig"), - .target = target, - .optimize = optimize, + .root_module = b.createModule(.{ + .root_source_file = b.path("common/globber.zig"), + .target = target, + .optimize = optimize, + }), }); const run_globber_test = b.addRunArtifact(globber_test); diff --git a/build.zig.zon b/build.zig.zon index 109eb1b..80d0cf8 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,7 +5,7 @@ // When a release is tagged, the "-dev" suffix should be removed for the // commit that gets tagged. Directly after the tagged commit, the version // should be bumped and the "-dev" suffix added. - .version = "0.3.12-dev", + .version = "0.3.12", .paths = .{""}, .dependencies = .{ .pixman = .{ @@ -13,12 +13,12 @@ .hash = "pixman-0.3.0-LClMnz2VAAAs7QSCGwLimV5VUYx0JFnX5xWU6HwtMuDX", }, .wayland = .{ - .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.3.0.tar.gz", - .hash = "wayland-0.3.0-lQa1kjPIAQDmhGYpY-zxiRzQJFHQ2VqhJkQLbKKdt5wl", + .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.4.0.tar.gz", + .hash = "wayland-0.4.0-lQa1khbMAQAsLS2eBR7M5lofyEGPIbu2iFDmoz8lPC27", }, .wlroots = .{ - .url = "https://codeberg.org/ifreund/zig-wlroots/archive/f92ba27133ecf702d85c9d3894f98a336389bbd9.tar.gz", - .hash = "wlroots-0.19.3-dev-jmOlcr7_AwClfjFwW8oOkWoqAbt9oPLqgdvfFYEXqlOF", + .url = "https://codeberg.org/ifreund/zig-wlroots/archive/v0.19.3.tar.gz", + .hash = "wlroots-0.19.3-jmOlcuL_AwBHhLCwpFsXbTizE3q9BugFmGX-XIxqcPMc", }, .xkbcommon = .{ .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.3.0.tar.gz", diff --git a/river/Control.zig b/river/Control.zig index 1c472b8..06e30d8 100644 --- a/river/Control.zig +++ b/river/Control.zig @@ -84,7 +84,7 @@ fn handleRequest(control_v1: *zriver.ControlV1, request: zriver.ControlV1.Reques }; }, .run_command => |run_command| { - const seat: *Seat = @alignCast(@ptrCast(wlr.Seat.Client.fromWlSeat(run_command.seat).?.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(wlr.Seat.Client.fromWlSeat(run_command.seat).?.seat.data)); const callback = zriver.CommandCallbackV1.create( control_v1.getClient(), diff --git a/river/Cursor.zig b/river/Cursor.zig index c792018..a92749b 100644 --- a/river/Cursor.zig +++ b/river/Cursor.zig @@ -26,7 +26,7 @@ const wayland = @import("wayland"); const wl = wayland.server.wl; const zwlr = wayland.server.zwlr; -const c = @import("c.zig"); +const c = @import("c.zig").c; const server = &@import("main.zig").server; const util = @import("util.zig"); @@ -296,7 +296,7 @@ pub fn setTheme(cursor: *Cursor, theme: ?[*:0]const u8, _size: ?u32) !void { // If this cursor belongs to the default seat, set the xcursor environment // variables as well as the xwayland cursor theme. if (cursor.seat == server.input_manager.defaultSeat()) { - const size_str = try std.fmt.allocPrintZ(util.gpa, "{}", .{size}); + const size_str = try std.fmt.allocPrintSentinel(util.gpa, "{}", .{size}, 0); defer util.gpa.free(size_str); if (c.setenv("XCURSOR_SIZE", size_str.ptr, 1) < 0) return error.OutOfMemory; if (theme) |t| if (c.setenv("XCURSOR_THEME", t, 1) < 0) return error.OutOfMemory; @@ -362,7 +362,7 @@ fn clearFocus(cursor: *Cursor) void { /// Axis event is a scroll wheel or similiar fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Pointer.event.Axis) void { const cursor: *Cursor = @fieldParentPtr("axis", listener); - const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); + const device: *InputDevice = @ptrCast(@alignCast(event.device.data)); cursor.seat.handleActivity(); cursor.unhide(); @@ -378,8 +378,8 @@ fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Point // @intFromFloat() call safe due to the max/min i32 not being exactly representable // by an f32. Dividing by 2 is a low effort way to ensure the value is in bounds and // allow users to set their scroll-factor to inf without crashing river. - math.minInt(i32) / 2, - math.maxInt(i32) / 2, + @as(f32, @floatFromInt(math.minInt(i32) / 2)), + @as(f32, @floatFromInt(math.maxInt(i32) / 2)), )), event.source, event.relative_direction, @@ -482,7 +482,7 @@ fn updateKeyboardFocus(cursor: Cursor, result: Root.AtResult) void { /// Requires a call to Root.applyPending() fn updateOutputFocus(cursor: Cursor, lx: f64, ly: f64) void { if (server.root.output_layout.outputAt(lx, ly)) |wlr_output| { - const output: *Output = @alignCast(@ptrCast(wlr_output.data)); + const output: *Output = @ptrCast(@alignCast(wlr_output.data)); cursor.seat.focusOutput(output); } } @@ -659,7 +659,7 @@ fn handleTabletToolAxis( _: *wl.Listener(*wlr.Tablet.event.Axis), event: *wlr.Tablet.event.Axis, ) void { - const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); + const device: *InputDevice = @ptrCast(@alignCast(event.device.data)); const tablet: *Tablet = @fieldParentPtr("device", device); device.seat.handleActivity(); @@ -673,7 +673,7 @@ fn handleTabletToolProximity( _: *wl.Listener(*wlr.Tablet.event.Proximity), event: *wlr.Tablet.event.Proximity, ) void { - const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); + const device: *InputDevice = @ptrCast(@alignCast(event.device.data)); const tablet: *Tablet = @fieldParentPtr("device", device); device.seat.handleActivity(); @@ -687,7 +687,7 @@ fn handleTabletToolTip( _: *wl.Listener(*wlr.Tablet.event.Tip), event: *wlr.Tablet.event.Tip, ) void { - const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); + const device: *InputDevice = @ptrCast(@alignCast(event.device.data)); const tablet: *Tablet = @fieldParentPtr("device", device); device.seat.handleActivity(); @@ -701,7 +701,7 @@ fn handleTabletToolButton( _: *wl.Listener(*wlr.Tablet.event.Button), event: *wlr.Tablet.event.Button, ) void { - const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); + const device: *InputDevice = @ptrCast(@alignCast(event.device.data)); const tablet: *Tablet = @fieldParentPtr("device", device); device.seat.handleActivity(); @@ -822,6 +822,7 @@ pub fn hide(cursor: *Cursor) void { cursor.hidden = true; cursor.wlr_cursor.unsetImage(); + cursor.seat.wlr_seat.pointerNotifyClearFocus(); cursor.hide_cursor_timer.timerUpdate(0) catch { log.err("failed to update cursor hide timeout", .{}); }; @@ -1300,7 +1301,7 @@ fn warp(cursor: *Cursor) void { fn updateDragIcons(cursor: *Cursor) void { var it = server.root.drag_icons.children.iterator(.forward); while (it.next()) |node| { - const icon: *DragIcon = @alignCast(@ptrCast(node.data)); + const icon: *DragIcon = @ptrCast(@alignCast(node.data)); if (icon.wlr_drag_icon.drag.seat == cursor.seat.wlr_seat) { icon.updatePosition(cursor); diff --git a/river/ForeignToplevelHandle.zig b/river/ForeignToplevelHandle.zig index 3434c89..90fafa2 100644 --- a/river/ForeignToplevelHandle.zig +++ b/river/ForeignToplevelHandle.zig @@ -89,7 +89,7 @@ fn handleForeignActivate( ) void { const handle: *ForeignToplevelHandle = @fieldParentPtr("foreign_activate", listener); const view: *View = @fieldParentPtr("foreign_toplevel_handle", handle); - const seat: *Seat = @alignCast(@ptrCast(event.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(event.seat.data)); seat.focus(view); server.root.applyPending(); diff --git a/river/IdleInhibitManager.zig b/river/IdleInhibitManager.zig index 46b3bc6..3fea494 100644 --- a/river/IdleInhibitManager.zig +++ b/river/IdleInhibitManager.zig @@ -30,28 +30,29 @@ const View = @import("View.zig"); wlr_manager: *wlr.IdleInhibitManagerV1, new_idle_inhibitor: wl.Listener(*wlr.IdleInhibitorV1) = wl.Listener(*wlr.IdleInhibitorV1).init(handleNewIdleInhibitor), -inhibitors: std.DoublyLinkedList(IdleInhibitor) = .{}, +inhibitors: wl.list.Head(IdleInhibitor, .link), pub fn init(inhibit_manager: *IdleInhibitManager) !void { inhibit_manager.* = .{ .wlr_manager = try wlr.IdleInhibitManagerV1.create(server.wl_server), + .inhibitors = undefined, }; + inhibit_manager.inhibitors.init(); inhibit_manager.wlr_manager.events.new_inhibitor.add(&inhibit_manager.new_idle_inhibitor); } pub fn deinit(inhibit_manager: *IdleInhibitManager) void { - while (inhibit_manager.inhibitors.pop()) |inhibitor| { - inhibitor.data.destroy.link.remove(); - util.gpa.destroy(inhibitor); + while (inhibit_manager.inhibitors.first()) |inhibitor| { + inhibitor.destroy(); } inhibit_manager.new_idle_inhibitor.link.remove(); } pub fn checkActive(inhibit_manager: *IdleInhibitManager) void { var inhibited = false; - var it = inhibit_manager.inhibitors.first; - while (it) |node| : (it = node.next) { - const node_data = SceneNodeData.fromSurface(node.data.wlr_inhibitor.surface) orelse continue; + var it = inhibit_manager.inhibitors.iterator(.forward); + while (it.next()) |inhibitor| { + const node_data = SceneNodeData.fromSurface(inhibitor.wlr_inhibitor.surface) orelse continue; switch (node_data.data) { .view => |view| { if (view.current.output != null and @@ -79,13 +80,8 @@ pub fn checkActive(inhibit_manager: *IdleInhibitManager) void { fn handleNewIdleInhibitor(listener: *wl.Listener(*wlr.IdleInhibitorV1), inhibitor: *wlr.IdleInhibitorV1) void { const inhibit_manager: *IdleInhibitManager = @fieldParentPtr("new_idle_inhibitor", listener); - const inhibitor_node = util.gpa.create(std.DoublyLinkedList(IdleInhibitor).Node) catch return; - inhibitor_node.data.init(inhibitor, inhibit_manager) catch { - util.gpa.destroy(inhibitor_node); + IdleInhibitor.create(inhibitor, inhibit_manager) catch { + std.log.err("out of memory", .{}); return; }; - - inhibit_manager.inhibitors.append(inhibitor_node); - - inhibit_manager.checkActive(); } diff --git a/river/IdleInhibitor.zig b/river/IdleInhibitor.zig index 2b45786..f3df45e 100644 --- a/river/IdleInhibitor.zig +++ b/river/IdleInhibitor.zig @@ -28,31 +28,38 @@ const IdleInhibitManager = @import("IdleInhibitManager.zig"); inhibit_manager: *IdleInhibitManager, wlr_inhibitor: *wlr.IdleInhibitorV1, -destroy: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleDestroy), +listen_destroy: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleDestroy), + +link: wl.list.Link, + +pub fn create(wlr_inhibitor: *wlr.IdleInhibitorV1, inhibit_manager: *IdleInhibitManager) !void { + const inhibitor = try util.gpa.create(IdleInhibitor); + errdefer util.gpa.destroy(inhibitor); -pub fn init( - inhibitor: *IdleInhibitor, - wlr_inhibitor: *wlr.IdleInhibitorV1, - inhibit_manager: *IdleInhibitManager, -) !void { inhibitor.* = .{ .inhibit_manager = inhibit_manager, .wlr_inhibitor = wlr_inhibitor, + .link = undefined, }; - wlr_inhibitor.events.destroy.add(&inhibitor.destroy); + wlr_inhibitor.events.destroy.add(&inhibitor.listen_destroy); + + inhibit_manager.inhibitors.append(inhibitor); inhibit_manager.checkActive(); } -fn handleDestroy(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { - const inhibitor: *IdleInhibitor = @fieldParentPtr("destroy", listener); +pub fn destroy(inhibitor: *IdleInhibitor) void { + inhibitor.listen_destroy.link.remove(); - inhibitor.destroy.link.remove(); - - const node: *std.DoublyLinkedList(IdleInhibitor).Node = @fieldParentPtr("data", inhibitor); - server.idle_inhibit_manager.inhibitors.remove(node); + inhibitor.link.remove(); inhibitor.inhibit_manager.checkActive(); - util.gpa.destroy(node); + util.gpa.destroy(inhibitor); +} + +fn handleDestroy(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { + const inhibitor: *IdleInhibitor = @fieldParentPtr("listen_destroy", listener); + + inhibitor.destroy(); } diff --git a/river/InputConfig.zig b/river/InputConfig.zig index c8a94bb..8557a26 100644 --- a/river/InputConfig.zig +++ b/river/InputConfig.zig @@ -25,7 +25,7 @@ const wlr = @import("wlroots"); const log = std.log.scoped(.input_config); -const c = @import("c.zig"); +const c = @import("c.zig").c; const server = &@import("main.zig").server; const util = @import("util.zig"); @@ -373,7 +373,7 @@ pub fn parse(config: *InputConfig, setting: []const u8, value: []const u8) !void return error.UnknownCommand; } -pub fn write(config: *InputConfig, writer: anytype) !void { +pub fn write(config: *InputConfig, writer: *std.Io.Writer) !void { try writer.print("{s}\n", .{config.glob}); inline for (@typeInfo(InputConfig).@"struct".fields) |field| { diff --git a/river/InputDevice.zig b/river/InputDevice.zig index 48c5a44..df26266 100644 --- a/river/InputDevice.zig +++ b/river/InputDevice.zig @@ -24,7 +24,7 @@ const wl = @import("wayland").server.wl; const globber = @import("globber"); -const c = @import("c.zig"); +const c = @import("c.zig").c; const server = &@import("main.zig").server; const util = @import("util.zig"); diff --git a/river/InputManager.zig b/river/InputManager.zig index 90497b9..0a744d4 100644 --- a/river/InputManager.zig +++ b/river/InputManager.zig @@ -55,10 +55,10 @@ tablet_manager: *wlr.TabletManagerV2, /// List of input device configurations. Ordered by glob generality, with /// the most general towards the start and the most specific towards the end. -configs: std.ArrayList(InputConfig), +configs: std.ArrayList(InputConfig) = .{}, devices: wl.list.Head(InputDevice, .link), -seats: std.DoublyLinkedList(Seat) = .{}, +seats: wl.list.Head(Seat, .link), exclusive_client: ?*wl.Client = null, @@ -74,9 +74,6 @@ new_text_input: wl.Listener(*wlr.TextInputV3) = wl.Listener(*wlr.TextInputV3).init(handleNewTextInput), pub fn init(input_manager: *InputManager) !void { - const seat_node = try util.gpa.create(std.DoublyLinkedList(Seat).Node); - errdefer util.gpa.destroy(seat_node); - input_manager.* = .{ // These are automatically freed when the display is destroyed .idle_notifier = try wlr.IdleNotifierV1.create(server.wl_server), @@ -88,14 +85,14 @@ pub fn init(input_manager: *InputManager) !void { .input_method_manager = try wlr.InputMethodManagerV2.create(server.wl_server), .text_input_manager = try wlr.TextInputManagerV3.create(server.wl_server), .tablet_manager = try wlr.TabletManagerV2.create(server.wl_server), - .configs = std.ArrayList(InputConfig).init(util.gpa), + .seats = undefined, .devices = undefined, }; + input_manager.seats.init(); input_manager.devices.init(); - input_manager.seats.prepend(seat_node); - try seat_node.data.init(default_seat_name); + try Seat.create(default_seat_name); if (build_options.xwayland) { if (server.xwayland) |xwayland| { @@ -121,19 +118,18 @@ pub fn deinit(input_manager: *InputManager) void { input_manager.new_input_method.link.remove(); input_manager.new_text_input.link.remove(); - while (input_manager.seats.pop()) |seat_node| { - seat_node.data.deinit(); - util.gpa.destroy(seat_node); + while (input_manager.seats.first()) |seat| { + seat.destroy(); } for (input_manager.configs.items) |*config| { config.deinit(); } - input_manager.configs.deinit(); + input_manager.configs.deinit(util.gpa); } -pub fn defaultSeat(input_manager: InputManager) *Seat { - return &input_manager.seats.first.?.data; +pub fn defaultSeat(input_manager: *InputManager) *Seat { + return input_manager.seats.first().?; } /// Returns true if input is currently allowed on the passed surface. @@ -220,7 +216,7 @@ const NoKeymapVirtKeyboard = struct { handleDestroy(&no_keymap.destroy, &virtual_keyboard.keyboard.base); - const seat: *Seat = @alignCast(@ptrCast(virtual_keyboard.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(virtual_keyboard.seat.data)); seat.addDevice(&virtual_keyboard.keyboard.base, true); } }; @@ -236,7 +232,7 @@ fn handleNewConstraint( } fn handleNewInputMethod(_: *wl.Listener(*wlr.InputMethodV2), input_method: *wlr.InputMethodV2) void { - const seat: *Seat = @alignCast(@ptrCast(input_method.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(input_method.seat.data)); log.debug("new input method on seat {s}", .{seat.wlr_seat.name}); diff --git a/river/Keyboard.zig b/river/Keyboard.zig index 9f2b92b..b117e3f 100644 --- a/river/Keyboard.zig +++ b/river/Keyboard.zig @@ -57,33 +57,52 @@ pub const Pressed = struct { assert(capacity == @typeInfo(std.meta.fieldInfo(wlr.Keyboard, .keycodes).type).array.len); } - keys: std.BoundedArray(Key, capacity) = .{}, + keys: [capacity]Key, + len: usize, + + const empty: Pressed = .{ .keys = undefined, .len = 0 }; + + pub fn slice(pressed: *Pressed) []Key { + return pressed.keys[0..pressed.len]; + } fn contains(pressed: *Pressed, code: u32) bool { - for (pressed.keys.constSlice()) |item| { + for (pressed.slice()) |item| { if (item.code == code) return true; } return false; } fn addAssumeCapacity(pressed: *Pressed, new: Key) void { + assert(pressed.len < pressed.keys.len); assert(!pressed.contains(new.code)); - pressed.keys.appendAssumeCapacity(new); + pressed.keys[pressed.len] = new; + pressed.len += 1; } fn remove(pressed: *Pressed, code: u32) ?KeyConsumer { - for (pressed.keys.constSlice(), 0..) |item, idx| { - if (item.code == code) return pressed.keys.swapRemove(idx).consumer; + for (pressed.slice(), 0..) |item, idx| { + if (item.code == code) return pressed.swapRemove(idx).consumer; } return null; } + + fn swapRemove(pressed: *Pressed, index: usize) Key { + defer pressed.len -= 1; + if (index == pressed.len - 1) { + return pressed.keys[index]; + } + const ret = pressed.keys[index]; + pressed.keys[index] = pressed.keys[pressed.len - 1]; + return ret; + } }; device: InputDevice, /// Pressed keys along with where their press event has been sent -pressed: Pressed = .{}, +pressed: Pressed = .empty, key: wl.Listener(*wlr.Keyboard.event.Key) = wl.Listener(*wlr.Keyboard.event.Key).init(handleKey), modifiers: wl.Listener(*wlr.Keyboard) = wl.Listener(*wlr.Keyboard).init(handleModifiers), @@ -203,7 +222,9 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa // Ignore key presses beyond 32 simultaneously pressed keys (see comments in Pressed). // We must ensure capacity before calling handleMapping() to ensure that we either run // both the press and release mapping for certain key or neither mapping. - keyboard.pressed.keys.ensureUnusedCapacity(1) catch return; + if (keyboard.pressed.len >= keyboard.pressed.keys.len) { + return; + } if (keyboard.device.seat.handleMapping(keycode, modifiers, released, xkb_state)) { break :blk .mapping; diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig index b89c472..da05fb7 100644 --- a/river/LayerSurface.zig +++ b/river/LayerSurface.zig @@ -43,7 +43,7 @@ commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit) new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup), pub fn create(wlr_layer_surface: *wlr.LayerSurfaceV1) error{OutOfMemory}!void { - const output: *Output = @alignCast(@ptrCast(wlr_layer_surface.output.?.data)); + const output: *Output = @ptrCast(@alignCast(wlr_layer_surface.output.?.data)); const layer_surface = try util.gpa.create(LayerSurface); errdefer util.gpa.destroy(layer_surface); @@ -157,7 +157,7 @@ fn handleKeyboardInteractiveExclusive(output: *Output, consider: ?*LayerSurface) var it = tree.children.iterator(.reverse); while (it.next()) |node| { assert(node.type == .tree); - if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| { + if (@as(?*SceneNodeData, @ptrCast(@alignCast(node.data)))) |node_data| { const layer_surface = node_data.data.layer_surface; const wlr_layer_surface = layer_surface.wlr_layer_surface; if (wlr_layer_surface.surface.mapped and @@ -173,10 +173,8 @@ fn handleKeyboardInteractiveExclusive(output: *Output, consider: ?*LayerSurface) assert(s.wlr_layer_surface.current.keyboard_interactive != .none); } - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const seat = &node.data; - + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { if (seat.focused_output == output) { if (to_focus) |s| { // If we found a surface on the output that requires focus, grab the focus of all diff --git a/river/Layout.zig b/river/Layout.zig index 54a0de9..8c9f7b0 100644 --- a/river/Layout.zig +++ b/river/Layout.zig @@ -38,6 +38,9 @@ layout_v3: *river.LayoutV3, namespace: []const u8, output: *Output, +// Output.layouts +link: wl.list.Link, + pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namespace: []const u8) !void { const layout_v3 = try river.LayoutV3.create(client, version, id); @@ -47,21 +50,22 @@ pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namesp return; } - const node = try util.gpa.create(std.DoublyLinkedList(Layout).Node); - errdefer util.gpa.destroy(node); - node.data = .{ + const layout = try util.gpa.create(Layout); + errdefer util.gpa.destroy(layout); + layout.* = .{ .layout_v3 = layout_v3, .namespace = try util.gpa.dupe(u8, namespace), .output = output, + .link = undefined, }; - output.layouts.append(node); + output.layouts.append(layout); - layout_v3.setHandler(*Layout, handleRequest, handleDestroy, &node.data); + layout_v3.setHandler(*Layout, handleRequest, handleDestroy, layout); // If the namespace matches that of the output, set the layout as // the active one of the output and arrange it. if (mem.eql(u8, namespace, output.layoutNamespace())) { - output.layout = &node.data; + output.layout = layout; server.root.applyPending(); } } @@ -71,17 +75,17 @@ pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namesp fn namespaceInUse(namespace: []const u8, output: *Output, client: *wl.Client) bool { var output_it = server.root.active_outputs.iterator(.forward); while (output_it.next()) |o| { - var layout_it = output.layouts.first; + var layout_it = output.layouts.iterator(.forward); if (o == output) { // On this output, no other layout can have our namespace. - while (layout_it) |layout_node| : (layout_it = layout_node.next) { - if (mem.eql(u8, namespace, layout_node.data.namespace)) return true; + while (layout_it.next()) |layout| { + if (mem.eql(u8, namespace, layout.namespace)) return true; } } else { // Layouts on other outputs may share the namespace, if they come from the same client. - while (layout_it) |layout_node| : (layout_it = layout_node.next) { - if (mem.eql(u8, namespace, layout_node.data.namespace) and - client != layout_node.data.layout_v3.getClient()) return true; + while (layout_it.next()) |layout| { + if (mem.eql(u8, namespace, layout.namespace) and + client != layout.layout_v3.getClient()) return true; } } } @@ -185,9 +189,7 @@ pub fn destroy(layout: *Layout) void { .{ layout.namespace, layout.output.wlr_output.name }, ); - // Remove layout from the list - const node: *std.DoublyLinkedList(Layout).Node = @fieldParentPtr("data", layout); - layout.output.layouts.remove(node); + layout.link.remove(); // If we are the currently active layout of an output, clean up. if (layout.output.layout == layout) { @@ -208,5 +210,5 @@ pub fn destroy(layout: *Layout) void { layout.layout_v3.setHandler(?*anyopaque, handleRequestInert, null, null); util.gpa.free(layout.namespace); - util.gpa.destroy(node); + util.gpa.destroy(layout); } diff --git a/river/LayoutManager.zig b/river/LayoutManager.zig index 847c714..0a1588f 100644 --- a/river/LayoutManager.zig +++ b/river/LayoutManager.zig @@ -68,7 +68,7 @@ fn handleRequest( .get_layout => |req| { // Ignore if the output is inert const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return; - const output: *Output = @alignCast(@ptrCast(wlr_output.data)); + const output: *Output = @ptrCast(@alignCast(wlr_output.data)); log.debug("bind layout '{s}' on output '{s}'", .{ req.namespace, output.wlr_output.name }); diff --git a/river/LockManager.zig b/river/LockManager.zig index 5b86c8c..f2a43d7 100644 --- a/river/LockManager.zig +++ b/river/LockManager.zig @@ -110,9 +110,8 @@ fn handleLock(listener: *wl.Listener(*wlr.SessionLockV1), lock: *wlr.SessionLock }; { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const seat = &node.data; + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { seat.setFocusRaw(.none); // Enter locked mode @@ -213,9 +212,8 @@ fn handleUnlock(listener: *wl.Listener(void)) void { } { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const seat = &node.data; + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { seat.setFocusRaw(.none); // Exit locked mode @@ -266,7 +264,7 @@ pub fn updateLockSurfaceSize(manager: *LockManager, output: *Output) void { var it = lock.surfaces.iterator(.forward); while (it.next()) |wlr_lock_surface| { - const lock_surface: *LockSurface = @alignCast(@ptrCast(wlr_lock_surface.data)); + const lock_surface: *LockSurface = @ptrCast(@alignCast(wlr_lock_surface.data)); if (output == lock_surface.getOutput()) { lock_surface.configure(); } diff --git a/river/LockSurface.zig b/river/LockSurface.zig index 285c8ec..c004761 100644 --- a/river/LockSurface.zig +++ b/river/LockSurface.zig @@ -65,12 +65,11 @@ pub fn destroy(lock_surface: *LockSurface) void { var surface_it = lock_surface.lock.surfaces.iterator(.forward); const new_focus: Seat.FocusTarget = while (surface_it.next()) |surface| { if (surface != lock_surface.wlr_lock_surface) - break .{ .lock_surface = @alignCast(@ptrCast(surface.data)) }; + break .{ .lock_surface = @ptrCast(@alignCast(surface.data)) }; } else .none; - var seat_it = server.input_manager.seats.first; - while (seat_it) |node| : (seat_it = node.next) { - const seat = &node.data; + var seat_it = server.input_manager.seats.iterator(.forward); + while (seat_it.next()) |seat| { if (seat.focused == .lock_surface and seat.focused.lock_surface == lock_surface) { seat.setFocusRaw(new_focus); } @@ -92,7 +91,7 @@ pub fn destroy(lock_surface: *LockSurface) void { } pub fn getOutput(lock_surface: *LockSurface) *Output { - return @alignCast(@ptrCast(lock_surface.wlr_lock_surface.output.data)); + return @ptrCast(@alignCast(lock_surface.wlr_lock_surface.output.data)); } pub fn configure(lock_surface: *LockSurface) void { @@ -122,9 +121,8 @@ fn handleMap(listener: *wl.Listener(void)) void { } fn updateFocus(lock_surface: *LockSurface) void { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const seat = &node.data; + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { if (seat.focused != .lock_surface) { seat.setFocusRaw(.{ .lock_surface = lock_surface }); } diff --git a/river/Output.zig b/river/Output.zig index daf6b7a..7fde739 100644 --- a/river/Output.zig +++ b/river/Output.zig @@ -167,7 +167,7 @@ previous_tags: u32 = 1 << 0, attach_mode: ?Config.AttachMode = null, /// List of all layouts -layouts: std.DoublyLinkedList(Layout) = .{}, +layouts: wl.list.Head(Layout, .link), /// The current layout namespace of the output. If null, /// config.default_layout_namespace should be used instead. @@ -290,9 +290,12 @@ pub fn create(wlr_output: *wlr.Output) !void { .height = height, }, .status = undefined, + .layouts = undefined, }; wlr_output.data = output; + output.layouts.init(); + output.pending.focus_stack.init(); output.pending.wm_stack.init(); output.inflight.focus_stack.init(); @@ -360,7 +363,7 @@ fn sendLayerConfigures( var it = tree.children.safeIterator(.forward); while (it.next()) |node| { assert(node.type == .tree); - if (@as(?*SceneNodeData, @alignCast(@ptrCast(node.data)))) |node_data| { + if (@as(?*SceneNodeData, @ptrCast(@alignCast(node.data)))) |node_data| { const layer_surface = node_data.data.layer_surface; if (!layer_surface.wlr_layer_surface.initialized) continue; @@ -416,7 +419,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.Output), _: *wlr.Output) void { assert(output.inflight.focus_stack.empty()); assert(output.inflight.wm_stack.empty()); assert(output.inflight.layout_demand == null); - assert(output.layouts.len == 0); + assert(output.layouts.length() == 0); output.all_link.remove(); @@ -452,7 +455,7 @@ fn handleRequestState(listener: *wl.Listener(*wlr.Output.event.RequestState), ev // TODO double buffer output state changes for frame perfection and cleaner code. // Schedule a frame and commit in the frame handler. // Get rid of this function. -pub fn applyState(output: *Output, state: *wlr.Output.State) error{CommitFailed}!void { +pub fn applyState(output: *Output, state: *const wlr.Output.State) error{CommitFailed}!void { // We need to be precise about this state change to make assertions // in updateLockRenderStateOnEnableDisable() possible. @@ -619,7 +622,7 @@ fn handlePresent( } fn setTitle(output: Output) void { - const title = fmt.allocPrintZ(util.gpa, "river - {s}", .{output.wlr_output.name}) catch return; + const title = fmt.allocPrintSentinel(util.gpa, "river - {s}", .{output.wlr_output.name}, 0) catch return; defer util.gpa.free(title); if (output.wlr_output.isWl()) { output.wlr_output.wlSetTitle(title); @@ -631,9 +634,9 @@ fn setTitle(output: Output) void { pub fn handleLayoutNamespaceChange(output: *Output) void { // The user changed the layout namespace of this output. Try to find a // matching layout. - var it = output.layouts.first; - output.layout = while (it) |node| : (it = node.next) { - if (mem.eql(u8, output.layoutNamespace(), node.data.namespace)) break &node.data; + var it = output.layouts.iterator(.forward); + output.layout = while (it.next()) |layout| { + if (mem.eql(u8, output.layoutNamespace(), layout.namespace)) break layout; } else null; server.root.applyPending(); } diff --git a/river/PointerConstraint.zig b/river/PointerConstraint.zig index 12708aa..7b3ae3b 100644 --- a/river/PointerConstraint.zig +++ b/river/PointerConstraint.zig @@ -47,7 +47,7 @@ commit: wl.Listener(*wlr.Surface) = wl.Listener(*wlr.Surface).init(handleCommit) node_destroy: wl.Listener(void) = wl.Listener(void).init(handleNodeDestroy), pub fn create(wlr_constraint: *wlr.PointerConstraintV1) error{OutOfMemory}!void { - const seat: *Seat = @alignCast(@ptrCast(wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(wlr_constraint.seat.data)); const constraint = try util.gpa.create(PointerConstraint); errdefer util.gpa.destroy(constraint); @@ -70,7 +70,7 @@ pub fn create(wlr_constraint: *wlr.PointerConstraintV1) error{OutOfMemory}!void } pub fn maybeActivate(constraint: *PointerConstraint) void { - const seat: *Seat = @alignCast(@ptrCast(constraint.wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(constraint.wlr_constraint.seat.data)); assert(seat.cursor.constraint == constraint); @@ -102,7 +102,7 @@ pub fn maybeActivate(constraint: *PointerConstraint) void { /// Called when the cursor position or content in the scene graph changes pub fn updateState(constraint: *PointerConstraint) void { - const seat: *Seat = @alignCast(@ptrCast(constraint.wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(constraint.wlr_constraint.seat.data)); constraint.maybeActivate(); @@ -154,7 +154,7 @@ pub fn confine(constraint: *PointerConstraint, dx: *f64, dy: *f64) void { } pub fn deactivate(constraint: *PointerConstraint) void { - const seat: *Seat = @alignCast(@ptrCast(constraint.wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(constraint.wlr_constraint.seat.data)); assert(seat.cursor.constraint == constraint); assert(constraint.state == .active); @@ -167,7 +167,7 @@ pub fn deactivate(constraint: *PointerConstraint) void { } fn warpToHintIfSet(constraint: *PointerConstraint) void { - const seat: *Seat = @alignCast(@ptrCast(constraint.wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(constraint.wlr_constraint.seat.data)); if (constraint.wlr_constraint.current.cursor_hint.enabled) { var lx: i32 = undefined; @@ -190,7 +190,7 @@ fn handleNodeDestroy(listener: *wl.Listener(void)) void { fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.PointerConstraintV1) void { const constraint: *PointerConstraint = @fieldParentPtr("destroy", listener); - const seat: *Seat = @alignCast(@ptrCast(constraint.wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(constraint.wlr_constraint.seat.data)); if (constraint.state == .active) { // We can't simply call deactivate() here as it calls sendDeactivated(), @@ -215,7 +215,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.Point // the surface changes. fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { const constraint: *PointerConstraint = @fieldParentPtr("commit", listener); - const seat: *Seat = @alignCast(@ptrCast(constraint.wlr_constraint.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(constraint.wlr_constraint.seat.data)); switch (constraint.state) { .active => |state| { diff --git a/river/Root.zig b/river/Root.zig index 33f53d6..3f62008 100644 --- a/river/Root.zig +++ b/river/Root.zig @@ -330,16 +330,15 @@ pub fn deactivateOutput(root: *Root, output: *Output) void { var it = tree.children.safeIterator(.forward); while (it.next()) |scene_node| { assert(scene_node.type == .tree); - if (@as(?*SceneNodeData, @alignCast(@ptrCast(scene_node.data)))) |node_data| { + if (@as(?*SceneNodeData, @ptrCast(@alignCast(scene_node.data)))) |node_data| { node_data.data.layer_surface.wlr_layer_surface.destroy(); } } } // If any seat has the removed output focused, focus the fallback one - var seat_it = server.input_manager.seats.first; - while (seat_it) |seat_node| : (seat_it = seat_node.next) { - const seat = &seat_node.data; + var seat_it = server.input_manager.seats.iterator(.forward); + while (seat_it.next()) |seat| { if (seat.focused_output == output) { seat.focusOutput(fallback_output); } @@ -353,7 +352,7 @@ pub fn deactivateOutput(root: *Root, output: *Output) void { output.inflight.layout_demand = null; root.notifyLayoutDemandDone(); } - while (output.layouts.first) |node| node.data.destroy(); + while (output.layouts.first()) |layout| layout.destroy(); // We must call reconfigureDevices here to unmap devices that might be mapped to this output // in order to prevent a segfault in wlroots. @@ -397,9 +396,8 @@ pub fn activateOutput(root: *Root, output: *Output) void { } { // Focus the new output with all seats - var it = server.input_manager.seats.first; - while (it) |seat_node| : (it = seat_node.next) { - const seat = &seat_node.data; + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { seat.focusOutput(output); } } @@ -435,8 +433,8 @@ pub fn applyPending(root: *Root) void { // state consistent. Instead of having focus(null) calls spread all // around the codebase and risk forgetting one, always ensure focus // state is synchronized here. - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) node.data.focus(null); + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| seat.focus(null); } // If there is already a transaction inflight, wait until it completes. @@ -546,11 +544,9 @@ pub fn applyPending(root: *Root) void { } { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const cursor = &node.data.cursor; - - switch (cursor.mode) { + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { + switch (seat.cursor.mode) { .passthrough, .down => {}, inline .move, .resize => |data| { if (data.view.inflight.output == null or @@ -558,14 +554,14 @@ pub fn applyPending(root: *Root) void { (!data.view.inflight.float and data.view.inflight.output.?.layout != null) or data.view.inflight.fullscreen) { - cursor.mode = .passthrough; + seat.cursor.mode = .passthrough; data.view.pending.resizing = false; data.view.inflight.resizing = false; } }, } - cursor.inflight_mode = cursor.mode; + seat.cursor.inflight_mode = seat.cursor.mode; } } @@ -710,11 +706,8 @@ fn commitTransaction(root: *Root) void { } { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - node.data.cursor.updateState(); - node.data.sendFocusedView(); - } + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| seat.cursor.updateState(); } { @@ -805,7 +798,7 @@ fn processOutputConfig( var it = config.heads.iterator(.forward); while (it.next()) |head| { const wlr_output = head.state.output; - const output: *Output = @alignCast(@ptrCast(wlr_output.data)); + const output: *Output = @ptrCast(@alignCast(wlr_output.data)); var proposed_state = wlr.Output.State.init(); head.state.apply(&proposed_state); @@ -857,7 +850,7 @@ fn handlePowerManagerSetMode( event: *wlr.OutputPowerManagerV1.event.SetMode, ) void { // The output may have been destroyed, in which case there is nothing to do - const output: *Output = @alignCast(@ptrCast(event.output.data orelse return)); + const output: *Output = @ptrCast(@alignCast(event.output.data orelse return)); std.log.debug("client requested dpms {s} for output {s}", .{ @tagName(event.mode), diff --git a/river/SceneNodeData.zig b/river/SceneNodeData.zig index 1e9116f..49c5d2c 100644 --- a/river/SceneNodeData.zig +++ b/river/SceneNodeData.zig @@ -54,7 +54,7 @@ pub fn attach(node: *wlr.SceneNode, data: Data) error{OutOfMemory}!void { pub fn fromNode(node: *wlr.SceneNode) ?*SceneNodeData { var n = node; while (true) { - if (@as(?*SceneNodeData, @alignCast(@ptrCast(n.data)))) |scene_node_data| { + if (@as(?*SceneNodeData, @ptrCast(@alignCast(n.data)))) |scene_node_data| { return scene_node_data; } if (n.parent) |parent_tree| { @@ -66,7 +66,7 @@ pub fn fromNode(node: *wlr.SceneNode) ?*SceneNodeData { } pub fn fromSurface(surface: *wlr.Surface) ?*SceneNodeData { - if (@as(?*wlr.SceneNode, @alignCast(@ptrCast(surface.getRootSurface().data)))) |node| { + if (@as(?*wlr.SceneNode, @ptrCast(@alignCast(surface.getRootSurface().data)))) |node| { return fromNode(node); } return null; diff --git a/river/Seat.zig b/river/Seat.zig index 7bc23a4..0fc0a34 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -91,7 +91,7 @@ focused_output: ?*Output = null, focused: FocusTarget = .none, /// List of status tracking objects relaying changes to this seat to clients. -status_trackers: std.SinglyLinkedList(SeatStatus) = .{}, +status_trackers: wl.list.Head(SeatStatus, .link), /// The currently in progress drag operation type. drag: enum { @@ -109,7 +109,13 @@ drag_destroy: wl.Listener(*wlr.Drag) = wl.Listener(*wlr.Drag).init(handleDragDes request_set_primary_selection: wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection) = wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection).init(handleRequestSetPrimarySelection), -pub fn init(seat: *Seat, name: [*:0]const u8) !void { +// InputManager.seats +link: wl.list.Link = undefined, + +pub fn create(name: [*:0]const u8) !void { + const seat = try util.gpa.create(Seat); + errdefer util.gpa.destroy(seat); + const event_loop = server.wl_server.getEventLoop(); const mapping_repeat_timer = try event_loop.addTimer(*Seat, handleMappingRepeatTimeout, seat); errdefer mapping_repeat_timer.remove(); @@ -121,9 +127,14 @@ pub fn init(seat: *Seat, name: [*:0]const u8) !void { .relay = undefined, .mapping_repeat_timer = mapping_repeat_timer, .keyboard_group = try wlr.KeyboardGroup.create(), + .status_trackers = undefined, + .link = undefined, }; seat.wlr_seat.data = seat; + server.input_manager.seats.append(seat); + seat.status_trackers.init(); + try seat.cursor.init(seat); seat.relay.init(); @@ -135,7 +146,7 @@ pub fn init(seat: *Seat, name: [*:0]const u8) !void { seat.wlr_seat.events.request_set_primary_selection.add(&seat.request_set_primary_selection); } -pub fn deinit(seat: *Seat) void { +pub fn destroy(seat: *Seat) void { { var it = server.input_manager.devices.iterator(.forward); while (it.next()) |device| assert(device.seat != seat); @@ -151,6 +162,10 @@ pub fn deinit(seat: *Seat) void { seat.start_drag.link.remove(); if (seat.drag != .none) seat.drag_destroy.link.remove(); seat.request_set_primary_selection.link.remove(); + + seat.link.remove(); + + util.gpa.destroy(seat); } /// Set the current focus. If a visible view is passed it will be focused. @@ -289,7 +304,7 @@ pub fn setFocusRaw(seat: *Seat, new_focus: FocusTarget) void { if (seat.cursor.constraint) |constraint| { assert(constraint.wlr_constraint == wlr_constraint); } else { - seat.cursor.constraint = @alignCast(@ptrCast(wlr_constraint.data)); + seat.cursor.constraint = @ptrCast(@alignCast(wlr_constraint.data)); assert(seat.cursor.constraint != null); } } @@ -300,7 +315,10 @@ pub fn setFocusRaw(seat: *Seat, new_focus: FocusTarget) void { seat.cursor.may_need_warp = true; // Inform any clients tracking status of the change - seat.sendFocusedView(); + var it = seat.status_trackers.iterator(.forward); + while (it.next()) |tracker| { + tracker.sendFocusedView(); + } } /// Send keyboard enter/leave events and handle pointer constraints @@ -316,16 +334,17 @@ pub fn keyboardEnterOrLeave(seat: *Seat, target_surface: ?*wlr.Surface) void { fn keyboardNotifyEnter(seat: *Seat, wlr_surface: *wlr.Surface) void { if (seat.wlr_seat.getKeyboard()) |wlr_keyboard| { - const keyboard: *Keyboard = @alignCast(@ptrCast(wlr_keyboard.data)); + const keyboard: *Keyboard = @ptrCast(@alignCast(wlr_keyboard.data)); - var keycodes: std.BoundedArray(u32, Keyboard.Pressed.capacity) = .{}; - for (keyboard.pressed.keys.constSlice()) |item| { + var buffer: [Keyboard.Pressed.capacity]u32 = undefined; + var keycodes: std.ArrayList(u32) = .initBuffer(&buffer); + for (keyboard.pressed.slice()) |item| { if (item.consumer == .focus) keycodes.appendAssumeCapacity(item.code); } seat.wlr_seat.keyboardNotifyEnter( wlr_surface, - keycodes.constSlice(), + keycodes.items, &wlr_keyboard.modifiers, ); } else { @@ -338,15 +357,15 @@ pub fn focusOutput(seat: *Seat, output: ?*Output) void { if (seat.focused_output == output) return; if (seat.focused_output) |old| { - var it = seat.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendOutput(old, .unfocused); + var it = seat.status_trackers.iterator(.forward); + while (it.next()) |tracker| tracker.sendOutput(old, .unfocused); } seat.focused_output = output; if (seat.focused_output) |new| { - var it = seat.status_trackers.first; - while (it) |node| : (it = node.next) node.data.sendOutput(new, .focused); + var it = seat.status_trackers.iterator(.forward); + while (it.next()) |tracker| tracker.sendOutput(new, .focused); } // Depending on configuration and cursor position, changing output focus @@ -361,9 +380,9 @@ pub fn handleActivity(seat: Seat) void { pub fn enterMode(seat: *Seat, mode_id: u32) void { seat.mode_id = mode_id; - var it = seat.status_trackers.first; - while (it) |node| : (it = node.next) { - node.data.sendMode(server.config.modes.items[mode_id].name); + var it = seat.status_trackers.iterator(.forward); + while (it.next()) |tracker| { + tracker.sendMode(server.config.modes.items[mode_id].name); } } @@ -457,8 +476,8 @@ pub fn runCommand(seat: *Seat, args: []const [:0]const u8) void { return; }; if (out) |s| { - const stdout = std.io.getStdOut().writer(); - stdout.print("{s}", .{s}) catch |err| { + var stdout = std.fs.File.stdout().writer(&.{}); + stdout.interface.print("{s}", .{s}) catch |err| { std.log.scoped(.command).err("{s}: write to stdout failed {}", .{ args[0], err }); }; } diff --git a/river/SeatStatus.zig b/river/SeatStatus.zig index 342de43..9bcd4ac 100644 --- a/river/SeatStatus.zig +++ b/river/SeatStatus.zig @@ -33,8 +33,19 @@ const View = @import("View.zig"); seat: *Seat, seat_status_v1: *zriver.SeatStatusV1, -pub fn init(seat_status: *SeatStatus, seat: *Seat, seat_status_v1: *zriver.SeatStatusV1) void { - seat_status.* = .{ .seat = seat, .seat_status_v1 = seat_status_v1 }; +link: wl.list.Link, + +pub fn create(seat: *Seat, seat_status_v1: *zriver.SeatStatusV1) !void { + const seat_status = try util.gpa.create(SeatStatus); + errdefer util.gpa.destroy(seat_status); + + seat_status.* = .{ + .seat = seat, + .seat_status_v1 = seat_status_v1, + .link = undefined, + }; + seat.status_trackers.append(seat_status); + errdefer comptime unreachable; seat_status_v1.setHandler(*SeatStatus, handleRequest, handleDestroy, seat_status); @@ -51,9 +62,8 @@ fn handleRequest(seat_status_v1: *zriver.SeatStatusV1, request: zriver.SeatStatu } fn handleDestroy(_: *zriver.SeatStatusV1, seat_status: *SeatStatus) void { - const node: *std.SinglyLinkedList(SeatStatus).Node = @fieldParentPtr("data", seat_status); - seat_status.seat.status_trackers.remove(node); - util.gpa.destroy(node); + seat_status.link.remove(); + util.gpa.destroy(seat_status); } pub fn sendOutput(seat_status: SeatStatus, output: *Output, state: enum { focused, unfocused }) void { diff --git a/river/Server.zig b/river/Server.zig index 35f50c5..2a8065d 100644 --- a/river/Server.zig +++ b/river/Server.zig @@ -24,7 +24,7 @@ const posix = std.posix; const wlr = @import("wlroots"); const wl = @import("wayland").server.wl; -const c = @import("c.zig"); +const c = @import("c.zig").c; const util = @import("util.zig"); const Config = @import("Config.zig"); @@ -510,7 +510,7 @@ fn handleRequestSetCursorShape( _: *wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape), event: *wlr.CursorShapeManagerV1.event.RequestSetShape, ) void { - const seat: *Seat = @alignCast(@ptrCast(event.seat_client.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(event.seat_client.seat.data)); if (event.tablet_tool) |wp_tool| { assert(event.device_type == .tablet_tool); diff --git a/river/StatusManager.zig b/river/StatusManager.zig index b18dd17..663aff6 100644 --- a/river/StatusManager.zig +++ b/river/StatusManager.zig @@ -69,7 +69,7 @@ fn handleRequest( .get_river_output_status => |req| { // ignore if the output is inert const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return; - const output: *Output = @alignCast(@ptrCast(wlr_output.data)); + const output: *Output = @ptrCast(@alignCast(wlr_output.data)); const resource = zriver.OutputStatusV1.create( status_manager_v1.getClient(), @@ -86,13 +86,7 @@ fn handleRequest( .get_river_seat_status => |req| { // ignore if the seat is inert const wlr_seat = wlr.Seat.Client.fromWlSeat(req.seat) orelse return; - const seat: *Seat = @alignCast(@ptrCast(wlr_seat.seat.data)); - - const node = util.gpa.create(std.SinglyLinkedList(SeatStatus).Node) catch { - status_manager_v1.getClient().postNoMemory(); - log.err("out of memory", .{}); - return; - }; + const seat: *Seat = @ptrCast(@alignCast(wlr_seat.seat.data)); const seat_status = zriver.SeatStatusV1.create( status_manager_v1.getClient(), @@ -100,13 +94,15 @@ fn handleRequest( req.id, ) catch { status_manager_v1.getClient().postNoMemory(); - util.gpa.destroy(node); log.err("out of memory", .{}); return; }; - node.data.init(seat, seat_status); - seat.status_trackers.prepend(node); + SeatStatus.create(seat, seat_status) catch { + status_manager_v1.getClient().postNoMemory(); + log.err("out of memory", .{}); + return; + }; }, } } diff --git a/river/TabletTool.zig b/river/TabletTool.zig index c1db876..f2a3769 100644 --- a/river/TabletTool.zig +++ b/river/TabletTool.zig @@ -59,7 +59,7 @@ set_cursor: wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor) = wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor).init(handleSetCursor), pub fn get(wlr_seat: *wlr.Seat, wlr_tool: *wlr.TabletTool) error{OutOfMemory}!*TabletTool { - if (@as(?*TabletTool, @alignCast(@ptrCast(wlr_tool.data)))) |tool| { + if (@as(?*TabletTool, @ptrCast(@alignCast(wlr_tool.data)))) |tool| { return tool; } else { return TabletTool.create(wlr_seat, wlr_tool); diff --git a/river/TextInput.zig b/river/TextInput.zig index 1df7d5f..e296cb4 100644 --- a/river/TextInput.zig +++ b/river/TextInput.zig @@ -43,7 +43,7 @@ destroy: wl.Listener(*wlr.TextInputV3) = wl.Listener(*wlr.TextInputV3).init(handleDestroy), pub fn create(wlr_text_input: *wlr.TextInputV3) !void { - const seat: *Seat = @alignCast(@ptrCast(wlr_text_input.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(wlr_text_input.seat.data)); const text_input = try util.gpa.create(TextInput); @@ -64,7 +64,7 @@ pub fn create(wlr_text_input: *wlr.TextInputV3) !void { fn handleEnable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { const text_input: *TextInput = @fieldParentPtr("enable", listener); - const seat: *Seat = @alignCast(@ptrCast(text_input.wlr_text_input.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data)); if (text_input.wlr_text_input.focused_surface == null) { log.err("client requested to enable text input without focus, ignoring request", .{}); @@ -91,7 +91,7 @@ fn handleEnable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) v fn handleCommit(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { const text_input: *TextInput = @fieldParentPtr("commit", listener); - const seat: *Seat = @alignCast(@ptrCast(text_input.wlr_text_input.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data)); if (seat.relay.text_input != text_input) { log.err("inactive text input tried to commit an update, client bug?", .{}); @@ -105,7 +105,7 @@ fn handleCommit(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) v fn handleDisable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { const text_input: *TextInput = @fieldParentPtr("disable", listener); - const seat: *Seat = @alignCast(@ptrCast(text_input.wlr_text_input.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data)); if (seat.relay.text_input == text_input) { seat.relay.disableTextInput(); @@ -114,7 +114,7 @@ fn handleDisable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) fn handleDestroy(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void { const text_input: *TextInput = @fieldParentPtr("destroy", listener); - const seat: *Seat = @alignCast(@ptrCast(text_input.wlr_text_input.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(text_input.wlr_text_input.seat.data)); if (seat.relay.text_input == text_input) { seat.relay.disableTextInput(); diff --git a/river/View.zig b/river/View.zig index ac061a3..00042c1 100644 --- a/river/View.zig +++ b/river/View.zig @@ -277,11 +277,12 @@ pub fn resizeUpdatePosition(view: *View, width: i32, height: i32) void { assert(view.inflight.resizing); const data = blk: { - var it = server.input_manager.seats.first; - while (it) |node| : (it = node.next) { - const cursor = &node.data.cursor; - if (cursor.inflight_mode == .resize and cursor.inflight_mode.resize.view == view) { - break :blk cursor.inflight_mode.resize; + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| { + if (seat.cursor.inflight_mode == .resize and + seat.cursor.inflight_mode.resize.view == view) + { + break :blk seat.cursor.inflight_mode.resize; } } else { // The view resizing state should never be set when the view is @@ -594,7 +595,7 @@ pub fn allowTearing(view: *View) bool { .window_hint => { if (server.config.allow_tearing) { if (view.rootSurface()) |root_surface| { - return server.tearing_control_manager.hintFromSurface(root_surface) == .@"async"; + return server.tearing_control_manager.hintFromSurface(root_surface) == .async; } } return false; @@ -720,8 +721,8 @@ pub fn map(view: *View) !void { if (output) |o| { view.setPendingOutput(o); - var it = server.input_manager.seats.first; - while (it) |seat_node| : (it = seat_node.next) seat_node.data.focus(view); + var it = server.input_manager.seats.iterator(.forward); + while (it.next()) |seat| seat.focus(view); } else { log.debug("no output available for newly mapped view, adding to fallback stacks", .{}); @@ -786,12 +787,13 @@ pub fn notifyState(view: *const View) void { .app_id = view.getAppId(), }); } - var seat_it = server.input_manager.seats.first; - while (seat_it) |seat_node| : (seat_it = seat_node.next) { - if (seat_node.data.focused == .view and seat_node.data.focused.view == view) { - var client_it = seat_node.data.status_trackers.first; - while (client_it) |client_node| : (client_it = client_node.next) { - client_node.data.sendFocusedView(); + // Send title to all status listeners attached to a seat which focuses this view + var seat_it = server.input_manager.seats.iterator(.forward); + while (seat_it.next()) |seat| { + if (seat.focused == .view and seat.focused.view == view) { + var it = seat.status_trackers.iterator(.forward); + while (it.next()) |tracker| { + tracker.sendFocusedView(); } } } diff --git a/river/XdgDecoration.zig b/river/XdgDecoration.zig index 7c72d6b..57374e4 100644 --- a/river/XdgDecoration.zig +++ b/river/XdgDecoration.zig @@ -34,7 +34,7 @@ request_mode: wl.Listener(*wlr.XdgToplevelDecorationV1) = wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleRequestMode), pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void { - const toplevel: *XdgToplevel = @alignCast(@ptrCast(wlr_decoration.toplevel.base.data)); + const toplevel: *XdgToplevel = @ptrCast(@alignCast(wlr_decoration.toplevel.base.data)); toplevel.decoration = .{ .wlr_decoration = wlr_decoration }; const decoration = &toplevel.decoration.?; @@ -48,7 +48,7 @@ pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void { } pub fn deinit(decoration: *XdgDecoration) void { - const toplevel: *XdgToplevel = @alignCast(@ptrCast(decoration.wlr_decoration.toplevel.base.data)); + const toplevel: *XdgToplevel = @ptrCast(@alignCast(decoration.wlr_decoration.toplevel.base.data)); decoration.destroy.link.remove(); decoration.request_mode.link.remove(); @@ -72,7 +72,7 @@ fn handleRequestMode( ) void { const decoration: *XdgDecoration = @fieldParentPtr("request_mode", listener); - const toplevel: *XdgToplevel = @alignCast(@ptrCast(decoration.wlr_decoration.toplevel.base.data)); + const toplevel: *XdgToplevel = @ptrCast(@alignCast(decoration.wlr_decoration.toplevel.base.data)); const view = toplevel.view; const ssd = server.config.rules.ssd.match(toplevel.view) orelse diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig index f73ea0a..d7ef3cf 100644 --- a/river/XdgToplevel.zig +++ b/river/XdgToplevel.zig @@ -423,7 +423,7 @@ fn handleRequestMove( event: *wlr.XdgToplevel.event.Move, ) void { const toplevel: *XdgToplevel = @fieldParentPtr("request_move", listener); - const seat: *Seat = @alignCast(@ptrCast(event.seat.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(event.seat.seat.data)); const view = toplevel.view; if (view.pending.fullscreen) return; @@ -446,7 +446,7 @@ fn handleRequestMove( fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), event: *wlr.XdgToplevel.event.Resize) void { const toplevel: *XdgToplevel = @fieldParentPtr("request_resize", listener); - const seat: *Seat = @alignCast(@ptrCast(event.seat.seat.data)); + const seat: *Seat = @ptrCast(@alignCast(event.seat.seat.data)); const view = toplevel.view; if (view.pending.fullscreen) return; diff --git a/river/XwaylandOverrideRedirect.zig b/river/XwaylandOverrideRedirect.zig index 2834acd..b685c1b 100644 --- a/river/XwaylandOverrideRedirect.zig +++ b/river/XwaylandOverrideRedirect.zig @@ -165,9 +165,8 @@ fn handleUnmap(listener: *wl.Listener(void)) void { // If the unmapped surface is currently focused, pass keyboard focus // to the most appropriate surface. - var seat_it = server.input_manager.seats.first; - while (seat_it) |seat_node| : (seat_it = seat_node.next) { - const seat = &seat_node.data; + var seat_it = server.input_manager.seats.iterator(.forward); + while (seat_it.next()) |seat| { if (seat.focused == .view and seat.focused.view.impl == .xwayland_view and seat.focused.view.impl.xwayland_view.xwayland_surface.pid == override_redirect.xwayland_surface.pid and seat.wlr_seat.keyboard_state.focused_surface == override_redirect.xwayland_surface.surface) diff --git a/river/c.zig b/river/c.zig index 05b5c25..59dd8a0 100644 --- a/river/c.zig +++ b/river/c.zig @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -pub usingnamespace @cImport({ +pub const c = @cImport({ @cDefine("_POSIX_C_SOURCE", "200809L"); @cInclude("stdlib.h"); diff --git a/river/command.zig b/river/command.zig index 85a5247..c21c0b3 100644 --- a/river/command.zig +++ b/river/command.zig @@ -122,6 +122,7 @@ pub const Error = error{ CannotParseFile, UnknownOption, ConflictingOptions, + WriteFailed, OutOfMemory, Other, }; @@ -165,7 +166,7 @@ pub fn errToMsg(err: Error) [:0]const u8 { Error.InvalidValue => "invalid value", Error.CannotReadFile => "cannot read file", Error.CannotParseFile => "cannot parse file", - Error.OutOfMemory => "out of memory", + Error.WriteFailed, Error.OutOfMemory => "out of memory", Error.Other => unreachable, }; } diff --git a/river/command/cursor.zig b/river/command/cursor.zig index 33b7b50..37f192b 100644 --- a/river/command/cursor.zig +++ b/river/command/cursor.zig @@ -35,9 +35,8 @@ pub fn cursor( if (args.len < 3) return Error.NotEnoughArguments; if (args.len > 3) return Error.TooManyArguments; server.config.cursor_hide_timeout = try std.fmt.parseInt(u31, args[2], 10); - var seat_it = server.input_manager.seats.first; - while (seat_it) |seat_node| : (seat_it = seat_node.next) { - const seat = &seat_node.data; + var seat_it = server.input_manager.seats.iterator(.forward); + while (seat_it.next()) |seat| { seat.cursor.unhide(); } } else if (std.mem.eql(u8, "when-typing", args[1])) { diff --git a/river/command/input.zig b/river/command/input.zig index acc7927..d334c02 100644 --- a/river/command/input.zig +++ b/river/command/input.zig @@ -34,8 +34,8 @@ pub fn listInputs( ) Error!void { if (args.len > 1) return error.TooManyArguments; - var input_list = std.ArrayList(u8).init(util.gpa); - const writer = input_list.writer(); + var aw: std.Io.Writer.Allocating = .init(util.gpa); + var prev = false; var it = server.input_manager.devices.iterator(.forward); @@ -46,16 +46,16 @@ pub fn listInputs( } } else false; - if (prev) try input_list.appendSlice("\n"); + if (prev) try aw.writer.writeByte('\n'); prev = true; - try writer.print("{s}\n\tconfigured: {}\n", .{ + try aw.writer.print("{s}\n\tconfigured: {}\n", .{ device.identifier, configured, }); } - out.* = try input_list.toOwnedSlice(); + out.* = try aw.toOwnedSlice(); } pub fn listInputConfigs( @@ -65,15 +65,14 @@ pub fn listInputConfigs( ) Error!void { if (args.len > 1) return error.TooManyArguments; - var input_list = std.ArrayList(u8).init(util.gpa); - const writer = input_list.writer(); + var aw: std.Io.Writer.Allocating = .init(util.gpa); for (server.input_manager.configs.items, 0..) |*input_config, i| { - if (i > 0) try writer.writeByte('\n'); - try input_config.write(writer); + if (i > 0) try aw.writer.writeByte('\n'); + try input_config.write(&aw.writer); } - out.* = try input_list.toOwnedSlice(); + out.* = try aw.toOwnedSlice(); } pub fn input( @@ -99,7 +98,7 @@ pub fn input( }; errdefer util.gpa.free(input_config.glob); - try server.input_manager.configs.ensureUnusedCapacity(1); + try server.input_manager.configs.ensureUnusedCapacity(util.gpa, 1); try input_config.parse(args[2], args[3]); diff --git a/river/command/layout.zig b/river/command/layout.zig index 798dff6..3668f4c 100644 --- a/river/command/layout.zig +++ b/river/command/layout.zig @@ -71,9 +71,8 @@ pub fn sendLayoutCmd( const output = seat.focused_output orelse return; const target_namespace = args[1]; - var it = output.layouts.first; - const layout = while (it) |node| : (it = node.next) { - const layout = &node.data; + var it = output.layouts.iterator(.forward); + const layout = while (it.next()) |layout| { if (mem.eql(u8, layout.namespace, target_namespace)) break layout; } else return; diff --git a/river/command/map.zig b/river/command/map.zig index 1ecc124..2778fa6 100644 --- a/river/command/map.zig +++ b/river/command/map.zig @@ -22,7 +22,7 @@ const wlr = @import("wlroots"); const xkb = @import("xkbcommon"); const flags = @import("flags"); -const c = @import("../c.zig"); +const c = @import("../c.zig").c; const server = &@import("../main.zig").server; const util = @import("../util.zig"); diff --git a/river/command/output.zig b/river/command/output.zig index a9ac836..98d1726 100644 --- a/river/command/output.zig +++ b/river/command/output.zig @@ -121,7 +121,7 @@ fn getOutput(seat: *Seat, str: []const u8) !?*Output { @floatFromInt(focus_box.x + @divTrunc(focus_box.width, 2)), @floatFromInt(focus_box.y + @divTrunc(focus_box.height, 2)), ) orelse return null; - return @alignCast(@ptrCast(wlr_output.data)); + return @ptrCast(@alignCast(wlr_output.data)); } else { // Check if an output matches by name var it = server.root.active_outputs.iterator(.forward); diff --git a/river/command/rule.zig b/river/command/rule.zig index 02e4402..fc9eadf 100644 --- a/river/command/rule.zig +++ b/river/command/rule.zig @@ -15,6 +15,7 @@ // along with this program. If not, see . const std = @import("std"); +const assert = std.debug.assert; const fmt = std.fmt; const globber = @import("globber"); @@ -250,6 +251,12 @@ fn apply_tearing_rules() void { } } +fn alignLeft(buf: []const u8, width: usize, writer: *std.io.Writer) Error!void { + assert(buf.len <= width); + try writer.writeAll(buf); + try writer.splatByteAll(' ', width - buf.len); +} + pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!void { if (args.len < 2) return error.NotEnoughArguments; if (args.len > 2) return error.TooManyArguments; @@ -271,11 +278,12 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! const app_id_column_max = 2 + @max("app-id".len, max_glob_len.app_id); const title_column_max = 2 + @max("title".len, max_glob_len.title); - var buffer = std.ArrayList(u8).init(util.gpa); - const writer = buffer.writer(); + var buffer = std.io.Writer.Allocating.init(util.gpa); + defer buffer.deinit(); + const writer = &buffer.writer; - try fmt.formatBuf("title", .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf("app-id", .{ .width = app_id_column_max, .alignment = .left }, writer); + try alignLeft("title", title_column_max, writer); + try alignLeft("app-id", app_id_column_max, writer); try writer.writeAll("action\n"); switch (rule_list) { @@ -290,8 +298,8 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! else => unreachable, }; for (rules) |rule| { - try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); + try alignLeft(rule.title_glob, title_column_max, writer); + try alignLeft(rule.app_id_glob, app_id_column_max, writer); try writer.print("{s}\n", .{switch (list) { .float => if (rule.value) "float" else "no-float", .ssd => if (rule.value) "ssd" else "csd", @@ -305,22 +313,22 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error! }, .tags => { for (server.config.rules.tags.rules.items) |rule| { - try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); + try alignLeft(rule.title_glob, title_column_max, writer); + try alignLeft(rule.app_id_glob, app_id_column_max, writer); try writer.print("{b}\n", .{rule.value}); } }, .position => { for (server.config.rules.position.rules.items) |rule| { - try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); - try writer.print("{s},{d},{d}", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y }); + try alignLeft(rule.title_glob, title_column_max, writer); + try alignLeft(rule.app_id_glob, app_id_column_max, writer); + try writer.print("{s},{d},{d}\n", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y }); } }, .dimensions => { for (server.config.rules.dimensions.rules.items) |rule| { - try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); - try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); + try alignLeft(rule.title_glob, title_column_max, writer); + try alignLeft(rule.app_id_glob, app_id_column_max, writer); try writer.print("{d}x{d}\n", .{ rule.value.width, rule.value.height }); } }, diff --git a/river/command/spawn.zig b/river/command/spawn.zig index 030535a..0462abe 100644 --- a/river/command/spawn.zig +++ b/river/command/spawn.zig @@ -17,7 +17,7 @@ const std = @import("std"); const posix = std.posix; -const c = @import("../c.zig"); +const c = @import("../c.zig").c; const util = @import("../util.zig"); const process = @import("../process.zig"); diff --git a/river/main.zig b/river/main.zig index 2bb0ed5..27a0bd5 100644 --- a/river/main.zig +++ b/river/main.zig @@ -18,14 +18,13 @@ const build_options = @import("build_options"); const std = @import("std"); const mem = std.mem; const fs = std.fs; -const io = std.io; const log = std.log; const posix = std.posix; const builtin = @import("builtin"); const wlr = @import("wlroots"); const flags = @import("flags"); -const c = @import("c.zig"); +const c = @import("c.zig").c; const util = @import("util.zig"); const process = @import("process.zig"); @@ -52,21 +51,21 @@ pub fn main() anyerror!void { .{ .name = "log-level", .kind = .arg }, .{ .name = "no-xwayland", .kind = .boolean }, }).parse(std.os.argv[1..]) catch { - try io.getStdErr().writeAll(usage); + try fs.File.stderr().writeAll(usage); posix.exit(1); }; if (result.flags.h) { - try io.getStdOut().writeAll(usage); + try fs.File.stdout().writeAll(usage); posix.exit(0); } if (result.args.len != 0) { log.err("unknown option '{s}'", .{result.args[0]}); - try io.getStdErr().writeAll(usage); + try fs.File.stderr().writeAll(usage); posix.exit(1); } if (result.flags.version) { - try io.getStdOut().writeAll(build_options.version ++ "\n"); + try fs.File.stdout().writeAll(build_options.version ++ "\n"); posix.exit(0); } if (result.flags.@"log-level") |level| { @@ -80,7 +79,7 @@ pub fn main() anyerror!void { runtime_log_level = .debug; } else { log.err("invalid log level '{s}'", .{level}); - try io.getStdErr().writeAll(usage); + try fs.File.stderr().writeAll(usage); posix.exit(1); } } @@ -189,8 +188,8 @@ pub fn logFn( const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; - const stderr = io.getStdErr().writer(); - stderr.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {}; + var stderr = fs.File.stderr().writer(&.{}); + stderr.interface.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {}; } /// See wlroots_log_wrapper.c diff --git a/river/process.zig b/river/process.zig index fb160e9..7709a7d 100644 --- a/river/process.zig +++ b/river/process.zig @@ -17,7 +17,7 @@ const std = @import("std"); const posix = std.posix; -const c = @import("c.zig"); +const c = @import("c.zig").c; var original_rlimit: ?posix.rlimit = null; @@ -26,7 +26,7 @@ pub fn setup() void { // has had its read end closed by another process. const sig_ign = posix.Sigaction{ .handler = .{ .handler = posix.SIG.IGN }, - .mask = posix.empty_sigset, + .mask = posix.sigemptyset(), .flags = 0, }; posix.sigaction(posix.SIG.PIPE, &sig_ign, null); @@ -61,11 +61,11 @@ pub fn setup() void { pub fn cleanupChild() void { if (c.setsid() < 0) unreachable; - if (posix.system.sigprocmask(posix.SIG.SETMASK, &posix.empty_sigset, null) < 0) unreachable; + if (posix.system.sigprocmask(posix.SIG.SETMASK, &posix.sigemptyset(), null) < 0) unreachable; const sig_dfl = posix.Sigaction{ .handler = .{ .handler = posix.SIG.DFL }, - .mask = posix.empty_sigset, + .mask = posix.sigemptyset(), .flags = 0, }; posix.sigaction(posix.SIG.PIPE, &sig_dfl, null); diff --git a/riverctl/main.zig b/riverctl/main.zig index 06d083b..39abacf 100644 --- a/riverctl/main.zig +++ b/riverctl/main.zig @@ -16,7 +16,7 @@ const std = @import("std"); const mem = std.mem; -const io = std.io; +const fs = std.fs; const posix = std.posix; const assert = std.debug.assert; const builtin = @import("builtin"); @@ -73,15 +73,15 @@ fn _main() !void { .{ .name = "h", .kind = .boolean }, .{ .name = "version", .kind = .boolean }, }).parse(std.os.argv[1..]) catch { - try io.getStdErr().writeAll(usage); + try fs.File.stderr().writeAll(usage); posix.exit(1); }; if (result.flags.h) { - try io.getStdOut().writeAll(usage); + try fs.File.stdout().writeAll(usage); posix.exit(0); } if (result.flags.version) { - try io.getStdOut().writeAll(@import("build_options").version ++ "\n"); + try fs.File.stdout().writeAll(@import("build_options").version ++ "\n"); posix.exit(0); } @@ -125,8 +125,8 @@ fn callbackListener(_: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV switch (event) { .success => |success| { if (mem.len(success.output) > 0) { - const stdout = io.getStdOut().writer(); - stdout.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout"); + var stdout = fs.File.stdout().writer(&.{}); + stdout.interface.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout"); } posix.exit(0); }, @@ -134,7 +134,7 @@ fn callbackListener(_: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV // A small hack to provide usage text when river reports an unknown command. if (mem.orderZ(u8, failure.failure_message, "unknown command") == .eq) { std.log.err("unknown command", .{}); - io.getStdErr().writeAll(usage) catch {}; + fs.File.stderr().writeAll(usage) catch {}; posix.exit(1); } fatal("{s}", .{failure.failure_message}); diff --git a/rivertile/main.zig b/rivertile/main.zig index e4e11ee..972641e 100644 --- a/rivertile/main.zig +++ b/rivertile/main.zig @@ -91,15 +91,12 @@ const gpa = std.heap.c_allocator; const Context = struct { initialized: bool = false, layout_manager: ?*river.LayoutManagerV3 = null, - outputs: std.DoublyLinkedList(Output) = .{}, + outputs: wl.list.Head(Output, .link), fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void { const wl_output = try registry.bind(name, wl.Output, 3); errdefer wl_output.release(); - const node = try gpa.create(std.DoublyLinkedList(Output).Node); - errdefer gpa.destroy(node); - try node.data.init(context, wl_output, name); - context.outputs.append(node); + try Output.create(context, wl_output, name); } }; @@ -113,15 +110,21 @@ const Output = struct { layout: *river.LayoutV3 = undefined, - fn init(output: *Output, context: *Context, wl_output: *wl.Output, name: u32) !void { + link: wl.list.Link, + + fn create(context: *Context, wl_output: *wl.Output, name: u32) !void { + const output = try gpa.create(Output); + errdefer gpa.destroy(output); output.* = .{ .wl_output = wl_output, .name = name, .main_location = default_main_location, .main_count = default_main_count, .main_ratio = default_main_ratio, + .link = undefined, }; if (context.initialized) try output.getLayout(context); + context.outputs.append(output); } fn getLayout(output: *Output, context: *Context) !void { @@ -130,9 +133,11 @@ const Output = struct { output.layout.setListener(*Output, layoutListener, output); } - fn deinit(output: *Output) void { + fn destroy(output: *Output) void { output.wl_output.release(); output.layout.destroy(); + output.link.remove(); + gpa.destroy(output); } fn layoutListener(layout: *river.LayoutV3, event: river.LayoutV3.Event, output: *Output) void { @@ -312,17 +317,17 @@ pub fn main() !void { .{ .name = "main-count", .kind = .arg }, .{ .name = "main-ratio", .kind = .arg }, }).parse(std.os.argv[1..]) catch { - try std.io.getStdErr().writeAll(usage); + try std.fs.File.stderr().writeAll(usage); posix.exit(1); }; if (result.flags.h) { - try std.io.getStdOut().writeAll(usage); + try std.fs.File.stdout().writeAll(usage); posix.exit(0); } if (result.args.len != 0) fatalPrintUsage("unknown option '{s}'", .{result.args[0]}); if (result.flags.version) { - try std.io.getStdOut().writeAll(@import("build_options").version ++ "\n"); + try std.fs.File.stdout().writeAll(@import("build_options").version ++ "\n"); posix.exit(0); } if (result.flags.@"view-padding") |raw| { @@ -356,7 +361,10 @@ pub fn main() !void { }; defer display.disconnect(); - var context: Context = .{}; + var context: Context = .{ + .outputs = undefined, + }; + context.outputs.init(); const registry = try display.getRegistry(); registry.setListener(*Context, registryListener, &context); @@ -368,9 +376,8 @@ pub fn main() !void { context.initialized = true; - var it = context.outputs.first; - while (it) |node| : (it = node.next) { - const output = &node.data; + var it = context.outputs.iterator(.forward); + while (it.next()) |output| { try output.getLayout(&context); } @@ -389,13 +396,10 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: * } }, .global_remove => |ev| { - var it = context.outputs.first; - while (it) |node| : (it = node.next) { - const output = &node.data; + var it = context.outputs.safeIterator(.forward); + while (it.next()) |output| { if (output.name == ev.name) { - context.outputs.remove(node); - output.deinit(); - gpa.destroy(node); + output.destroy(); break; } } @@ -410,7 +414,7 @@ fn fatal(comptime format: []const u8, args: anytype) noreturn { fn fatalPrintUsage(comptime format: []const u8, args: anytype) noreturn { std.log.err(format, args); - std.io.getStdErr().writeAll(usage) catch {}; + std.fs.File.stderr().writeAll(usage) catch {}; posix.exit(1); }