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