dwmblocks.c (7059B)
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <time.h> 6 #include <signal.h> 7 #include <errno.h> 8 #include <X11/Xlib.h> 9 #define LENGTH(X) (sizeof(X) / sizeof (X[0])) 10 #define CMDLENGTH 50 11 12 typedef struct { 13 char* icon; 14 char* command; 15 unsigned int interval; 16 unsigned int signal; 17 } Block; 18 void sighandler(int num); 19 void buttonhandler(int sig, siginfo_t *si, void *ucontext); 20 void replace(char *str, char old, char new); 21 void remove_all(char *str, char to_remove); 22 void getcmds(int time); 23 #ifndef __OpenBSD__ 24 void getsigcmds(int signal); 25 void setupsignals(); 26 void sighandler(int signum); 27 #endif 28 int getstatus(char *str, char *last); 29 void setroot(); 30 void statusloop(); 31 void termhandler(int signum); 32 33 34 #include "config.h" 35 36 static Display *dpy; 37 static int screen; 38 static Window root; 39 static char statusbar[LENGTH(blocks)][CMDLENGTH] = {0}; 40 static char statusstr[2][256]; 41 static int statusContinue = 1; 42 static void (*writestatus) () = setroot; 43 44 void replace(char *str, char old, char new) 45 { 46 for(char * c = str; *c; c++) 47 if(*c == old) 48 *c = new; 49 } 50 51 // the previous function looked nice but unfortunately it didnt work if to_remove was in any position other than the last character 52 // theres probably still a better way of doing this 53 void remove_all(char *str, char to_remove) { 54 char *read = str; 55 char *write = str; 56 while (*read) { 57 if (*read != to_remove) { 58 *write++ = *read; 59 } 60 ++read; 61 } 62 *write = '\0'; 63 } 64 65 int gcd(int a, int b) 66 { 67 int temp; 68 while (b > 0){ 69 temp = a % b; 70 71 a = b; 72 b = temp; 73 } 74 return a; 75 } 76 77 78 //opens process *cmd and stores output in *output 79 void getcmd(const Block *block, char *output) 80 { 81 if (block->signal) 82 { 83 output[0] = block->signal; 84 output++; 85 } 86 char *cmd = block->command; 87 FILE *cmdf = popen(cmd,"r"); 88 if (!cmdf){ 89 //printf("failed to run: %s, %d\n", block->command, errno); 90 return; 91 } 92 char tmpstr[CMDLENGTH] = ""; 93 // TODO decide whether its better to use the last value till next time or just keep trying while the error was the interrupt 94 // this keeps trying to read if it got nothing and the error was an interrupt 95 // could also just read to a separate buffer and not move the data over if interrupted 96 // this way will take longer trying to complete 1 thing but will get it done 97 // the other way will move on to keep going with everything and the part that failed to read will be wrong till its updated again 98 // either way you have to save the data to a temp buffer because when it fails it writes nothing and then then it gets displayed before this finishes 99 char * s; 100 int e; 101 do { 102 errno = 0; 103 s = fgets(tmpstr, CMDLENGTH-(strlen(delim)+1), cmdf); 104 e = errno; 105 } while (!s && e == EINTR); 106 pclose(cmdf); 107 int i = strlen(block->icon); 108 strcpy(output, block->icon); 109 strcpy(output+i, tmpstr); 110 remove_all(output, '\n'); 111 i = strlen(output); 112 if ((i > 0 && block != &blocks[LENGTH(blocks) - 1])){ 113 strcat(output, delim); 114 } 115 i+=strlen(delim); 116 output[i++] = '\0'; 117 } 118 119 void getcmds(int time) 120 { 121 const Block* current; 122 for(int i = 0; i < LENGTH(blocks); i++) 123 { 124 current = blocks + i; 125 if ((current->interval != 0 && time % current->interval == 0) || time == -1){ 126 getcmd(current,statusbar[i]); 127 } 128 } 129 } 130 131 #ifndef __OpenBSD__ 132 void getsigcmds(int signal) 133 { 134 const Block *current; 135 for (int i = 0; i < LENGTH(blocks); i++) 136 { 137 current = blocks + i; 138 if (current->signal == signal){ 139 getcmd(current,statusbar[i]); 140 } 141 } 142 } 143 144 void setupsignals() 145 { 146 struct sigaction sa; 147 148 for(int i = SIGRTMIN; i <= SIGRTMAX; i++) 149 signal(i, SIG_IGN); 150 151 for(int i = 0; i < LENGTH(blocks); i++) 152 { 153 if (blocks[i].signal > 0) 154 { 155 signal(SIGRTMIN+blocks[i].signal, sighandler); 156 sigaddset(&sa.sa_mask, SIGRTMIN+blocks[i].signal); 157 } 158 } 159 sa.sa_sigaction = buttonhandler; 160 sa.sa_flags = SA_SIGINFO; 161 sigaction(SIGUSR1, &sa, NULL); 162 struct sigaction sigchld_action = { 163 .sa_handler = SIG_DFL, 164 .sa_flags = SA_NOCLDWAIT 165 }; 166 sigaction(SIGCHLD, &sigchld_action, NULL); 167 168 } 169 #endif 170 171 int getstatus(char *str, char *last) 172 { 173 strcpy(last, str); 174 str[0] = '\0'; 175 for(int i = 0; i < LENGTH(blocks); i++) { 176 strcat(str, statusbar[i]); 177 if (i == LENGTH(blocks) - 1) 178 strcat(str, " "); 179 } 180 str[strlen(str)-1] = '\0'; 181 return strcmp(str, last);//0 if they are the same 182 } 183 184 void setroot() 185 { 186 if (!getstatus(statusstr[0], statusstr[1]))//Only set root if text has changed. 187 return; 188 Display *d = XOpenDisplay(NULL); 189 if (d) { 190 dpy = d; 191 } 192 screen = DefaultScreen(dpy); 193 root = RootWindow(dpy, screen); 194 XStoreName(dpy, root, statusstr[0]); 195 XCloseDisplay(dpy); 196 } 197 198 void pstdout() 199 { 200 if (!getstatus(statusstr[0], statusstr[1]))//Only write out if text has changed. 201 return; 202 printf("%s\n",statusstr[0]); 203 fflush(stdout); 204 } 205 206 207 void statusloop() 208 { 209 #ifndef __OpenBSD__ 210 setupsignals(); 211 #endif 212 // first figure out the default wait interval by finding the 213 // greatest common denominator of the intervals 214 unsigned int interval = -1; 215 for(int i = 0; i < LENGTH(blocks); i++){ 216 if(blocks[i].interval){ 217 interval = gcd(blocks[i].interval, interval); 218 } 219 } 220 unsigned int i = 0; 221 int interrupted = 0; 222 const struct timespec sleeptime = {interval, 0}; 223 struct timespec tosleep = sleeptime; 224 getcmds(-1); 225 while(statusContinue) 226 { 227 // sleep for tosleep (should be a sleeptime of interval seconds) and put what was left if interrupted back into tosleep 228 interrupted = nanosleep(&tosleep, &tosleep); 229 // if interrupted then just go sleep again for the remaining time 230 if(interrupted == -1){ 231 continue; 232 } 233 // if not interrupted then do the calling and writing 234 getcmds(i); 235 writestatus(); 236 // then increment since its actually been a second (plus the time it took the commands to run) 237 i += interval; 238 // set the time to sleep back to the sleeptime of 1s 239 tosleep = sleeptime; 240 } 241 } 242 243 #ifndef __OpenBSD__ 244 void sighandler(int signum) 245 { 246 getsigcmds(signum-SIGRTMIN); 247 writestatus(); 248 } 249 250 void buttonhandler(int sig, siginfo_t *si, void *ucontext) 251 { 252 char button[2] = {'0' + si->si_value.sival_int & 0xff, '\0'}; 253 pid_t process_id = getpid(); 254 sig = si->si_value.sival_int >> 8; 255 if (fork() == 0) 256 { 257 const Block *current; 258 for (int i = 0; i < LENGTH(blocks); i++) 259 { 260 current = blocks + i; 261 if (current->signal == sig) 262 break; 263 } 264 char shcmd[1024]; 265 sprintf(shcmd,"%s && kill -%d %d",current->command, current->signal+34,process_id); 266 char *command[] = { "/bin/sh", "-c", shcmd, NULL }; 267 setenv("BLOCK_BUTTON", button, 1); 268 setsid(); 269 execvp(command[0], command); 270 exit(EXIT_SUCCESS); 271 } 272 } 273 274 #endif 275 276 void termhandler(int signum) 277 { 278 statusContinue = 0; 279 exit(0); 280 } 281 282 int main(int argc, char** argv) 283 { 284 for(int i = 0; i < argc; i++) 285 { 286 if (!strcmp("-d",argv[i])) 287 delim = argv[++i]; 288 else if(!strcmp("-p",argv[i])) 289 writestatus = pstdout; 290 } 291 signal(SIGTERM, termhandler); 292 signal(SIGINT, termhandler); 293 statusloop(); 294 }