diff --git a/config.h b/config.h index c2fbd92..b7e8dc2 100644 --- a/config.h +++ b/config.h @@ -8,8 +8,8 @@ const Block blocks[] = { BLOCK("sb-disk", 1800, 19) BLOCK("sb-memory", 1800, 20) BLOCK("sb-loadavg", 1800, 21) - BLOCK("sb-volume", 1800, 22) + BLOCK("sb-volume", 0, 22) BLOCK("sb-battery", 5, 23) - BLOCK("sb-date", 5, 24) + BLOCK("sb-date", 1, 24) BLOCK("sb-network", 5, 25) }; diff --git a/main.c b/main.c index e316679..6313471 100644 --- a/main.c +++ b/main.c @@ -1,33 +1,37 @@ #define _GNU_SOURCE #include -#include +#include #include +#include #include #include #include +#include +#include #include #include #define LEN(arr) (sizeof(arr) / sizeof(arr[0])) -#define STR2(a) #a -#define STR(a) STR2(a) -#define BLOCK(cmd, interval, signal) {"echo \"" STR(__COUNTER__) "$(" cmd ")\"", interval, signal}, +#define BLOCK(cmd, interval, signal) {"echo \"_$(" cmd ")\"", interval, signal}, typedef struct { - const char *command; + const char* command; const unsigned int interval; const unsigned int signal; } Block; #include "config.h" -static Display *dpy; +static Display* dpy; static int screen; static Window root; +static unsigned short int statusContinue = 1; static char outputs[LEN(blocks)][CMDLENGTH + 2]; static char statusBar[2][LEN(blocks) * ((LEN(outputs[0]) - 1) + (LEN(DELIMITER) - 1)) + 1]; -static int statusContinue = 1; -static volatile sig_atomic_t updatedBlocks = 0; +static struct epoll_event event, events[LEN(blocks) + 2]; +static int pipes[LEN(blocks)][2]; +static int timerFD[2]; +static int signalFD; +static int epollFD; void (*writeStatus)(); -static int pipeFD[2]; int gcd(int a, int b) { int temp; @@ -39,16 +43,10 @@ int gcd(int a, int b) { return a; } -void replace(char *str, char old, char new) { - for (char *ch = str; *ch; ch++) - if (*ch == old) - *ch = new; -} - -void getCommand(int i, const char *button) { +void execBlock(int i, const char* button) { if (fork() == 0) { - dup2(pipeFD[1], STDOUT_FILENO); - close(pipeFD[0]); + dup2(pipes[i][1], STDOUT_FILENO); + close(pipes[i][0]); if (button) setenv("BLOCK_BUTTON", button, 1); @@ -56,35 +54,47 @@ void getCommand(int i, const char *button) { } } -void getCommands(int time) { +void execBlocks(unsigned long long int time) { for (int i = 0; i < LEN(blocks); i++) if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0)) - getCommand(i, NULL); + execBlock(i, NULL); } -void getSignalCommand(int signal) { - for (int i = 0; i < LEN(blocks); i++) - if (blocks[i].signal == signal) - getCommand(i, NULL); -} - -int getStatus(char *new, char *old) { +int getStatus(char* new, char* old) { strcpy(old, new); - new[0] = 0; + new[0] = '\0'; for (int i = 0; i < LEN(blocks); i++) { - #ifdef TRAILING_DELIMITER +#ifdef TRAILING_DELIMITER if (strlen(outputs[i]) > (blocks[i].signal > 0)) - #else +#else if (strlen(new) && strlen(outputs[i]) > (blocks[i].signal > 0)) - #endif +#endif strcat(new, DELIMITER); strcat(new, outputs[i]); } - new[strlen(new)] = 0; return strcmp(new, old); } +void updateBlock(int i) { + char* output = outputs[i]; + char buffer[LEN(outputs[0])]; + int bytesRead = read(pipes[i][0], buffer, LEN(buffer)); + buffer[bytesRead - 1] = '\0'; + + // Clear the pipe + if (bytesRead == LEN(buffer)) { + char ch; + while (read(pipes[i][0], &ch, 1) == 1 && ch != '\n') + ; + } + + if (blocks[i].signal > 0) + buffer[0] = blocks[i].signal; + + strcpy(output, buffer); +} + void debug() { // Only write out if text has changed if (!getStatus(statusBar[0], statusBar[1])) @@ -99,7 +109,7 @@ void setRoot() { if (!getStatus(statusBar[0], statusBar[1])) return; - Display *d = XOpenDisplay(NULL); + Display* d = XOpenDisplay(NULL); if (d) dpy = d; screen = DefaultScreen(dpy); @@ -108,87 +118,107 @@ void setRoot() { XCloseDisplay(dpy); } -void signalHandler(int sig, siginfo_t *si, void *ucontext) { - sig -= SIGRTMIN; - int i = 0; - while (blocks[i].signal != sig) - i++; - const char button[2] = {'0' + si->si_value.sival_int & 0xff, 0}; - getCommand(i, button); +void signalHandler() { + struct signalfd_siginfo info; + read(signalFD, &info, sizeof(info)); + + for (int j = 0; j < LEN(blocks); j++) { + if (blocks[j].signal == info.ssi_signo - SIGRTMIN) { + char button[] = {'0' + info.ssi_int & 0xff, 0}; + execBlock(j, button); + break; + } + } } -void termHandler(int signal) { +void termHandler() { statusContinue = 0; - exit(EXIT_SUCCESS); -} - -void childHandler() { - char i; - read(pipeFD[0], &i, 1); - i -= '0'; - - char ch; - char buffer[LEN(outputs[0]) - 1]; - int j = 0; - while (j < LEN(buffer) - 1 && read(pipeFD[0], &ch, 1) == 1 && ch != '\n') - buffer[j++] = ch; - buffer[j] = 0; - - // Clear the pipe until newline - while (ch != '\n' && read(pipeFD[0], &ch, 1) == 1); - - char *output = outputs[i]; - if (blocks[i].signal > 0) { - output[0] = blocks[i].signal; - output++; - } - - // Don't write stale output from the pipe. This only happens when signals - // are received in a rapid succession. - if ((updatedBlocks & (1 << i)) == 0) { - updatedBlocks |= 1 << i; - strcpy(output, buffer); - writeStatus(); - } - updatedBlocks &= ~(1 << i); } void setupSignals() { signal(SIGTERM, termHandler); signal(SIGINT, termHandler); - // Handle block update signals + // Avoid zombie subprocesses struct sigaction sa; - sa.sa_flags = SA_SIGINFO; - sa.sa_sigaction = signalHandler; - for (int i = 0; i < LEN(blocks); i++) { + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDWAIT; + sigaction(SIGCHLD, &sa, 0); + + // Handle block update signals + sigset_t sigset; + sigemptyset(&sigset); + for (int i = 0; i < LEN(blocks); i++) if (blocks[i].signal > 0) - sigaction(SIGRTMIN + blocks[i].signal, &sa, NULL); + sigaddset(&sigset, SIGRTMIN + blocks[i].signal); + + signalFD = signalfd(-1, &sigset, 0); + sigprocmask(SIG_BLOCK, &sigset, NULL); + event.data.u32 = LEN(blocks) + 1; + epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event); +} + +void init() { + epollFD = epoll_create(LEN(blocks) + 1); + event.events = EPOLLIN; + + for (int i = 0; i < LEN(blocks); i++) { + pipe(pipes[i]); + event.data.u32 = i; + epoll_ctl(epollFD, EPOLL_CTL_ADD, pipes[i][0], &event); } - // Handle exit of forks - signal(SIGCHLD, childHandler); + pipe(timerFD); + event.data.u32 = LEN(blocks); + epoll_ctl(epollFD, EPOLL_CTL_ADD, timerFD[0], &event); + + setupSignals(); } void statusLoop() { - getCommands(0); + execBlocks(0); + + while (statusContinue) { + int eventCount = epoll_wait(epollFD, events, LEN(events), 1000); + + for (int i = 0; i < eventCount; i++) { + unsigned int id = events[i].data.u32; + + if (id == LEN(blocks)) { + unsigned long long int j = 0; + read(timerFD[0], &j, sizeof(j)); + execBlocks(j); + } else if (id < LEN(blocks)) { + updateBlock(events[i].data.u32); + } else { + signalHandler(); + } + } + if (eventCount) + writeStatus(); + } +} + +void timerLoop() { + close(timerFD[0]); unsigned int sleepInterval = -1; for (int i = 0; i < LEN(blocks); i++) if (blocks[i].interval) sleepInterval = gcd(blocks[i].interval, sleepInterval); - unsigned int i = 0; + unsigned long long int i = 0; struct timespec sleepTime = {sleepInterval, 0}; struct timespec toSleep = sleepTime; - while (statusContinue) { + while (1) { // Sleep for `sleepTime` even on being interrupted if (nanosleep(&toSleep, &toSleep) == -1) continue; - // Write to status after sleeping - getCommands(i); + // Notify parent to update blocks + write(timerFD[1], &i, sizeof(i)); // After sleep, reset timer and update counter toSleep = sleepTime; @@ -196,13 +226,19 @@ void statusLoop() { } } -int main(int argc, char **argv) { - pipe(pipeFD); +int main(const int argc, const char* argv[]) { writeStatus = setRoot; for (int i = 0; i < argc; i++) if (!strcmp("-d", argv[i])) writeStatus = debug; - setupSignals(); - statusLoop(); + init(); + + if (fork() == 0) + timerLoop(); + else + statusLoop(); + + close(epollFD); + return 0; }