battery.c 5.1 KB

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