commit 0dda108b1b88baeb9ffe236c3239e5777d66a190
parent 17cbb4975c2b8e2fb80abc70425609e8836d1327
Author: Andrea Feletto <andrea@andreafeletto.com>
Date: Sat, 29 Jan 2022 15:54:08 +0100
use udev for backlight
Diffstat:
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,