stevee

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

Battery.zig (5059B)


      1 const std = @import("std");
      2 
      3 const udev = @import("udev");
      4 
      5 const Module = @import("../Modules.zig").Module;
      6 const Event = @import("../Loop.zig").Event;
      7 const render = @import("../render.zig");
      8 const utils = @import("../utils.zig");
      9 const Battery = @This();
     10 
     11 const state = &@import("root").state;
     12 
     13 context: *udev.Udev,
     14 fd: std.posix.fd_t,
     15 devices: DeviceList,
     16 
     17 const Device = struct {
     18     name: []const u8,
     19     status: []const u8,
     20     capacity: u8,
     21 };
     22 
     23 const DeviceList = std.ArrayList(Device);
     24 
     25 pub fn init() !Battery {
     26     const tfd = tfd: {
     27         const fd = try std.posix.timerfd_create(
     28             std.os.linux.TIMERFD_CLOCK.MONOTONIC,
     29             .{ .CLOEXEC = true },
     30         );
     31         const interval: std.os.linux.itimerspec = .{
     32             .it_interval = .{ .sec = 10, .nsec = 0 },
     33             .it_value = .{ .sec = 10, .nsec = 0 },
     34         };
     35         try std.posix.timerfd_settime(@intCast(fd), .{}, &interval, null);
     36         break :tfd @as(std.posix.fd_t, @intCast(fd));
     37     };
     38 
     39     const context = try udev.Udev.new();
     40 
     41     var devices = DeviceList.init(state.gpa);
     42     try updateDevices(state.gpa, context, &devices);
     43 
     44     return Battery{
     45         .context = context,
     46         .fd = tfd,
     47         .devices = devices,
     48     };
     49 }
     50 
     51 pub fn deinit(self: *Battery) void {
     52     _ = self.context.unref();
     53     for (self.devices.items) |*device| {
     54         state.gpa.free(device.name);
     55         state.gpa.free(device.status);
     56     }
     57     self.devices.deinit();
     58 }
     59 
     60 pub fn print(self: *Battery, writer: anytype) !void {
     61     try updateDevices(state.gpa, self.context, &self.devices);
     62     const device = self.devices.items[0];
     63 
     64     var icon: []const u8 = "❓";
     65     if (std.mem.eql(u8, device.status, "Discharging")) {
     66         icon = "󰁹";
     67     } else if (std.mem.eql(u8, device.status, "Charging")) {
     68         icon = "󰂄";
     69     } else if (std.mem.eql(u8, device.status, "Full")) {
     70         icon = "⚡";
     71     }
     72 
     73     try std.fmt.format(writer, "{s} {d}%", .{ icon, device.capacity });
     74 }
     75 
     76 pub fn refresh(self: *Battery) !void {
     77     var expirations = std.mem.zeroes([8]u8);
     78     _ = try std.posix.read(self.fd, &expirations);
     79 
     80     for (state.wayland.monitors.items) |monitor| {
     81         if (monitor.bar) |bar| {
     82             if (bar.configured) {
     83                 render.renderClock(bar) catch continue;
     84                 render.renderModules(bar) catch continue;
     85                 bar.clock.surface.commit();
     86                 bar.modules.surface.commit();
     87                 bar.background.surface.commit();
     88             }
     89         }
     90     }
     91 }
     92 
     93 fn updateDevices(
     94     gpa: std.mem.Allocator,
     95     context: *udev.Udev,
     96     devices: *DeviceList,
     97 ) !void {
     98     const enumerate = try udev.Enumerate.new(context);
     99     defer _ = enumerate.unref();
    100 
    101     try enumerate.addMatchSubsystem("power_supply");
    102     try enumerate.addMatchSysattr("type", "Battery");
    103     try enumerate.scanDevices();
    104 
    105     const entries = enumerate.getListEntry();
    106 
    107     var maybe_entry = entries;
    108     while (maybe_entry) |entry| : (maybe_entry = entry.getNext()) {
    109         const path = entry.getName();
    110         const device = try udev.Device.newFromSyspath(context, path);
    111         try updateOrAppend(gpa, devices, device);
    112     }
    113 }
    114 
    115 fn updateOrAppend(
    116     gpa: std.mem.Allocator,
    117     devices: *DeviceList,
    118     dev: *udev.Device,
    119 ) !void {
    120     const name = dev.getSysname() catch return;
    121     const status = dev.getSysattrValue("status") catch return;
    122     const capacity = getCapacity(dev) catch return;
    123 
    124     const device = blk: {
    125         for (devices.items) |*device| {
    126             if (std.mem.eql(u8, device.name, name)) {
    127                 gpa.free(device.status);
    128                 break :blk device;
    129             }
    130         } else {
    131             const device = try devices.addOne();
    132             device.name = try gpa.dupe(u8, name);
    133             break :blk device;
    134         }
    135     };
    136 
    137     device.status = try gpa.dupe(u8, status);
    138     device.capacity = capacity;
    139 }
    140 
    141 fn getCapacity(dev: *udev.Device) !u8 {
    142     const capacity_str = dev.getSysattrValue("capacity") catch {
    143         return computeCapacityFromCharge(dev) catch {
    144             return computeCapacityFromEnergy(dev);
    145         };
    146     };
    147 
    148     const capacity = try std.fmt.parseInt(u8, capacity_str, 10);
    149     return capacity;
    150 }
    151 
    152 fn computeCapacityFromEnergy(dev: *udev.Device) !u8 {
    153     const energy_str = try dev.getSysattrValue("energy_now");
    154     const energy_full_str = try dev.getSysattrValue("energy_full");
    155 
    156     const energy = try std.fmt.parseFloat(f64, energy_str);
    157     const energy_full = try std.fmt.parseFloat(f64, energy_full_str);
    158 
    159     const capacity = energy * 100.0 / energy_full;
    160     return @as(u8, @intFromFloat(@round(capacity)));
    161 }
    162 
    163 fn computeCapacityFromCharge(dev: *udev.Device) !u8 {
    164     const charge_str = try dev.getSysattrValue("charge_now");
    165     const charge_full_str = try dev.getSysattrValue("charge_full");
    166 
    167     const charge = try std.fmt.parseFloat(f64, charge_str);
    168     const charge_full = try std.fmt.parseFloat(f64, charge_full_str);
    169 
    170     const capacity = charge * 100.0 / charge_full;
    171     return @as(u8, @intFromFloat(@round(capacity)));
    172 }