Switch to poll-based execution of blocks
This commit is contained in:
parent
770742a8ee
commit
839ccc5b1d
4
config.h
4
config.h
|
@ -8,8 +8,8 @@ const Block blocks[] = {
|
||||||
BLOCK("sb-disk", 1800, 19)
|
BLOCK("sb-disk", 1800, 19)
|
||||||
BLOCK("sb-memory", 1800, 20)
|
BLOCK("sb-memory", 1800, 20)
|
||||||
BLOCK("sb-loadavg", 1800, 21)
|
BLOCK("sb-loadavg", 1800, 21)
|
||||||
BLOCK("sb-volume", 1800, 22)
|
BLOCK("sb-volume", 0, 22)
|
||||||
BLOCK("sb-battery", 5, 23)
|
BLOCK("sb-battery", 5, 23)
|
||||||
BLOCK("sb-date", 5, 24)
|
BLOCK("sb-date", 1, 24)
|
||||||
BLOCK("sb-network", 5, 25)
|
BLOCK("sb-network", 5, 25)
|
||||||
};
|
};
|
||||||
|
|
214
main.c
214
main.c
|
@ -1,33 +1,37 @@
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <limits.h>
|
#include <assert.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/epoll.h>
|
||||||
|
#include <sys/signalfd.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
#define STR2(a) #a
|
#define BLOCK(cmd, interval, signal) {"echo \"_$(" cmd ")\"", interval, signal},
|
||||||
#define STR(a) STR2(a)
|
|
||||||
#define BLOCK(cmd, interval, signal) {"echo \"" STR(__COUNTER__) "$(" cmd ")\"", interval, signal},
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *command;
|
const char* command;
|
||||||
const unsigned int interval;
|
const unsigned int interval;
|
||||||
const unsigned int signal;
|
const unsigned int signal;
|
||||||
} Block;
|
} Block;
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
static Display *dpy;
|
static Display* dpy;
|
||||||
static int screen;
|
static int screen;
|
||||||
static Window root;
|
static Window root;
|
||||||
|
static unsigned short int statusContinue = 1;
|
||||||
static char outputs[LEN(blocks)][CMDLENGTH + 2];
|
static char outputs[LEN(blocks)][CMDLENGTH + 2];
|
||||||
static char statusBar[2][LEN(blocks) * ((LEN(outputs[0]) - 1) + (LEN(DELIMITER) - 1)) + 1];
|
static char statusBar[2][LEN(blocks) * ((LEN(outputs[0]) - 1) + (LEN(DELIMITER) - 1)) + 1];
|
||||||
static int statusContinue = 1;
|
static struct epoll_event event, events[LEN(blocks) + 2];
|
||||||
static volatile sig_atomic_t updatedBlocks = 0;
|
static int pipes[LEN(blocks)][2];
|
||||||
|
static int timerFD[2];
|
||||||
|
static int signalFD;
|
||||||
|
static int epollFD;
|
||||||
void (*writeStatus)();
|
void (*writeStatus)();
|
||||||
static int pipeFD[2];
|
|
||||||
|
|
||||||
int gcd(int a, int b) {
|
int gcd(int a, int b) {
|
||||||
int temp;
|
int temp;
|
||||||
|
@ -39,16 +43,10 @@ int gcd(int a, int b) {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
void replace(char *str, char old, char new) {
|
void execBlock(int i, const char* button) {
|
||||||
for (char *ch = str; *ch; ch++)
|
|
||||||
if (*ch == old)
|
|
||||||
*ch = new;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getCommand(int i, const char *button) {
|
|
||||||
if (fork() == 0) {
|
if (fork() == 0) {
|
||||||
dup2(pipeFD[1], STDOUT_FILENO);
|
dup2(pipes[i][1], STDOUT_FILENO);
|
||||||
close(pipeFD[0]);
|
close(pipes[i][0]);
|
||||||
|
|
||||||
if (button)
|
if (button)
|
||||||
setenv("BLOCK_BUTTON", button, 1);
|
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++)
|
for (int i = 0; i < LEN(blocks); i++)
|
||||||
if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0))
|
if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0))
|
||||||
getCommand(i, NULL);
|
execBlock(i, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSignalCommand(int signal) {
|
int getStatus(char* new, char* old) {
|
||||||
for (int i = 0; i < LEN(blocks); i++)
|
|
||||||
if (blocks[i].signal == signal)
|
|
||||||
getCommand(i, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getStatus(char *new, char *old) {
|
|
||||||
strcpy(old, new);
|
strcpy(old, new);
|
||||||
new[0] = 0;
|
new[0] = '\0';
|
||||||
|
|
||||||
for (int i = 0; i < LEN(blocks); i++) {
|
for (int i = 0; i < LEN(blocks); i++) {
|
||||||
#ifdef TRAILING_DELIMITER
|
#ifdef TRAILING_DELIMITER
|
||||||
if (strlen(outputs[i]) > (blocks[i].signal > 0))
|
if (strlen(outputs[i]) > (blocks[i].signal > 0))
|
||||||
#else
|
#else
|
||||||
if (strlen(new) && strlen(outputs[i]) > (blocks[i].signal > 0))
|
if (strlen(new) && strlen(outputs[i]) > (blocks[i].signal > 0))
|
||||||
#endif
|
#endif
|
||||||
strcat(new, DELIMITER);
|
strcat(new, DELIMITER);
|
||||||
strcat(new, outputs[i]);
|
strcat(new, outputs[i]);
|
||||||
}
|
}
|
||||||
new[strlen(new)] = 0;
|
|
||||||
return strcmp(new, old);
|
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() {
|
void debug() {
|
||||||
// Only write out if text has changed
|
// Only write out if text has changed
|
||||||
if (!getStatus(statusBar[0], statusBar[1]))
|
if (!getStatus(statusBar[0], statusBar[1]))
|
||||||
|
@ -99,7 +109,7 @@ void setRoot() {
|
||||||
if (!getStatus(statusBar[0], statusBar[1]))
|
if (!getStatus(statusBar[0], statusBar[1]))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Display *d = XOpenDisplay(NULL);
|
Display* d = XOpenDisplay(NULL);
|
||||||
if (d)
|
if (d)
|
||||||
dpy = d;
|
dpy = d;
|
||||||
screen = DefaultScreen(dpy);
|
screen = DefaultScreen(dpy);
|
||||||
|
@ -108,87 +118,107 @@ void setRoot() {
|
||||||
XCloseDisplay(dpy);
|
XCloseDisplay(dpy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void signalHandler(int sig, siginfo_t *si, void *ucontext) {
|
void signalHandler() {
|
||||||
sig -= SIGRTMIN;
|
struct signalfd_siginfo info;
|
||||||
int i = 0;
|
read(signalFD, &info, sizeof(info));
|
||||||
while (blocks[i].signal != sig)
|
|
||||||
i++;
|
for (int j = 0; j < LEN(blocks); j++) {
|
||||||
const char button[2] = {'0' + si->si_value.sival_int & 0xff, 0};
|
if (blocks[j].signal == info.ssi_signo - SIGRTMIN) {
|
||||||
getCommand(i, button);
|
char button[] = {'0' + info.ssi_int & 0xff, 0};
|
||||||
|
execBlock(j, button);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void termHandler(int signal) {
|
void termHandler() {
|
||||||
statusContinue = 0;
|
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() {
|
void setupSignals() {
|
||||||
signal(SIGTERM, termHandler);
|
signal(SIGTERM, termHandler);
|
||||||
signal(SIGINT, termHandler);
|
signal(SIGINT, termHandler);
|
||||||
|
|
||||||
// Handle block update signals
|
// Avoid zombie subprocesses
|
||||||
struct sigaction sa;
|
struct sigaction sa;
|
||||||
sa.sa_flags = SA_SIGINFO;
|
sa.sa_handler = SIG_DFL;
|
||||||
sa.sa_sigaction = signalHandler;
|
sigemptyset(&sa.sa_mask);
|
||||||
for (int i = 0; i < LEN(blocks); i++) {
|
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)
|
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
|
pipe(timerFD);
|
||||||
signal(SIGCHLD, childHandler);
|
event.data.u32 = LEN(blocks);
|
||||||
|
epoll_ctl(epollFD, EPOLL_CTL_ADD, timerFD[0], &event);
|
||||||
|
|
||||||
|
setupSignals();
|
||||||
}
|
}
|
||||||
|
|
||||||
void statusLoop() {
|
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;
|
unsigned int sleepInterval = -1;
|
||||||
for (int i = 0; i < LEN(blocks); i++)
|
for (int i = 0; i < LEN(blocks); i++)
|
||||||
if (blocks[i].interval)
|
if (blocks[i].interval)
|
||||||
sleepInterval = gcd(blocks[i].interval, sleepInterval);
|
sleepInterval = gcd(blocks[i].interval, sleepInterval);
|
||||||
|
|
||||||
unsigned int i = 0;
|
unsigned long long int i = 0;
|
||||||
struct timespec sleepTime = {sleepInterval, 0};
|
struct timespec sleepTime = {sleepInterval, 0};
|
||||||
struct timespec toSleep = sleepTime;
|
struct timespec toSleep = sleepTime;
|
||||||
|
|
||||||
while (statusContinue) {
|
while (1) {
|
||||||
// Sleep for `sleepTime` even on being interrupted
|
// Sleep for `sleepTime` even on being interrupted
|
||||||
if (nanosleep(&toSleep, &toSleep) == -1)
|
if (nanosleep(&toSleep, &toSleep) == -1)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Write to status after sleeping
|
// Notify parent to update blocks
|
||||||
getCommands(i);
|
write(timerFD[1], &i, sizeof(i));
|
||||||
|
|
||||||
// After sleep, reset timer and update counter
|
// After sleep, reset timer and update counter
|
||||||
toSleep = sleepTime;
|
toSleep = sleepTime;
|
||||||
|
@ -196,13 +226,19 @@ void statusLoop() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(const int argc, const char* argv[]) {
|
||||||
pipe(pipeFD);
|
|
||||||
writeStatus = setRoot;
|
writeStatus = setRoot;
|
||||||
for (int i = 0; i < argc; i++)
|
for (int i = 0; i < argc; i++)
|
||||||
if (!strcmp("-d", argv[i]))
|
if (!strcmp("-d", argv[i]))
|
||||||
writeStatus = debug;
|
writeStatus = debug;
|
||||||
|
|
||||||
setupSignals();
|
init();
|
||||||
statusLoop();
|
|
||||||
|
if (fork() == 0)
|
||||||
|
timerLoop();
|
||||||
|
else
|
||||||
|
statusLoop();
|
||||||
|
|
||||||
|
close(epollFD);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue