stevee

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

commit 0dda108b1b88baeb9ffe236c3239e5777d66a190
parent 17cbb4975c2b8e2fb80abc70425609e8836d1327
Author: Andrea Feletto <andrea@andreafeletto.com>
Date:   Sat, 29 Jan 2022 15:54:08 +0100

use udev for backlight

Diffstat:
Mbuild.zig | 1+
Asrc/c.zig | 2++
Msrc/event.zig | 59++++++++++++++++++++++++++++++++++++++++-------------------
Msrc/modules.zig | 121++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msrc/render.zig | 10++++++----
5 files changed, 120 insertions(+), 73 deletions(-)

diff --git a/build.zig b/build.zig @@ -32,6 +32,7 @@ pub fn build(b: *std.build.Builder) void { exe.setBuildMode(mode); exe.linkLibC(); + exe.linkSystemLibrary("libudev"); exe.addPackage(wayland); exe.linkSystemLibrary("wayland-client"); diff --git a/src/c.zig b/src/c.zig @@ -0,0 +1,2 @@ +pub const udev = @cImport(@cInclude("libudev.h")); +pub const time = @cImport(@cInclude("time.h")); diff --git a/src/event.zig b/src/event.zig @@ -5,12 +5,14 @@ const ArrayList = std.ArrayList; const wl = @import("wayland").client.wl; +const c = @import("c.zig"); const render = @import("render.zig"); const State = @import("main.zig").State; pub const Loop = struct { state: *State, - fds: [4]os.pollfd, + fds: [5]os.pollfd, + monitor: *c.udev.udev_monitor, pub fn init(state: *State) !Loop { // signals @@ -35,6 +37,19 @@ pub const Loop = struct { // inotify const ifd = os.linux.inotify_init1(os.linux.IN.CLOEXEC); + // udev + const udev = c.udev.udev_new(); + if (udev == null) return error.UdevError; + const monitor = c.udev.udev_monitor_new_from_netlink(udev, "udev"); + if (monitor == null) return error.UdevError; + _ = c.udev.udev_monitor_filter_add_match_subsystem_devtype( + monitor, + "backlight", + null, + ); + _ = c.udev.udev_monitor_enable_receiving(monitor); + const ufd = c.udev.udev_monitor_get_fd(monitor); + return Loop{ .state = state, .fds = .{ @@ -58,7 +73,13 @@ pub const Loop = struct { .events = os.POLL.IN, .revents = 0, }, + .{ + .fd = @intCast(os.fd_t, ufd), + .events = os.POLL.IN, + .revents = 0, + }, }, + .monitor= monitor.?, }; } @@ -100,16 +121,7 @@ pub const Loop = struct { if (self.fds[2].revents & os.POLL.IN != 0) { var expirations = mem.zeroes([8]u8); _ = try os.read(tfd, &expirations); - - for (self.state.wayland.outputs.items) |output| { - if (output.surface) |surface| { - if (surface.configured) { - render.renderClock(surface) catch continue; - surface.clockSurface.commit(); - surface.backgroundSurface.commit(); - } - } - } + self.renderAllSurfaces(render.renderClock); } // inotify @@ -117,15 +129,24 @@ pub const Loop = struct { const ifd = self.fds[3].fd; var event = mem.zeroes(os.linux.inotify_event); _ = try os.read(ifd, mem.asBytes(&event)); + self.renderAllSurfaces(render.renderModules); + } + + // udev + if (self.fds[4].revents & os.POLL.IN != 0) { + _ = c.udev.udev_monitor_receive_device(self.monitor); + self.renderAllSurfaces(render.renderModules); + } + } + } - for (self.state.wayland.outputs.items) |output| { - if (output.surface) |surface| { - if (surface.configured) { - render.renderModules(surface) catch continue; - surface.modulesSurface.commit(); - surface.backgroundSurface.commit(); - } - } + fn renderAllSurfaces(self: *Loop, renderFn: render.RenderFn) void { + for (self.state.wayland.outputs.items) |output| { + if (output.surface) |surface| { + if (surface.configured) { + renderFn(surface) catch continue; + surface.modulesSurface.commit(); + surface.backgroundSurface.commit(); } } } diff --git a/src/modules.zig b/src/modules.zig @@ -5,6 +5,7 @@ const io = std.io; const mem = std.mem; const os = std.os; +const c = @import("c.zig"); const State = @import("main.zig").State; const StringWriter = std.ArrayList(u8).Writer; @@ -50,7 +51,7 @@ pub const Battery = struct { defer state.allocator.free(uevent_path); const watch = os.linux.inotify_add_watch( - state.loop.fds[2].fd, + state.loop.fds[3].fd, uevent_path, os.linux.IN.ACCESS, ); @@ -127,35 +128,28 @@ pub const Battery = struct { pub const Backlight = struct { state: *State, - path: []const u8, - watch: i32, + udev: *c.udev.udev, + devices: DeviceList, - pub const Data = struct { - value: u8, + const Device = struct { + name: []const u8, + value: u64, + max: u64, }; + const DeviceList = std.ArrayList(Device); pub fn init(state: *State) !Backlight { - const path = try fs.path.join( - state.allocator, - &.{ "/sys/class/backlight", state.config.backlightDev }, - ); + const udev = c.udev.udev_new(); + if (udev == null) return error.UdevError; - const uevent_path = try fs.path.joinZ( - state.allocator, - &.{ path, "brightness" }, - ); - defer state.allocator.free(uevent_path); - - const watch = os.linux.inotify_add_watch( - state.loop.fds[2].fd, - uevent_path, - os.linux.IN.ACCESS, - ); + var devices = DeviceList.init(state.allocator); + try updateDevices(state.allocator, udev.?, &devices); + if (devices.items.len == 0) return error.NoDevicesFound; return Backlight{ .state = state, - .path = path, - .watch = @intCast(i32, watch), + .udev = udev.?, + .devices = devices, }; } @@ -166,39 +160,66 @@ pub const Backlight = struct { pub fn print(self_opaque: *anyopaque, writer: StringWriter) !void { const self = Module.cast(Backlight)(self_opaque); - const data = try self.readData(); - try fmt.format(writer, "💡 {d}%", .{ data.value }); - } - - fn readData(self: *const Backlight) !Data { - const value = try self.readInt("actual_brightness"); - const max = try self.readInt("max_brightness"); + try updateDevices(self.state.allocator, self.udev, &self.devices); + const device = self.devices.items[0]; + var percent = @intToFloat(f64, device.value) * 100.0; + percent /= @intToFloat(f64, device.max); + const value = @floatToInt(u8, @round(percent)); - const percent = @intToFloat(f64, value) * 100.0 / @intToFloat(f64, max); - - return Data{ .value = @floatToInt(u8, @round(percent)) }; + try writer.print("💡 {d}%", .{value}); } - fn readInt(self: *const Backlight, filename: []const u8) !u32 { - const value = try self.readValue(filename); - defer self.state.allocator.free(value); - - return fmt.parseInt(u32, value, 10); + fn updateDevices( + allocator: mem.Allocator, + udev: *c.udev.udev, + devices: *DeviceList, + ) !void { + const enumerate = c.udev.udev_enumerate_new(udev); + _ = c.udev.udev_enumerate_add_match_subsystem(enumerate, "backlight"); + _ = c.udev.udev_enumerate_scan_devices(enumerate); + const entries = c.udev.udev_enumerate_get_list_entry(enumerate); + + var entry = entries; + while (entry != null) { + const path = c.udev.udev_list_entry_get_name(entry); + const device = c.udev.udev_device_new_from_syspath(udev, path); + try updateOrAppend(allocator, devices, device.?); + + entry = c.udev.udev_list_entry_get_next(entries); + } } - fn readValue(self: *const Backlight, filename: []const u8) ![]u8 { - const state = self.state; - - const path = try fs.path.join( - state.allocator, - &.{ self.path, filename }, + fn updateOrAppend( + allocator: mem.Allocator, + devices: *DeviceList, + dev: *c.udev.udev_device, + ) !void { + const value_c = c.udev.udev_device_get_sysattr_value( + dev, + "actual_brightness", ); - defer state.allocator.free(path); - - const file = try fs.openFileAbsolute(path, .{}); - defer file.close(); - - var str = try file.readToEndAlloc(state.allocator, 128); - return state.allocator.resize(str, str.len - 1).?; + const max_c = c.udev.udev_device_get_sysattr_value( + dev, + "max_brightness", + ); + const name_c = c.udev.udev_device_get_sysname(dev); + + const value = mem.span(value_c); + const max = mem.span(max_c); + const name = mem.span(name_c); + + const device = blk: { + for (devices.items) |*device| { + if (mem.eql(u8, device.name, name)) { + break :blk device; + } + } else { + const device = try devices.addOne(); + device.name = try allocator.dupe(u8, name); + break :blk device; + } + }; + device.value = try fmt.parseInt(u64, value, 10); + device.max = try fmt.parseInt(u64, max, 10); } }; diff --git a/src/render.zig b/src/render.zig @@ -1,16 +1,18 @@ const std = @import("std"); const mem = std.mem; -const cTime = @cImport(@cInclude("time.h")); const fcft = @import("fcft"); const pixman = @import("pixman"); const Buffer = @import("shm.zig").Buffer; +const c = @import("c.zig"); const State = @import("main.zig").State; const Surface = @import("wayland.zig").Surface; const Tag = @import("tags.zig").Tag; const Tags = @import("tags.zig").Tags; +pub const RenderFn = fn (*Surface) anyerror!void; + pub fn renderBackground(surface: *Surface) !void { const state = surface.output.state; const wlSurface = surface.backgroundSurface; @@ -239,9 +241,9 @@ fn renderTag( fn formatDatetime(state: *State) ![]const u8 { var buf = try state.allocator.alloc(u8, 256); - const now = cTime.time(null); - const local = cTime.localtime(&now); - const len = cTime.strftime( + const now = c.time.time(null); + const local = c.time.localtime(&now); + const len = c.time.strftime( buf.ptr, buf.len, state.config.clockFormat,