#include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Required for timercmp */ #define __USE_MISC #include #include #include /* Constants */ #define ELEMENT_SEPERATOR " " #define STATUS_STRBUF_SZ 512 #define ELEMENT_STRBUF_SZ 64 /* Type definitions */ typedef void (*element_function)(char*); /* Data structures */ struct element { const element_function f; const struct timeval fire_interval; struct timeval fire_previous; char buf[ELEMENT_STRBUF_SZ]; }; enum battery_status_charge { bat_unknown, bat_not_charging, bat_charging, bat_discharging }; struct battery_status { enum battery_status_charge status; float charge; }; /* Prototypes */ void date(char* buf); struct battery_status get_battery_status(const char* buf); void get_battery1_status(char* buf); void get_battery0_status(char* buf); void get_all_bat_status(char* buf); void get_net_link_status(char* buf); /* Data */ static char* battery_level_icon[] = { "", /* "\uf579" */ "", /* "\uf57a" */ "", /* "\uf57b" */ "", /* "\uf57c" */ "", /* "\uf57d" */ "", /* "\uf57e" */ "", /* "\uf57f" */ "", /* "\uf580" */ "", /* "\uf581" */ "", /* "\uf578" */ "", /* "\uf578" */ }; static struct element statusbar[] = { /* Add status elements here */ {get_net_link_status, {5, 0}, {0}, {0}}, { get_all_bat_status, {60, 0}, {0}, {0}}, { date, {2, 500000}, {0}, {0}}, }; /* Functions */ void date(char* buf) { time_t now = time(NULL); struct tm tm; tm = *localtime(&now); strftime(buf, ELEMENT_STRBUF_SZ, "%Y-%m-%d %H:%M", &tm); } struct battery_status get_battery_status(const char* buf) { const char path_prefix[] = "/sys/class/power_supply/"; char charge_path[128]; char capacity_path[128]; char charge_str[512]; char capacity_str[512]; int charge = 0; int capacity = 0; FILE* bat_charge; FILE* bat_capacity; memset(charge_path, 0, 128); memset(capacity_path, 0, 128); strcat(charge_path, path_prefix); strcat(charge_path, buf); strcat(charge_path, "/energy_now"); strcat(capacity_path, path_prefix); strcat(capacity_path, buf); strcat(capacity_path, "/energy_full"); bat_charge = fopen(charge_path, "r"); bat_capacity = fopen(capacity_path, "r"); if (!bat_charge) { printf("%d: \"%s\" %s\n", errno, charge_path, strerror(errno)); return (struct battery_status){bat_unknown, -1}; } if (!bat_capacity) { printf("%d: \"%s\" %s\n", errno, capacity_path, strerror(errno)); return (struct battery_status){bat_unknown, -1}; } fread(charge_str, sizeof(char), 512, bat_charge); fread(capacity_str, sizeof(char), 512, bat_capacity); fclose(bat_charge); fclose(bat_capacity); charge = atoi(charge_str); capacity = atoi(capacity_str); return (struct battery_status){bat_unknown, (float)charge / capacity}; } void get_battery1_status(char* buf) { struct battery_status s = get_battery_status("BAT1"); int batlvl = (int)(s.charge * 100.f) / 10; char* batlvl_icon = battery_level_icon[batlvl]; snprintf(buf, ELEMENT_STRBUF_SZ, "%s %.1f%%", batlvl_icon, 100.f * s.charge); } void get_battery0_status(char* buf) { struct battery_status s = get_battery_status("BAT0"); int batlvl = (int)(s.charge * 100.f) / 10; char* batlvl_icon = battery_level_icon[batlvl]; snprintf(buf, ELEMENT_STRBUF_SZ, "%s %.1f%%", batlvl_icon, 100.f * s.charge); } /* todo, remake this to enumerate all possible batteries */ void get_all_bat_status(char* buf) { struct battery_status s = get_battery_status("BAT0"); struct battery_status t = get_battery_status("BAT1"); s.charge = (s.charge + t.charge) / 2.f; int batlvl = (int)(s.charge * 100.f) / 10; char* batlvl_icon = battery_level_icon[batlvl]; /*snprintf(buf, ELEMENT_STRBUF_SZ, "%.1f%%", 100.f * s.charge);*/ snprintf(buf, ELEMENT_STRBUF_SZ, "%s %.1f%%", batlvl_icon, 100.f * s.charge); } void get_net_link_status(char* buf) { struct ifaddrs* if_addr; size_t n = 0; getifaddrs(&if_addr); for (struct ifaddrs* ifa = if_addr; ifa != NULL; ifa = ifa->ifa_next) { /* Ignore loopback interface flag */ if (ifa->ifa_flags & IFF_LOOPBACK) continue; /* Ignore interfaces without an address */ if (ifa->ifa_addr == NULL) continue; /* Ignore everythings not an IPv4 link */ /* (AF_PACKET might be usefull if you want to display tx) */ if (ifa->ifa_addr->sa_family != AF_INET) continue; if (ifa->ifa_flags & IFF_UP) { size_t l = strlen(ifa->ifa_name); struct iwreq wreq; int sock; const size_t ipv6_charlen = 8 * 4 + 7; char address[ipv6_charlen + 1]; // in case of ipv6, plus null-byte char id[IW_ESSID_MAX_SIZE + 1]; /* Get the IP address */ memset(address, 0, ipv6_charlen + 1); /* Use sa_family instead of hardcoding AF_INET, just in case * we get ip6'ed */ /* Does inet_ntop add a null-byte? */ inet_ntop(ifa->ifa_addr->sa_family, (void*)&((struct sockaddr_in*)ifa->ifa_addr)->sin_addr, address, ipv6_charlen); /* Get the SSID */ memset(id, 0, IW_ESSID_MAX_SIZE + 1); memset(&wreq, 0, sizeof(struct iwreq)); sock = socket(AF_INET, SOCK_DGRAM, 0); setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifa->ifa_name, l); strncpy(wreq.ifr_name, ifa->ifa_name, l); wreq.u.essid.pointer = id; wreq.u.essid.length = IW_ESSID_MAX_SIZE; ioctl(sock, SIOCGIWESSID, &wreq); strncpy(buf + n, ifa->ifa_name, l); size_t ssid_len = strlen(id); if (ssid_len > 0) { buf[n + l] = ' '; buf[n + l + 1] = '('; strncpy(buf + n + l + 2, id, ssid_len); l += ssid_len; buf[n + l + 2] = ')'; } n += l; strcat(buf + n, address); n += strlen(address); strcat(buf + n, " "); n += 2; } } } int main(void) { const int num_elems = sizeof(statusbar) / sizeof(statusbar[0]); struct timeval now; const struct timeval one_minute = {60, 0}; while (true) { gettimeofday(&now, NULL); unsigned i; /* Stall updating for at most 1 minute */ struct timeval next_update; timeradd(&now, &one_minute, &next_update); for (i = 0; i < num_elems; i++) { struct timeval next_fire; timeradd(&statusbar[i].fire_previous, &statusbar[i].fire_interval, &next_fire); if (timercmp(&next_fire, &now, >)) { /* Check if this is the next to-be-updated element */ if (timercmp(&next_fire, &next_update, <)) { next_update = next_fire; } continue; } memset(statusbar[i].buf, 0, ELEMENT_STRBUF_SZ); statusbar[i].f(statusbar[i].buf); statusbar[i].fire_previous = now; /* Check if this element needs to be refreshed next, again */ timeradd(&statusbar[i].fire_previous, &statusbar[i].fire_interval, &next_fire); if (timercmp(&next_fire, &next_update, <)) { next_update = next_fire; } /* printf("[%ld.%ld] %s\n", * statusbar[i].fire_interval.tv_sec, * statusbar[i].fire_interval.tv_usec, * (char*)statusbar[i].buf); */ } char buf[STATUS_STRBUF_SZ]; memset(buf, 0, STATUS_STRBUF_SZ); for (i = 0; i < num_elems; i++) { strcat(buf, statusbar[i].buf); if (i != num_elems - 1) strcat(buf, ELEMENT_SEPERATOR); } /* strcat(buf, "\0"); */ puts(buf); fflush(stdout); timersub(&next_update, &now, &next_update); usleep(next_update.tv_sec * 1000 * 1000 + next_update.tv_usec); } return 0; }