battery.c (5232B)
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 #elif defined(__OpenBSD__) 115 #include <fcntl.h> 116 #include <machine/apmvar.h> 117 #include <sys/ioctl.h> 118 #include <unistd.h> 119 120 static int 121 load_apm_power_info(struct apm_power_info *apm_info) 122 { 123 int fd; 124 125 fd = open("/dev/apm", O_RDONLY); 126 if (fd < 0) { 127 warn("open '/dev/apm':"); 128 return 0; 129 } 130 131 memset(apm_info, 0, sizeof(struct apm_power_info)); 132 if (ioctl(fd, APM_IOC_GETPOWER, apm_info) < 0) { 133 warn("ioctl 'APM_IOC_GETPOWER':"); 134 close(fd); 135 return 0; 136 } 137 return close(fd), 1; 138 } 139 140 const char * 141 battery_perc(const char *unused) 142 { 143 struct apm_power_info apm_info; 144 145 if (load_apm_power_info(&apm_info)) 146 return bprintf("%d", apm_info.battery_life); 147 148 return NULL; 149 } 150 151 const char * 152 battery_state(const char *unused) 153 { 154 struct { 155 unsigned int state; 156 char *symbol; 157 } map[] = { 158 { APM_AC_ON, "+" }, 159 { APM_AC_OFF, "-" }, 160 }; 161 struct apm_power_info apm_info; 162 size_t i; 163 164 if (load_apm_power_info(&apm_info)) { 165 for (i = 0; i < LEN(map); i++) 166 if (map[i].state == apm_info.ac_state) 167 break; 168 169 return (i == LEN(map)) ? "?" : map[i].symbol; 170 } 171 172 return NULL; 173 } 174 175 const char * 176 battery_remaining(const char *unused) 177 { 178 struct apm_power_info apm_info; 179 unsigned int h, m; 180 181 if (load_apm_power_info(&apm_info)) { 182 if (apm_info.ac_state != APM_AC_ON) { 183 h = apm_info.minutes_left / 60; 184 m = apm_info.minutes_left % 60; 185 return bprintf("%uh %02um", h, m); 186 } else { 187 return ""; 188 } 189 } 190 191 return NULL; 192 } 193 #elif defined(__FreeBSD__) 194 #include <sys/sysctl.h> 195 196 #define BATTERY_LIFE "hw.acpi.battery.life" 197 #define BATTERY_STATE "hw.acpi.battery.state" 198 #define BATTERY_TIME "hw.acpi.battery.time" 199 200 const char * 201 battery_perc(const char *unused) 202 { 203 int cap_perc; 204 size_t len; 205 206 len = sizeof(cap_perc); 207 if (sysctlbyname(BATTERY_LIFE, &cap_perc, &len, NULL, 0) < 0 || !len) 208 return NULL; 209 210 return bprintf("%d", cap_perc); 211 } 212 213 const char * 214 battery_state(const char *unused) 215 { 216 int state; 217 size_t len; 218 219 len = sizeof(state); 220 if (sysctlbyname(BATTERY_STATE, &state, &len, NULL, 0) < 0 || !len) 221 return NULL; 222 223 switch (state) { 224 case 0: /* FALLTHROUGH */ 225 case 2: 226 return "+"; 227 case 1: 228 return "-"; 229 default: 230 return "?"; 231 } 232 } 233 234 const char * 235 battery_remaining(const char *unused) 236 { 237 int rem; 238 size_t len; 239 240 len = sizeof(rem); 241 if (sysctlbyname(BATTERY_TIME, &rem, &len, NULL, 0) < 0 || !len 242 || rem < 0) 243 return NULL; 244 245 return bprintf("%uh %02um", rem / 60, rem % 60); 246 } 247 #endif