This commit is contained in:
2025-08-31 15:54:01 -07:00
47 changed files with 375 additions and 326 deletions

View File

@ -37,10 +37,10 @@ tasks:
cd .. cd ..
# Eat Github's resources rather than the Zig Software Foundation's resources! # 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 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-linux-x86_64-0.14.0.tar.xz tar xf zig-x86_64-linux-0.15.1.tar.xz
sudo mv zig-linux-x86_64-0.14.0/zig /usr/bin/ sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/
sudo mv zig-linux-x86_64-0.14.0/lib /usr/lib/zig sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig
- build: | - build: |
cd river cd river
zig build --summary all zig build --summary all

View File

@ -34,10 +34,10 @@ tasks:
cd .. cd ..
# Eat Github's resources rather than the Zig Software Foundation's resources! # 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 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-linux-x86_64-0.14.0.tar.xz tar xf zig-x86_64-linux-0.15.1.tar.xz
sudo mv zig-linux-x86_64-0.14.0/zig /usr/bin/ sudo mv zig-x86_64-linux-0.15.1/zig /usr/bin/
sudo mv zig-linux-x86_64-0.14.0/lib /usr/lib/zig sudo mv zig-x86_64-linux-0.15.1/lib /usr/lib/zig
- build: | - build: |
cd river cd river
zig build --summary all zig build --summary all

View File

@ -41,10 +41,10 @@ tasks:
cd .. cd ..
# Eat Github's resources rather than the Zig Software Foundation's resources! # 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 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-freebsd-x86_64-0.14.0-unofficial.tar.xz tar xf zig-x86_64-freebsd-0.15.1.tar.xz
sudo mv zig-freebsd-x86_64-0.14.0-unofficial/zig /usr/bin/ sudo mv zig-x86_64-freebsd-0.15.1/zig /usr/bin/
sudo mv zig-freebsd-x86_64-0.14.0-unofficial/lib /usr/lib/zig sudo mv zig-x86_64-freebsd-0.15.1/lib /usr/lib/zig
- build: | - build: |
cd river cd river
zig build --summary all zig build --summary all

View File

