/*- * wmQStat - WindowMaker/AfterStep/BB/FB/Waimea dockable front-end to * Steve Jankowski's qstat (http://www.qstat.org/). * * Copyright (c) 2003-2005 Alexey Dokuchaev. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $Id: wmqstat.c,v 1.16 2005/02/21 10:59:40 danfe Exp $ */ #include #include #include #include #include #include #include #include #include #include "../wmgeneral/wmgeneral.h" #include "../wmgeneral/misc.h" #include "wmqstat.xpm" #define _WMQSTAT #include "proto.h" #define PROG_NAME "wmqstat" #define PROG_TITLE "wmQStat" #define PROG_VER "0.0.4" #define COPYRIGHT "Copyright (c) 2003-2005 Alexey Dokuchaev" #define RC_FILE "/." PROG_NAME "rc" #define MAX_PATH 254 #define TIME_FRAQ 5 #define MAX_TMP_STR 128 #define CHAR_WIDTH 6 #define CHAR_HEIGHT 8 char mask_bits[64 * 64]; int mask_width = 64; int mask_height = 64; int offset = 0; int what = FRAGS; struct slist *cursrv; /* must be global to be visible to signal() */ int main(int argc, char **argv) { XEvent event; int bpr = -1, brl = -1; struct itimerval itv; char *rcfile; struct slist *servers; if (!(rcfile = parse_cmd_line(argc, argv))) { fprintf(stderr, "Unable to read configuration file.\n"); return (1); } servers = spawn_servers(rcfile); free(rcfile); /* we don't need it anymore */ if (!(cursrv = servers)) { fprintf(stderr, "Could not load any servers.\n"); return (1); } createXBMfromXPM(mask_bits, wmqstat_xpm, mask_width, mask_height); openXwindow(argc, argv, wmqstat_xpm, mask_bits, mask_width, mask_height); AddMouseRegion(0, 5, 5, 58, 58); /* entire active area */ itv.it_value.tv_sec = itv.it_value.tv_usec = 1; /* start immediately */ itv.it_interval.tv_sec = TIME_FRAQ; itv.it_interval.tv_usec = 0; signal(SIGALRM, (void (*)(int))update_stats); setitimer(ITIMER_REAL, &itv, NULL); for (;;) { while (XPending(display)) { XNextEvent(display, &event); switch (event.type) { case Expose: RedrawWindow(); break; case DestroyNotify: XCloseDisplay(display); clean_up(servers); return (0); /* bye-bye! */ break; case ButtonPress: bpr = CheckMouseRegion(event.xbutton.x, event.xbutton.y); break; case ButtonRelease: brl = CheckMouseRegion(event.xbutton.x, event.xbutton.y); if (bpr == brl && brl >= 0) { switch (event.xbutton.button) { case 1: if (!servers->next) break; cursrv = cursrv->next ? cursrv->next : servers; update_stats(); break; case 3: if (!what--) what = FRAGS; display_stats(cursrv->srv); break; case 4: if (offset) offset--, display_stats(cursrv->srv); break; case 5: if (cursrv->srv->npl - offset > 4) offset++, display_stats(cursrv->srv); break; } } brl = -1; RedrawWindow(); break; } } usleep(100000l); } clean_up(servers); return (1); } /* * Re-fetch data from current server and display them. * Called once in a while normally (every TIME_FRAQ seconds), and when * user had selected next server to monitor. */ static void update_stats(void) { static int lock = 0; if (lock) return; lock++; /* grab the lock */ obtain_stats(cursrv->srv); draw_header(cursrv->srv); display_stats(cursrv->srv); RedrawWindow(); lock--; /* release lock */ } /* * For now, the only who things that can be specified as program * arguments (except X ones) are alternate config file and what to * display by default (frags, ping, or time). * Don't forget to free() the allocated memory afterwards. * Unknown options are ignored (instead of aborting and/or calling * usage(), due to no desire to take special care of -display/geometry * ones that `wmgeneral' does for us already (no matter how screwy it * might seem). */ static char * parse_cmd_line(int ac, char **av) { char *res = NULL; while (--ac) { if (!strncmp(av[ac - 1], "-c", 2) && ac) { res = strdup(av[ac]); /* to shup up free() */ continue; } if (!strncmp(av[ac], "-p", 2)) { what = PING; continue; } if (!strncmp(av[ac], "-t", 2)) { what = TIME; continue; } if (!strncmp(av[ac], "-h", 2)) usage(); if (!strncmp(av[ac], "-v", 2)) { fprintf(stderr, PROG_TITLE " version " PROG_VER "\n" COPYRIGHT "\n"); exit(1); } } if (!res) if (res = malloc(MAX_PATH + 1)) { strncpy(res, getenv("HOME"), MAX_PATH - sizeof(RC_FILE) + 1); strncat(res, RC_FILE, sizeof(RC_FILE) - 1); } return (res); } /* * Read configuration file, and spawn initial server monitors * from it (that is, allocate memory and obtain some preliminary * server information. */ static struct slist * spawn_servers(const char *rcfile) { FILE *fp; char type[MAX_TMP_STR], addr[MAX_TMP_STR]; struct server *srv; struct slist *servers = NULL; if (fp = fopen(rcfile, "r")) { while (fscanf(fp, "%64s%64s", type, addr) > 1) { /* * We always want to read 2 tokens; 1 or EOF (-1) * simply aren't good enough for us. */ if (*type == '#' || *addr == '#') /* ignore comments (token starting with #) */ continue; if (!(srv = add_server(type, addr))) continue; if (servers) { struct slist *tmp; if (tmp = malloc(sizeof(struct slist))) { tmp->srv = srv; tmp->next = servers; servers = tmp; } else kill_server(srv); } else { if (servers = malloc(sizeof(struct slist))) { servers->srv = srv; servers->next = NULL; } else kill_server(srv); } } } return (servers); } /* * Free all memory allocated for servers and clients, and destroy linked * list of servers. We do not immediately exit() here since this * might be called not necessarily at the end of dockapp's life. */ static void clean_up(struct slist *servers) { struct slist *tmp; while (servers) { kill_server(servers->srv); tmp = servers->next; free(servers); servers = tmp; } } /* * Display current map and total number of players. * I might consider adding that nifty marquee thing like in XMMS. * And tinting, probably, too. */ static void draw_header(const struct server *srv) { wm_printf(5, 5, "%-6.6s", (strlen(srv->map) > 6) ? (srv->map + strlen(srv->map) - 6) : srv->map); wm_printf(45, 5, "%2d", srv->npl); } /* * Display 4 players, skipping `offset' number from top. * `offset' is automagically adjusted when needed: there should not * left any blank lines if it's possible to display something in * there, and we always vertically align to top. * Display either players' frags, ping, or time (each if available), * depending on global `what' variable. */ static void display_stats(const struct server *srv) { int i = 4; if (srv->npl > 4) { if (srv->npl - offset < 4) offset = srv->npl - 4; if (offset < 0) offset = 0; /* valign always = top */ } else while (i > srv->npl) copyXPMArea(72, 50, 51, 7, 6, 17 + --i * 11); while (i--) { /* dump the `for' loop and thus save variable */ //copyXPMArea(124, 52, 1, 5, 37, 19 + i * 11); /* `:' */ wm_printf(5, 16 + i * 11, "%-5.5s", srv->players[i + offset].name); wm_printf(39, 16 + i * 11, "%3d", (what == FRAGS) ? srv->players[i + offset].frags : (what == PING) ? srv->players[i + offset].ping : (srv->players[i + offset].time > -1) ? srv->players[i + offset].time / 60 : -1); } } /* * Print something according to format string, on the dockapp * virtual LCD display. */ static void wm_printf(int x, const int y, const char *fmt, ...) { char p[MAX_TMP_STR]; va_list ap; int i; va_start(ap, fmt); vsnprintf(p, MAX_TMP_STR, fmt, ap); va_end(ap); for (i = 0; p[i]; i++) { copyXPMArea(((p[i] & 0x7f) - 32) * CHAR_WIDTH, 64, CHAR_WIDTH, CHAR_HEIGHT, x, y); x += CHAR_WIDTH; } } /* * This one is self-explanatory, I hope. ;-) */ static void usage(void) { fprintf(stderr, "\nUsage: " PROG_NAME " [option(s) ...]\n\n" "Available options:\n" " -p \t\t\tdisplay ping by default instead of frags\n" " -t \t\t\tdisplay time by default instead of frags\n" " -c filename\t\tuse alternate configuration file\n" " -v\t\t\tprint the version number\n" " -h\t\t\tdisplay this usage information\n" " -display a:b\t\tspecify the X server to contact\n" " -geometry +x+y\tset preferred dockapp geometry\n\n"); exit(1); }