/* * Copyright (c) 2007 Daniel Borca All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "dockapp.h" #include "sensors.h" #include "system.h" #include "util.h" #include "font.h" #include "frame0.xpm" #include "frame1.xpm" #include "frame2.xpm" #include "frame3.xpm" #include "frame4.xpm" #include "frame5.xpm" #define DockApp App.d #define NELEM(tab) (const int)(sizeof(tab) / sizeof((tab)[0])) #define INSIDE(x, y, xmin, ymin, xmax, ymax) ((x) >= (xmin) && (x) <= (xmax) && (y) >= (ymin) && (y) <= (ymax)) #define SWITCH_FRAME(a, n) \ do { \ if ((a)->frames[n].v) { \ (a)->page = n; \ (a)->millis = (a)->frames[n].u; \ (a)->handle = handle_frame##n; \ (a)->update = update_frame##n; \ (a)->frames[n].p = 0; \ (a)->update(a, 1); \ } \ } while (0) #define SCROLL_START(start, num_items, max_items) \ do { \ if ((start) > (num_items) - (max_items)) { \ (start) = (num_items) - (max_items); \ } \ if ((start) < 0) { \ (start) = 0; \ } \ } while (0) #define MAX_DISPLAY_CPU 2 #define MAX_DISPLAY_NET 2 #define MAX_DISPLAY_TEMP 5 typedef struct { Pixmap f; Pixmap m; int v; int u; int p; } FRAME; typedef struct { int row; int height; const int *width; const int *offset; } FONT; typedef struct APP { DOCKAPP d; FONT fontx9; FONT fontx8; FONT fontx7; FONT fontx6; FONT meter2; Pixmap font; SENSOR *list; FRAME *frames; void (*handle) (struct APP *a, XEvent *event); void (*update) (const struct APP *a, int full); int page; int millis; } APP; static int draw_text (const APP *a, const FONT *font, const char *str, int dx, int dy) { const DOCKAPP *d = (DOCKAPP *)a; int ch; while ((ch = *(unsigned char *)str++)) { int w = font->width[ch]; dockapp_copy_area(d, a->font, font->offset[ch], font->row, w, font->height, dx, dy); dx += w; } /*printf("dx = %d\n", dx);*/ return dx; } static void draw_meter (const APP *a, const FONT *meter, int num, int den, int dx, int dy) { const DOCKAPP *d = (DOCKAPP *)a; const int w = meter->width[0]; const int h = meter->height; int col = 1 * w; if (num) { int ratio = w * num / den; XCopyArea(d->display, a->font, a->font, d->gc, 1 * w, meter->row, w, h, 2 * w, meter->row); XCopyArea(d->display, a->font, a->font, d->gc, 0 * w, meter->row, ratio, h, 2 * w, meter->row); col = 2 * w; } dockapp_copy_area(d, a->font, col, meter->row, w, h, dx, dy); } static void handle_frame0 (APP *a, XEvent *event); #define handle_frame1 handle_frameN #define handle_frame2 handle_frameN #define handle_frame3 handle_frameN #define handle_frame4 handle_frameN #define handle_frame5 handle_frameN static void handle_frameN (APP *a, XEvent *event); static void update_frame0 (const APP *a, int full); static void update_frame1 (const APP *a, int full); static void update_frame2 (const APP *a, int full); static void update_frame3 (const APP *a, int full); static void update_frame4 (const APP *a, int full); static void update_frame5 (const APP *a, int full); static void handle_frame0 (APP *a, XEvent *event) { XButtonPressedEvent *bevent; switch (event->type) { case ButtonPress: bevent = (XButtonPressedEvent *)event; switch (bevent->button & 0xFF) { case Button1: /*printf("handle0: b1\n");*/ if (INSIDE(bevent->x, bevent->y, 4, 4, 10, 10)) { /*printf("next: %d %d\n", bevent->x, bevent->y);*/ break; } if (INSIDE(bevent->x, bevent->y, 50, 4, 56, 10)) { /*printf("quit: %d %d\n", bevent->x, bevent->y);*/ ((DOCKAPP *)a)->quit = True; break; } if (INSIDE(bevent->x, bevent->y, 1, 1, 59, 13)) { /*printf("time: %d %d\n", bevent->x, bevent->y);*/ SWITCH_FRAME(a, 1); break; } if (INSIDE(bevent->x, bevent->y, 1, 16, 59, 27)) { /*printf("cpu : %d %d\n", bevent->x, bevent->y);*/ SWITCH_FRAME(a, 2); break; } if (INSIDE(bevent->x, bevent->y, 1, 30, 29, 41)) { /*printf("wifi: %d %d\n", bevent->x, bevent->y);*/ SWITCH_FRAME(a, 3); break; } if (INSIDE(bevent->x, bevent->y, 32, 30, 59, 41)) { /*printf("temp: %d %d\n", bevent->x, bevent->y);*/ SWITCH_FRAME(a, 4); break; } if (INSIDE(bevent->x, bevent->y, 1, 44, 59, 59)) { /*printf("batt: %d %d\n", bevent->x, bevent->y);*/ SWITCH_FRAME(a, 5); break; } break; case Button2: /*printf("handle0: b2\n");*/ break; case Button3: /*printf("handle0: b3\n");*/ break; } break; } } static void handle_frameN (APP *a, XEvent *event) { XButtonPressedEvent *bevent; switch (event->type) { case ButtonPress: bevent = (XButtonPressedEvent *)event; switch (bevent->button & 0xFF) { case Button1: /*printf("handleN: b1\n");*/ SWITCH_FRAME(a, 0); break; case Button2: /*printf("handleN: b2\n");*/ break; case Button3: /*printf("handleN: b3\n");*/ break; case Button4: /*printf("handle0: b4\n");*/ a->frames[a->page].p--; a->update(a, 0); break; case Button5: /*printf("handle0: b5\n");*/ a->frames[a->page].p++; a->update(a, 0); break; } break; } } static void update_frame0 (const APP *a, int full) { const DOCKAPP *d = (DOCKAPP *)a; const FRAME *frame = &a->frames[0]; char buf[16]; time_t at; struct tm *bt; static CPUSTAT avg; int wifi; int temp; int crit; int on_line; int battery; BATSTAT total; if (full) { if (!d->iswindowed) { dockapp_set_shape(d, frame->m); } dockapp_copy_area(d, frame->f, 0, 0, d->width, d->height, 0, 0); } at = time(NULL); bt = localtime(&at); sprintf(buf, "%2d:%02d", bt->tm_hour, bt->tm_min); draw_text(a, &a->fontx9, buf, 15, 2); if (system_get_cpu_load(0, &avg, NULL) > 0) { sprintf(buf, "__CPU__%3d%%", avg.used); } else { strcpy(buf, " _____"); } draw_text(a, &a->fontx8, buf, 1, 17); if (system_get_best_wifi(&wifi)) { sprintf(buf, "%3d\xF7", wifi); } else { strcpy(buf, " \xF7"); } draw_text(a, &a->fontx8, buf, 0, 31); if (system_get_max_temp(a->list, &temp, &crit)) { sprintf(buf, "%3d\xF8", temp); } else { strcpy(buf, " \xF8"); } draw_text(a, &a->fontx8, buf, 31, 31); battery = system_get_battery(a->list, 0, &total, NULL); on_line = system_get_ac_adapter(a->list); if (battery) { int ratio = 100 * total.curr_cap / total.full_cap; int ch = 0xB0; if (ratio >= 33) { ch++; } if (ratio >= 66) { ch++; } if (total.rate) { if (on_line) { /* charging */ int estimate = 60 * (total.full_cap - total.curr_cap) / total.rate; int hours = estimate / 60; if (hours > 99) { hours = 99; } sprintf(buf, "%c__%2d:%02d\xAE\x98", ch, hours, estimate % 60); } else { /* discharging */ int estimate = 60 * total.curr_cap / total.rate; int hours = estimate / 60; if (hours > 99) { hours = 99; } sprintf(buf, "%c__%2d:%02d__ ", ch, hours, estimate % 60); } } else { if (on_line) { /* bat + AC */ sprintf(buf, "%c__%3d%%___\x98", ch, ratio); } else { /* bat */ sprintf(buf, "%c__%3d%%___ ", ch, ratio); } } draw_meter(a, &a->meter2, total.curr_cap, total.full_cap, 3, 55); } else { if (on_line) { /* only AC */ strcpy(buf, " _____\x98"); } else { /* nada */ strcpy(buf, " _____ "); } draw_meter(a, &a->meter2, 0, 0, 3, 55); } draw_text(a, &a->fontx9, buf, 2, 44); dockapp_update(d); } static void update_frame1 (const APP *a, int full) { const DOCKAPP *d = (DOCKAPP *)a; const FRAME *frame = &a->frames[1]; char buf[16]; time_t at; struct tm *bt; int days, hours, mins; static const char *dow[] = { "___SUNDAY___", "___MONDAY___", "__TUESDAY__", "WEDNESDAY", "_THURSDAY_", "___FRIDAY___", "_SATURDAY_" }; static const char *mon[] = { "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" }; if (full) { if (!d->iswindowed) { dockapp_set_shape(d, frame->m); } dockapp_copy_area(d, frame->f, 0, 0, d->width, d->height, 0, 0); } at = time(NULL); bt = localtime(&at); draw_text(a, &a->fontx9, dow[bt->tm_wday], 3, 2); sprintf(buf, "%-2d %s", bt->tm_mday, mon[bt->tm_mon]); draw_text(a, &a->fontx9, buf, 10, 13); sprintf(buf, "%d", 1900 + bt->tm_year); draw_text(a, &a->fontx9, buf, 16, 24); draw_text(a, &a->fontx7, "UPTIME", 15, 39); strcpy(buf, " _ _ "); if (system_get_uptime(&days, &hours, &mins)) { if (days > 999) { days = 999; } sprintf(buf, "%03d_%02d:%02d", days, hours, mins); } draw_text(a, &a->fontx8, buf, 2, 48); dockapp_update(d); } static void update_frame2 (const APP *a, int full) { const DOCKAPP *d = (DOCKAPP *)a; FRAME *frame = &a->frames[2]; char buf[16]; int speed[8]; static CPUSTAT load[8]; int mem_free, mem_total, swp_free, swp_total; int i, j, n, k; if (full) { if (!d->iswindowed) { dockapp_set_shape(d, frame->m); } dockapp_copy_area(d, frame->f, 0, 0, d->width, d->height, 0, 0); } n = system_get_cpu_speed(NELEM(speed), speed); system_get_cpu_load(NELEM(load), NULL, load); SCROLL_START(frame->p, n, MAX_DISPLAY_CPU); j = frame->p + MAX_DISPLAY_CPU; if (n < MAX_DISPLAY_CPU) { while (j > n) { j--; draw_text(a, &a->fontx7, " ", 2, 2 + j * 20); draw_text(a, &a->fontx7, " ", 2, 10 + j * 20); } } for (k = 0, i = 0; k < j; i++, k++) { int r; char *gov; if (speed[i] < 0) { k--; continue; } r = k - frame->p; if (r < 0) { continue; } sprintf(buf, "CPU%d %4d M", i, speed[i]); draw_text(a, &a->fontx7, buf, 2, 2 + r * 20); strcpy(buf, " "); if (load[i].used >= 0) { sprintf(buf, "%3d%%", load[i].used); } draw_text(a, &a->fontx7, buf, 2, 10 + r * 20); gov = " "; if (system_get_cpu_gov(i, sizeof(buf), buf)) { if (!strcmp(buf, "performance")) { gov = "PERFRM"; } else if (!strcmp(buf, "powersave")) { gov = "PWRSAV"; } else if (!strcmp(buf, "userspace")) { gov = "USRSPC"; } else if (!strcmp(buf, "ondemand")) { gov = "ONDMND"; } else if (!strcmp(buf, "conservative")) { gov = "CONSRV"; } } draw_text(a, &a->fontx7, gov, 27, 10 + r * 20); } if (system_get_mem_stat(&mem_free, &mem_total, &swp_free, &swp_total)) { strcpy(buf, " "); if (mem_total) { sprintf(buf, "MEM %3d%%", 100 * (mem_total - mem_free) / mem_total); } draw_text(a, &a->fontx7, buf, 7, 42); strcpy(buf, " "); if (swp_total) { sprintf(buf, "SWP %3d%%", 100 * (swp_total - swp_free) / swp_total); } draw_text(a, &a->fontx7, buf, 7, 50); } dockapp_update(d); } static void update_frame3 (const APP *a, int full) { const DOCKAPP *d = (DOCKAPP *)a; FRAME *frame = &a->frames[3]; char buf[16]; IFSTAT ifs[8]; int i, j, n; if (full) { if (!d->iswindowed) { dockapp_set_shape(d, frame->m); } dockapp_copy_area(d, frame->f, 0, 0, d->width, d->height, 0, 0); } n = system_get_netif(NELEM(ifs), ifs); SCROLL_START(frame->p, n, MAX_DISPLAY_NET); j = frame->p + MAX_DISPLAY_NET; if (n < MAX_DISPLAY_NET) { while (j > n) { j--; draw_text(a, &a->fontx7, " ", 2, 2 + j * 30); draw_text(a, &a->fontx7, " ", 2, 2 + j * 30 + 9); draw_text(a, &a->fontx6, " _ _ _ ", 2, 2 + j * 30 + 18); } } for (i = frame->p; i < j; i++) { int r = i - frame->p; strupr(ifs[i].name, ifs[i].name); ifs[i].name[6] = '\0'; if (ifs[i].wlink >= 0) { sprintf(buf, "%-6s_%3d\xF7", ifs[i].name, ifs[i].wlink); draw_text(a, &a->fontx7, buf, 2, 2 + r * 30); strupr(buf, ifs[i].essid); buf[11] = '\0'; } else { sprintf(buf, "%-6s_ _", ifs[i].name); draw_text(a, &a->fontx7, buf, 2, 2 + r * 30); strcpy(buf, " "); } draw_text(a, &a->fontx7, buf, 2, 2 + r * 30 + 9); if (ifs[i].ipv4 != -1) { int addr = ifs[i].ipv4; sprintf(buf, "%3d.%3d.%3d.%3d", (addr >> 24) & 0xFF, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF); } else { strcpy(buf, " _ _ _ "); } draw_text(a, &a->fontx6, buf, 2, 2 + r * 30 + 18); } dockapp_update(d); } static void update_frame4 (const APP *a, int full) { const DOCKAPP *d = (DOCKAPP *)a; FRAME *frame = &a->frames[4]; char buf[16]; TEMPSTAT temp[16]; int i, j, n; if (full) { if (!d->iswindowed) { dockapp_set_shape(d, frame->m); } dockapp_copy_area(d, frame->f, 0, 0, d->width, d->height, 0, 0); } n = system_get_temperature(a->list, NELEM(temp), temp); SCROLL_START(frame->p, n, MAX_DISPLAY_TEMP); j = frame->p + MAX_DISPLAY_TEMP; if (n < MAX_DISPLAY_TEMP) { while (j > n) { j--; draw_text(a, &a->fontx7, " _ ", 1, 1 + j * 12); } } for (i = frame->p; i < j; i++) { int r = i - frame->p; if (!strncmp(temp[i].name, "Core ", 5) && isdigit(temp[i].name[5]) && !temp[i].name[6]) { temp[i].name[3] = temp[i].name[5]; } temp[i].name[4] = '\0'; strupr(temp[i].name, temp[i].name); sprintf(buf, "%-4s:%3d\xA7%3d", temp[i].name, temp[i].temp, temp[i].max); if (!temp[i].max) { int l = strlen(buf); buf[--l] = ' '; buf[--l] = ' '; buf[--l] = ' '; } draw_text(a, &a->fontx7, buf, 1, 1 + r * 12); } dockapp_update(d); } static void update_frame5 (const APP *a, int full) { const DOCKAPP *d = (DOCKAPP *)a; const FRAME *frame = &a->frames[5]; char buf[16]; int on_line; int battery; BATSTAT total, batt[2]; int i; if (full) { if (!d->iswindowed) { dockapp_set_shape(d, frame->m); } dockapp_copy_area(d, frame->f, 0, 0, d->width, d->height, 0, 0); } on_line = system_get_ac_adapter(a->list); battery = system_get_battery(a->list, NELEM(batt), &total, batt); for (i = 0; i < battery; i++) { char *state; int ratio = 100 * batt[i].curr_cap / batt[i].full_cap; if (batt[i].rate) { int estimate; int hours; if (on_line) { estimate = 60 * (batt[i].full_cap - batt[i].curr_cap) / batt[i].rate; state = "CHARGING "; } else { estimate = 60 * batt[i].curr_cap / batt[i].rate; state = "DISCHARGING"; } hours = estimate / 60; if (hours > 99) { hours = 99; } sprintf(buf, "%3d%% %2d:%02d", ratio, hours, estimate % 60); } else { sprintf(buf, "%3d%% _ ", ratio); state = "STEADY "; } draw_text(a, &a->fontx7, batt[i].name, 2, 1 + i * 30); draw_text(a, &a->fontx7, state, 2, 9 + i * 30); draw_text(a, &a->fontx7, buf, 2, 17 + i * 30); draw_meter(a, &a->meter2, batt[i].curr_cap, batt[i].full_cap, 3, 25 + i * 30); } for (; i < 2; i++) { draw_text(a, &a->fontx7, " ", 2, 1 + i * 30); draw_text(a, &a->fontx7, " ", 2, 9 + i * 30); draw_text(a, &a->fontx7, " _ ", 2, 17 + i * 30); draw_text(a, &a->fontx7, " ", 2, 20 + i * 30); } dockapp_update(d); } static int update_text (SENSOR *list) { (void)list; /* XXX cool stuff here */ } static int run_text (SENSOR *list, unsigned long millis) { struct timeval timeout; fd_set rset; struct stat buf; int redir = fstat(STDOUT_FILENO, &buf) == 0 && (S_ISREG(buf.st_mode) || S_ISFIFO(buf.st_mode)); for (;;) { update_text(list); if (redir) { break; } printf("not yet implemented -- press Enter to quit\n"); timeout.tv_sec = millis / 1000; timeout.tv_usec = (millis % 1000) * 1000; FD_ZERO(&rset); FD_SET(0, &rset); if (select(0 + 1, &rset, NULL, NULL, &timeout) > 0) { break; } } return 0; } static void args (int argc, char **argv, int *iswindowed, const char **display_name, int *textmode) { const char *myself = argv[0]; *iswindowed = 0; *display_name = NULL; *textmode = 0; while (--argc) { const char *p = *++argv; if (!strcmp(p, "-h") || !strcmp(p, "--help")) { printf("wmfu v1.0 (c) 2007 Daniel Borca\n"); printf("wmfu is distributed under GNU GPL v2\n\n"); printf("usage: %s [OPTIONS]\n", myself); printf(" -display use specified display\n"); printf(" --windowed run in regular window\n"); printf(" --textmode run in text mode\n"); exit(0); } if (!strcmp(p, "-display")) { if (argc < 2) { fprintf(stderr, "%s: argument to `%s' is missing\n", myself, p); exit(1); } --argc; *display_name = *++argv; } if (!strcmp(p, "--windowed")) { *iswindowed = 1; } if (!strcmp(p, "--textmode")) { *textmode = 1; } } } int main (int argc, char **argv) { int rv; APP App; FRAME frames[6]; int iswindowed; const char *display_name; int textmode; args(argc, argv, &iswindowed, &display_name, &textmode); App.list = sensors_init(); if (App.list == NULL) { fprintf(stderr, "Error allocating sensor structure\n"); return -1; } if (textmode) { int rv = run_text(App.list, 1000); sensors_free(App.list); return rv; } rv = dockapp_open_window(&DockApp, display_name, "wmfu", iswindowed, argc, argv); if (rv != 0) { fprintf(stderr, "Could not open display `%s'\n", display_name); sensors_free(App.list); return rv; } frames[0].v = dockapp_xpm2pixmap(&DockApp, frame0_xpm, &frames[0].f, &frames[0].m, NULL, 0); if (!frames[0].v) { fprintf(stderr, "Error initializing frame\n"); sensors_free(App.list); return -1; } if (!dockapp_xpm2pixmap(&DockApp, font_xpm, &App.font, NULL, NULL, 0)) { fprintf(stderr, "Error initializing font\n"); sensors_free(App.list); return -1; } App.fontx9.row = FONTX9_ROW; App.fontx9.height = 9; App.fontx9.width = fontx9_width; App.fontx9.offset = fontx9_offset; App.fontx8.row = FONTX8_ROW; App.fontx8.height = 8; App.fontx8.width = fontx8_width; App.fontx8.offset = fontx8_offset; App.fontx7.row = FONTX7_ROW; App.fontx7.height = 7; App.fontx7.width = fontx7_width; App.fontx7.offset = fontx7_offset; App.fontx6.row = FONTX6_ROW; App.fontx6.height = 6; App.fontx6.width = fontx6_width; App.fontx6.offset = fontx6_offset; App.meter2.row = METER2_ROW; App.meter2.height = 2; App.meter2.width = meter2_width; App.meter2.offset = NULL; frames[1].v = dockapp_xpm2pixmap(&DockApp, frame1_xpm, &frames[1].f, &frames[1].m, NULL, 0); frames[2].v = dockapp_xpm2pixmap(&DockApp, frame2_xpm, &frames[2].f, &frames[2].m, NULL, 0); frames[3].v = dockapp_xpm2pixmap(&DockApp, frame3_xpm, &frames[3].f, &frames[3].m, NULL, 0); frames[4].v = dockapp_xpm2pixmap(&DockApp, frame4_xpm, &frames[4].f, &frames[4].m, NULL, 0); frames[5].v = dockapp_xpm2pixmap(&DockApp, frame5_xpm, &frames[5].f, &frames[5].m, NULL, 0); frames[0].u = 2000; frames[1].u = 10000; frames[2].u = 5000; frames[3].u = 5000; frames[4].u = 5000; frames[5].u = 10000; App.frames = frames; SWITCH_FRAME(&App, 0); dockapp_set_eventmask(&DockApp, ButtonPressMask); #if 1 while (!DockApp.quit) { XEvent event; if (dockapp_nextevent_or_timeout(&DockApp, &event, App.millis)) { App.handle(&App, &event); } else { App.update(&App, 0); } } #else { int i; for (i = 0; i < 1000; i++) { App.update(&App, 0); } } #endif XCloseDisplay(DockApp.display); sensors_free(App.list); return 0; }