dotfiles

personal configuration files and scripts
git clone https://tongong.net/git/dotfiles.git
Log | Files | Refs | README

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 }