master ohmu/fastfetch-openbsd / src / detection / battery / battery_bsd.c
ohmu  ·  2026-10-15
 1#include "fastfetch.h"
 2#include "common/debug.h"
 3#include "common/sysctl.h"
 4#include "common/io.h"
 5#include "battery.h"
 6
 7#include <dev/acpica/acpiio.h>
 8#include <sys/ioctl.h>
 9#include <sys/fcntl.h>
10#include <errno.h>
11#include <string.h>
12#include <unistd.h>
13
14const char* ffDetectBattery([[maybe_unused]] FFBatteryOptions* options, FFlist* results) {
15    // https://www.freebsd.org/cgi/man.cgi?acpi_battery(4)
16    // https://gitlab.xfce.org/panel-plugins/xfce4-battery-plugin/-/blob/master/panel-plugin/libacpi.c
17
18    int units = ffSysctlGetInt("hw.acpi.battery.units", -100);
19    if (units < 0) {
20        FF_DEBUG("sysctlbyname(\"hw.acpi.battery.units\") failed: %s", strerror(errno));
21        return "sysctlbyname(\"hw.acpi.battery.units\") failed";
22    }
23
24    if (units == 0) {
25        return nullptr;
26    }
27
28    FF_AUTO_CLOSE_FD int acpifd = open("/dev/acpi", O_RDONLY | O_CLOEXEC);
29    if (acpifd < 0) {
30        FF_DEBUG("open(\"/dev/acpi\", O_RDONLY | O_CLOEXEC) failed: %s", strerror(errno));
31        return "open(\"/dev/acpi\", O_RDONLY | O_CLOEXEC) failed";
32    }
33
34    for (int i = 0; i < units; ++i) {
35        union acpi_battery_ioctl_arg battio;
36        battio.unit = i;
37
38        if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) < 0 || (battio.battinfo.state == ACPI_BATT_STAT_NOT_PRESENT)) {
39            continue;
40        }
41
42        FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results);
43        battery->temperature = FF_BATTERY_TEMP_UNSET;
44        battery->cycleCount = 0;
45        ffStrbufInit(&battery->manufacturer);
46        ffStrbufInit(&battery->modelName);
47        ffStrbufInit(&battery->technology);
48        ffStrbufInit(&battery->serial);
49        ffStrbufInit(&battery->manufactureDate);
50        battery->status = FF_BATTERY_STATUS_NONE;
51        battery->timeRemaining = -1;
52        if (battio.battinfo.min > 0) {
53            battery->timeRemaining = battio.battinfo.min * 60;
54        }
55        battery->capacity = battio.battinfo.cap;
56        if (battio.battinfo.state == ACPI_BATT_STAT_INVALID) {
57            battery->status |= FF_BATTERY_STATUS_UNKNOWN;
58        } else {
59            if (battio.battinfo.state & ACPI_BATT_STAT_DISCHARG) {
60                battery->status |= FF_BATTERY_STATUS_DISCHARGING;
61            }
62            if (battio.battinfo.state & ACPI_BATT_STAT_CHARGING) {
63                battery->status |= FF_BATTERY_STATUS_CHARGING;
64            }
65            if (battio.battinfo.state & ACPI_BATT_STAT_CRITICAL) {
66                battery->status |= FF_BATTERY_STATUS_CRITICAL;
67            }
68        }
69
70        int acadStatus;
71        if (ioctl(acpifd, ACPIIO_ACAD_GET_STATUS, &acadStatus) >= 0 && acadStatus) {
72            battery->status |= FF_BATTERY_STATUS_AC_CONNECTED;
73        }
74
75#ifdef ACPIIO_BATT_GET_BIX
76        battio.unit = i;
77        if (ioctl(acpifd, ACPIIO_BATT_GET_BIX, &battio) >= 0) {
78            ffStrbufAppendS(&battery->manufacturer, battio.bix.oeminfo);
79            ffStrbufAppendS(&battery->modelName, battio.bix.model);
80            ffStrbufAppendS(&battery->technology, battio.bix.type);
81            ffStrbufAppendS(&battery->serial, battio.bix.serial);
82            battery->cycleCount = battio.bix.cycles;
83        }
84#elif defined(ACPIIO_BATT_GET_BIF)
85        battio.unit = i;
86        if (ioctl(acpifd, ACPIIO_BATT_GET_BIF, &battio) >= 0) {
87            ffStrbufAppendS(&battery->manufacturer, battio.bif.oeminfo);
88            ffStrbufAppendS(&battery->modelName, battio.bif.model);
89            ffStrbufAppendS(&battery->technology, battio.bif.type);
90            ffStrbufAppendS(&battery->serial, battio.bif.serial);
91        }
92#endif
93    }
94    return nullptr;
95}