stevee

My wayland statusbar
git clone git://gtms.dev/stevee
Log | Files | Refs | Submodules | README | LICENSE

commit a28f39e9f7014e1cea6976522693c0ec740d094f
parent c0f0e5a8160064e8dfbf98b7895a4dab9a03ffd9
Author: Andrea Feletto <andrea@andreafeletto.com>
Date:   Thu, 30 Jun 2022 18:31:01 +0200

remove inheritance for modules

Diffstat:
Msrc/Loop.zig | 122++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/Modules.zig | 65+++++++++++++++++++++++++++++------------------------------------
Msrc/Wayland.zig | 97+++++++++++++++++++++++++++++--------------------------------------------------
Msrc/main.zig | 36++++++++++++++++++++----------------
Msrc/modules/Backlight.zig | 83+++++++++++++++++++++++--------------------------------------------------------
Msrc/modules/Battery.zig | 78+++++++++++++++++++++---------------------------------------------------------
Msrc/modules/Pulse.zig | 131++++++++++++++++++++++++++++++-------------------------------------------------
Msrc/render.zig | 14+++++++++++---
Msrc/utils.zig | 5-----
9 files changed, 255 insertions(+), 376 deletions(-)

diff --git a/src/Loop.zig b/src/Loop.zig @@ -10,24 +10,6 @@ const Loop = @This(); state: *State, sfd: os.fd_t, -pub const Event = struct { - fd: os.pollfd, - data: *anyopaque, - callbackIn: Callback, - callbackOut: Callback, - - pub const Action = enum { ok, terminate }; - pub const Callback = fn (*anyopaque) Action; - - pub fn terminate(_: *anyopaque) Action { - return .terminate; - } - - pub fn noop(_: *anyopaque) Action { - return .ok; - } -}; - pub fn init(state: *State) !Loop { var mask = os.empty_sigset; os.linux.sigaddset(&mask, os.linux.SIG.INT); @@ -41,56 +23,82 @@ pub fn init(state: *State) !Loop { } pub fn run(self: *Loop) !void { - const gpa = self.state.gpa; - const display = self.state.wayland.display; + const wayland = &self.state.wayland; + const modules = &self.state.modules; + + var fds = [_]os.pollfd{ + .{ + .fd = self.sfd, + .events = os.POLL.IN, + .revents = undefined, + }, + .{ + .fd = wayland.fd, + .events = os.POLL.IN, + .revents = undefined, + }, + .{ + .fd = if (modules.backlight) |mod| mod.fd else -1, + .events = os.POLL.IN, + .revents = undefined, + }, + .{ + .fd = if (modules.battery) |mod| mod.fd else -1, + .events = os.POLL.IN, + .revents = undefined, + }, + .{ + .fd = if (modules.pulse) |mod| mod.fd else -1, + .events = os.POLL.IN, + .revents = undefined, + }, + }; - var events: std.MultiArrayList(Event) = .{}; - defer events.deinit(gpa); - - try events.append(gpa, .{ - .fd = .{ .fd = self.sfd, .events = os.POLL.IN, .revents = 0 }, - .data = undefined, - .callbackIn = Event.terminate, - .callbackOut = Event.noop, - }); - try events.append(gpa, try self.state.wayland.getEvent()); - for (self.state.modules.modules.items) |*module| { - try events.append(gpa, try module.getEvent()); - } - - const fds = events.items(.fd); while (true) { while (true) { - const ret = display.dispatchPending(); - _ = display.flush(); + const ret = wayland.display.dispatchPending(); + _ = wayland.display.flush(); if (ret == .SUCCESS) break; } - _ = os.poll(fds, -1) catch |err| { + _ = 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; - if (fd.revents & os.POLL.ERR != 0) return; - - if (fd.revents & os.POLL.IN != 0) { - const event = events.get(i); - const action = event.callbackIn(event.data); - switch (action) { - .ok => {}, - .terminate => return, - } - } - if (fd.revents & os.POLL.OUT != 0) { - const event = events.get(i); - const action = event.callbackOut(event.data); - switch (action) { - .ok => {}, - .terminate => return, - } + for (fds) |fd| { + if (fd.revents & os.POLL.HUP != 0 or fd.revents & os.POLL.ERR != 0) { + return; } } + + // signals + if (fds[0].revents & os.POLL.IN != 0) { + return; + } + + // wayland + if (fds[1].revents & os.POLL.IN != 0) { + const errno = wayland.display.dispatch(); + if (errno != .SUCCESS) return; + } + if (fds[1].revents & os.POLL.OUT != 0) { + const errno = wayland.display.flush(); + if (errno != .SUCCESS) return; + } + + // modules + if (modules.backlight) |*mod| if (fds[2].revents & os.POLL.IN != 0) { + log.info("backlight", .{}); + mod.refresh() catch return; + }; + if (modules.battery) |*mod| if (fds[3].revents & os.POLL.IN != 0) { + log.info("battery", .{}); + mod.refresh() catch return; + }; + if (modules.pulse) |*mod| if (fds[4].revents & os.POLL.IN != 0) { + log.info("pulse", .{}); + mod.refresh() catch return; + }; } } diff --git a/src/Modules.zig b/src/Modules.zig @@ -1,55 +1,48 @@ const std = @import("std"); +const mem = std.mem; const ArrayList = std.ArrayList; -const log = std.log; -const Event = @import("Loop.zig").Event; const State = @import("main.zig").State; const Modules = @This(); -state: *State, -modules: ArrayList(Module), - -pub const Backlight = @import("modules/Backlight.zig"); -pub const Battery = @import("modules/Battery.zig"); -pub const Pulse = @import("modules/Pulse.zig"); - -pub const Module = struct { - impl: *anyopaque, - funcs: struct { - getEvent: fn (*anyopaque) anyerror!Event, - print: fn (*anyopaque, StringWriter) anyerror!void, - destroy: fn (*anyopaque) void, - }, - - pub const StringWriter = std.ArrayList(u8).Writer; - - pub fn getEvent(self: *Module) !Event { - return self.funcs.getEvent(self.impl); - } +const Backlight = @import("modules/Backlight.zig"); +const Battery = @import("modules/Battery.zig"); +const Pulse = @import("modules/Pulse.zig"); - pub fn print(self: *Module, writer: StringWriter) !void { - return self.funcs.print(self.impl, writer); - } +const Tag = enum { backlight, battery, pulse }; - pub fn destroyInstance(self: *Module) void { - return self.funcs.destroy(self.impl); - } -}; +state: *State, +backlight: ?Backlight = null, +battery: ?Battery = null, +pulse: ?Pulse = null, +order: ArrayList(Tag), pub fn init(state: *State) Modules { return Modules{ .state = state, - .modules = ArrayList(Module).init(state.gpa), + .order = ArrayList(Tag).init(state.gpa), }; } pub fn deinit(self: *Modules) void { - for (self.modules.items) |*module| module.destroyInstance(); - self.modules.deinit(); + if (self.backlight) |*mod| mod.deinit(); + if (self.battery) |*mod| mod.deinit(); + if (self.pulse) |*mod| mod.deinit(); + self.order.deinit(); } -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)}); +pub fn register(self: *Modules, name: []const u8) !void { + if (mem.eql(u8, name, "backlight")) { + self.backlight = try Backlight.init(self.state); + try self.order.append(.backlight); + } else if (mem.eql(u8, name, "battery")) { + self.battery = try Battery.init(self.state); + try self.order.append(.battery); + } else if (mem.eql(u8, name, "pulse")) { + self.pulse = try Pulse.init(self.state); + try self.pulse.?.start(); + try self.order.append(.pulse); + } else { + return error.UnknownModule; + } } diff --git a/src/Wayland.zig b/src/Wayland.zig @@ -21,6 +21,7 @@ const Wayland = @This(); state: *State, display: *wl.Display, +fd: os.fd_t, monitors: ArrayList(*Monitor), inputs: ArrayList(*Input), @@ -39,13 +40,16 @@ const Globals = struct { const GlobalsMask = utils.Mask(Globals); pub fn init(state: *State) !Wayland { - const display = wl.Display.connect(null) catch |err| { - utils.fatal("failed to connect to a wayland compositor: {s}", .{@errorName(err)}); + const display = try wl.Display.connect(null); + const wfd = wfd: { + const fd = display.getFd(); + break :wfd @intCast(os.fd_t, fd); }; return Wayland{ .state = state, .display = display, + .fd = wfd, .monitors = ArrayList(*Monitor).init(state.gpa), .inputs = ArrayList(*Input).init(state.gpa), .globals = undefined, @@ -69,53 +73,33 @@ pub fn deinit(self: *Wayland) void { } pub fn registerGlobals(self: *Wayland) !void { - const registry = self.display.getRegistry() catch |err| { - utils.fatal("out of memory during initialization: {s}", .{@errorName(err)}); - }; + const registry = try self.display.getRegistry(); defer registry.destroy(); registry.setListener(*State, registryListener, self.state); const errno = self.display.roundtrip(); - if (errno != .SUCCESS) { - utils.fatal("initial roundtrip failed", .{}); - } - + if (errno != .SUCCESS) return error.RoundtripFailed; for (self.globalsMask) |is_registered| if (!is_registered) { - utils.fatal("global not advertised", .{}); - }; -} - -pub fn getEvent(self: *Wayland) !Event { - const fd = self.display.getFd(); - - return Event{ - .fd = .{ - .fd = @intCast(os.fd_t, fd), - .events = os.POLL.IN, - .revents = undefined, - }, - .data = @ptrCast(*anyopaque, self), - .callbackIn = dispatch, - .callbackOut = flush, + return error.GlobalNotAdvertized; }; } -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, +pub fn findBar(self: *Wayland, wlSurface: ?*wl.Surface) ?*Bar { + if (wlSurface == null) { + return null; } -} - -fn flush(self_opaque: *anyopaque) Event.Action { - const self = utils.cast(Wayland)(self_opaque); - const errno = self.display.flush(); - switch (errno) { - .SUCCESS => return .ok, - else => return .terminate, + for (self.monitors.items) |monitor| { + if (monitor.bar) |bar| { + if (bar.background.surface == wlSurface or + bar.tags.surface == wlSurface or + bar.clock.surface == wlSurface or + bar.modules.surface == wlSurface) + { + return bar; + } + } } + return null; } fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *State) void { @@ -146,7 +130,10 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *St 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", .{}); + if (version < 4) { + log.err("wl_compositor version 4 is required", .{}); + return; + } const global = try registry.bind(name, wl.Compositor, 4); self.setGlobal(global); } else if (strcmp(iface, wl.Subcompositor.getInterface().name) == 0) { @@ -168,17 +155,23 @@ fn bindGlobal(self: *Wayland, registry: *wl.Registry, name: u32, iface: [*:0]con 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", .{}); + if (version < 3) { + log.err("wl_output version 3 is required", .{}); + return; + } 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", .{}); + if (version < 5) { + log.err("wl_seat version 5 is required", .{}); + return; + } const input = try Input.create(self.state, registry, name); try self.inputs.append(input); } } -pub fn setGlobal(self: *Wayland, global: anytype) void { +fn setGlobal(self: *Wayland, global: anytype) void { inline for (meta.fields(Globals)) |field, i| { if (field.field_type == @TypeOf(global)) { @field(self.globals, field.name) = global; @@ -187,21 +180,3 @@ pub fn setGlobal(self: *Wayland, global: anytype) void { } } } - -pub fn findBar(self: *Wayland, wlSurface: ?*wl.Surface) ?*Bar { - if (wlSurface == null) { - return null; - } - for (self.monitors.items) |monitor| { - if (monitor.bar) |bar| { - if (bar.background.surface == wlSurface or - bar.tags.surface == wlSurface or - bar.clock.surface == wlSurface or - bar.modules.surface == wlSurface) - { - return bar; - } - } - } - return null; -} diff --git a/src/main.zig b/src/main.zig @@ -42,21 +42,24 @@ pub fn main() anyerror!void { const program_name = args.nextPosix() orelse unreachable; while (args.nextPosix()) |arg| { - if (mem.eql(u8, arg, "backlight")) { - try state.modules.register(Modules.Backlight); - } else if (mem.eql(u8, arg, "battery")) { - try state.modules.register(Modules.Battery); - } else if (mem.eql(u8, arg, "pulse")) { - try state.modules.register(Modules.Pulse); - } else { - try help(program_name); + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + help(program_name); return; } - } - - if (state.modules.modules.items.len == 0) { - try help(program_name); - return; + state.modules.register(arg) catch |err| { + switch (err) { + error.UnknownModule => { + log.err("unknown module: {s}", .{arg}); + }, + else => { + log.err( + "initialization error for module {s}: {s}", + .{ arg, @errorName(err) }, + ); + }, + } + return; + }; } // event loop @@ -64,8 +67,8 @@ pub fn main() anyerror!void { try state.loop.run(); } -fn help(program_name: []const u8) !void { - const help_text = +fn help(program_name: []const u8) void { + const text = \\Usage: {s} [module]... \\ \\Available modules: @@ -74,5 +77,6 @@ fn help(program_name: []const u8) !void { \\ pulse speaker volume with pulseaudio \\ ; - try io.getStdErr().writer().print(help_text, .{program_name}); + const w = io.getStdErr().writer(); + w.print(text, .{program_name}) catch unreachable; } diff --git a/src/modules/Backlight.zig b/src/modules/Backlight.zig @@ -6,8 +6,6 @@ const os = std.os; const udev = @import("udev"); -const Module = @import("../Modules.zig").Module; -const Event = @import("../Loop.zig").Event; const render = @import("../render.zig"); const State = @import("../main.zig").State; const utils = @import("../utils.zig"); @@ -16,6 +14,7 @@ const Backlight = @This(); state: *State, context: *udev.Udev, monitor: *udev.Monitor, +fd: os.fd_t, devices: DeviceList, const Device = struct { @@ -26,56 +25,36 @@ const Device = struct { const DeviceList = std.ArrayList(Device); -pub fn create(state: *State) !*Backlight { - const self = try state.gpa.create(Backlight); - self.state = state; - self.context = try udev.Udev.new(); +pub fn init(state: *State) !Backlight { + const context = try udev.Udev.new(); - self.monitor = try udev.Monitor.newFromNetlink(self.context, "udev"); - try self.monitor.filterAddMatchSubsystemDevType("backlight", null); - try self.monitor.filterAddMatchSubsystemDevType("power_supply", null); - try self.monitor.enableReceiving(); + const monitor = try udev.Monitor.newFromNetlink(context, "udev"); + try monitor.filterAddMatchSubsystemDevType("backlight", null); + try monitor.filterAddMatchSubsystemDevType("power_supply", null); + try monitor.enableReceiving(); - self.devices = DeviceList.init(state.gpa); - try updateDevices(state.gpa, self.context, &self.devices); - if (self.devices.items.len == 0) return error.NoDevicesFound; + var devices = DeviceList.init(state.gpa); + try updateDevices(state.gpa, context, &devices); - return self; -} - -pub fn module(self: *Backlight) !Module { - return Module{ - .impl = @ptrCast(*anyopaque, self), - .funcs = .{ - .getEvent = getEvent, - .print = print, - .destroy = destroy, - }, + return Backlight{ + .state = state, + .context = context, + .monitor = monitor, + .fd = try monitor.getFd(), + .devices = devices, }; } -fn getEvent(self_opaque: *anyopaque) !Event { - const self = utils.cast(Backlight)(self_opaque); - - return Event{ - .fd = .{ - .fd = try self.monitor.getFd(), - .events = os.POLL.IN, - .revents = undefined, - }, - .data = self_opaque, - .callbackIn = callbackIn, - .callbackOut = Event.noop, - }; +pub fn deinit(self: *Backlight) void { + _ = self.context.unref(); + for (self.devices.items) |*device| { + self.state.gpa.free(device.name); + } + self.devices.deinit(); } -fn callbackIn(self_opaque: *anyopaque) Event.Action { - const self = utils.cast(Backlight)(self_opaque); - - _ = self.monitor.receiveDevice() catch |err| { - log.err("failed to receive udev device: {s}", .{@errorName(err)}); - return .terminate; - }; +pub fn refresh(self: *Backlight) !void { + _ = try self.monitor.receiveDevice(); for (self.state.wayland.monitors.items) |monitor| { if (monitor.bar) |bar| { @@ -86,12 +65,9 @@ fn callbackIn(self_opaque: *anyopaque) Event.Action { } } } - return .ok; } -fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void { - const self = utils.cast(Backlight)(self_opaque); - +pub fn print(self: *Backlight, writer: anytype) !void { try updateDevices(self.state.gpa, self.context, &self.devices); const device = self.devices.items[0]; var percent = @intToFloat(f64, device.value) * 100.0; @@ -142,14 +118,3 @@ fn updateOrAppend( device.value = try fmt.parseInt(u64, value, 10); device.max = try fmt.parseInt(u64, max, 10); } - -fn destroy(self_opaque: *anyopaque) void { - const self = utils.cast(Backlight)(self_opaque); - - _ = self.context.unref(); - for (self.devices.items) |*device| { - self.state.gpa.free(device.name); - } - self.devices.deinit(); - self.state.gpa.destroy(self); -} diff --git a/src/modules/Battery.zig b/src/modules/Battery.zig @@ -15,7 +15,7 @@ const Battery = @This(); state: *State, context: *udev.Udev, -timerFd: os.fd_t, +fd: os.fd_t, devices: DeviceList, const Device = struct { @@ -26,11 +26,8 @@ const Device = struct { const DeviceList = std.ArrayList(Device); -pub fn create(state: *State) !*Battery { - const self = try state.gpa.create(Battery); - self.state = state; - - self.timerFd = tfd: { +pub fn init(state: *State) !Battery { + const tfd = tfd: { const fd = os.linux.timerfd_create( os.CLOCK.MONOTONIC, os.linux.TFD.CLOEXEC, @@ -43,44 +40,29 @@ pub fn create(state: *State) !*Battery { break :tfd @intCast(os.fd_t, fd); }; - self.context = try udev.Udev.new(); - - self.devices = DeviceList.init(state.gpa); - try updateDevices(state.gpa, self.context, &self.devices); - if (self.devices.items.len == 0) return error.NoDevicesFound; + const context = try udev.Udev.new(); - return self; -} + var devices = DeviceList.init(state.gpa); + try updateDevices(state.gpa, context, &devices); -pub fn module(self: *Battery) !Module { - return Module{ - .impl = @ptrCast(*anyopaque, self), - .funcs = .{ - .getEvent = getEvent, - .print = print, - .destroy = destroy, - }, + return Battery{ + .state = state, + .context = context, + .fd = tfd, + .devices = devices, }; } -pub fn getEvent(self_opaque: *anyopaque) !Event { - const self = utils.cast(Battery)(self_opaque); - - return Event{ - .fd = .{ - .fd = self.timerFd, - .events = os.POLL.IN, - .revents = undefined, - }, - .data = self_opaque, - .callbackIn = callbackIn, - .callbackOut = Event.noop, - }; +pub fn deinit(self: *Battery) void { + _ = self.context.unref(); + for (self.devices.items) |*device| { + self.state.gpa.free(device.name); + self.state.gpa.free(device.status); + } + self.devices.deinit(); } -pub fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void { - const self = utils.cast(Battery)(self_opaque); - +pub fn print(self: *Battery, writer: anytype) !void { try updateDevices(self.state.gpa, self.context, &self.devices); const device = self.devices.items[0]; @@ -96,26 +78,9 @@ pub fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void { try fmt.format(writer, "{s} {d}%", .{ icon, device.capacity }); } -pub fn destroy(self_opaque: *anyopaque) void { - const self = utils.cast(Battery)(self_opaque); - - _ = self.context.unref(); - for (self.devices.items) |*device| { - self.state.gpa.free(device.name); - self.state.gpa.free(device.status); - } - self.devices.deinit(); - self.state.gpa.destroy(self); -} - -fn callbackIn(self_opaque: *anyopaque) Event.Action { - const self = utils.cast(Battery)(self_opaque); - +pub fn refresh(self: *Battery) !void { var expirations = mem.zeroes([8]u8); - _ = os.read(self.timerFd, &expirations) catch |err| { - log.err("failed to read timer: {s}", .{@errorName(err)}); - return .terminate; - }; + _ = try os.read(self.fd, &expirations); for (self.state.wayland.monitors.items) |monitor| { if (monitor.bar) |bar| { @@ -128,7 +93,6 @@ fn callbackIn(self_opaque: *anyopaque) Event.Action { } } } - return .ok; } fn updateDevices( diff --git a/src/modules/Pulse.zig b/src/modules/Pulse.zig @@ -13,69 +13,66 @@ const utils = @import("../utils.zig"); const Pulse = @This(); state: *State, +fd: os.fd_t, mainloop: *pulse.pa_threaded_mainloop, api: *pulse.pa_mainloop_api, context: *pulse.pa_context, -fd: os.fd_t, +// owned by pulse api sink_name: []const u8, sink_is_running: bool, volume: u8, muted: bool, -pub fn create(state: *State) !*Pulse { - const self = try state.gpa.create(Pulse); - self.state = state; - self.volume = 0; - self.muted = false; - try self.initPulse(); - +pub fn init(state: *State) !Pulse { // create descriptor for poll in Loop - const fd = try os.eventfd(0, os.linux.EFD.NONBLOCK); - self.fd = @intCast(os.fd_t, fd); - - return self; -} - -pub fn module(self: *Pulse) !Module { - return Module{ - .impl = @ptrCast(*anyopaque, self), - .funcs = .{ - .getEvent = getEvent, - .print = print, - .destroy = destroy, - }, + const efd = efd: { + const fd = try os.eventfd(0, os.linux.EFD.NONBLOCK); + break :efd @intCast(os.fd_t, fd); }; -} -fn getEvent(self_opaque: *anyopaque) !Event { - const self = utils.cast(Pulse)(self_opaque); + // setup pulseaudio api + const mainloop = pulse.pa_threaded_mainloop_new() orelse { + return error.InitFailed; + }; + const api = pulse.pa_threaded_mainloop_get_api(mainloop); + const context = pulse.pa_context_new(api, "levee") orelse { + return error.InitFailed; + }; + const connected = pulse.pa_context_connect(context, null, pulse.PA_CONTEXT_NOFAIL, null); + if (connected < 0) return error.InitFailed; - return Event{ - .fd = .{ .fd = self.fd, .events = os.POLL.IN, .revents = undefined }, - .data = self_opaque, - .callbackIn = callbackIn, - .callbackOut = Event.noop, + return Pulse{ + .state = state, + .fd = efd, + .mainloop = mainloop, + .api = api, + .context = context, + .sink_name = "", + .sink_is_running = false, + .volume = 0, + .muted = false, }; } -fn print(self_opaque: *anyopaque, writer: Module.StringWriter) !void { - const self = utils.cast(Pulse)(self_opaque); - - if (self.muted) { - try writer.print(" 🔇 ", .{}); - } else { - try writer.print("🔊 {d}%", .{self.volume}); - } +pub fn deinit(self: *Pulse) void { + if (self.api.quit) |quit| quit(self.api, 0); + pulse.pa_threaded_mainloop_stop(self.mainloop); + pulse.pa_threaded_mainloop_free(self.mainloop); } -fn callbackIn(self_opaque: *anyopaque) Event.Action { - const self = utils.cast(Pulse)(self_opaque); +pub fn start(self: *Pulse) !void { + pulse.pa_context_set_state_callback( + self.context, + contextStateCallback, + @ptrCast(*anyopaque, self), + ); + const started = pulse.pa_threaded_mainloop_start(self.mainloop); + if (started < 0) return error.StartFailed; +} +pub fn refresh(self: *Pulse) !void { var data = mem.zeroes([8]u8); - _ = os.read(self.fd, &data) catch |err| { - log.err("pulse: failed to read: {s}", .{@errorName(err)}); - return .terminate; - }; + _ = try os.read(self.fd, &data); for (self.state.wayland.monitors.items) |monitor| { if (monitor.bar) |bar| { @@ -88,44 +85,14 @@ fn callbackIn(self_opaque: *anyopaque) Event.Action { } } } - return .ok; -} - -fn destroy(self_opaque: *anyopaque) void { - const self = utils.cast(Pulse)(self_opaque); - - self.deinitPulse(); - self.state.gpa.destroy(self); -} - -fn initPulse(self: *Pulse) !void { - self.mainloop = pulse.pa_threaded_mainloop_new() orelse { - return error.InitFailed; - }; - self.api = pulse.pa_threaded_mainloop_get_api(self.mainloop); - self.context = pulse.pa_context_new(self.api, "levee") orelse { - return error.InitFailed; - }; - const connected = pulse.pa_context_connect( - self.context, - null, - pulse.PA_CONTEXT_NOFAIL, - null, - ); - if (connected < 0) return error.InitFailed; - pulse.pa_context_set_state_callback( - self.context, - contextStateCallback, - @ptrCast(*anyopaque, self), - ); - const started = pulse.pa_threaded_mainloop_start(self.mainloop); - if (started < 0) return error.InitFailed; } -fn deinitPulse(self: *Pulse) void { - if (self.api.quit) |quit| quit(self.api, 0); - pulse.pa_threaded_mainloop_stop(self.mainloop); - pulse.pa_threaded_mainloop_free(self.mainloop); +pub fn print(self: *Pulse, writer: anytype) !void { + if (self.muted) { + try writer.print(" 🔇 ", .{}); + } else { + try writer.print("🔊 {d}%", .{self.volume}); + } } export fn contextStateCallback( @@ -156,8 +123,8 @@ export fn contextStateCallback( }, pulse.PA_CONTEXT_TERMINATED, pulse.PA_CONTEXT_FAILED => { log.info("pulse: restarting", .{}); - self.deinitPulse(); - self.initPulse() catch return; + self.deinit(); + self.* = Pulse.init(self.state) catch return; log.info("pulse: restarted", .{}); }, else => {}, diff --git a/src/render.zig b/src/render.zig @@ -11,6 +11,10 @@ const Bar = @import("Bar.zig"); const Tag = @import("Tags.zig").Tag; const utils = @import("utils.zig"); +const Backlight = @import("modules/Backlight.zig"); +const Battery = @import("modules/Battery.zig"); +const Pulse = @import("modules/Pulse.zig"); + pub const RenderFn = fn (*Bar) anyerror!void; pub fn renderTags(bar: *Bar) !void { @@ -102,9 +106,13 @@ pub fn renderModules(bar: *Bar) !void { defer string.deinit(); const writer = string.writer(); - for (state.modules.modules.items) |*module| { - try std.fmt.format(writer, " | ", .{}); - try module.print(writer); + for (state.modules.order.items) |tag| { + try writer.print(" | ", .{}); + switch (tag) { + .backlight => try state.modules.backlight.?.print(writer), + .battery => try state.modules.battery.?.print(writer), + .pulse => try state.modules.pulse.?.print(writer), + } } // ut8 encoding diff --git a/src/utils.zig b/src/utils.zig @@ -5,11 +5,6 @@ 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 {