commit f2cfbc477f3e962d13118e8c43fe5eef68ef5c6c
parent 3e383f3dbd3662b267ad6157c66efd2f70776155
Author: Andrea Feletto <andrea@andreafeletto.com>
Date: Thu, 19 May 2022 19:12:46 +0200
wayland: fix flush/dispatch
Diffstat:
11 files changed, 126 insertions(+), 111 deletions(-)
diff --git a/build.zig b/build.zig
@@ -20,8 +20,8 @@ pub fn build(b: *std.build.Builder) void {
scanner.generate("wl_compositor", 4);
scanner.generate("wl_subcompositor", 1);
scanner.generate("wl_shm", 1);
- scanner.generate("wl_output", 4);
- scanner.generate("wl_seat", 7);
+ scanner.generate("wl_output", 3);
+ scanner.generate("wl_seat", 5);
scanner.generate("zwlr_layer_shell_v1", 1);
scanner.generate("zriver_status_manager_v1", 1);
scanner.generate("zriver_control_v1", 1);
diff --git a/src/Buffer.zig b/src/Buffer.zig
@@ -20,7 +20,7 @@ pub fn init(self: *Buffer, shm: *wl.Shm, width: u31, height: u31) !void {
self.width = width;
self.height = height;
- const fd = try os.memfd_create("levee-wayland-shm-buffer-pool", 0);
+ const fd = try os.memfd_create("levee-shm", os.linux.MFD_CLOEXEC);
defer os.close(fd);
const stride = width * 4;
diff --git a/src/Loop.zig b/src/Loop.zig
@@ -1,8 +1,10 @@
const std = @import("std");
+const log = std.log;
const mem = std.mem;
const os = std.os;
const State = @import("main.zig").State;
+const utils = @import("utils.zig");
const Loop = @This();
state: *State,
@@ -14,22 +16,24 @@ pub const Event = struct {
callbackIn: Callback,
callbackOut: Callback,
- pub const Callback = fn (*anyopaque) void;
+ pub const Action = enum { ok, terminate };
+ pub const Callback = fn (*anyopaque) Action;
- pub fn terminate(_: *anyopaque) void {
- os.exit(0);
+ pub fn terminate(_: *anyopaque) Action {
+ return .terminate;
}
- pub fn noop(_: *anyopaque) void {
- return;
+ pub fn noop(_: *anyopaque) Action {
+ return .ok;
}
};
pub fn init(state: *State) !Loop {
- var mask = mem.zeroes(os.linux.sigset_t);
+ var mask = os.empty_sigset;
os.linux.sigaddset(&mask, os.linux.SIG.INT);
os.linux.sigaddset(&mask, os.linux.SIG.TERM);
os.linux.sigaddset(&mask, os.linux.SIG.QUIT);
+
_ = os.linux.sigprocmask(os.linux.SIG.BLOCK, &mask, null);
const sfd = os.linux.signalfd(-1, &mask, os.linux.SFD.NONBLOCK);
@@ -38,6 +42,7 @@ pub fn init(state: *State) !Loop {
pub fn run(self: *Loop) !void {
const gpa = self.state.gpa;
+ const display = self.state.wayland.display;
var events: std.MultiArrayList(Event) = .{};
defer events.deinit(gpa);
@@ -55,8 +60,16 @@ pub fn run(self: *Loop) !void {
const fds = events.items(.fd);
while (true) {
- self.state.wayland.flushAndPrepareRead();
- _ = try os.poll(fds, -1);
+ while (true) {
+ const ret = display.dispatchPending();
+ _ = display.flush();
+ if (ret == .SUCCESS) break;
+ }
+
+ _ = os.poll(fds, -1) catch |err| {
+ log.err("poll failed: {s}", .{@errorName(err)});
+ return;
+ };
for (fds) |fd, i| {
if (fd.revents & os.POLL.HUP != 0) return;
@@ -64,11 +77,19 @@ pub fn run(self: *Loop) !void {
if (fd.revents & os.POLL.IN != 0) {
const event = events.get(i);
- event.callbackIn(event.data);
+ const action = event.callbackIn(event.data);
+ switch (action) {
+ .ok => {},
+ .terminate => return,
+ }
}
if (fd.revents & os.POLL.OUT != 0) {
const event = events.get(i);
- event.callbackOut(event.data);
+ const action = event.callbackOut(event.data);
+ switch (action) {
+ .ok => {},
+ .terminate => return,
+ }
}
}
}
diff --git a/src/Modules.zig b/src/Modules.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const ArrayList = std.ArrayList;
+const log = std.log;
const Event = @import("Loop.zig").Event;
const State = @import("main.zig").State;
@@ -50,4 +51,5 @@ pub fn deinit(self: *Modules) void {
pub fn register(self: *Modules, comptime ModuleType: type) !void {
const instance = try ModuleType.create(self.state);
try self.modules.append(try instance.module());
+ log.info("registered module: {s}", .{@typeName(ModuleType)});
}
diff --git a/src/Monitor.zig b/src/Monitor.zig
@@ -42,8 +42,6 @@ fn listener(_: *wl.Output, event: wl.Output.Event, monitor: *Monitor) void {
},
.geometry => {},
.mode => {},
- .name => {},
- .description => {},
.done => {
if (monitor.bar) |_| {} else {
monitor.bar = Bar.create(monitor) catch return;
diff --git a/src/Wayland.zig b/src/Wayland.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const log = std.log;
const mem = std.mem;
const meta = std.meta;
const os = std.os;
@@ -19,7 +20,6 @@ const Wayland = @This();
state: *State,
display: *wl.Display,
-registry: *wl.Registry,
monitors: ArrayList(*Monitor),
inputs: ArrayList(*Input),
@@ -37,12 +37,13 @@ const Globals = struct {
const GlobalsMask = utils.Mask(Globals);
pub fn init(state: *State) !Wayland {
- const display = try wl.Display.connect(null);
+ const display = wl.Display.connect(null) catch |err| {
+ utils.fatal("failed to connect to a wayland compositor: {s}", .{@errorName(err)});
+ };
return Wayland{
.state = state,
.display = display,
- .registry = try display.getRegistry(),
.monitors = ArrayList(*Monitor).init(state.gpa),
.inputs = ArrayList(*Input).init(state.gpa),
.globals = undefined,
@@ -56,18 +57,27 @@ pub fn deinit(self: *Wayland) void {
self.monitors.deinit();
self.inputs.deinit();
+
+ inline for (@typeInfo(Globals).Struct.fields) |field| {
+ @field(self.globals, field.name).destroy();
+ }
+ self.display.disconnect();
}
pub fn registerGlobals(self: *Wayland) !void {
- self.registry.setListener(*State, registryListener, self.state);
- if (self.display.roundtrip() != .SUCCESS) {
- std.log.err("failed roundtrip for initialization of globals", .{});
- os.exit(1);
+ const registry = self.display.getRegistry() catch |err| {
+ utils.fatal("out of memory during initialization: {s}", .{@errorName(err)});
+ };
+ defer registry.destroy();
+
+ registry.setListener(*State, registryListener, self.state);
+ const errno = self.display.roundtrip();
+ if (errno != .SUCCESS) {
+ utils.fatal("initial roundtrip failed", .{});
}
for (self.globalsMask) |is_registered| if (!is_registered) {
- std.log.err("missing global", .{});
- os.exit(1);
+ utils.fatal("global not advertised", .{});
};
}
@@ -81,94 +91,58 @@ pub fn getEvent(self: *Wayland) !Event {
.revents = undefined,
},
.data = @ptrCast(*anyopaque, self),
- .callbackIn = read,
- .callbackOut = Event.noop,
+ .callbackIn = dispatch,
+ .callbackOut = flush,
};
}
-pub fn flushAndPrepareRead(self: *Wayland) void {
- while (!self.display.prepareRead()) {
- const errno = self.display.dispatchPending();
- if (errno != .SUCCESS) {
- std.log.err("failed to dispatch pending wayland events", .{});
- os.exit(1);
- }
- }
-
- while (true) {
- const errno = self.display.flush();
- switch (errno) {
- .SUCCESS => return,
- .PIPE => {
- _ = self.display.readEvents();
- std.log.err("connection to wayland server unexpectedly terminated", .{});
- os.exit(1);
- },
- .AGAIN => {
- var wayland_out = [_]os.pollfd{.{
- .fd = self.display.getFd(),
- .events = os.POLL.OUT,
- .revents = undefined,
- }};
- _ = os.poll(&wayland_out, -1) catch {
- std.log.err("polling for wayland socket being writable failed", .{});
- os.exit(1);
- };
- },
- else => {
- std.log.err("failed to flush wayland requests", .{});
- os.exit(1);
- },
- }
+fn dispatch(self_opaque: *anyopaque) Event.Action {
+ const self = utils.cast(Wayland)(self_opaque);
+ const errno = self.display.dispatch();
+ switch (errno) {
+ .SUCCESS => return .ok,
+ else => return .terminate,
}
}
-fn read(self_opaque: *anyopaque) void {
+fn flush(self_opaque: *anyopaque) Event.Action {
const self = utils.cast(Wayland)(self_opaque);
- const errno = self.display.readEvents();
- if (errno != .SUCCESS) {
- std.log.err("failed to read wayland events", .{});
- os.exit(1);
+ const errno = self.display.flush();
+ switch (errno) {
+ .SUCCESS => return .ok,
+ else => return .terminate,
}
}
-fn registryListener(
- registry: *wl.Registry,
- event: wl.Registry.Event,
- state: *State,
-) void {
+fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *State) void {
const self = &state.wayland;
-
switch (event) {
.global => |g| {
- self.bindGlobal(registry, g.interface, g.name) catch return;
+ self.bindGlobal(registry, g.name, g.interface, g.version) catch |err| switch (err) {
+ error.OutOfMemory => {
+ log.err("out of memory", .{});
+ return;
+ },
+ };
},
- .global_remove => |data| {
- for (self.monitors.items) |monitor, i| {
- if (monitor.globalName == data.name) {
- monitor.destroy();
- _ = self.monitors.swapRemove(i);
- break;
- }
- }
- for (self.inputs.items) |input, i| {
- if (input.globalName == data.name) {
- input.destroy();
- _ = self.inputs.swapRemove(i);
- break;
- }
- }
+ .global_remove => |g| {
+ for (self.monitors.items) |monitor, i| if (monitor.globalName == g.name) {
+ monitor.destroy();
+ _ = self.monitors.swapRemove(i);
+ break;
+ };
+ for (self.inputs.items) |input, i| if (input.globalName == g.name) {
+ input.destroy();
+ _ = self.inputs.swapRemove(i);
+ break;
+ };
},
}
}
-fn bindGlobal(
- self: *Wayland,
- registry: *wl.Registry,
- iface: [*:0]const u8,
- name: u32,
-) !void {
+fn bindGlobal(self: *Wayland, registry: *wl.Registry, name: u32, iface: [*:0]const u8, version: u32) !void {
if (strcmp(iface, wl.Compositor.getInterface().name) == 0) {
+ if (version < 4) utils.fatal("wl_compositor version 4 is required", .{});
const global = try registry.bind(name, wl.Compositor, 4);
self.setGlobal(global);
} else if (strcmp(iface, wl.Subcompositor.getInterface().name) == 0) {
@@ -187,9 +161,11 @@ fn bindGlobal(
const global = try registry.bind(name, zriver.ControlV1, 1);
self.setGlobal(global);
} else if (strcmp(iface, wl.Output.getInterface().name) == 0) {
+ if (version < 3) utils.fatal("wl_output version 3 is required", .{});
const monitor = try Monitor.create(self.state, registry, name);
try self.monitors.append(monitor);
} else if (strcmp(iface, wl.Seat.getInterface().name) == 0) {
+ if (version < 5) utils.fatal("wl_seat version 5 is required", .{});
const input = try Input.create(self.state, registry, name);
try self.inputs.append(input);
}
diff --git a/src/main.zig b/src/main.zig
@@ -1,6 +1,7 @@
const std = @import("std");
const heap = std.heap;
const io = std.io;
+const log = std.log;
const mem = std.mem;
const os = std.os;
@@ -28,7 +29,7 @@ pub fn main() anyerror!void {
// cli arguments
const params = comptime [_]clap.Param(clap.Help){
- try clap.parseParam("-h, --help Display this help and exit."),
+ try clap.parseParam("-h, --help Display this help and exit."),
try clap.parseParam("-m, --module <str>... Add module."),
};
var args = try clap.parse(clap.Help, ¶ms, .{});
@@ -56,13 +57,13 @@ pub fn main() anyerror!void {
} else if (mem.eql(u8, module_name, "pulse")) {
try state.modules.register(Modules.Pulse);
} else {
- std.log.err("unknown module: {s}", .{module_name});
- os.exit(1);
+ log.err("unknown module: {s}", .{module_name});
+ return clap.help(io.getStdErr().writer(), ¶ms);
}
}
if (state.modules.modules.items.len == 0) {
- std.log.err("having no module is currently not supported", .{});
+ log.err("having no modules is currently not supported", .{});
return clap.help(io.getStdErr().writer(), ¶ms);
}
diff --git a/src/modules/Backlight.zig b/src/modules/Backlight.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const fmt = std.fmt;
+const log = std.log;
const mem = std.mem;
const os = std.os;
@@ -68,13 +69,14 @@ fn getEvent(self_opaque: *anyopaque) !Event {
};
}
-fn callbackIn(self_opaque: *anyopaque) void {
+fn callbackIn(self_opaque: *anyopaque) Event.Action {
const self = utils.cast(Backlight)(self_opaque);
- _ = self.monitor.receiveDevice() catch {
- std.log.err("failed to receive udev device", .{});
- os.exit(1);
+ _ = self.monitor.receiveDevice() catch |err| {
+ log.err("failed to receive udev device: {s}", .{@errorName(err)});
+ return .terminate;
};
+
for (self.state.wayland.monitors.items) |monitor| {
if (monitor.bar) |bar| {
if (bar.configured) {
@@ -84,6 +86,7 @@ fn callbackIn(self_opaque: *anyopaque) void {
}
}
}
+ return .ok;
}
fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void {
diff --git a/src/modules/Battery.zig b/src/modules/Battery.zig
@@ -1,5 +1,6 @@
const std = @import("std");
const fmt = std.fmt;
+const log = std.log;
const mem = std.mem;
const os = std.os;
@@ -107,11 +108,14 @@ pub fn destroy(self_opaque: *anyopaque) void {
self.state.gpa.destroy(self);
}
-fn callbackIn(self_opaque: *anyopaque) void {
+fn callbackIn(self_opaque: *anyopaque) Event.Action {
const self = utils.cast(Battery)(self_opaque);
var expirations = mem.zeroes([8]u8);
- _ = os.read(self.timerFd, &expirations) catch return;
+ _ = os.read(self.timerFd, &expirations) catch |err| {
+ log.err("failed to read timer: {s}", .{@errorName(err)});
+ return .terminate;
+ };
for (self.state.wayland.monitors.items) |monitor| {
if (monitor.bar) |bar| {
@@ -124,6 +128,7 @@ fn callbackIn(self_opaque: *anyopaque) void {
}
}
}
+ return .ok;
}
fn updateDevices(
diff --git a/src/modules/Pulse.zig b/src/modules/Pulse.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const log = std.log;
const mem = std.mem;
const os = std.os;
@@ -67,13 +68,13 @@ fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void {
}
}
-fn callbackIn(self_opaque: *anyopaque) void {
+fn callbackIn(self_opaque: *anyopaque) Event.Action {
const self = utils.cast(Pulse)(self_opaque);
var data = mem.zeroes([8]u8);
- _ = os.read(self.fd, &data) catch {
- std.log.err("read failed", .{});
- os.exit(1);
+ _ = os.read(self.fd, &data) catch |err| {
+ log.err("pulse: failed to read: {s}", .{@errorName(err)});
+ return .terminate;
};
for (self.state.wayland.monitors.items) |monitor| {
@@ -87,6 +88,7 @@ fn callbackIn(self_opaque: *anyopaque) void {
}
}
}
+ return .ok;
}
fn destroy(self_opaque: *anyopaque) void {
@@ -153,10 +155,10 @@ export fn contextStateCallback(
_ = pulse.pa_context_subscribe(ctx, mask, null, null);
},
pulse.PA_CONTEXT_TERMINATED, pulse.PA_CONTEXT_FAILED => {
- std.log.info("pulse: restarting", .{});
+ log.info("pulse: restarting", .{});
self.deinitPulse();
self.initPulse() catch return;
- std.log.info("pulse: restarted", .{});
+ log.info("pulse: restarted", .{});
},
else => {},
}
@@ -171,7 +173,7 @@ export fn serverInfoCallback(
self.sink_name = mem.span(info.?.default_sink_name);
self.sink_is_running = true;
- std.log.info("pulse: sink set to {s}", .{self.sink_name});
+ log.info("pulse: sink set to {s}", .{self.sink_name});
_ = pulse.pa_context_get_sink_info_list(ctx, sinkInfoCallback, self_opaque);
}
@@ -214,7 +216,7 @@ export fn sinkInfoCallback(
if (!self.sink_is_running and is_running) {
self.sink_name = sink_name;
self.sink_is_running = true;
- std.log.info("pulse: sink set to {s}", .{sink_name});
+ log.info("pulse: sink set to {s}", .{sink_name});
}
self.volume = volume: {
diff --git a/src/utils.zig b/src/utils.zig
@@ -1,8 +1,15 @@
const std = @import("std");
+const log = std.log;
const mem = std.mem;
const meta = std.meta;
+const os = std.os;
const unicode = std.unicode;
+pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
+ log.err(format, args);
+ os.exit(1);
+}
+
pub fn cast(comptime to: type) fn (*anyopaque) *to {
return (struct {
pub fn cast(module: *anyopaque) *to {