dotfiles

[void/arch] linux dotfiles
git clone git://git.mdnr.space/dotfiles
Log | Files | Refs

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 }