status.c (4303B)
1 /* See LICENSE for license details. */ 2 #include <signal.h> 3 #include <stdarg.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <time.h> 8 #include <unistd.h> 9 #include <X11/Xlib.h> 10 11 #define ABS(a) ((a) < 0 ? -(a) : (a)) 12 #define LEN(a) (sizeof(a) / sizeof(*a)) 13 #define BLOCKLEN 128 14 #define BLOCKPAD 4 15 #define STATUSLEN ((LEN(blks) - 1) * BLOCKLEN + 1) 16 17 struct Block { 18 size_t (*fn)(struct Block *b); 19 const char *fmt; 20 int interval; 21 int signal; 22 void *arg; 23 char curstr[BLOCKLEN]; 24 char prevstr[BLOCKLEN]; 25 size_t len; 26 }; 27 28 static char buf[BLOCKLEN - BLOCKPAD]; 29 static Display *dpy; 30 static int dflag = 0; 31 static sigset_t blocksigmask; 32 static struct Block *dirty; 33 34 static void 35 die(const char *errstr, ...) 36 { 37 va_list ap; 38 39 va_start(ap, errstr); 40 vfprintf(stderr, errstr, ap); 41 va_end(ap); 42 exit(1); 43 } 44 45 static int 46 pscanf(const char *path, const char *fmt, ...) 47 { 48 FILE *fp; 49 va_list ap; 50 int ret; 51 52 if (!(fp = fopen(path, "r"))) 53 return -1; 54 55 va_start(ap, fmt); 56 ret = vfscanf(fp, fmt, ap); 57 va_end(ap); 58 fclose(fp); 59 60 return (ret == EOF) ? -1 : ret; 61 } 62 63 #include "config.h" 64 65 static void 66 terminate(int signo) 67 { 68 if (!dflag) { 69 XStoreName(dpy, DefaultRootWindow(dpy), NULL); 70 XCloseDisplay(dpy); 71 } 72 73 exit(0); 74 } 75 76 static void 77 updateblock(struct Block *b) 78 { 79 b->len = b->fn(b); 80 if ((b->len == 0 && b->prevstr[0] != 0) || 81 memcmp(b->curstr, b->prevstr, b->len)) { 82 if (b->len == 0) 83 b->prevstr[0] = b->curstr[0] = 0; 84 else 85 memcpy(b->prevstr, b->curstr, b->len); 86 if (!dirty || b < dirty) 87 dirty = b; 88 } 89 } 90 91 static void 92 updatestatus(void) 93 { 94 static char status[STATUSLEN]; 95 struct Block *b; 96 char *s = status; 97 98 for (b = blks; b < dirty; b++) 99 s += b->len; 100 101 for (; b->fn; b++) { 102 memcpy(s, b->curstr, b->len); 103 s += b->len; 104 } 105 s[0] = '\0'; 106 dirty = NULL; 107 108 if (dflag) { 109 puts(status); 110 return; 111 } 112 113 XStoreName(dpy, DefaultRootWindow(dpy), status); 114 XSync(dpy, False); 115 } 116 117 static void 118 sighandler(int signo, siginfo_t *info, void *context) 119 { 120 struct Block *b; 121 122 signo -= SIGRTMIN; 123 for (b = blks; b->fn; b++) 124 if (b->signal == signo) 125 updateblock(b); 126 if (dirty) 127 updatestatus(); 128 } 129 130 static void 131 setupsigs(void) 132 { 133 int i; 134 struct Block *b; 135 struct sigaction sa; 136 137 /* add signals to blocksigmask */ 138 sigemptyset(&blocksigmask); 139 for (b = blks; b->fn; b++) { 140 if (b->signal <= 0) 141 continue; 142 143 if (b->signal > SIGRTMAX - SIGRTMIN) 144 die("SIGRTMIN + %d exceeds SIGRTMAX\n", b->signal); 145 146 sigaddset(&blocksigmask, SIGRTMIN + b->signal); 147 } 148 149 /* handle terminating signals */ 150 sa.sa_flags = SA_RESTART; 151 sigemptyset(&sa.sa_mask); 152 sa.sa_handler = terminate; 153 sigaction(SIGHUP, &sa, NULL); 154 sigaction(SIGINT, &sa, NULL); 155 sigaction(SIGTERM, &sa, NULL); 156 157 /* ignore unused realtime signals */ 158 sa.sa_handler = SIG_IGN; 159 for (i = SIGRTMIN + 1; i <= SIGRTMAX; i++) 160 sigaction(i, &sa, NULL); 161 162 /* handle update signals for blocks */ 163 sa.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO; 164 sa.sa_mask = blocksigmask; 165 sa.sa_sigaction = sighandler; 166 for (b = blks; b->fn; b++) 167 if (b->signal > 0) 168 sigaction(SIGRTMIN + b->signal, &sa, NULL); 169 170 /* start with signals blocked */ 171 sigprocmask(SIG_BLOCK, &blocksigmask, NULL); 172 } 173 174 static void 175 statusinit(void) 176 { 177 struct Block *b; 178 179 setupsigs(); 180 181 if (!dflag && !(dpy = XOpenDisplay(NULL))) 182 die("XOpenDisplay: can't open display\n"); 183 184 /* initialize blocks before first print */ 185 for (b = blks; b->fn; b++) 186 if (b->interval != -1) 187 updateblock(b); 188 updatestatus(); 189 } 190 191 static void 192 statusloop(void) 193 { 194 unsigned int i = 0; 195 struct Block *b; 196 struct timespec t; 197 198 for (;;) { 199 sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); 200 t.tv_sec = INTERVAL_SEC; 201 t.tv_nsec = INTERVAL_NANO; 202 while (nanosleep(&t, &t) == -1); 203 sigprocmask(SIG_BLOCK, &blocksigmask, NULL); 204 205 for (b = blks; b->fn; b++) 206 if (b->interval > 0 && i % b->interval == 0) 207 updateblock(b); 208 if (dirty) 209 updatestatus(); 210 i++; 211 } 212 } 213 214 int 215 main(int argc, char *argv[]) 216 { 217 int i; 218 char *argv0 = *argv; 219 220 for (argv++; --argc && *argv && argv[0][0] == '-' && argv[0][1]; argv++) { 221 if (argv[0][1] == '-' && argv[0][2] == '\0') { 222 argv++; 223 argc--; 224 break; 225 } 226 227 for (i = 1; argv[0][i]; i++) 228 switch (argv[0][i]) { 229 case 'd': 230 dflag = 1; 231 break; 232 default: 233 die("usage: %s [-d]\n", argv0); 234 } 235 } 236 237 statusinit(); 238 statusloop(); 239 240 return 0; 241 }