dwmblocks.c (11963B)
1 #include <errno.h> 2 #include <fcntl.h> 3 #include <limits.h> 4 #include <signal.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <X11/Xlib.h> 10 11 #define CMDLENGTH 25 12 #define STTLENGTH 256 13 #define NILL INT_MIN 14 #define LOCKFILE "/tmp/dwmblocks.pid" 15 16 typedef struct { 17 char *pathu; 18 char *pathc; 19 const int interval; 20 const int signal; 21 char cmdoutcur[CMDLENGTH]; 22 char cmdoutprv[CMDLENGTH]; 23 } Block; 24 25 #include "blocks.h" 26 27 static void buttonhandler(int signal, siginfo_t *si, void *ucontext); 28 static void getcmd(Block *block, int sigval); 29 static void setroot(); 30 static void setupsignals(); 31 static void sighandler(int signal, siginfo_t *si, void *ucontext); 32 static void statusloop(); 33 static void termhandler(int signum); 34 static int updatestatus(); 35 static void writepid(); 36 37 static int statuscontinue = 1; 38 static char statusstr[STTLENGTH]; 39 static size_t delimlength; 40 static Display *dpy; 41 static sigset_t blocksigmask; 42 43 void 44 buttonhandler(int signal, siginfo_t *si, void *ucontext) 45 { 46 signal = si->si_value.sival_int >> 8; 47 for (Block *current = blocks; current->pathu; current++) 48 if (current->signal == signal) 49 switch (fork()) { 50 case -1: 51 perror("buttonhandler - fork"); 52 break; 53 case 0: 54 { 55 char button[] = { '0' + (si->si_value.sival_int & 0xff), '\0' }; 56 char *arg[] = { current->pathc, button, NULL }; 57 58 close(ConnectionNumber(dpy)); 59 setsid(); 60 execvp(arg[0], arg); 61 perror("buttonhandler - child - execv"); 62 _exit(127); 63 } 64 } 65 } 66 67 void 68 getcmd(Block *block, int sigval) 69 { 70 int fd[2]; 71 72 if (pipe(fd) == -1) { 73 perror("getcmd - pipe"); 74 exit(1); 75 } 76 switch (fork()) { 77 case -1: 78 perror("getcmd - fork"); 79 exit(1); 80 case 0: 81 close(ConnectionNumber(dpy)); 82 close(fd[0]); 83 if (fd[1] != STDOUT_FILENO) { 84 if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) { 85 perror("getcmd - child - dup2"); 86 exit(1); 87 } 88 close(fd[1]); 89 } 90 if (sigval == NILL) { 91 char *arg[] = { block->pathu, NULL }; 92 93 execvp(arg[0], arg); 94 } else { 95 char buf[12]; 96 char *arg[] = { block->pathu, buf, NULL }; 97 98 snprintf(buf, sizeof buf, "%d", sigval); 99 execvp(arg[0], arg); 100 } 101 perror("getcmd - child - execv"); 102 _exit(127); 103 default: 104 close(fd[1]); 105 if (read(fd[0], block->cmdoutcur, CMDLENGTH) == -1) { 106 perror("getcmd - read"); 107 exit(1); 108 } 109 close(fd[0]); 110 } 111 } 112 113 void 114 setroot() 115 { 116 if (updatestatus()) { 117 XStoreName(dpy, DefaultRootWindow(dpy), statusstr); 118 XSync(dpy, False); 119 } 120 } 121 122 void 123 setupsignals() 124 { 125 struct sigaction sa; 126 127 /* to handle HUP, INT and TERM */ 128 sa.sa_flags = SA_RESTART; 129 sigemptyset(&sa.sa_mask); 130 sa.sa_handler = termhandler; 131 sigaction(SIGHUP, &sa, NULL); 132 sigaction(SIGINT, &sa, NULL); 133 sigaction(SIGTERM, &sa, NULL); 134 135 /* to ignore unused realtime signals */ 136 // sa.sa_flags = SA_RESTART; 137 // sigemptyset(&sa.sa_mask); 138 sa.sa_handler = SIG_IGN; 139 for (int i = SIGRTMIN + 1; i <= SIGRTMAX; i++) 140 sigaction(i, &sa, NULL); 141 142 /* to prevent forked children from becoming zombies */ 143 sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; 144 // sigemptyset(&sa.sa_mask); 145 sa.sa_handler = SIG_DFL; 146 sigaction(SIGCHLD, &sa, NULL); 147 148 /* to handle signals generated by dwm on click events */ 149 sa.sa_flags = SA_RESTART | SA_SIGINFO; 150 // sigemptyset(&sa.sa_mask); 151 sa.sa_sigaction = buttonhandler; 152 sigaction(SIGRTMIN, &sa, NULL); 153 154 /* to handle update signals for individual blocks */ 155 sa.sa_flags |= SA_NODEFER; 156 sa.sa_mask = blocksigmask; 157 sa.sa_sigaction = sighandler; 158 for (Block *current = blocks; current->pathu; current++) 159 if (current->signal > 0) 160 sigaction(SIGRTMIN + current->signal, &sa, NULL); 161 } 162 163 void 164 sighandler(int signal, siginfo_t *si, void *ucontext) 165 { 166 signal -= SIGRTMIN; 167 for (Block *current = blocks; current->pathu; current++) 168 if (current->signal == signal) 169 getcmd(current, si->si_value.sival_int); 170 setroot(); 171 } 172 173 void 174 statusloop() 175 { 176 int i; 177 178 /* first run */ 179 sigprocmask(SIG_BLOCK, &blocksigmask, NULL); 180 for (Block *current = blocks; current->pathu; current++) 181 if (current->interval >= 0) 182 getcmd(current, NILL); 183 setroot(); 184 sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); 185 sleep(SLEEPINTERVAL); 186 i = SLEEPINTERVAL; 187 /* main loop */ 188 while (statuscontinue) { 189 sigprocmask(SIG_BLOCK, &blocksigmask, NULL); 190 for (Block *current = blocks; current->pathu; current++) 191 if (current->interval > 0 && i % current->interval == 0) 192 getcmd(current, NILL); 193 setroot(); 194 sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL); 195 sleep(SLEEPINTERVAL); 196 i += SLEEPINTERVAL; 197 } 198 } 199 200 void 201 termhandler(int signum) 202 { 203 statuscontinue = 0; 204 } 205 206 /* returns whether block outputs have changed and updates statusstr if they have */ 207 int 208 updatestatus() 209 { 210 char *s = statusstr; 211 char *c, *p; /* for cmdoutcur and cmdoutprv */ 212 const char *d; /* for delimiter */ 213 Block *current = blocks; 214 215 /* checking half of the function */ 216 /* find the first non-empty block */ 217 for (;; current++) { 218 /* all blocks are empty */ 219 if (!current->pathu) 220 return 0; 221 /* contents of the current block changed */ 222 if (*current->cmdoutcur != *current->cmdoutprv) 223 goto update0; 224 /* skip delimiter handler for the first non-empty block */ 225 if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0') 226 goto skipdelimc; 227 } 228 /* main loop */ 229 for (; current->pathu; current++) { 230 /* contents of the current block changed */ 231 if (*current->cmdoutcur != *current->cmdoutprv) 232 goto update1; 233 /* delimiter handler */ 234 if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0') 235 s += delimlength; 236 /* skip over empty blocks */ 237 else 238 continue; 239 skipdelimc: 240 /* checking for the first byte has been done */ 241 c = current->cmdoutcur + 1, p = current->cmdoutprv + 1; 242 for (; *c != '\n' && *c != '\0'; c++, p++) 243 /* contents of the current block changed */ 244 if (*c != *p) { 245 s += c - current->cmdoutcur; 246 goto update2; 247 } 248 s += c - current->cmdoutcur; 249 /* byte containing info about signal number for the block */ 250 if (current->pathc && current->signal) 251 s++; 252 } 253 return 0; 254 255 /* updating half of the function */ 256 /* find the first non-empty block */ 257 for (;; current++) { 258 /* all blocks are empty */ 259 if (!current->pathu) 260 return 1; 261 update0: 262 /* don't add delimiter before the first non-empty block */ 263 if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0') 264 goto skipdelimu; 265 *current->cmdoutprv = *current->cmdoutcur; 266 } 267 /* main loop */ 268 for (; current->pathu; current++) { 269 update1: 270 /* delimiter handler */ 271 if (*current->cmdoutcur != '\n' && *current->cmdoutcur != '\0') { 272 d = delim; 273 while (*d != '\0') 274 *(s++) = *(d++); 275 *(s++) = '\n'; /* to mark the end of delimiter */ 276 /* skip over empty blocks */ 277 } else { 278 *current->cmdoutprv = *current->cmdoutcur; 279 continue; 280 } 281 skipdelimu: 282 c = current->cmdoutcur, p = current->cmdoutprv; 283 update2: 284 do { 285 *(s++) = *c; 286 *p = *c; 287 c++, p++; 288 } while (*c != '\n' && *c != '\0'); 289 if (current->pathc && current->signal) 290 *(s++) = current->signal; 291 } 292 *s = '\0'; 293 return 1; 294 } 295 296 void 297 writepid() 298 { 299 int fd; 300 struct flock fl; 301 302 fd = open(LOCKFILE, O_RDWR|O_CREAT, 0644); 303 if (fd == -1) { 304 perror("writepid - open"); 305 exit(1); 306 } 307 fl.l_type = F_WRLCK; 308 fl.l_start = 0; 309 fl.l_whence = SEEK_SET; 310 fl.l_len = 0; 311 if (fcntl(fd, F_SETLK, &fl) == -1) { 312 if (errno == EACCES || errno == EAGAIN) { 313 fputs("Error: another instance of dwmblocks is already running.\n", stderr); 314 exit(2); 315 } 316 perror("writepid - fcntl"); 317 exit(1); 318 } 319 if (ftruncate(fd, 0) == -1) { 320 perror("writepid - ftruncate"); 321 exit(1); 322 } 323 if (dprintf(fd, "%ld", (long)getpid()) < 0) { 324 perror("writepid - dprintf"); 325 exit(1); 326 } 327 } 328 329 int 330 main(int argc, char *argv[]) 331 { 332 writepid(); 333 if (argc > 2) 334 if (strcmp(argv[1], "-d") == 0) 335 delim = argv[2]; 336 delimlength = strlen(delim) + 1; 337 if (!(dpy = XOpenDisplay(NULL))) { 338 fputs("Error: could not open display.\n", stderr); 339 return 1; 340 } 341 sigemptyset(&blocksigmask); 342 sigaddset(&blocksigmask, SIGHUP); 343 sigaddset(&blocksigmask, SIGINT); 344 sigaddset(&blocksigmask, SIGTERM); 345 for (Block *current = blocks; current->pathu; current++) 346 if (current->signal > 0) 347 sigaddset(&blocksigmask, SIGRTMIN + current->signal); 348 setupsignals(); 349 statusloop(); 350 unlink(LOCKFILE); 351 XStoreName(dpy, DefaultRootWindow(dpy), ""); 352 XCloseDisplay(dpy); 353 return 0; 354 }