dwmblocks.c (4648B)
1 #include <X11/Xlib.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 <time.h> 9 #include <unistd.h> 10 11 #include "config.h" 12 13 #define LEN(arr) (sizeof(arr) / sizeof(arr[0])) 14 #define MIN(x, y) (x > y ? y : x) 15 16 static Display *dpy; 17 static int screen; 18 static Window root; 19 static char statusBar[2][LEN(blocks) * (CMDLENGTH + LEN(DELIMITER) - 1) + 1]; 20 static int statusContinue = 1; 21 void (*writeStatus)(); 22 23 int gcd(int a, int b) { 24 int temp; 25 while (b > 0) { 26 temp = a % b; 27 a = b; 28 b = temp; 29 } 30 return a; 31 } 32 33 void replace(char *str, char old, char new) { 34 for (char *ch = str; *ch; ch++) 35 if (*ch == old) 36 *ch = new; 37 } 38 39 void getCommand(int i, const char *button) { 40 Block *block = blocks + i; 41 42 if (fork() == 0) { 43 dup2(block->pipe[1], STDOUT_FILENO); 44 close(block->pipe[0]); 45 close(block->pipe[1]); 46 47 // Temporary hack 48 char cmd[1024]; 49 sprintf(cmd, "echo \"_$(%s)\"", block->command); 50 51 char *command[] = {"/bin/sh", "-c", cmd, NULL}; 52 if (button) setenv("BLOCK_BUTTON", button, 1); 53 setsid(); 54 execvp(command[0], command); 55 } 56 } 57 58 void getCommands(int time) { 59 for (int i = 0; i < LEN(blocks); i++) 60 if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0)) 61 getCommand(i, NULL); 62 } 63 64 void getSignalCommand(int signal) { 65 for (int i = 0; i < LEN(blocks); i++) 66 if (blocks[i].signal == signal) 67 getCommand(i, NULL); 68 } 69 70 int getStatus(char *new, char *old) { 71 strcpy(old, new); 72 new[0] = '\0'; 73 for (int i = 0; i < LEN(blocks); i++) { 74 Block *block = blocks + i; 75 if (strlen(block->output) > (block->signal != 0)) 76 strcat(new, DELIMITER); 77 strcat(new, block->output); 78 } 79 new[strlen(new)] = '\0'; 80 return strcmp(new, old); 81 } 82 83 void debug() { 84 // Only write out if text has changed 85 if (!getStatus(statusBar[0], statusBar[1])) return; 86 87 write(STDOUT_FILENO, statusBar[0], strlen(statusBar[0])); 88 write(STDOUT_FILENO, "\n", 1); 89 } 90 91 void setRoot() { 92 // Only set root if text has changed 93 if (!getStatus(statusBar[0], statusBar[1])) return; 94 95 Display *d = XOpenDisplay(NULL); 96 if (d) dpy = d; 97 screen = DefaultScreen(dpy); 98 root = RootWindow(dpy, screen); 99 XStoreName(dpy, root, statusBar[0]); 100 XCloseDisplay(dpy); 101 } 102 103 void buttonHandler(int sig, siginfo_t *si, void *ucontext) { 104 sig = si->si_value.sival_int >> 8; 105 106 int i = 0; 107 while (blocks[i].signal != sig) i++; 108 const char button[2] = {'0' + si->si_value.sival_int & 0xff, '\0'}; 109 getCommand(i, button); 110 } 111 112 void signalHandler(int signal) { getSignalCommand(signal - SIGRTMIN); } 113 114 void termHandler(int signal) { 115 statusContinue = 0; 116 exit(EXIT_SUCCESS); 117 } 118 119 void childHandler() { 120 for (int i = 0; i < LEN(blocks); i++) { 121 Block *block = blocks + i; 122 123 int bytesToRead = CMDLENGTH; 124 char *output = block->output; 125 if (block->signal) output++, bytesToRead--; 126 127 char placebo; 128 if (read(block->pipe[0], &placebo, 1) == 1) { 129 char buffer[PIPE_BUF]; 130 read(block->pipe[0], buffer, PIPE_BUF); 131 replace(buffer, '\n', '\0'); 132 strncpy(output, buffer, bytesToRead); 133 break; 134 } 135 } 136 writeStatus(); 137 } 138 139 void setupSignals() { 140 signal(SIGTERM, termHandler); 141 signal(SIGINT, termHandler); 142 143 // Handle block update signals 144 struct sigaction sa; 145 for (int i = 0; i < LEN(blocks); i++) { 146 if (blocks[i].signal > 0) { 147 signal(SIGRTMIN + blocks[i].signal, signalHandler); 148 sigaddset(&sa.sa_mask, SIGRTMIN + blocks[i].signal); 149 } 150 } 151 152 // Handle mouse events 153 sa.sa_sigaction = buttonHandler; 154 sa.sa_flags = SA_SIGINFO; 155 sigaction(SIGUSR1, &sa, NULL); 156 157 // Handle exit of forks 158 struct sigaction sigchld_action = { 159 .sa_handler = childHandler, 160 .sa_flags = SA_NOCLDWAIT, 161 }; 162 sigaction(SIGCHLD, &sigchld_action, NULL); 163 } 164 165 void statusLoop() { 166 getCommands(0); 167 168 unsigned int sleepInterval = -1; 169 for (int i = 0; i < LEN(blocks); i++) 170 if (blocks[i].interval) 171 sleepInterval = gcd(blocks[i].interval, sleepInterval); 172 173 unsigned int i = 0; 174 struct timespec sleepTime = {sleepInterval, 0}; 175 struct timespec toSleep = sleepTime; 176 177 while (statusContinue) { 178 // Sleep for `sleepTime` even on being interrupted 179 if (nanosleep(&toSleep, &toSleep) == -1) continue; 180 181 // Write to status after sleeping 182 getCommands(i); 183 184 // After sleep, reset timer and update counter 185 toSleep = sleepTime; 186 i += sleepInterval; 187 } 188 } 189 190 int main(int argc, char **argv) { 191 writeStatus = setRoot; 192 for (int i = 0; i < argc; i++) 193 if (!strcmp("-d", argv[i])) writeStatus = debug; 194 195 for (int i = 0; i < LEN(blocks); i++) { 196 Block *block = blocks + i; 197 pipe(block->pipe); 198 fcntl(block->pipe[0], F_SETFL, O_NONBLOCK); 199 200 if (block->signal) block->output[0] = block->signal; 201 } 202 203 setupSignals(); 204 statusLoop(); 205 }