#include #include #include #include #include #include #include #include #define INTERVAL 2 #ifdef TTY #define NORMAL "" #define SELECTED "" #define WARNING "" #define ERROR "" #define DELIM "" #define HOT "" #define MEDIUM "" #define COOL "" #else #define NORMAL "\x01" #define SELECTED "\x02" #define WARNING "\x03" #define ERROR "\x04" #define DELIM "\x05" #define HOT "\x06" #define MEDIUM "\x07" #define COOL "\x08" #endif static Display *dpy; static char statusline[2048]; static void set_status(const char *str) { XStoreName(dpy, DefaultRootWindow(dpy), str); XSync(dpy, False); } static void reset_status(void) { statusline[0] = '\0'; } static void add_status(const char *fmt, ...) { va_list ap; size_t len; va_start(ap, fmt); if (statusline[0]) { len = strlen(statusline); snprintf(statusline + len, sizeof(statusline) - len, DELIM " | " NORMAL); } len = strlen(statusline); vsnprintf(statusline + len, sizeof(statusline) - len, fmt, ap); va_end(ap); } static int parse_file(const char *file, const char *fmt, ...) { va_list ap; FILE *fp; int ret = 0; if ((fp = fopen(file, "r")) != NULL) { va_start(ap, fmt); ret = vfscanf(fp, fmt, ap); va_end(ap); fclose(fp); } return ret; } static void status_cpu(void) { FILE *fp; char line[256]; char *tok; unsigned long long ticks = 0, idle = 0; static unsigned long long prev_ticks, prev_idle; int i; double usage = 0, freq = 0, temp = 0; if ((fp = fopen("/proc/stat", "r")) == NULL) return; fgets(line, sizeof(line), fp); fclose(fp); tok = strtok(line, " "); if (strcmp(tok, "cpu") != 0) return; for (i = 0; (tok = strtok(NULL, " ")) != NULL; i++) { unsigned long long value = strtoull(tok, NULL, 10); if (i == 3) idle = value; ticks += value; } usage = 100.0 - ((idle - prev_idle) * 100.0 / (ticks - prev_ticks)); prev_ticks = ticks; prev_idle = idle; parse_file("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "%lf", &freq); parse_file("/sys/devices/virtual/thermal/thermal_zone0/temp", "%lf", &temp); freq /= 1e6; temp /= 1000; add_status("CPU %s%3.f%s%% @ %.1fGHz %s%.f%s\xc2\xb0", usage >= 90 ? HOT : usage >= 80 ? MEDIUM : COOL, usage, NORMAL, freq, temp >= 98 ? ERROR : temp >= 85 ? HOT : temp >= 75 ? MEDIUM : COOL, temp, NORMAL); } static void status_mem(void) { FILE *fp; char line[256], field[128]; unsigned long long value, total = 0, free = 0, cached = 0, avail = 0; double used; if ((fp = fopen("/proc/meminfo", "r")) == NULL) { return; } while (fgets(line, sizeof(line), fp)) { if (sscanf(line, "%s %Lu", field, &value)) { if (strcmp(field, "MemTotal:") == 0) total = value; else if (strcmp(field, "MemFree:") == 0) free = value; else if (strcmp(field, "MemAvailable:") == 0) avail = value; else if (strcmp(field, "Cached:") == 0) cached = value; } } fclose(fp); if (avail < 1) { avail = free + cached; } used = (total - avail) * 100.0 / total; add_status("Mem %s%2.f%s%%", used > 90.0 ? HOT : COOL, used, NORMAL); } static void status_disk(void) { struct statvfs st; double used; if (statvfs("/", &st) == -1) return; used = (st.f_blocks - st.f_bfree) * 100.0 / st.f_blocks; add_status("Disk %s%.f%s%%", used >= 90 ? HOT : COOL, used, NORMAL); } static void status_net(void) { FILE *fp; char line[256], iface[128], tmp[128], net[128], path[256]; unsigned long long rx_bytes = 0, tx_bytes = 0; static unsigned long long prev_rx_bytes = 0, prev_tx_bytes = 0; double up = 0, down = 0; char upstr[128], downstr[128]; if ((fp = fopen("/proc/net/route", "r")) == NULL) return; strcpy(iface, "eth0"); fgets(line, sizeof(line), fp); while (fgets(line, sizeof(line), fp)) { if (sscanf(line, "%s %s", tmp, net) && strcmp(net, "00000000") == 0) { strcpy(iface, tmp); break; } } fclose(fp); snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/rx_bytes", iface); parse_file(path, "%llu", &rx_bytes); snprintf(path, sizeof(path), "/sys/class/net/%s/statistics/tx_bytes", iface); parse_file(path, "%llu", &tx_bytes); if (prev_rx_bytes > 0 || prev_tx_bytes > 0) { up = tx_bytes > prev_tx_bytes ? (tx_bytes - prev_tx_bytes) * 8 / INTERVAL : 0; down = rx_bytes > prev_rx_bytes ? (rx_bytes - prev_rx_bytes) * 8 / INTERVAL : 0; } prev_rx_bytes = rx_bytes; prev_tx_bytes = tx_bytes; if (up >= 1e6) { snprintf(upstr, sizeof(upstr), "%s%4.1f%sM", COOL, up / 1e6, NORMAL); } else { snprintf(upstr, sizeof(upstr), "%s%4.f%sk", COOL, up / 1000, NORMAL); } if (down >= 1e6) { snprintf(downstr, sizeof(downstr), "%s%4.1f%sM", COOL, down / 1e6, NORMAL); } else { snprintf(downstr, sizeof(downstr), "%s%4.f%sk", COOL, down / 1000, NORMAL); } // add_status("%s \xe2\x86\x91%s / \xe2\x86\x93%s", iface, upstr, downstr); add_status("%s %s / %s", iface, upstr, downstr); } static void status_bat(void) { char status[128]; const char *ind = ""; int online = 0; double charge; if (parse_file("/sys/class/power_supply/BAT0/status", "%s", status)) { parse_file("/sys/class/power_supply/BAT0/capacity", "%lf", &charge); parse_file("/sys/class/power_supply/AC0/online", "%u", &online); if (!online && strcmp(status, "Discharging") == 0) ind = "<"; else if (strcmp(status, "Charging") == 0) ind = ">"; if (online && charge > 99.9) { // add_status("AC"); } else { add_status("Bat %s%s%.f%s%%", charge <= 5.0 ? ERROR : charge <= 10.0 ? HOT : COOL, ind, charge, NORMAL); } } } static void status_wifi(void) { } static void status_date(void) { char date[128]; time_t t; struct tm tm; time(&t); strftime(date, sizeof(date), "%a, %b %d %H:%M", localtime_r(&t, &tm)); add_status("%s", date); } int main() { #ifndef TTY if (!(dpy = XOpenDisplay(NULL))) { fprintf(stderr, "statusbar: cannot open display.\n"); return 1; } #endif for (;;) { reset_status(); status_cpu(); status_mem(); status_disk(); status_net(); status_bat(); status_wifi(); status_date(); #ifdef TTY printf("%s\n", statusline); #else set_status(statusline); #endif sleep(INTERVAL); } }