// For nanosleep(), pread() #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include // Warning when battery percentage is <= this static const int WARN_LO = 10; // Warning when battery percentage is >= this (only warns once per charge cycle) static const int WARN_HI = 65; // Time to sleep between checks static const struct timespec SLEEP_SPEC = {60, 0}; // Format string with percentage %d static const char *MESSAGE_FORMAT = "Battery at %d%%!"; static inline void parse(const char *buffer, bool *discharging, int *charge, int *full) { while (true) { if (memcmp(buffer, "POWER_SUPPLY_STATUS", 19) == 0) { *discharging = memcmp(buffer + 20, "Discharging", 11) == 0; } else if (memcmp(buffer, "POWER_SUPPLY_CHARGE_NOW", 23) == 0) { *charge = strtol(buffer + 24, NULL, 10); } else if (memcmp(buffer, "POWER_SUPPLY_CHARGE_FULL_DESIGN", 31) == 0) { *full = strtol(buffer + 32, NULL, 10); } buffer = strchr(buffer, '\n'); if (buffer != NULL) buffer++; else break; } } static void show_nagbar(int percentage, const char *nagtype) { pid_t pid = fork(); if (pid < 0) perror("fork"); else if (pid == 0) { char msg[strlen(MESSAGE_FORMAT) + 16]; snprintf(msg, sizeof msg, MESSAGE_FORMAT, percentage); char nagtypebuf[16]; assert(strlen(nagtype) < sizeof nagtypebuf); strcpy(nagtypebuf, nagtype); char *argv[6] = {"i3-nagbar", "-m", msg, "-t", nagtypebuf, NULL}; execv("/usr/bin/i3-nagbar", argv); perror("execv"); exit(255); } else { int status; do if (waitpid(pid, &status, 0) < 0 && errno == ECHILD) break; while (!WIFEXITED(status) && !WIFSIGNALED(status)); } } int main(void) { int fd = open("/sys/class/power_supply/BAT1/uevent", O_RDONLY); if (fd < 0) { perror("open"); return 1; } char buffer[1024]; bool prev_less_than_hi = false; while (true) { ssize_t nr = pread(fd, buffer, sizeof buffer, 0); if (nr < 0) { perror("pread"); break; } bool discharging = false; int charge = 1, full = 1; parse(buffer, &discharging, &charge, &full); int percentage = charge * 100 / full; if (full > 0 && discharging && percentage <= WARN_LO) { show_nagbar(percentage, "error"); } else if (percentage < WARN_HI) { prev_less_than_hi = true; } else if (prev_less_than_hi) { prev_less_than_hi = false; show_nagbar(percentage, "warning"); } (void)nanosleep(&SLEEP_SPEC, NULL); } if (close(fd) < 0) { perror("close"); return 1; } }