dwmblocks

git clone git://mattcarlson.org/repos/dwmblocks.git
Log | Files | Refs

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 }