build: update to Zig 0.15
This commit is contained in:
		| @ -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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
							
								
								
									
										45
									
								
								build.zig
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								build.zig
									
									
									
									
									
								
							| @ -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); | ||||||
|  |  | ||||||
|  | |||||||
| @ -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", | ||||||
|  | |||||||
| @ -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; | ||||||
| @ -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, | ||||||
|  | |||||||
| @ -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(); |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -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(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -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| { | ||||||
|  | |||||||
| @ -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"); | ||||||
|  |  | ||||||
|  | |||||||
| @ -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. | ||||||
|  | |||||||
| @ -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; | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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); | ||||||
| } | } | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -68,9 +68,8 @@ pub fn destroy(lock_surface: *LockSurface) void { | |||||||
|                 break .{ .lock_surface = @alignCast(@ptrCast(surface.data)) }; |                 break .{ .lock_surface = @alignCast(@ptrCast(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); | ||||||
|             } |             } | ||||||
| @ -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 }); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -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(); | ||||||
| @ -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(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -337,9 +337,8 @@ pub fn deactivateOutput(root: *Root, output: *Output) void { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // 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,8 +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) node.data.cursor.updateState(); |         while (it.next()) |seat| seat.cursor.updateState(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     { |     { | ||||||
|  | |||||||
| @ -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. | ||||||
| @ -295,8 +310,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 | ||||||
|     var it = seat.status_trackers.first; |     var it = seat.status_trackers.iterator(.forward); | ||||||
|     while (it) |node| : (it = node.next) node.data.sendFocusedView(); |     while (it.next()) |tracker| { | ||||||
|  |         tracker.sendFocusedView(); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// Send keyboard enter/leave events and handle pointer constraints | /// Send keyboard enter/leave events and handle pointer constraints | ||||||
| @ -314,14 +331,15 @@ 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 = @alignCast(@ptrCast(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 { | ||||||
| @ -334,15 +352,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 | ||||||
| @ -357,9 +375,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); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -453,8 +471,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 }); | ||||||
|         }; |         }; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -31,8 +31,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); | ||||||
|  |  | ||||||
| @ -49,9 +60,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 { | ||||||
|  | |||||||
| @ -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"); | ||||||
|  | |||||||
| @ -88,25 +88,21 @@ fn handleRequest( | |||||||
|             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 = @alignCast(@ptrCast(wlr_seat.seat.data)); | ||||||
|  |  | ||||||
|             const node = util.gpa.create(std.SinglyLinkedList(SeatStatus).Node) catch { |  | ||||||
|                 status_manager_v1.getClient().postNoMemory(); |  | ||||||
|                 log.err("out of memory", .{}); |  | ||||||
|                 return; |  | ||||||
|             }; |  | ||||||
|  |  | ||||||
|             const seat_status = zriver.SeatStatusV1.create( |             const seat_status = zriver.SeatStatusV1.create( | ||||||
|                 status_manager_v1.getClient(), |                 status_manager_v1.getClient(), | ||||||
|                 status_manager_v1.getVersion(), |                 status_manager_v1.getVersion(), | ||||||
|                 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; | ||||||
|  |             }; | ||||||
|         }, |         }, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -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 | ||||||
| @ -710,8 +711,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", .{}); | ||||||
|  |  | ||||||
| @ -778,12 +779,12 @@ pub fn notifyTitle(view: *const View) void { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Send title to all status listeners attached to a seat which focuses this view |     // Send title to all status listeners attached to a seat which focuses this view | ||||||
|     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| { | ||||||
|         if (seat_node.data.focused == .view and seat_node.data.focused.view == view) { |         if (seat.focused == .view and seat.focused.view == view) { | ||||||
|             var client_it = seat_node.data.status_trackers.first; |             var it = seat.status_trackers.iterator(.forward); | ||||||
|             while (client_it) |client_node| : (client_it = client_node.next) { |             while (it.next()) |tracker| { | ||||||
|                 client_node.data.sendFocusedView(); |                 tracker.sendFocusedView(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -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) | ||||||
|  | |||||||
| @ -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"); | ||||||
|  | |||||||
| @ -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, | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  | |||||||
| @ -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])) { | ||||||
|  | |||||||
| @ -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]); | ||||||
|  |  | ||||||
|  | |||||||
| @ -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; | ||||||
|  |  | ||||||
|  | |||||||
| @ -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"); | ||||||
|  |  | ||||||
|  | |||||||
| @ -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"); | ||||||
| @ -217,6 +218,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; | ||||||
| @ -237,11 +244,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) { | ||||||
| @ -255,8 +263,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", | ||||||
| @ -269,22 +277,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("{d},{d}\n", .{ rule.value.x, rule.value.y }); |                 try writer.print("{d},{d}\n", .{ 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 }); | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|  | |||||||
| @ -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"); | ||||||
|  |  | ||||||
|  | |||||||
| @ -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 | ||||||
|  | |||||||
| @ -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); | ||||||
|  | |||||||
| @ -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}); | ||||||
|  | |||||||
| @ -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); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user