@ -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 installed. The "development" versions are required if applicable to your
distribution. distribution.
- [zig](https://ziglang.org/download/) 0.14 - [zig](https://ziglang.org/download/) 0.15
- wayland - wayland
- wayland-protocols - wayland-protocols
- [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.19 - [wlroots](https://gitlab.freedesktop.org/wlroots/wlroots) 0.19

View File

@ -12,7 +12,6 @@ pub fn build(b: *Build) !void {
const strip = b.option(bool, "strip", "Omit debug information") orelse false; 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 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) { const omit_frame_pointer = switch (optimize) {
.Debug, .ReleaseSafe => false, .Debug, .ReleaseSafe => false,
@ -150,12 +149,12 @@ pub fn build(b: *Build) !void {
{ {
const river = b.addExecutable(.{ const river = b.addExecutable(.{
.name = "river", .name = "river",
.root_source_file = b.path("river/main.zig"), .root_module = b.createModule(.{
.target = target, .root_source_file = b.path("river/main.zig"),
.optimize = optimize, .target = target,
.strip = strip, .optimize = optimize,
.use_llvm = llvm, .strip = strip,
.use_lld = llvm, }),
}); });
river.root_module.addOptions("build_options", options); river.root_module.addOptions("build_options", options);
@ -188,12 +187,12 @@ pub fn build(b: *Build) !void {
{ {
const riverctl = b.addExecutable(.{ const riverctl = b.addExecutable(.{
.name = "riverctl", .name = "riverctl",
.root_source_file = b.path("riverctl/main.zig"), .root_module = b.createModule(.{
.target = target, .root_source_file = b.path("riverctl/main.zig"),
.optimize = optimize, .target = target,
.strip = strip, .optimize = optimize,
.use_llvm = llvm, .strip = strip,
.use_lld = llvm, }),
}); });
riverctl.root_module.addOptions("build_options", options); riverctl.root_module.addOptions("build_options", options);
@ -211,12 +210,12 @@ pub fn build(b: *Build) !void {
{ {
const rivertile = b.addExecutable(.{ const rivertile = b.addExecutable(.{
.name = "rivertile", .name = "rivertile",
.root_source_file = b.path("rivertile/main.zig"), .root_module = b.createModule(.{
.target = target, .root_source_file = b.path("rivertile/main.zig"),
.optimize = optimize, .target = target,
.strip = strip, .optimize = optimize,
.use_llvm = llvm, .strip = strip,
.use_lld = llvm, }),
}); });
rivertile.root_module.addOptions("build_options", options); rivertile.root_module.addOptions("build_options", options);
@ -275,9 +274,11 @@ pub fn build(b: *Build) !void {
{ {
const globber_test = b.addTest(.{ const globber_test = b.addTest(.{
.root_source_file = b.path("common/globber.zig"), .root_module = b.createModule(.{
.target = target, .root_source_file = b.path("common/globber.zig"),
.optimize = optimize, .target = target,
.optimize = optimize,
}),
}); });
const run_globber_test = b.addRunArtifact(globber_test); const run_globber_test = b.addRunArtifact(globber_test);

View File

@ -5,7 +5,7 @@
// When a release is tagged, the "-dev" suffix should be removed for the // When a release is tagged, the "-dev" suffix should be removed for the
// commit that gets tagged. Directly after the tagged commit, the version // commit that gets tagged. Directly after the tagged commit, the version
// should be bumped and the "-dev" suffix added. // should be bumped and the "-dev" suffix added.
.version = "0.3.12-dev", .version = "0.3.12",
.paths = .{""}, .paths = .{""},
.dependencies = .{ .dependencies = .{
.pixman = .{ .pixman = .{
@ -13,12 +13,12 @@
.hash = "pixman-0.3.0-LClMnz2VAAAs7QSCGwLimV5VUYx0JFnX5xWU6HwtMuDX", .hash = "pixman-0.3.0-LClMnz2VAAAs7QSCGwLimV5VUYx0JFnX5xWU6HwtMuDX",
}, },
.wayland = .{ .wayland = .{
.url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.3.0.tar.gz", .url = "https://codeberg.org/ifreund/zig-wayland/archive/v0.4.0.tar.gz",
.hash = "wayland-0.3.0-lQa1kjPIAQDmhGYpY-zxiRzQJFHQ2VqhJkQLbKKdt5wl", .hash = "wayland-0.4.0-lQa1khbMAQAsLS2eBR7M5lofyEGPIbu2iFDmoz8lPC27",
}, },
.wlroots = .{ .wlroots = .{
.url = "https://codeberg.org/ifreund/zig-wlroots/archive/f92ba27133ecf702d85c9d3894f98a336389bbd9.tar.gz", .url = "https://codeberg.org/ifreund/zig-wlroots/archive/v0.19.3.tar.gz",
.hash = "wlroots-0.19.3-dev-jmOlcr7_AwClfjFwW8oOkWoqAbt9oPLqgdvfFYEXqlOF", .hash = "wlroots-0.19.3-jmOlcuL_AwBHhLCwpFsXbTizE3q9BugFmGX-XIxqcPMc",
}, },
.xkbcommon = .{ .xkbcommon = .{
.url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.3.0.tar.gz", .url = "https://codeberg.org/ifreund/zig-xkbcommon/archive/v0.3.0.tar.gz",

View File

@ -84,7 +84,7 @@ fn handleRequest(control_v1: *zriver.ControlV1, request: zriver.ControlV1.Reques
}; };
}, },
.run_command => |run_command| { .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( const callback = zriver.CommandCallbackV1.create(
control_v1.getClient(), control_v1.getClient(),

View File

@ -26,7 +26,7 @@ const wayland = @import("wayland");
const wl = wayland.server.wl; const wl = wayland.server.wl;
const zwlr = wayland.server.zwlr; const zwlr = wayland.server.zwlr;
const c = @import("c.zig"); const c = @import("c.zig").c;
const server = &@import("main.zig").server; const server = &@import("main.zig").server;
const util = @import("util.zig"); 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 // If this cursor belongs to the default seat, set the xcursor environment
// variables as well as the xwayland cursor theme. // variables as well as the xwayland cursor theme.
if (cursor.seat == server.input_manager.defaultSeat()) { 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); defer util.gpa.free(size_str);
if (c.setenv("XCURSOR_SIZE", size_str.ptr, 1) < 0) return error.OutOfMemory; 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; 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 /// Axis event is a scroll wheel or similiar
fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Pointer.event.Axis) void { fn handleAxis(listener: *wl.Listener(*wlr.Pointer.event.Axis), event: *wlr.Pointer.event.Axis) void {
const cursor: *Cursor = @fieldParentPtr("axis", listener); 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.seat.handleActivity();
cursor.unhide(); 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 // @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 // 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. // allow users to set their scroll-factor to inf without crashing river.
math.minInt(i32) / 2, @as(f32, @floatFromInt(math.minInt(i32) / 2)),
math.maxInt(i32) / 2, @as(f32, @floatFromInt(math.maxInt(i32) / 2)),
)), )),
event.source, event.source,
event.relative_direction, event.relative_direction,
@ -482,7 +482,7 @@ fn updateKeyboardFocus(cursor: Cursor, result: Root.AtResult) void {
/// Requires a call to Root.applyPending() /// Requires a call to Root.applyPending()
fn updateOutputFocus(cursor: Cursor, lx: f64, ly: f64) void { fn updateOutputFocus(cursor: Cursor, lx: f64, ly: f64) void {
if (server.root.output_layout.outputAt(lx, ly)) |wlr_output| { 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); cursor.seat.focusOutput(output);
} }
} }
@ -659,7 +659,7 @@ fn handleTabletToolAxis(
_: *wl.Listener(*wlr.Tablet.event.Axis), _: *wl.Listener(*wlr.Tablet.event.Axis),
event: *wlr.Tablet.event.Axis, event: *wlr.Tablet.event.Axis,
) void { ) void {
const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); const device: *InputDevice = @ptrCast(@alignCast(event.device.data));
const tablet: *Tablet = @fieldParentPtr("device", device); const tablet: *Tablet = @fieldParentPtr("device", device);
device.seat.handleActivity(); device.seat.handleActivity();
@ -673,7 +673,7 @@ fn handleTabletToolProximity(
_: *wl.Listener(*wlr.Tablet.event.Proximity), _: *wl.Listener(*wlr.Tablet.event.Proximity),
event: *wlr.Tablet.event.Proximity, event: *wlr.Tablet.event.Proximity,
) void { ) void {
const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); const device: *InputDevice = @ptrCast(@alignCast(event.device.data));
const tablet: *Tablet = @fieldParentPtr("device", device); const tablet: *Tablet = @fieldParentPtr("device", device);
device.seat.handleActivity(); device.seat.handleActivity();
@ -687,7 +687,7 @@ fn handleTabletToolTip(
_: *wl.Listener(*wlr.Tablet.event.Tip), _: *wl.Listener(*wlr.Tablet.event.Tip),
event: *wlr.Tablet.event.Tip, event: *wlr.Tablet.event.Tip,
) void { ) void {
const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); const device: *InputDevice = @ptrCast(@alignCast(event.device.data));
const tablet: *Tablet = @fieldParentPtr("device", device); const tablet: *Tablet = @fieldParentPtr("device", device);
device.seat.handleActivity(); device.seat.handleActivity();
@ -701,7 +701,7 @@ fn handleTabletToolButton(
_: *wl.Listener(*wlr.Tablet.event.Button), _: *wl.Listener(*wlr.Tablet.event.Button),
event: *wlr.Tablet.event.Button, event: *wlr.Tablet.event.Button,
) void { ) void {
const device: *InputDevice = @alignCast(@ptrCast(event.device.data)); const device: *InputDevice = @ptrCast(@alignCast(event.device.data));
const tablet: *Tablet = @fieldParentPtr("device", device); const tablet: *Tablet = @fieldParentPtr("device", device);
device.seat.handleActivity(); device.seat.handleActivity();
@ -822,6 +822,7 @@ pub fn hide(cursor: *Cursor) void {
cursor.hidden = true; cursor.hidden = true;
cursor.wlr_cursor.unsetImage(); cursor.wlr_cursor.unsetImage();
cursor.seat.wlr_seat.pointerNotifyClearFocus();
cursor.hide_cursor_timer.timerUpdate(0) catch { cursor.hide_cursor_timer.timerUpdate(0) catch {
log.err("failed to update cursor hide timeout", .{}); log.err("failed to update cursor hide timeout", .{});
}; };
@ -1300,7 +1301,7 @@ fn warp(cursor: *Cursor) void {
fn updateDragIcons(cursor: *Cursor) void { fn updateDragIcons(cursor: *Cursor) void {
var it = server.root.drag_icons.children.iterator(.forward); var it = server.root.drag_icons.children.iterator(.forward);
while (it.next()) |node| { 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) { if (icon.wlr_drag_icon.drag.seat == cursor.seat.wlr_seat) {
icon.updatePosition(cursor); icon.updatePosition(cursor);

View File

@ -89,7 +89,7 @@ fn handleForeignActivate(
) void { ) void {
const handle: *ForeignToplevelHandle = @fieldParentPtr("foreign_activate", listener); const handle: *ForeignToplevelHandle = @fieldParentPtr("foreign_activate", listener);
const view: *View = @fieldParentPtr("foreign_toplevel_handle", handle); 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); seat.focus(view);
server.root.applyPending(); server.root.applyPending();

View File

@ -30,28 +30,29 @@ const View = @import("View.zig");
wlr_manager: *wlr.IdleInhibitManagerV1, wlr_manager: *wlr.IdleInhibitManagerV1,
new_idle_inhibitor: wl.Listener(*wlr.IdleInhibitorV1) = new_idle_inhibitor: wl.Listener(*wlr.IdleInhibitorV1) =
wl.Listener(*wlr.IdleInhibitorV1).init(handleNewIdleInhibitor), wl.Listener(*wlr.IdleInhibitorV1).init(handleNewIdleInhibitor),
inhibitors: std.DoublyLinkedList(IdleInhibitor) = .{}, inhibitors: wl.list.Head(IdleInhibitor, .link),
pub fn init(inhibit_manager: *IdleInhibitManager) !void { pub fn init(inhibit_manager: *IdleInhibitManager) !void {
inhibit_manager.* = .{ inhibit_manager.* = .{
.wlr_manager = try wlr.IdleInhibitManagerV1.create(server.wl_server), .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); inhibit_manager.wlr_manager.events.new_inhibitor.add(&inhibit_manager.new_idle_inhibitor);
} }
pub fn deinit(inhibit_manager: *IdleInhibitManager) void { pub fn deinit(inhibit_manager: *IdleInhibitManager) void {
while (inhibit_manager.inhibitors.pop()) |inhibitor| { while (inhibit_manager.inhibitors.first()) |inhibitor| {
inhibitor.data.destroy.link.remove(); inhibitor.destroy();
util.gpa.destroy(inhibitor);
} }
inhibit_manager.new_idle_inhibitor.link.remove(); inhibit_manager.new_idle_inhibitor.link.remove();
} }
pub fn checkActive(inhibit_manager: *IdleInhibitManager) void { pub fn checkActive(inhibit_manager: *IdleInhibitManager) void {
var inhibited = false; var inhibited = false;
var it = inhibit_manager.inhibitors.first; var it = inhibit_manager.inhibitors.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |inhibitor| {
const node_data = SceneNodeData.fromSurface(node.data.wlr_inhibitor.surface) orelse continue; const node_data = SceneNodeData.fromSurface(inhibitor.wlr_inhibitor.surface) orelse continue;
switch (node_data.data) { switch (node_data.data) {
.view => |view| { .view => |view| {
if (view.current.output != null and 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 { fn handleNewIdleInhibitor(listener: *wl.Listener(*wlr.IdleInhibitorV1), inhibitor: *wlr.IdleInhibitorV1) void {
const inhibit_manager: *IdleInhibitManager = @fieldParentPtr("new_idle_inhibitor", listener); const inhibit_manager: *IdleInhibitManager = @fieldParentPtr("new_idle_inhibitor", listener);
const inhibitor_node = util.gpa.create(std.DoublyLinkedList(IdleInhibitor).Node) catch return; IdleInhibitor.create(inhibitor, inhibit_manager) catch {
inhibitor_node.data.init(inhibitor, inhibit_manager) catch { std.log.err("out of memory", .{});
util.gpa.destroy(inhibitor_node);
return; return;
}; };
inhibit_manager.inhibitors.append(inhibitor_node);
inhibit_manager.checkActive();
} }

View File

@ -28,31 +28,38 @@ const IdleInhibitManager = @import("IdleInhibitManager.zig");
inhibit_manager: *IdleInhibitManager, inhibit_manager: *IdleInhibitManager,
wlr_inhibitor: *wlr.IdleInhibitorV1, 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.* = .{ inhibitor.* = .{
.inhibit_manager = inhibit_manager, .inhibit_manager = inhibit_manager,
.wlr_inhibitor = wlr_inhibitor, .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(); inhibit_manager.checkActive();
} }
fn handleDestroy(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { pub fn destroy(inhibitor: *IdleInhibitor) void {
const inhibitor: *IdleInhibitor = @fieldParentPtr("destroy", listener); inhibitor.listen_destroy.link.remove();
inhibitor.destroy.link.remove(); inhibitor.link.remove();
const node: *std.DoublyLinkedList(IdleInhibitor).Node = @fieldParentPtr("data", inhibitor);
server.idle_inhibit_manager.inhibitors.remove(node);
inhibitor.inhibit_manager.checkActive(); 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();
} }

View File

@ -25,7 +25,7 @@ const wlr = @import("wlroots");
const log = std.log.scoped(.input_config); 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 server = &@import("main.zig").server;
const util = @import("util.zig"); const util = @import("util.zig");
@ -373,7 +373,7 @@ pub fn parse(config: *InputConfig, setting: []const u8, value: []const u8) !void
return error.UnknownCommand; 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}); try writer.print("{s}\n", .{config.glob});
inline for (@typeInfo(InputConfig).@"struct".fields) |field| { inline for (@typeInfo(InputConfig).@"struct".fields) |field| {

View File

@ -24,7 +24,7 @@ const wl = @import("wayland").server.wl;
const globber = @import("globber"); const globber = @import("globber");
const c = @import("c.zig"); const c = @import("c.zig").c;
const server = &@import("main.zig").server; const server = &@import("main.zig").server;
const util = @import("util.zig"); const util = @import("util.zig");

View File

@ -55,10 +55,10 @@ tablet_manager: *wlr.TabletManagerV2,
/// List of input device configurations. Ordered by glob generality, with /// List of input device configurations. Ordered by glob generality, with
/// the most general towards the start and the most specific towards the end. /// 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), devices: wl.list.Head(InputDevice, .link),
seats: std.DoublyLinkedList(Seat) = .{}, seats: wl.list.Head(Seat, .link),
exclusive_client: ?*wl.Client = null, exclusive_client: ?*wl.Client = null,
@ -74,9 +74,6 @@ new_text_input: wl.Listener(*wlr.TextInputV3) =
wl.Listener(*wlr.TextInputV3).init(handleNewTextInput), wl.Listener(*wlr.TextInputV3).init(handleNewTextInput),
pub fn init(input_manager: *InputManager) !void { 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.* = .{ input_manager.* = .{
// These are automatically freed when the display is destroyed // These are automatically freed when the display is destroyed
.idle_notifier = try wlr.IdleNotifierV1.create(server.wl_server), .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), .input_method_manager = try wlr.InputMethodManagerV2.create(server.wl_server),
.text_input_manager = try wlr.TextInputManagerV3.create(server.wl_server), .text_input_manager = try wlr.TextInputManagerV3.create(server.wl_server),
.tablet_manager = try wlr.TabletManagerV2.create(server.wl_server), .tablet_manager = try wlr.TabletManagerV2.create(server.wl_server),
.configs = std.ArrayList(InputConfig).init(util.gpa),
.seats = undefined,
.devices = undefined, .devices = undefined,
}; };
input_manager.seats.init();
input_manager.devices.init(); input_manager.devices.init();
input_manager.seats.prepend(seat_node); try Seat.create(default_seat_name);
try seat_node.data.init(default_seat_name);
if (build_options.xwayland) { if (build_options.xwayland) {
if (server.xwayland) |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_input_method.link.remove();
input_manager.new_text_input.link.remove(); input_manager.new_text_input.link.remove();
while (input_manager.seats.pop()) |seat_node| { while (input_manager.seats.first()) |seat| {
seat_node.data.deinit(); seat.destroy();
util.gpa.destroy(seat_node);
} }
for (input_manager.configs.items) |*config| { for (input_manager.configs.items) |*config| {
config.deinit(); config.deinit();
} }
input_manager.configs.deinit(); input_manager.configs.deinit(util.gpa);
} }
pub fn defaultSeat(input_manager: InputManager) *Seat { pub fn defaultSeat(input_manager: *InputManager) *Seat {
return &input_manager.seats.first.?.data; return input_manager.seats.first().?;
} }
/// Returns true if input is currently allowed on the passed surface. /// 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); 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); seat.addDevice(&virtual_keyboard.keyboard.base, true);
} }
}; };
@ -236,7 +232,7 @@ fn handleNewConstraint(
} }
fn handleNewInputMethod(_: *wl.Listener(*wlr.InputMethodV2), input_method: *wlr.InputMethodV2) void { 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}); log.debug("new input method on seat {s}", .{seat.wlr_seat.name});

View File

@ -57,33 +57,52 @@ pub const Pressed = struct {
assert(capacity == @typeInfo(std.meta.fieldInfo(wlr.Keyboard, .keycodes).type).array.len); 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 { fn contains(pressed: *Pressed, code: u32) bool {
for (pressed.keys.constSlice()) |item| { for (pressed.slice()) |item| {
if (item.code == code) return true; if (item.code == code) return true;
} }
return false; return false;
} }
fn addAssumeCapacity(pressed: *Pressed, new: Key) void { fn addAssumeCapacity(pressed: *Pressed, new: Key) void {
assert(pressed.len < pressed.keys.len);
assert(!pressed.contains(new.code)); assert(!pressed.contains(new.code));
pressed.keys.appendAssumeCapacity(new); pressed.keys[pressed.len] = new;
pressed.len += 1;
} }
fn remove(pressed: *Pressed, code: u32) ?KeyConsumer { fn remove(pressed: *Pressed, code: u32) ?KeyConsumer {
for (pressed.keys.constSlice(), 0..) |item, idx| { for (pressed.slice(), 0..) |item, idx| {
if (item.code == code) return pressed.keys.swapRemove(idx).consumer; if (item.code == code) return pressed.swapRemove(idx).consumer;
} }
return null; 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, device: InputDevice,
/// Pressed keys along with where their press event has been sent /// 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), 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), 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). // 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 // 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. // 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)) { if (keyboard.device.seat.handleMapping(keycode, modifiers, released, xkb_state)) {
break :blk .mapping; break :blk .mapping;

View File

@ -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), new_popup: wl.Listener(*wlr.XdgPopup) = wl.Listener(*wlr.XdgPopup).init(handleNewPopup),
pub fn create(wlr_layer_surface: *wlr.LayerSurfaceV1) error{OutOfMemory}!void { 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); const layer_surface = try util.gpa.create(LayerSurface);
errdefer util.gpa.destroy(layer_surface); errdefer util.gpa.destroy(layer_surface);
@ -157,7 +157,7 @@ fn handleKeyboardInteractiveExclusive(output: *Output, consider: ?*LayerSurface)
var it = tree.children.iterator(.reverse); var it = tree.children.iterator(.reverse);
while (it.next()) |node| { while (it.next()) |node| {
assert(node.type == .tree); 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 layer_surface = node_data.data.layer_surface;
const wlr_layer_surface = layer_surface.wlr_layer_surface; const wlr_layer_surface = layer_surface.wlr_layer_surface;
if (wlr_layer_surface.surface.mapped and 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); assert(s.wlr_layer_surface.current.keyboard_interactive != .none);
} }
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| {
const seat = &node.data;
if (seat.focused_output == output) { if (seat.focused_output == output) {
if (to_focus) |s| { if (to_focus) |s| {
// If we found a surface on the output that requires focus, grab the focus of all // If we found a surface on the output that requires focus, grab the focus of all

View File

@ -38,6 +38,9 @@ layout_v3: *river.LayoutV3,
namespace: []const u8, namespace: []const u8,
output: *Output, output: *Output,
// Output.layouts
link: wl.list.Link,
pub fn create(client: *wl.Client, version: u32, id: u32, output: *Output, namespace: []const u8) !void { 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); 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; return;
} }
const node = try util.gpa.create(std.DoublyLinkedList(Layout).Node); const layout = try util.gpa.create(Layout);
errdefer util.gpa.destroy(node); errdefer util.gpa.destroy(layout);
node.data = .{ layout.* = .{
.layout_v3 = layout_v3, .layout_v3 = layout_v3,
.namespace = try util.gpa.dupe(u8, namespace), .namespace = try util.gpa.dupe(u8, namespace),
.output = output, .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 // If the namespace matches that of the output, set the layout as
// the active one of the output and arrange it. // the active one of the output and arrange it.
if (mem.eql(u8, namespace, output.layoutNamespace())) { if (mem.eql(u8, namespace, output.layoutNamespace())) {
output.layout = &node.data; output.layout = layout;
server.root.applyPending(); 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 { fn namespaceInUse(namespace: []const u8, output: *Output, client: *wl.Client) bool {
var output_it = server.root.active_outputs.iterator(.forward); var output_it = server.root.active_outputs.iterator(.forward);
while (output_it.next()) |o| { while (output_it.next()) |o| {
var layout_it = output.layouts.first; var layout_it = output.layouts.iterator(.forward);
if (o == output) { if (o == output) {
// On this output, no other layout can have our namespace. // On this output, no other layout can have our namespace.
while (layout_it) |layout_node| : (layout_it = layout_node.next) { while (layout_it.next()) |layout| {
if (mem.eql(u8, namespace, layout_node.data.namespace)) return true; if (mem.eql(u8, namespace, layout.namespace)) return true;
} }
} else { } else {
// Layouts on other outputs may share the namespace, if they come from the same client. // 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) { while (layout_it.next()) |layout| {
if (mem.eql(u8, namespace, layout_node.data.namespace) and if (mem.eql(u8, namespace, layout.namespace) and
client != layout_node.data.layout_v3.getClient()) return true; client != layout.layout_v3.getClient()) return true;
} }
} }
} }
@ -185,9 +189,7 @@ pub fn destroy(layout: *Layout) void {
.{ layout.namespace, layout.output.wlr_output.name }, .{ layout.namespace, layout.output.wlr_output.name },
); );
// Remove layout from the list layout.link.remove();
const node: *std.DoublyLinkedList(Layout).Node = @fieldParentPtr("data", layout);
layout.output.layouts.remove(node);
// If we are the currently active layout of an output, clean up. // If we are the currently active layout of an output, clean up.
if (layout.output.layout == layout) { if (layout.output.layout == layout) {
@ -208,5 +210,5 @@ pub fn destroy(layout: *Layout) void {
layout.layout_v3.setHandler(?*anyopaque, handleRequestInert, null, null); layout.layout_v3.setHandler(?*anyopaque, handleRequestInert, null, null);
util.gpa.free(layout.namespace); util.gpa.free(layout.namespace);
util.gpa.destroy(node); util.gpa.destroy(layout);
} }

View File

@ -68,7 +68,7 @@ fn handleRequest(
.get_layout => |req| { .get_layout => |req| {
// Ignore if the output is inert // Ignore if the output is inert
const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return; 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 }); log.debug("bind layout '{s}' on output '{s}'", .{ req.namespace, output.wlr_output.name });

View File

@ -110,9 +110,8 @@ fn handleLock(listener: *wl.Listener(*wlr.SessionLockV1), lock: *wlr.SessionLock
}; };
{ {
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| {
const seat = &node.data;
seat.setFocusRaw(.none); seat.setFocusRaw(.none);
// Enter locked mode // Enter locked mode
@ -213,9 +212,8 @@ fn handleUnlock(listener: *wl.Listener(void)) void {
} }
{ {
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| {
const seat = &node.data;
seat.setFocusRaw(.none); seat.setFocusRaw(.none);
// Exit locked mode // Exit locked mode
@ -266,7 +264,7 @@ pub fn updateLockSurfaceSize(manager: *LockManager, output: *Output) void {
var it = lock.surfaces.iterator(.forward); var it = lock.surfaces.iterator(.forward);
while (it.next()) |wlr_lock_surface| { 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()) { if (output == lock_surface.getOutput()) {
lock_surface.configure(); lock_surface.configure();
} }

View File

@ -65,12 +65,11 @@ pub fn destroy(lock_surface: *LockSurface) void {
var surface_it = lock_surface.lock.surfaces.iterator(.forward); var surface_it = lock_surface.lock.surfaces.iterator(.forward);
const new_focus: Seat.FocusTarget = while (surface_it.next()) |surface| { const new_focus: Seat.FocusTarget = while (surface_it.next()) |surface| {
if (surface != lock_surface.wlr_lock_surface) if (surface != lock_surface.wlr_lock_surface)
break .{ .lock_surface = @alignCast(@ptrCast(surface.data)) }; break .{ .lock_surface = @ptrCast(@alignCast(surface.data)) };
} else .none; } else .none;
var seat_it = server.input_manager.seats.first; var seat_it = server.input_manager.seats.iterator(.forward);
while (seat_it) |node| : (seat_it = node.next) { while (seat_it.next()) |seat| {
const seat = &node.data;
if (seat.focused == .lock_surface and seat.focused.lock_surface == lock_surface) { if (seat.focused == .lock_surface and seat.focused.lock_surface == lock_surface) {
seat.setFocusRaw(new_focus); seat.setFocusRaw(new_focus);
} }
@ -92,7 +91,7 @@ pub fn destroy(lock_surface: *LockSurface) void {
} }
pub fn getOutput(lock_surface: *LockSurface) *Output { 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 { pub fn configure(lock_surface: *LockSurface) void {
@ -122,9 +121,8 @@ fn handleMap(listener: *wl.Listener(void)) void {
} }
fn updateFocus(lock_surface: *LockSurface) void { fn updateFocus(lock_surface: *LockSurface) void {
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| {
const seat = &node.data;
if (seat.focused != .lock_surface) { if (seat.focused != .lock_surface) {
seat.setFocusRaw(.{ .lock_surface = lock_surface }); seat.setFocusRaw(.{ .lock_surface = lock_surface });
} }

View File

@ -167,7 +167,7 @@ previous_tags: u32 = 1 << 0,
attach_mode: ?Config.AttachMode = null, attach_mode: ?Config.AttachMode = null,
/// List of all layouts /// List of all layouts
layouts: std.DoublyLinkedList(Layout) = .{}, layouts: wl.list.Head(Layout, .link),
/// The current layout namespace of the output. If null, /// The current layout namespace of the output. If null,
/// config.default_layout_namespace should be used instead. /// config.default_layout_namespace should be used instead.
@ -290,9 +290,12 @@ pub fn create(wlr_output: *wlr.Output) !void {
.height = height, .height = height,
}, },
.status = undefined, .status = undefined,
.layouts = undefined,
}; };
wlr_output.data = output; wlr_output.data = output;
output.layouts.init();
output.pending.focus_stack.init(); output.pending.focus_stack.init();
output.pending.wm_stack.init(); output.pending.wm_stack.init();
output.inflight.focus_stack.init(); output.inflight.focus_stack.init();
@ -360,7 +363,7 @@ fn sendLayerConfigures(
var it = tree.children.safeIterator(.forward); var it = tree.children.safeIterator(.forward);
while (it.next()) |node| { while (it.next()) |node| {
assert(node.type == .tree); 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 layer_surface = node_data.data.layer_surface;
if (!layer_surface.wlr_layer_surface.initialized) continue; 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.focus_stack.empty());
assert(output.inflight.wm_stack.empty()); assert(output.inflight.wm_stack.empty());
assert(output.inflight.layout_demand == null); assert(output.inflight.layout_demand == null);
assert(output.layouts.len == 0); assert(output.layouts.length() == 0);
output.all_link.remove(); 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. // TODO double buffer output state changes for frame perfection and cleaner code.
// Schedule a frame and commit in the frame handler. // Schedule a frame and commit in the frame handler.
// Get rid of this function. // 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 // We need to be precise about this state change to make assertions
// in updateLockRenderStateOnEnableDisable() possible. // in updateLockRenderStateOnEnableDisable() possible.
@ -619,7 +622,7 @@ fn handlePresent(
} }
fn setTitle(output: Output) void { 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); defer util.gpa.free(title);
if (output.wlr_output.isWl()) { if (output.wlr_output.isWl()) {
output.wlr_output.wlSetTitle(title); output.wlr_output.wlSetTitle(title);
@ -631,9 +634,9 @@ fn setTitle(output: Output) void {
pub fn handleLayoutNamespaceChange(output: *Output) void { pub fn handleLayoutNamespaceChange(output: *Output) void {
// The user changed the layout namespace of this output. Try to find a // The user changed the layout namespace of this output. Try to find a
// matching layout. // matching layout.
var it = output.layouts.first; var it = output.layouts.iterator(.forward);
output.layout = while (it) |node| : (it = node.next) { output.layout = while (it.next()) |layout| {
if (mem.eql(u8, output.layoutNamespace(), node.data.namespace)) break &node.data; if (mem.eql(u8, output.layoutNamespace(), layout.namespace)) break layout;
} else null; } else null;
server.root.applyPending(); server.root.applyPending();
} }

View File

@ -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), node_destroy: wl.Listener(void) = wl.Listener(void).init(handleNodeDestroy),
pub fn create(wlr_constraint: *wlr.PointerConstraintV1) error{OutOfMemory}!void { 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); const constraint = try util.gpa.create(PointerConstraint);
errdefer util.gpa.destroy(constraint); 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 { 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); 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 /// Called when the cursor position or content in the scene graph changes
pub fn updateState(constraint: *PointerConstraint) void { 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(); constraint.maybeActivate();
@ -154,7 +154,7 @@ pub fn confine(constraint: *PointerConstraint, dx: *f64, dy: *f64) void {
} }
pub fn deactivate(constraint: *PointerConstraint) 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(seat.cursor.constraint == constraint);
assert(constraint.state == .active); assert(constraint.state == .active);
@ -167,7 +167,7 @@ pub fn deactivate(constraint: *PointerConstraint) void {
} }
fn warpToHintIfSet(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) { if (constraint.wlr_constraint.current.cursor_hint.enabled) {
var lx: i32 = undefined; 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 { fn handleDestroy(listener: *wl.Listener(*wlr.PointerConstraintV1), _: *wlr.PointerConstraintV1) void {
const constraint: *PointerConstraint = @fieldParentPtr("destroy", listener); 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) { if (constraint.state == .active) {
// We can't simply call deactivate() here as it calls sendDeactivated(), // 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. // the surface changes.
fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void { fn handleCommit(listener: *wl.Listener(*wlr.Surface), _: *wlr.Surface) void {
const constraint: *PointerConstraint = @fieldParentPtr("commit", listener); 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) { switch (constraint.state) {
.active => |state| { .active => |state| {

View File

@ -330,16 +330,15 @@ pub fn deactivateOutput(root: *Root, output: *Output) void {
var it = tree.children.safeIterator(.forward); var it = tree.children.safeIterator(.forward);
while (it.next()) |scene_node| { while (it.next()) |scene_node| {
assert(scene_node.type == .tree); 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(); node_data.data.layer_surface.wlr_layer_surface.destroy();
} }
} }
} }
// If any seat has the removed output focused, focus the fallback one // If any seat has the removed output focused, focus the fallback one
var seat_it = server.input_manager.seats.first; var seat_it = server.input_manager.seats.iterator(.forward);
while (seat_it) |seat_node| : (seat_it = seat_node.next) { while (seat_it.next()) |seat| {
const seat = &seat_node.data;
if (seat.focused_output == output) { if (seat.focused_output == output) {
seat.focusOutput(fallback_output); seat.focusOutput(fallback_output);
} }
@ -353,7 +352,7 @@ pub fn deactivateOutput(root: *Root, output: *Output) void {
output.inflight.layout_demand = null; output.inflight.layout_demand = null;
root.notifyLayoutDemandDone(); 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 // We must call reconfigureDevices here to unmap devices that might be mapped to this output
// in order to prevent a segfault in wlroots. // 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 // Focus the new output with all seats
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |seat_node| : (it = seat_node.next) { while (it.next()) |seat| {
const seat = &seat_node.data;
seat.focusOutput(output); seat.focusOutput(output);
} }
} }
@ -435,8 +433,8 @@ pub fn applyPending(root: *Root) void {
// state consistent. Instead of having focus(null) calls spread all // state consistent. Instead of having focus(null) calls spread all
// around the codebase and risk forgetting one, always ensure focus // around the codebase and risk forgetting one, always ensure focus
// state is synchronized here. // state is synchronized here.
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) node.data.focus(null); while (it.next()) |seat| seat.focus(null);
} }
// If there is already a transaction inflight, wait until it completes. // 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; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| {
const cursor = &node.data.cursor; switch (seat.cursor.mode) {
switch (cursor.mode) {
.passthrough, .down => {}, .passthrough, .down => {},
inline .move, .resize => |data| { inline .move, .resize => |data| {
if (data.view.inflight.output == null or 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.float and data.view.inflight.output.?.layout != null) or
data.view.inflight.fullscreen) data.view.inflight.fullscreen)
{ {
cursor.mode = .passthrough; seat.cursor.mode = .passthrough;
data.view.pending.resizing = false; data.view.pending.resizing = false;
data.view.inflight.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; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| seat.cursor.updateState();
node.data.cursor.updateState();
node.data.sendFocusedView();
}
} }
{ {
@ -805,7 +798,7 @@ fn processOutputConfig(
var it = config.heads.iterator(.forward); var it = config.heads.iterator(.forward);
while (it.next()) |head| { while (it.next()) |head| {
const wlr_output = head.state.output; 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(); var proposed_state = wlr.Output.State.init();
head.state.apply(&proposed_state); head.state.apply(&proposed_state);
@ -857,7 +850,7 @@ fn handlePowerManagerSetMode(
event: *wlr.OutputPowerManagerV1.event.SetMode, event: *wlr.OutputPowerManagerV1.event.SetMode,
) void { ) void {
// The output may have been destroyed, in which case there is nothing to do // 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}", .{ std.log.debug("client requested dpms {s} for output {s}", .{
@tagName(event.mode), @tagName(event.mode),

View File

@ -54,7 +54,7 @@ pub fn attach(node: *wlr.SceneNode, data: Data) error{OutOfMemory}!void {
pub fn fromNode(node: *wlr.SceneNode) ?*SceneNodeData { pub fn fromNode(node: *wlr.SceneNode) ?*SceneNodeData {
var n = node; var n = node;
while (true) { 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; return scene_node_data;
} }
if (n.parent) |parent_tree| { if (n.parent) |parent_tree| {
@ -66,7 +66,7 @@ pub fn fromNode(node: *wlr.SceneNode) ?*SceneNodeData {
} }
pub fn fromSurface(surface: *wlr.Surface) ?*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 fromNode(node);
} }
return null; return null;

View File

@ -91,7 +91,7 @@ focused_output: ?*Output = null,
focused: FocusTarget = .none, focused: FocusTarget = .none,
/// List of status tracking objects relaying changes to this seat to clients. /// 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. /// The currently in progress drag operation type.
drag: enum { 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) = request_set_primary_selection: wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection) =
wl.Listener(*wlr.Seat.event.RequestSetPrimarySelection).init(handleRequestSetPrimarySelection), 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 event_loop = server.wl_server.getEventLoop();
const mapping_repeat_timer = try event_loop.addTimer(*Seat, handleMappingRepeatTimeout, seat); const mapping_repeat_timer = try event_loop.addTimer(*Seat, handleMappingRepeatTimeout, seat);
errdefer mapping_repeat_timer.remove(); errdefer mapping_repeat_timer.remove();
@ -121,9 +127,14 @@ pub fn init(seat: *Seat, name: [*:0]const u8) !void {
.relay = undefined, .relay = undefined,
.mapping_repeat_timer = mapping_repeat_timer, .mapping_repeat_timer = mapping_repeat_timer,
.keyboard_group = try wlr.KeyboardGroup.create(), .keyboard_group = try wlr.KeyboardGroup.create(),
.status_trackers = undefined,
.link = undefined,
}; };
seat.wlr_seat.data = seat; seat.wlr_seat.data = seat;
server.input_manager.seats.append(seat);
seat.status_trackers.init();
try seat.cursor.init(seat); try seat.cursor.init(seat);
seat.relay.init(); 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); 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); var it = server.input_manager.devices.iterator(.forward);
while (it.next()) |device| assert(device.seat != seat); while (it.next()) |device| assert(device.seat != seat);
@ -151,6 +162,10 @@ pub fn deinit(seat: *Seat) void {
seat.start_drag.link.remove(); seat.start_drag.link.remove();
if (seat.drag != .none) seat.drag_destroy.link.remove(); if (seat.drag != .none) seat.drag_destroy.link.remove();
seat.request_set_primary_selection.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. /// 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| { if (seat.cursor.constraint) |constraint| {
assert(constraint.wlr_constraint == wlr_constraint); assert(constraint.wlr_constraint == wlr_constraint);
} else { } else {
seat.cursor.constraint = @alignCast(@ptrCast(wlr_constraint.data)); seat.cursor.constraint = @ptrCast(@alignCast(wlr_constraint.data));
assert(seat.cursor.constraint != null); assert(seat.cursor.constraint != null);
} }
} }
@ -300,7 +315,10 @@ pub fn setFocusRaw(seat: *Seat, new_focus: FocusTarget) void {
seat.cursor.may_need_warp = true; seat.cursor.may_need_warp = true;
// Inform any clients tracking status of the change // 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 /// 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 { fn keyboardNotifyEnter(seat: *Seat, wlr_surface: *wlr.Surface) void {
if (seat.wlr_seat.getKeyboard()) |wlr_keyboard| { 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) = .{}; var buffer: [Keyboard.Pressed.capacity]u32 = undefined;
for (keyboard.pressed.keys.constSlice()) |item| { var keycodes: std.ArrayList(u32) = .initBuffer(&buffer);
for (keyboard.pressed.slice()) |item| {
if (item.consumer == .focus) keycodes.appendAssumeCapacity(item.code); if (item.consumer == .focus) keycodes.appendAssumeCapacity(item.code);
} }
seat.wlr_seat.keyboardNotifyEnter( seat.wlr_seat.keyboardNotifyEnter(
wlr_surface, wlr_surface,
keycodes.constSlice(), keycodes.items,
&wlr_keyboard.modifiers, &wlr_keyboard.modifiers,
); );
} else { } else {
@ -338,15 +357,15 @@ pub fn focusOutput(seat: *Seat, output: ?*Output) void {
if (seat.focused_output == output) return; if (seat.focused_output == output) return;
if (seat.focused_output) |old| { if (seat.focused_output) |old| {
var it = seat.status_trackers.first; var it = seat.status_trackers.iterator(.forward);
while (it) |node| : (it = node.next) node.data.sendOutput(old, .unfocused); while (it.next()) |tracker| tracker.sendOutput(old, .unfocused);
} }
seat.focused_output = output; seat.focused_output = output;
if (seat.focused_output) |new| { if (seat.focused_output) |new| {
var it = seat.status_trackers.first; var it = seat.status_trackers.iterator(.forward);
while (it) |node| : (it = node.next) node.data.sendOutput(new, .focused); while (it.next()) |tracker| tracker.sendOutput(new, .focused);
} }
// Depending on configuration and cursor position, changing output focus // 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 { pub fn enterMode(seat: *Seat, mode_id: u32) void {
seat.mode_id = mode_id; seat.mode_id = mode_id;
var it = seat.status_trackers.first; var it = seat.status_trackers.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |tracker| {
node.data.sendMode(server.config.modes.items[mode_id].name); 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; return;
}; };
if (out) |s| { if (out) |s| {
const stdout = std.io.getStdOut().writer(); var stdout = std.fs.File.stdout().writer(&.{});
stdout.print("{s}", .{s}) catch |err| { stdout.interface.print("{s}", .{s}) catch |err| {
std.log.scoped(.command).err("{s}: write to stdout failed {}", .{ args[0], err }); std.log.scoped(.command).err("{s}: write to stdout failed {}", .{ args[0], err });
}; };
} }

View File

@ -33,8 +33,19 @@ const View = @import("View.zig");
seat: *Seat, seat: *Seat,
seat_status_v1: *zriver.SeatStatusV1, seat_status_v1: *zriver.SeatStatusV1,
pub fn init(seat_status: *SeatStatus, seat: *Seat, seat_status_v1: *zriver.SeatStatusV1) void { link: wl.list.Link,
seat_status.* = .{ .seat = seat, .seat_status_v1 = seat_status_v1 };
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); 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 { fn handleDestroy(_: *zriver.SeatStatusV1, seat_status: *SeatStatus) void {
const node: *std.SinglyLinkedList(SeatStatus).Node = @fieldParentPtr("data", seat_status); seat_status.link.remove();
seat_status.seat.status_trackers.remove(node); util.gpa.destroy(seat_status);
util.gpa.destroy(node);
} }
pub fn sendOutput(seat_status: SeatStatus, output: *Output, state: enum { focused, unfocused }) void { pub fn sendOutput(seat_status: SeatStatus, output: *Output, state: enum { focused, unfocused }) void {

View File

@ -24,7 +24,7 @@ const posix = std.posix;
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const wl = @import("wayland").server.wl; const wl = @import("wayland").server.wl;
const c = @import("c.zig"); const c = @import("c.zig").c;
const util = @import("util.zig"); const util = @import("util.zig");
const Config = @import("Config.zig"); const Config = @import("Config.zig");
@ -510,7 +510,7 @@ fn handleRequestSetCursorShape(
_: *wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape), _: *wl.Listener(*wlr.CursorShapeManagerV1.event.RequestSetShape),
event: *wlr.CursorShapeManagerV1.event.RequestSetShape, event: *wlr.CursorShapeManagerV1.event.RequestSetShape,
) void { ) 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| { if (event.tablet_tool) |wp_tool| {
assert(event.device_type == .tablet_tool); assert(event.device_type == .tablet_tool);

View File

@ -69,7 +69,7 @@ fn handleRequest(
.get_river_output_status => |req| { .get_river_output_status => |req| {
// ignore if the output is inert // ignore if the output is inert
const wlr_output = wlr.Output.fromWlOutput(req.output) orelse return; 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( const resource = zriver.OutputStatusV1.create(
status_manager_v1.getClient(), status_manager_v1.getClient(),
@ -86,13 +86,7 @@ fn handleRequest(
.get_river_seat_status => |req| { .get_river_seat_status => |req| {
// ignore if the seat is inert // ignore if the seat is inert
const wlr_seat = wlr.Seat.Client.fromWlSeat(req.seat) orelse return; const wlr_seat = wlr.Seat.Client.fromWlSeat(req.seat) orelse return;
const seat: *Seat = @alignCast(@ptrCast(wlr_seat.seat.data)); const seat: *Seat = @ptrCast(@alignCast(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_status = zriver.SeatStatusV1.create( const seat_status = zriver.SeatStatusV1.create(
status_manager_v1.getClient(), status_manager_v1.getClient(),
@ -100,13 +94,15 @@ fn handleRequest(
req.id, req.id,
) catch { ) catch {
status_manager_v1.getClient().postNoMemory(); status_manager_v1.getClient().postNoMemory();
util.gpa.destroy(node);
log.err("out of memory", .{}); log.err("out of memory", .{});
return; return;
}; };
node.data.init(seat, seat_status); SeatStatus.create(seat, seat_status) catch {
seat.status_trackers.prepend(node); status_manager_v1.getClient().postNoMemory();
log.err("out of memory", .{});
return;
};
}, },
} }
} }

View File

@ -59,7 +59,7 @@ set_cursor: wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor) =
wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor).init(handleSetCursor), wl.Listener(*wlr.TabletV2TabletTool.event.SetCursor).init(handleSetCursor),
pub fn get(wlr_seat: *wlr.Seat, wlr_tool: *wlr.TabletTool) error{OutOfMemory}!*TabletTool { 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; return tool;
} else { } else {
return TabletTool.create(wlr_seat, wlr_tool); return TabletTool.create(wlr_seat, wlr_tool);

View File

@ -43,7 +43,7 @@ destroy: wl.Listener(*wlr.TextInputV3) =
wl.Listener(*wlr.TextInputV3).init(handleDestroy), wl.Listener(*wlr.TextInputV3).init(handleDestroy),
pub fn create(wlr_text_input: *wlr.TextInputV3) !void { 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); 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 { fn handleEnable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
const text_input: *TextInput = @fieldParentPtr("enable", listener); 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) { if (text_input.wlr_text_input.focused_surface == null) {
log.err("client requested to enable text input without focus, ignoring request", .{}); 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 { fn handleCommit(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
const text_input: *TextInput = @fieldParentPtr("commit", listener); 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) { if (seat.relay.text_input != text_input) {
log.err("inactive text input tried to commit an update, client bug?", .{}); 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 { fn handleDisable(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
const text_input: *TextInput = @fieldParentPtr("disable", listener); 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) { if (seat.relay.text_input == text_input) {
seat.relay.disableTextInput(); 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 { fn handleDestroy(listener: *wl.Listener(*wlr.TextInputV3), _: *wlr.TextInputV3) void {
const text_input: *TextInput = @fieldParentPtr("destroy", listener); 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) { if (seat.relay.text_input == text_input) {
seat.relay.disableTextInput(); seat.relay.disableTextInput();

View File

@ -277,11 +277,12 @@ pub fn resizeUpdatePosition(view: *View, width: i32, height: i32) void {
assert(view.inflight.resizing); assert(view.inflight.resizing);
const data = blk: { const data = blk: {
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |seat| {
const cursor = &node.data.cursor; if (seat.cursor.inflight_mode == .resize and
if (cursor.inflight_mode == .resize and cursor.inflight_mode.resize.view == view) { seat.cursor.inflight_mode.resize.view == view)
break :blk cursor.inflight_mode.resize; {
break :blk seat.cursor.inflight_mode.resize;
} }
} else { } else {
// The view resizing state should never be set when the view is // The view resizing state should never be set when the view is
@ -594,7 +595,7 @@ pub fn allowTearing(view: *View) bool {
.window_hint => { .window_hint => {
if (server.config.allow_tearing) { if (server.config.allow_tearing) {
if (view.rootSurface()) |root_surface| { 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; return false;
@ -720,8 +721,8 @@ pub fn map(view: *View) !void {
if (output) |o| { if (output) |o| {
view.setPendingOutput(o); view.setPendingOutput(o);
var it = server.input_manager.seats.first; var it = server.input_manager.seats.iterator(.forward);
while (it) |seat_node| : (it = seat_node.next) seat_node.data.focus(view); while (it.next()) |seat| seat.focus(view);
} else { } else {
log.debug("no output available for newly mapped view, adding to fallback stacks", .{}); 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(), .app_id = view.getAppId(),
}); });
} }
var seat_it = server.input_manager.seats.first; // Send title to all status listeners attached to a seat which focuses this view
while (seat_it) |seat_node| : (seat_it = seat_node.next) { var seat_it = server.input_manager.seats.iterator(.forward);
if (seat_node.data.focused == .view and seat_node.data.focused.view == view) { while (seat_it.next()) |seat| {
var client_it = seat_node.data.status_trackers.first; if (seat.focused == .view and seat.focused.view == view) {
while (client_it) |client_node| : (client_it = client_node.next) { var it = seat.status_trackers.iterator(.forward);
client_node.data.sendFocusedView(); while (it.next()) |tracker| {
tracker.sendFocusedView();
} }
} }
} }

View File

@ -34,7 +34,7 @@ request_mode: wl.Listener(*wlr.XdgToplevelDecorationV1) =
wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleRequestMode), wl.Listener(*wlr.XdgToplevelDecorationV1).init(handleRequestMode),
pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void { 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 }; toplevel.decoration = .{ .wlr_decoration = wlr_decoration };
const decoration = &toplevel.decoration.?; const decoration = &toplevel.decoration.?;
@ -48,7 +48,7 @@ pub fn init(wlr_decoration: *wlr.XdgToplevelDecorationV1) void {
} }
pub fn deinit(decoration: *XdgDecoration) 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.destroy.link.remove();
decoration.request_mode.link.remove(); decoration.request_mode.link.remove();
@ -72,7 +72,7 @@ fn handleRequestMode(
) void { ) void {
const decoration: *XdgDecoration = @fieldParentPtr("request_mode", listener); 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 view = toplevel.view;
const ssd = server.config.rules.ssd.match(toplevel.view) orelse const ssd = server.config.rules.ssd.match(toplevel.view) orelse

View File

@ -423,7 +423,7 @@ fn handleRequestMove(
event: *wlr.XdgToplevel.event.Move, event: *wlr.XdgToplevel.event.Move,
) void { ) void {
const toplevel: *XdgToplevel = @fieldParentPtr("request_move", listener); 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; const view = toplevel.view;
if (view.pending.fullscreen) return; 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 { fn handleRequestResize(listener: *wl.Listener(*wlr.XdgToplevel.event.Resize), event: *wlr.XdgToplevel.event.Resize) void {
const toplevel: *XdgToplevel = @fieldParentPtr("request_resize", listener); 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; const view = toplevel.view;
if (view.pending.fullscreen) return; if (view.pending.fullscreen) return;

View File

@ -165,9 +165,8 @@ fn handleUnmap(listener: *wl.Listener(void)) void {
// If the unmapped surface is currently focused, pass keyboard focus // If the unmapped surface is currently focused, pass keyboard focus
// to the most appropriate surface. // to the most appropriate surface.
var seat_it = server.input_manager.seats.first; var seat_it = server.input_manager.seats.iterator(.forward);
while (seat_it) |seat_node| : (seat_it = seat_node.next) { while (seat_it.next()) |seat| {
const seat = &seat_node.data;
if (seat.focused == .view and seat.focused.view.impl == .xwayland_view and 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.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) seat.wlr_seat.keyboard_state.focused_surface == override_redirect.xwayland_surface.surface)

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
pub usingnamespace @cImport({ pub const c = @cImport({
@cDefine("_POSIX_C_SOURCE", "200809L"); @cDefine("_POSIX_C_SOURCE", "200809L");
@cInclude("stdlib.h"); @cInclude("stdlib.h");

View File

@ -122,6 +122,7 @@ pub const Error = error{
CannotParseFile, CannotParseFile,
UnknownOption, UnknownOption,
ConflictingOptions, ConflictingOptions,
WriteFailed,
OutOfMemory, OutOfMemory,
Other, Other,
}; };
@ -165,7 +166,7 @@ pub fn errToMsg(err: Error) [:0]const u8 {
Error.InvalidValue => "invalid value", Error.InvalidValue => "invalid value",
Error.CannotReadFile => "cannot read file", Error.CannotReadFile => "cannot read file",
Error.CannotParseFile => "cannot parse file", Error.CannotParseFile => "cannot parse file",
Error.OutOfMemory => "out of memory", Error.WriteFailed, Error.OutOfMemory => "out of memory",
Error.Other => unreachable, Error.Other => unreachable,
}; };
} }

View File

@ -35,9 +35,8 @@ pub fn cursor(
if (args.len < 3) return Error.NotEnoughArguments; if (args.len < 3) return Error.NotEnoughArguments;
if (args.len > 3) return Error.TooManyArguments; if (args.len > 3) return Error.TooManyArguments;
server.config.cursor_hide_timeout = try std.fmt.parseInt(u31, args[2], 10); server.config.cursor_hide_timeout = try std.fmt.parseInt(u31, args[2], 10);
var seat_it = server.input_manager.seats.first; var seat_it = server.input_manager.seats.iterator(.forward);
while (seat_it) |seat_node| : (seat_it = seat_node.next) { while (seat_it.next()) |seat| {
const seat = &seat_node.data;
seat.cursor.unhide(); seat.cursor.unhide();
} }
} else if (std.mem.eql(u8, "when-typing", args[1])) { } else if (std.mem.eql(u8, "when-typing", args[1])) {

View File

@ -34,8 +34,8 @@ pub fn listInputs(
) Error!void { ) Error!void {
if (args.len > 1) return error.TooManyArguments; if (args.len > 1) return error.TooManyArguments;
var input_list = std.ArrayList(u8).init(util.gpa); var aw: std.Io.Writer.Allocating = .init(util.gpa);
const writer = input_list.writer();
var prev = false; var prev = false;
var it = server.input_manager.devices.iterator(.forward); var it = server.input_manager.devices.iterator(.forward);
@ -46,16 +46,16 @@ pub fn listInputs(
} }
} else false; } else false;
if (prev) try input_list.appendSlice("\n"); if (prev) try aw.writer.writeByte('\n');
prev = true; prev = true;
try writer.print("{s}\n\tconfigured: {}\n", .{ try aw.writer.print("{s}\n\tconfigured: {}\n", .{
device.identifier, device.identifier,
configured, configured,
}); });
} }
out.* = try input_list.toOwnedSlice(); out.* = try aw.toOwnedSlice();
} }
pub fn listInputConfigs( pub fn listInputConfigs(
@ -65,15 +65,14 @@ pub fn listInputConfigs(
) Error!void { ) Error!void {
if (args.len > 1) return error.TooManyArguments; if (args.len > 1) return error.TooManyArguments;
var input_list = std.ArrayList(u8).init(util.gpa); var aw: std.Io.Writer.Allocating = .init(util.gpa);
const writer = input_list.writer();
for (server.input_manager.configs.items, 0..) |*input_config, i| { for (server.input_manager.configs.items, 0..) |*input_config, i| {
if (i > 0) try writer.writeByte('\n'); if (i > 0) try aw.writer.writeByte('\n');
try input_config.write(writer); try input_config.write(&aw.writer);
} }
out.* = try input_list.toOwnedSlice(); out.* = try aw.toOwnedSlice();
} }
pub fn input( pub fn input(
@ -99,7 +98,7 @@ pub fn input(
}; };
errdefer util.gpa.free(input_config.glob); 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]); try input_config.parse(args[2], args[3]);

View File

@ -71,9 +71,8 @@ pub fn sendLayoutCmd(
const output = seat.focused_output orelse return; const output = seat.focused_output orelse return;
const target_namespace = args[1]; const target_namespace = args[1];
var it = output.layouts.first; var it = output.layouts.iterator(.forward);
const layout = while (it) |node| : (it = node.next) { const layout = while (it.next()) |layout| {
const layout = &node.data;
if (mem.eql(u8, layout.namespace, target_namespace)) break layout; if (mem.eql(u8, layout.namespace, target_namespace)) break layout;
} else return; } else return;

View File

@ -22,7 +22,7 @@ const wlr = @import("wlroots");
const xkb = @import("xkbcommon"); const xkb = @import("xkbcommon");
const flags = @import("flags"); const flags = @import("flags");
const c = @import("../c.zig"); const c = @import("../c.zig").c;
const server = &@import("../main.zig").server; const server = &@import("../main.zig").server;
const util = @import("../util.zig"); const util = @import("../util.zig");

View File

@ -121,7 +121,7 @@ fn getOutput(seat: *Seat, str: []const u8) !?*Output {
@floatFromInt(focus_box.x + @divTrunc(focus_box.width, 2)), @floatFromInt(focus_box.x + @divTrunc(focus_box.width, 2)),
@floatFromInt(focus_box.y + @divTrunc(focus_box.height, 2)), @floatFromInt(focus_box.y + @divTrunc(focus_box.height, 2)),
) orelse return null; ) orelse return null;
return @alignCast(@ptrCast(wlr_output.data)); return @ptrCast(@alignCast(wlr_output.data));
} else { } else {
// Check if an output matches by name // Check if an output matches by name
var it = server.root.active_outputs.iterator(.forward); var it = server.root.active_outputs.iterator(.forward);

View File

@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const assert = std.debug.assert;
const fmt = std.fmt; const fmt = std.fmt;
const globber = @import("globber"); 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 { 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.NotEnoughArguments;
if (args.len > 2) return error.TooManyArguments; 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 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); const title_column_max = 2 + @max("title".len, max_glob_len.title);
var buffer = std.ArrayList(u8).init(util.gpa); var buffer = std.io.Writer.Allocating.init(util.gpa);
const writer = buffer.writer(); defer buffer.deinit();
const writer = &buffer.writer;
try fmt.formatBuf("title", .{ .width = title_column_max, .alignment = .left }, writer); try alignLeft("title", title_column_max, writer);
try fmt.formatBuf("app-id", .{ .width = app_id_column_max, .alignment = .left }, writer); try alignLeft("app-id", app_id_column_max, writer);
try writer.writeAll("action\n"); try writer.writeAll("action\n");
switch (rule_list) { switch (rule_list) {
@ -290,8 +298,8 @@ pub fn listRules(_: *Seat, args: []const [:0]const u8, out: *?[]const u8) Error!
else => unreachable, else => unreachable,
}; };
for (rules) |rule| { for (rules) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try alignLeft(rule.title_glob, title_column_max, writer);
try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try alignLeft(rule.app_id_glob, app_id_column_max, writer);
try writer.print("{s}\n", .{switch (list) { try writer.print("{s}\n", .{switch (list) {
.float => if (rule.value) "float" else "no-float", .float => if (rule.value) "float" else "no-float",
.ssd => if (rule.value) "ssd" else "csd", .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 => { .tags => {
for (server.config.rules.tags.rules.items) |rule| { for (server.config.rules.tags.rules.items) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try alignLeft(rule.title_glob, title_column_max, writer);
try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try alignLeft(rule.app_id_glob, app_id_column_max, writer);
try writer.print("{b}\n", .{rule.value}); try writer.print("{b}\n", .{rule.value});
} }
}, },
.position => { .position => {
for (server.config.rules.position.rules.items) |rule| { for (server.config.rules.position.rules.items) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try alignLeft(rule.title_glob, title_column_max, writer);
try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, writer); try alignLeft(rule.app_id_glob, app_id_column_max, writer);
try writer.print("{s},{d},{d}", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y }); try writer.print("{s},{d},{d}\n", .{ @tagName(rule.value.anchor), rule.value.x, rule.value.y });
} }
}, },
.dimensions => { .dimensions => {
for (server.config.rules.dimensions.rules.items) |rule| { for (server.config.rules.dimensions.rules.items) |rule| {
try fmt.formatBuf(rule.title_glob, .{ .width = title_column_max, .alignment = .left }, writer); try alignLeft(rule.title_glob, title_column_max, writer);
try fmt.formatBuf(rule.app_id_glob, .{ .width = app_id_column_max, .alignment = .left }, 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 }); try writer.print("{d}x{d}\n", .{ rule.value.width, rule.value.height });
} }
}, },

View File

@ -17,7 +17,7 @@
const std = @import("std"); const std = @import("std");
const posix = std.posix; const posix = std.posix;
const c = @import("../c.zig"); const c = @import("../c.zig").c;
const util = @import("../util.zig"); const util = @import("../util.zig");
const process = @import("../process.zig"); const process = @import("../process.zig");

View File

@ -18,14 +18,13 @@ const build_options = @import("build_options");
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
const fs = std.fs; const fs = std.fs;
const io = std.io;
const log = std.log; const log = std.log;
const posix = std.posix; const posix = std.posix;
const builtin = @import("builtin"); const builtin = @import("builtin");
const wlr = @import("wlroots"); const wlr = @import("wlroots");
const flags = @import("flags"); const flags = @import("flags");
const c = @import("c.zig"); const c = @import("c.zig").c;
const util = @import("util.zig"); const util = @import("util.zig");
const process = @import("process.zig"); const process = @import("process.zig");
@ -52,21 +51,21 @@ pub fn main() anyerror!void {
.{ .name = "log-level", .kind = .arg }, .{ .name = "log-level", .kind = .arg },
.{ .name = "no-xwayland", .kind = .boolean }, .{ .name = "no-xwayland", .kind = .boolean },
}).parse(std.os.argv[1..]) catch { }).parse(std.os.argv[1..]) catch {
try io.getStdErr().writeAll(usage); try fs.File.stderr().writeAll(usage);
posix.exit(1); posix.exit(1);
}; };
if (result.flags.h) { if (result.flags.h) {
try io.getStdOut().writeAll(usage); try fs.File.stdout().writeAll(usage);
posix.exit(0); posix.exit(0);
} }
if (result.args.len != 0) { if (result.args.len != 0) {
log.err("unknown option '{s}'", .{result.args[0]}); log.err("unknown option '{s}'", .{result.args[0]});
try io.getStdErr().writeAll(usage); try fs.File.stderr().writeAll(usage);
posix.exit(1); posix.exit(1);
} }
if (result.flags.version) { if (result.flags.version) {
try io.getStdOut().writeAll(build_options.version ++ "\n"); try fs.File.stdout().writeAll(build_options.version ++ "\n");
posix.exit(0); posix.exit(0);
} }
if (result.flags.@"log-level") |level| { if (result.flags.@"log-level") |level| {
@ -80,7 +79,7 @@ pub fn main() anyerror!void {
runtime_log_level = .debug; runtime_log_level = .debug;
} else { } else {
log.err("invalid log level '{s}'", .{level}); log.err("invalid log level '{s}'", .{level});
try io.getStdErr().writeAll(usage); try fs.File.stderr().writeAll(usage);
posix.exit(1); posix.exit(1);
} }
} }
@ -189,8 +188,8 @@ pub fn logFn(
const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; const scope_prefix = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): ";
const stderr = io.getStdErr().writer(); var stderr = fs.File.stderr().writer(&.{});
stderr.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {}; stderr.interface.print(level.asText() ++ scope_prefix ++ format ++ "\n", args) catch {};
} }
/// See wlroots_log_wrapper.c /// See wlroots_log_wrapper.c

View File

@ -17,7 +17,7 @@
const std = @import("std"); const std = @import("std");
const posix = std.posix; const posix = std.posix;
const c = @import("c.zig"); const c = @import("c.zig").c;
var original_rlimit: ?posix.rlimit = null; var original_rlimit: ?posix.rlimit = null;
@ -26,7 +26,7 @@ pub fn setup() void {
// has had its read end closed by another process. // has had its read end closed by another process.
const sig_ign = posix.Sigaction{ const sig_ign = posix.Sigaction{
.handler = .{ .handler = posix.SIG.IGN }, .handler = .{ .handler = posix.SIG.IGN },
.mask = posix.empty_sigset, .mask = posix.sigemptyset(),
.flags = 0, .flags = 0,
}; };
posix.sigaction(posix.SIG.PIPE, &sig_ign, null); posix.sigaction(posix.SIG.PIPE, &sig_ign, null);
@ -61,11 +61,11 @@ pub fn setup() void {
pub fn cleanupChild() void { pub fn cleanupChild() void {
if (c.setsid() < 0) unreachable; 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{ const sig_dfl = posix.Sigaction{
.handler = .{ .handler = posix.SIG.DFL }, .handler = .{ .handler = posix.SIG.DFL },
.mask = posix.empty_sigset, .mask = posix.sigemptyset(),
.flags = 0, .flags = 0,
}; };
posix.sigaction(posix.SIG.PIPE, &sig_dfl, null); posix.sigaction(posix.SIG.PIPE, &sig_dfl, null);

View File

@ -16,7 +16,7 @@
const std = @import("std"); const std = @import("std");
const mem = std.mem; const mem = std.mem;
const io = std.io; const fs = std.fs;
const posix = std.posix; const posix = std.posix;
const assert = std.debug.assert; const assert = std.debug.assert;
const builtin = @import("builtin"); const builtin = @import("builtin");
@ -73,15 +73,15 @@ fn _main() !void {
.{ .name = "h", .kind = .boolean }, .{ .name = "h", .kind = .boolean },
.{ .name = "version", .kind = .boolean }, .{ .name = "version", .kind = .boolean },
}).parse(std.os.argv[1..]) catch { }).parse(std.os.argv[1..]) catch {
try io.getStdErr().writeAll(usage); try fs.File.stderr().writeAll(usage);
posix.exit(1); posix.exit(1);
}; };
if (result.flags.h) { if (result.flags.h) {
try io.getStdOut().writeAll(usage); try fs.File.stdout().writeAll(usage);
posix.exit(0); posix.exit(0);
} }
if (result.flags.version) { 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); posix.exit(0);
} }
@ -125,8 +125,8 @@ fn callbackListener(_: *zriver.CommandCallbackV1, event: zriver.CommandCallbackV
switch (event) { switch (event) {
.success => |success| { .success => |success| {
if (mem.len(success.output) > 0) { if (mem.len(success.output) > 0) {
const stdout = io.getStdOut().writer(); var stdout = fs.File.stdout().writer(&.{});
stdout.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout"); stdout.interface.print("{s}\n", .{success.output}) catch @panic("failed to write to stdout");
} }
posix.exit(0); 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. // A small hack to provide usage text when river reports an unknown command.
if (mem.orderZ(u8, failure.failure_message, "unknown command") == .eq) { if (mem.orderZ(u8, failure.failure_message, "unknown command") == .eq) {
std.log.err("unknown command", .{}); std.log.err("unknown command", .{});
io.getStdErr().writeAll(usage) catch {}; fs.File.stderr().writeAll(usage) catch {};
posix.exit(1); posix.exit(1);
} }
fatal("{s}", .{failure.failure_message}); fatal("{s}", .{failure.failure_message});

View File

@ -91,15 +91,12 @@ const gpa = std.heap.c_allocator;
const Context = struct { const Context = struct {
initialized: bool = false, initialized: bool = false,
layout_manager: ?*river.LayoutManagerV3 = null, 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 { fn addOutput(context: *Context, registry: *wl.Registry, name: u32) !void {
const wl_output = try registry.bind(name, wl.Output, 3); const wl_output = try registry.bind(name, wl.Output, 3);
errdefer wl_output.release(); errdefer wl_output.release();
const node = try gpa.create(std.DoublyLinkedList(Output).Node); try Output.create(context, wl_output, name);
errdefer gpa.destroy(node);
try node.data.init(context, wl_output, name);
context.outputs.append(node);
} }
}; };
@ -113,15 +110,21 @@ const Output = struct {
layout: *river.LayoutV3 = undefined, 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.* = .{ output.* = .{
.wl_output = wl_output, .wl_output = wl_output,
.name = name, .name = name,
.main_location = default_main_location, .main_location = default_main_location,
.main_count = default_main_count, .main_count = default_main_count,
.main_ratio = default_main_ratio, .main_ratio = default_main_ratio,
.link = undefined,
}; };
if (context.initialized) try output.getLayout(context); if (context.initialized) try output.getLayout(context);
context.outputs.append(output);
} }
fn getLayout(output: *Output, context: *Context) !void { fn getLayout(output: *Output, context: *Context) !void {
@ -130,9 +133,11 @@ const Output = struct {
output.layout.setListener(*Output, layoutListener, output); output.layout.setListener(*Output, layoutListener, output);
} }
fn deinit(output: *Output) void { fn destroy(output: *Output) void {
output.wl_output.release(); output.wl_output.release();
output.layout.destroy(); output.layout.destroy();
output.link.remove();
gpa.destroy(output);
} }
fn layoutListener(layout: *river.LayoutV3, event: river.LayoutV3.Event, output: *Output) void { 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-count", .kind = .arg },
.{ .name = "main-ratio", .kind = .arg }, .{ .name = "main-ratio", .kind = .arg },
}).parse(std.os.argv[1..]) catch { }).parse(std.os.argv[1..]) catch {
try std.io.getStdErr().writeAll(usage); try std.fs.File.stderr().writeAll(usage);
posix.exit(1); posix.exit(1);
}; };
if (result.flags.h) { if (result.flags.h) {
try std.io.getStdOut().writeAll(usage); try std.fs.File.stdout().writeAll(usage);
posix.exit(0); posix.exit(0);
} }
if (result.args.len != 0) fatalPrintUsage("unknown option '{s}'", .{result.args[0]}); if (result.args.len != 0) fatalPrintUsage("unknown option '{s}'", .{result.args[0]});
if (result.flags.version) { 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); posix.exit(0);
} }
if (result.flags.@"view-padding") |raw| { if (result.flags.@"view-padding") |raw| {
@ -356,7 +361,10 @@ pub fn main() !void {
}; };
defer display.disconnect(); defer display.disconnect();
var context: Context = .{}; var context: Context = .{
.outputs = undefined,
};
context.outputs.init();
const registry = try display.getRegistry(); const registry = try display.getRegistry();
registry.setListener(*Context, registryListener, &context); registry.setListener(*Context, registryListener, &context);
@ -368,9 +376,8 @@ pub fn main() !void {
context.initialized = true; context.initialized = true;
var it = context.outputs.first; var it = context.outputs.iterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |output| {
const output = &node.data;
try output.getLayout(&context); try output.getLayout(&context);
} }
@ -389,13 +396,10 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *
} }
}, },
.global_remove => |ev| { .global_remove => |ev| {
var it = context.outputs.first; var it = context.outputs.safeIterator(.forward);
while (it) |node| : (it = node.next) { while (it.next()) |output| {
const output = &node.data;
if (output.name == ev.name) { if (output.name == ev.name) {
context.outputs.remove(node); output.destroy();
output.deinit();
gpa.destroy(node);
break; break;
} }
} }
@ -410,7 +414,7 @@ fn fatal(comptime format: []const u8, args: anytype) noreturn {
fn fatalPrintUsage(comptime format: []const u8, args: anytype) noreturn { fn fatalPrintUsage(comptime format: []const u8, args: anytype) noreturn {
std.log.err(format, args); std.log.err(format, args);
std.io.getStdErr().writeAll(usage) catch {}; std.fs.File.stderr().writeAll(usage) catch {};
posix.exit(1); posix.exit(1);
} }