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", ¤t_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