battery.c (5517B) download
1/* See LICENSE file for copyright and license details. */
2#include <stdio.h>
3#include <string.h>
4
5#include "../slstatus.h"
6#include "../util.h"
7
8#if defined(__linux__)
9/*
10 * https://www.kernel.org/doc/html/latest/power/power_supply_class.html
11 */
12 #include <limits.h>
13 #include <stdint.h>
14 #include <unistd.h>
15
16 #define POWER_SUPPLY_CAPACITY "/sys/class/power_supply/%s/capacity"
17 #define POWER_SUPPLY_STATUS "/sys/class/power_supply/%s/status"
18 #define POWER_SUPPLY_CHARGE "/sys/class/power_supply/%s/charge_now"
19 #define POWER_SUPPLY_ENERGY "/sys/class/power_supply/%s/energy_now"
20 #define POWER_SUPPLY_CURRENT "/sys/class/power_supply/%s/current_now"
21 #define POWER_SUPPLY_POWER "/sys/class/power_supply/%s/power_now"
22
23 static const char *
24 pick(const char *bat, const char *f1, const char *f2, char *path,
25 size_t length)
26 {
27 if (esnprintf(path, length, f1, bat) > 0 &&
28 access(path, R_OK) == 0)
29 return f1;
30
31 if (esnprintf(path, length, f2, bat) > 0 &&
32 access(path, R_OK) == 0)
33 return f2;
34
35 return NULL;
36 }
37
38 const char *
39 battery_perc(const char *bat)
40 {
41 int cap_perc;
42 char path[PATH_MAX];
43
44 if (esnprintf(path, sizeof(path), POWER_SUPPLY_CAPACITY, bat) < 0)
45 return NULL;
46 if (pscanf(path, "%d", &cap_perc) != 1)
47 return NULL;
48
49 return bprintf("%d", cap_perc);
50 }
51
52 const char *
53 battery_state(const char *bat)
54 {
55 static struct {
56 char *state;
57 char *symbol;
58 } map[] = {
59 { "Charging", "+" },
60 { "Discharging", "-" },
61 { "Full", "o" },
62 { "Not charging", "o" },
63 };
64 size_t i;
65 char path[PATH_MAX], state[12];
66
67 if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
68 return NULL;
69 if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
70 return NULL;
71
72 for (i = 0; i < LEN(map); i++)
73 if (!strcmp(map[i].state, state))
74 break;
75
76 return (i == LEN(map)) ? "?" : map[i].symbol;
77 }
78
79 const char *
80 battery_remaining(const char *bat)
81 {
82 uintmax_t charge_now, current_now, m, h;
83 double timeleft;
84 char path[PATH_MAX], state[12];
85
86 if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
87 return NULL;
88 if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
89 return NULL;
90
91 if (!pick(bat, POWER_SUPPLY_CHARGE, POWER_SUPPLY_ENERGY, path,
92 sizeof(path)) ||
93 pscanf(path, "%ju", &charge_now) < 0)
94 return NULL;
95
96 if (!strcmp(state, "Discharging")) {
97 if (!pick(bat, POWER_SUPPLY_CURRENT, POWER_SUPPLY_POWER, path,
98 sizeof(path)) ||
99 pscanf(path, "%ju", ¤t_now) < 0)
100 return NULL;
101
102 if (current_now == 0)
103 return NULL;
104
105 timeleft = (double)charge_now / (double)current_now;
106 h = timeleft;
107 m = (timeleft - (double)h) * 60;
108
109 return bprintf("%juh %jum", h, m);
110 }
111
112 return "";
113 }
114
115 int battery_discharge(const char *bat) {
116 char path[PATH_MAX], state[12];
117
118 if (esnprintf(path, sizeof(path), POWER_SUPPLY_STATUS, bat) < 0)
119 return NULL;
120 if (pscanf(path, "%12[a-zA-Z ]", state) != 1)
121 return NULL;
122
123 return !strcmp(state, "Discharging");
124 }
125#elif defined(__OpenBSD__)
126 #include <fcntl.h>
127 #include <machine/apmvar.h>
128 #include <sys/ioctl.h>
129 #include <unistd.h>
130
131 static int
132 load_apm_power_info(struct apm_power_info *apm_info)
133 {
134 int fd;
135
136 fd = open("/dev/apm", O_RDONLY);
137 if (fd < 0) {
138 warn("open '/dev/apm':");
139 return 0;
140 }
141
142 memset(apm_info, 0, sizeof(struct apm_power_info));
143 if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) {
144 warn("ioctl 'APM_IOC_GETPOWER':");
145 close(fd);
146 return 0;
147 }
148 return close(fd), 1;
149 }
150
151 const char *
152 battery_perc(const char *unused)
153 {
154 struct apm_power_info apm_info;
155
156 if (load_apm_power_info(&apm_info))
157 return bprintf("%d", apm_info.battery_life);
158
159 return NULL;
160 }
161
162 const char *
163 battery_state(const char *unused)
164 {
165 struct {
166 unsigned int state;
167 char *symbol;
168 } map[] = {
169 { APM_AC_ON, "+" },
170 { APM_AC_OFF, "-" },
171 };
172 struct apm_power_info apm_info;
173 size_t i;
174
175 if (load_apm_power_info(&apm_info)) {
176 for (i = 0; i < LEN(map); i++)
177 if (map[i].state == apm_info.ac_state)
178 break;
179
180 return (i == LEN(map)) ? "?" : map[i].symbol;
181 }
182
183 return NULL;
184 }
185
186 const char *
187 battery_remaining(const char *unused)
188 {
189 struct apm_power_info apm_info;
190 unsigned int h, m;
191
192 if (load_apm_power_info(&apm_info)) {
193 if (apm_info.ac_state != APM_AC_ON) {
194 h = apm_info.minutes_left / 60;
195 m = apm_info.minutes_left % 60;
196 return bprintf("%uh %02um", h, m);
197 } else {
198 return "";
199 }
200 }
201
202 return NULL;
203 }
204#elif defined(__FreeBSD__)
205 #include <sys/sysctl.h>
206
207 #define BATTERY_LIFE "hw.acpi.battery.life"
208 #define BATTERY_STATE "hw.acpi.battery.state"
209 #define BATTERY_TIME "hw.acpi.battery.time"
210
211 const char *
212 battery_perc(const char *unused)
213 {
214 int cap_perc;
215 size_t len;
216
217 len = sizeof(cap_perc);
218 if (sysctlbyname(BATTERY_LIFE, &cap_perc, &len, NULL, 0) < 0 || !len)
219 return NULL;
220
221 return bprintf("%d", cap_perc);
222 }
223
224 const char *
225 battery_state(const char *unused)
226 {
227 int state;
228 size_t len;
229
230 len = sizeof(state);
231 if (sysctlbyname(BATTERY_STATE, &state, &len, NULL, 0) < 0 || !len)
232 return NULL;
233
234 switch (state) {
235 case 0: /* FALLTHROUGH */
236 case 2:
237 return "+";
238 case 1:
239 return "-";
240 default:
241 return "?";
242 }
243 }
244
245 const char *
246 battery_remaining(const char *unused)
247 {
248 int rem;
249 size_t len;
250
251 len = sizeof(rem);
252 if (sysctlbyname(BATTERY_TIME, &rem, &len, NULL, 0) < 0 || !len
253 || rem < 0)
254 return NULL;
255
256 return bprintf("%uh %02um", rem / 60, rem % 60);
257 }
258#endif