dotfiles

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

dsblocks.c (7755B)


      1 #include <errno.h>
      2 #include <fcntl.h>
      3 #include <signal.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 #include "shared.h"
     12 
     13 #define LOCKFILE                        "/tmp/dsblocks.pid"
     14 
     15 #define DELIMITERLENGTH                 (sizeof delimiter)
     16 #define STATUSLENGTH                    (LENGTH(blocks) * (BLOCKLENGTH + DELIMITERLENGTH) + 1)
     17 
     18 #include "config.h"
     19 
     20 _Static_assert(INTERVALs >= 0, "INTERVALs must be greater than or equal to 0");
     21 _Static_assert(INTERVALn >= 0 && INTERVALn <= 999999999, "INTERVALn must be between 0 and 999999999");
     22 
     23 void cleanup();
     24 
     25 static void buttonhandler(int sig, siginfo_t *info, void *ucontext);
     26 static void setupsignals();
     27 static void sighandler(int sig, siginfo_t *info, void *ucontext);
     28 static void statusloop();
     29 static void termhandler(int sig);
     30 static void updateblock(Block *block, int sigval);
     31 static void updatestatus();
     32 static void writepid();
     33 
     34 Display *dpy;
     35 pid_t pid;
     36 
     37 static Block *dirtyblock;
     38 static sigset_t blocksigmask;
     39 
     40 void
     41 buttonhandler(int sig, siginfo_t *info, void *ucontext)
     42 {
     43         sig = info->si_value.sival_int >> 8;
     44         for (Block *block = blocks; block->funcu; block++)
     45                 if (block->signal == sig)
     46                         switch (fork()) {
     47                                 case -1:
     48                                         perror("buttonhandler - fork");
     49                                         break;
     50                                 case 0:
     51                                         close(ConnectionNumber(dpy));
     52                                         block->funcc(info->si_value.sival_int & 0xff);
     53                                         exit(0);
     54                         }
     55 }
     56 
     57 void
     58 cleanup()
     59 {
     60         unlink(LOCKFILE);
     61         XStoreName(dpy, DefaultRootWindow(dpy), "");
     62         XCloseDisplay(dpy);
     63 }
     64 
     65 void
     66 setupsignals()
     67 {
     68         struct sigaction sa;
     69 
     70         /* populate blocksigmask and check validity of signals */
     71         sigemptyset(&blocksigmask);
     72         sigaddset(&blocksigmask, SIGHUP);
     73         sigaddset(&blocksigmask, SIGINT);
     74         sigaddset(&blocksigmask, SIGTERM);
     75         for (Block *block = blocks; block->funcu; block++) {
     76                 if (block->signal <= 0)
     77                         continue;
     78                 if (block->signal > SIGRTMAX - SIGRTMIN) {
     79                         fprintf(stderr, "Error: SIGRTMIN + %d exceeds SIGRTMAX.\n", block->signal);
     80                         unlink(LOCKFILE);
     81                         XCloseDisplay(dpy);
     82                         exit(2);
     83                 }
     84                 sigaddset(&blocksigmask, SIGRTMIN + block->signal);
     85         }
     86 
     87         /* setup signal handlers */
     88         /* to handle HUP, INT and TERM */
     89         sa.sa_flags = SA_RESTART;
     90         sigemptyset(&sa.sa_mask);
     91         sa.sa_handler = termhandler;
     92         sigaction(SIGHUP, &sa, NULL);
     93         sigaction(SIGINT, &sa, NULL);
     94         sigaction(SIGTERM, &sa, NULL);
     95 
     96         /* to ignore unused realtime signals */
     97         // sa.sa_flags = SA_RESTART;
     98         // sigemptyset(&sa.sa_mask);
     99         sa.sa_handler = SIG_IGN;
    100         for (int i = SIGRTMIN + 1; i <= SIGRTMAX; i++)
    101                 sigaction(i, &sa, NULL);
    102 
    103         /* to prevent forked children from becoming zombies */
    104         sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
    105         // sigemptyset(&sa.sa_mask);
    106         sa.sa_handler = SIG_DFL;
    107         sigaction(SIGCHLD, &sa, NULL);
    108 
    109         /* to handle signals generated by dwm on click events */
    110         sa.sa_flags = SA_RESTART | SA_SIGINFO;
    111         // sigemptyset(&sa.sa_mask);
    112         sa.sa_sigaction = buttonhandler;
    113         sigaction(SIGRTMIN, &sa, NULL);
    114 
    115         /* to handle update signals for individual blocks */
    116         sa.sa_flags = SA_NODEFER | SA_RESTART | SA_SIGINFO;
    117         sa.sa_mask = blocksigmask;
    118         sa.sa_sigaction = sighandler;
    119         for (Block *block = blocks; block->funcu; block++)
    120                 if (block->signal > 0)
    121                         sigaction(SIGRTMIN + block->signal, &sa, NULL);
    122 }
    123 
    124 void
    125 sighandler(int sig, siginfo_t *info, void *ucontext)
    126 {
    127         sig -= SIGRTMIN;
    128         for (Block *block = blocks; block->funcu; block++)
    129                 if (block->signal == sig)
    130                         updateblock(block, info->si_value.sival_int);
    131         updatestatus();
    132 }
    133 
    134 void
    135 statusloop()
    136 {
    137         int i;
    138         struct timespec t;
    139 
    140         sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
    141         for (Block *block = blocks; block->funcu; block++)
    142                 if (block->interval >= 0)
    143                         updateblock(block, NILL);
    144         for (i = 1; ; i++) {
    145                 updatestatus();
    146                 sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL);
    147                 t.tv_sec = INTERVALs, t.tv_nsec = INTERVALn;
    148                 while (nanosleep(&t, &t) == -1);
    149                 sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
    150                 for (Block *block = blocks; block->funcu; block++)
    151                         if (block->interval > 0 && i % block->interval == 0)
    152                                 updateblock(block, NILL);
    153         }
    154 }
    155 
    156 void
    157 termhandler(int sig)
    158 {
    159         cleanup();
    160         exit(0);
    161 }
    162 
    163 void
    164 updateblock(Block *block, int sigval)
    165 {
    166         size_t l;
    167 
    168         if (!(l = block->funcu(block->curtext, sigval)))
    169                 return;
    170         if (memcmp(block->curtext, block->prvtext, l) != 0) {
    171                 memcpy(block->prvtext, block->curtext, l);
    172                 if (!dirtyblock || block < dirtyblock)
    173                         dirtyblock = block;
    174         }
    175         if (l == 1)
    176                 block->length = 0;
    177         else {
    178                 if (block->funcc)
    179                         block->curtext[l - 1] = block->signal;
    180                 else
    181                         l--;
    182                 memcpy(block->curtext + l, delimiter, DELIMITERLENGTH);
    183                 block->length = l + DELIMITERLENGTH;
    184         }
    185 }
    186 
    187 void
    188 updatestatus()
    189 {
    190         static char statustext[STATUSLENGTH];
    191         char *s = statustext;
    192         Block *block;
    193 
    194         if (!dirtyblock)
    195                 return;
    196         for (block = blocks; block < dirtyblock; block++)
    197                 s += block->length;
    198         for (; block->funcu; block++) {
    199                 memcpy(s, block->curtext, block->length);
    200                 s += block->length;
    201         }
    202         s[s == statustext ? 0 : -DELIMITERLENGTH] = '\0';
    203         dirtyblock = NULL;
    204 
    205         XStoreName(dpy, DefaultRootWindow(dpy), statustext);
    206         XSync(dpy, False);
    207 }
    208 
    209 void
    210 writepid()
    211 {
    212         int fd;
    213         struct flock fl;
    214 
    215         if ((fd = open(LOCKFILE, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
    216                 perror("writepid - open");
    217                 exit(1);
    218         }
    219         fl.l_type = F_WRLCK;
    220         fl.l_whence = SEEK_SET;
    221         fl.l_start = 0;
    222         fl.l_len = 0;
    223         if (fcntl(fd, F_SETLK, &fl) == -1) {
    224                 if (errno == EACCES || errno == EAGAIN) {
    225                         fputs("Error: another instance of dsblocks is already running.\n", stderr);
    226                         exit(2);
    227                 }
    228                 perror("writepid - fcntl");
    229                 exit(1);
    230         }
    231         if (ftruncate(fd, 0) == -1) {
    232                 perror("writepid - ftruncate");
    233                 exit(1);
    234         }
    235         if (dprintf(fd, "%ld", (long)pid) < 0) {
    236                 perror("writepid - dprintf");
    237                 exit(1);
    238         }
    239 }
    240 
    241 int
    242 main(int argc, char *argv[])
    243 {
    244         pid = getpid();
    245         writepid();
    246         if (!(dpy = XOpenDisplay(NULL))) {
    247                 fputs("Error: could not open display.\n", stderr);
    248                 unlink(LOCKFILE);
    249                 return 1;
    250         }
    251         setupsignals();
    252         statusloop();
    253         cleanup();
    254         return 0;
    255 }