master ohmu/fastfetch-openbsd / src / detection / battery / battery_nbsd.c
ohmu  ·  2026-10-15
  1#include "battery.h"
  2#include "common/debug.h"
  3#include "common/io.h"
  4#include "common/FFstrbuf.h"
  5#include "common/strutil.h"
  6
  7#include <prop/prop_array.h>
  8#include <prop/prop_bool.h>
  9#include <prop/prop_dictionary.h>
 10#include <prop/prop_object.h>
 11#include <sys/envsys.h>
 12#include <prop/proplib.h>
 13#include <paths.h>
 14#include <time.h>
 15#include <unistd.h>
 16#include <fcntl.h>
 17
 18const char* ffDetectBattery([[maybe_unused]] FFBatteryOptions* options, FFlist* results) {
 19    FF_AUTO_CLOSE_FD int fd = open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC);
 20    if (fd < 0) {
 21        FF_DEBUG("open(_PATH_SYSMON) failed: %s", strerror(errno));
 22        return "open(_PATH_SYSMON, O_RDONLY | O_CLOEXEC) failed";
 23    }
 24
 25    prop_dictionary_t root = nullptr;
 26    if (prop_dictionary_recv_ioctl(fd, ENVSYS_GETDICTIONARY, &root) < 0) {
 27        FF_DEBUG("prop_dictionary_recv_ioctl(ENVSYS_GETDICTIONARY) failed");
 28        return "prop_dictionary_recv_ioctl(ENVSYS_GETDICTIONARY) failed";
 29    }
 30
 31    bool acConnected = false;
 32    {
 33        prop_array_t acad = prop_dictionary_get(root, "acpiacad0");
 34        if (acad) {
 35            prop_dictionary_t dict = prop_array_get(acad, 0);
 36            prop_dictionary_get_uint8(dict, "cur-value", (uint8_t*) &acConnected);
 37        }
 38    }
 39
 40    prop_object_iterator_t itKey = prop_dictionary_iterator(root);
 41    for (prop_dictionary_keysym_t key; (key = prop_object_iterator_next(itKey)) != nullptr;) {
 42        if (!ffStrStartsWith(prop_dictionary_keysym_value(key), "acpibat")) {
 43            continue;
 44        }
 45
 46        prop_array_t bat = prop_dictionary_get_keysym(root, key);
 47        uint32_t max = 0, curr = 0, dischargeRate = 0;
 48        bool charging = false, critical = false;
 49        prop_object_iterator_t iter = prop_array_iterator(bat);
 50        for (prop_dictionary_t dict; (dict = prop_object_iterator_next(iter)) != nullptr;) {
 51            if (prop_object_type(dict) != PROP_TYPE_DICTIONARY) {
 52                continue;
 53            }
 54
 55            const char* desc = nullptr;
 56            if (!prop_dictionary_get_string(dict, "description", &desc)) {
 57                continue;
 58            }
 59
 60            if (ffStrEquals(desc, "present")) {
 61                int value = 0;
 62                if (prop_dictionary_get_int(dict, "cur-value", &value) && value == 0) {
 63                    continue;
 64                }
 65            } else if (ffStrEquals(desc, "charging")) {
 66                prop_dictionary_get_uint8(dict, "cur-value", (uint8_t*) &charging);
 67            } else if (ffStrEquals(desc, "charge")) {
 68                prop_dictionary_get_uint32(dict, "max-value", &max);
 69                prop_dictionary_get_uint32(dict, "cur-value", &curr);
 70                const char* state = nullptr;
 71                if (prop_dictionary_get_string(dict, "state", &state) && ffStrEquals(state, "critical")) {
 72                    critical = true;
 73                }
 74            } else if (ffStrEquals(desc, "discharge rate")) {
 75                prop_dictionary_get_uint(dict, "cur-value", &dischargeRate);
 76            }
 77        }
 78
 79        if (max > 0) {
 80            FFBatteryResult* battery = FF_LIST_ADD(FFBatteryResult, *results);
 81            battery->temperature = FF_BATTERY_TEMP_UNSET;
 82            battery->cycleCount = 0;
 83            ffStrbufInit(&battery->manufacturer);
 84            ffStrbufInit(&battery->modelName);
 85            ffStrbufInit(&battery->technology);
 86            ffStrbufInit(&battery->serial);
 87            ffStrbufInit(&battery->manufactureDate);
 88            battery->status = FF_BATTERY_STATUS_NONE;
 89            battery->timeRemaining = -1;
 90
 91            battery->capacity = (double) curr / (double) max * 100.;
 92            if (charging) {
 93                battery->status |= FF_BATTERY_STATUS_CHARGING;
 94            } else if (dischargeRate) {
 95                battery->status |= FF_BATTERY_STATUS_DISCHARGING;
 96                battery->timeRemaining = (int32_t) ((double) curr / dischargeRate * 3600);
 97            }
 98            if (critical) {
 99                battery->status |= FF_BATTERY_STATUS_CRITICAL;
100            }
101            if (acConnected) {
102                battery->status |= FF_BATTERY_STATUS_AC_CONNECTED;
103            }
104        }
105
106        prop_object_iterator_release(iter);
107    }
108    prop_object_iterator_release(itKey);
109    prop_object_release(root);
110
111    return nullptr;
112}