slstatus

My fork of https://tools.suckless.org/slstatus/
git clone https://git.inz.fi/slstatus
Log | Files | Refs | README | LICENSE

battery.c (6113B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <stdio.h>
      3 #include <string.h>
      4 #include <grapheme.h>
      5 
      6 #include "../util.h"
      7 
      8 #if defined(__linux__)
      9 	#include <limits.h>
     10 	#include <stdint.h>
     11 	#include <unistd.h>
     12 
     13 	static const char *
     14 	pick(const char *f1, const char *f2, char *path,
     15 	     size_t length, ...)
     16 	{
     17 		va_list a1;
     18 		va_list a2;
     19 
     20 		va_start(a1, length);
     21 		va_copy(a2, a1);
     22 
     23 		if (evsnprintf(path, length, f1, a1) > 0 &&
     24 		    access(path, R_OK) == 0) {
     25 			return f1;
     26 		}
     27 
     28 		if (evsnprintf(path, length, f2, a2) > 0 &&
     29 		    access(path, R_OK) == 0) {
     30 			return f2;
     31 		}
     32 
     33 		return NULL;
     34 	}
     35 
     36 	static int
     37 	_battery_perc(const char *bat)
     38 	{
     39 		int perc;
     40 		char path[PATH_MAX];
     41 
     42 		if (bat) {
     43 			if (esnprintf(path, sizeof(path),
     44 				      "/sys/class/power_supply/%s/capacity", bat) < 0) {
     45 				return -1;
     46 			}
     47 			if (pscanf(path, "%d", &perc) != 1) {
     48 				return -1;
     49 			}
     50 		} else {
     51 			int bi;
     52 			int ef = 0;
     53 			int en = 0;
     54 			for (bi = 0;; bi++) {
     55 				int f;
     56 				int n;
     57 
     58 				if (!pick("/sys/class/power_supply/BAT%d/charge_full",
     59 					  "/sys/class/power_supply/BAT%d/energy_full",
     60 					  path, sizeof(path), bi))
     61 					break;
     62 				if (pscanf(path, "%d", &f) != 1)
     63 					return -1;
     64 				if (!pick("/sys/class/power_supply/BAT%d/charge_now",
     65 					  "/sys/class/power_supply/BAT%d/energy_now",
     66 					  path, sizeof(path), bi))
     67 					break;
     68 				if (pscanf(path, "%d", &n) != 1)
     69 					return -1;
     70 
     71 				ef += f;
     72 				en += n;
     73 			}
     74 			if (ef == 0)
     75 				return -1;
     76 			perc = (en + ef / 200) / (ef / 100);
     77 		}
     78 
     79 		return perc;
     80 	}
     81 
     82 	const char *
     83 	battery_icon(const char *str)
     84 	{
     85 		int perc = _battery_perc(NULL);
     86 		size_t p;
     87 		size_t l;
     88 		int i = 0;
     89 
     90 		for (p = 0; str[p]; p += l, i++)
     91 			l = grapheme_next_character_break_utf8(str + p, SIZE_MAX);
     92 
     93 		i = i * perc / 101;
     94 
     95 		for (p = 0; i--; p += l)
     96 			l = grapheme_next_character_break_utf8(str + p, SIZE_MAX);
     97 		l = grapheme_next_character_break_utf8(str + p, SIZE_MAX);
     98 
     99 		return bprintf("%.*s", (int)l, str + p);
    100 	}
    101 
    102 	const char *
    103 	battery_perc(const char *bat)
    104 	{
    105 		int perc = _battery_perc(bat);
    106 		if (perc < 0)
    107 			return NULL;
    108 		return bprintf("%d", perc);
    109 	}
    110 
    111 	const char *
    112 	battery_state(const char *bat)
    113 	{
    114 		static struct {
    115 			char *state;
    116 			char *symbol;
    117 		} map[] = {
    118 			{ "Charging",    "+" },
    119 			{ "Discharging", "-" },
    120 			{ "Full",        "o" },
    121 		};
    122 		size_t i;
    123 		char path[PATH_MAX], state[12];
    124 
    125 		if (esnprintf(path, sizeof(path),
    126 		              "/sys/class/power_supply/%s/status", bat) < 0) {
    127 			return NULL;
    128 		}
    129 		if (pscanf(path, "%12s", state) != 1) {
    130 			return NULL;
    131 		}
    132 
    133 		for (i = 0; i < LEN(map); i++) {
    134 			if (!strcmp(map[i].state, state)) {
    135 				break;
    136 			}
    137 		}
    138 		return (i == LEN(map)) ? "?" : map[i].symbol;
    139 	}
    140 
    141 	const char *
    142 	battery_remaining(const char *bat)
    143 	{
    144 		uintmax_t charge_now, current_now, m, h;
    145 		double timeleft;
    146 		char path[PATH_MAX], state[12];
    147 
    148 		if (esnprintf(path, sizeof(path),
    149 		              "/sys/class/power_supply/%s/status", bat) < 0) {
    150 			return NULL;
    151 		}
    152 		if (pscanf(path, "%12s", state) != 1) {
    153 			return NULL;
    154 		}
    155 
    156 		if (!pick("/sys/class/power_supply/%s/charge_now",
    157 		          "/sys/class/power_supply/%s/energy_now", path,
    158 		          sizeof(path), bat) ||
    159 		    pscanf(path, "%ju", &charge_now) < 0) {
    160 			return NULL;
    161 		}
    162 
    163 		if (!strcmp(state, "Discharging")) {
    164 			if (!pick("/sys/class/power_supply/%s/current_now",
    165 			          "/sys/class/power_supply/%s/power_now", path,
    166 			          sizeof(path), bat) ||
    167 			    pscanf(path, "%ju", &current_now) < 0) {
    168 				return NULL;
    169 			}
    170 
    171 			if (current_now == 0) {
    172 				return NULL;
    173 			}
    174 
    175 			timeleft = (double)charge_now / (double)current_now;
    176 			h = timeleft;
    177 			m = (timeleft - (double)h) * 60;
    178 
    179 			return bprintf("%juh %jum", h, m);
    180 		}
    181 
    182 		return "";
    183 	}
    184 #elif defined(__OpenBSD__)
    185 	#include <fcntl.h>
    186 	#include <machine/apmvar.h>
    187 	#include <sys/ioctl.h>
    188 	#include <unistd.h>
    189 
    190 	static int
    191 	load_apm_power_info(struct apm_power_info *apm_info)
    192 	{
    193 		int fd;
    194 
    195 		fd = open("/dev/apm", O_RDONLY);
    196 		if (fd < 0) {
    197 			warn("open '/dev/apm':");
    198 			return 0;
    199 		}
    200 
    201 		memset(apm_info, 0, sizeof(struct apm_power_info));
    202 		if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) {
    203 			warn("ioctl 'APM_IOC_GETPOWER':");
    204 			close(fd);
    205 			return 0;
    206 		}
    207 		return close(fd), 1;
    208 	}
    209 
    210 	const char *
    211 	battery_perc(const char *unused)
    212 	{
    213 		struct apm_power_info apm_info;
    214 
    215 		if (load_apm_power_info(&apm_info)) {
    216 			return bprintf("%d", apm_info.battery_life);
    217 		}
    218 
    219 		return NULL;
    220 	}
    221 
    222 	const char *
    223 	battery_state(const char *unused)
    224 	{
    225 		struct {
    226 			unsigned int state;
    227 			char *symbol;
    228 		} map[] = {
    229 			{ APM_AC_ON,      "+" },
    230 			{ APM_AC_OFF,     "-" },
    231 		};
    232 		struct apm_power_info apm_info;
    233 		size_t i;
    234 
    235 		if (load_apm_power_info(&apm_info)) {
    236 			for (i = 0; i < LEN(map); i++) {
    237 				if (map[i].state == apm_info.ac_state) {
    238 					break;
    239 				}
    240 			}
    241 			return (i == LEN(map)) ? "?" : map[i].symbol;
    242 		}
    243 
    244 		return NULL;
    245 	}
    246 
    247 	const char *
    248 	battery_remaining(const char *unused)
    249 	{
    250 		struct apm_power_info apm_info;
    251 
    252 		if (load_apm_power_info(&apm_info)) {
    253 			if (apm_info.ac_state != APM_AC_ON) {
    254 				return bprintf("%uh %02um",
    255 			                       apm_info.minutes_left / 60,
    256 				               apm_info.minutes_left % 60);
    257 			} else {
    258 				return "";
    259 			}
    260 		}
    261 
    262 		return NULL;
    263 	}
    264 #elif defined(__FreeBSD__)
    265 	#include <sys/sysctl.h>
    266 
    267 	const char *
    268 	battery_perc(const char *unused)
    269 	{
    270 		int cap;
    271 		size_t len;
    272 
    273 		len = sizeof(cap);
    274 		if (sysctlbyname("hw.acpi.battery.life", &cap, &len, NULL, 0) == -1
    275 				|| !len)
    276 			return NULL;
    277 
    278 		return bprintf("%d", cap);
    279 	}
    280 
    281 	const char *
    282 	battery_state(const char *unused)
    283 	{
    284 		int state;
    285 		size_t len;
    286 
    287 		len = sizeof(state);
    288 		if (sysctlbyname("hw.acpi.battery.state", &state, &len, NULL, 0) == -1
    289 				|| !len)
    290 			return NULL;
    291 
    292 		switch(state) {
    293 			case 0:
    294 			case 2:
    295 				return "+";
    296 			case 1:
    297 				return "-";
    298 			default:
    299 				return "?";
    300 		}
    301 	}
    302 
    303 	const char *
    304 	battery_remaining(const char *unused)
    305 	{
    306 		int rem;
    307 		size_t len;
    308 
    309 		len = sizeof(rem);
    310 		if (sysctlbyname("hw.acpi.battery.time", &rem, &len, NULL, 0) == -1
    311 				|| !len
    312 				|| rem == -1)
    313 			return NULL;
    314 
    315 		return bprintf("%uh %02um", rem / 60, rem % 60);
    316 	}
    317 #endif