2021-10-13 03:07:55 +02:00
|
|
|
#define _GNU_SOURCE
|
2021-03-20 10:52:45 +01:00
|
|
|
#include <X11/Xlib.h>
|
2021-04-17 12:17:40 +02:00
|
|
|
#include <limits.h>
|
2021-03-20 10:52:45 +01:00
|
|
|
#include <signal.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2021-10-12 16:53:42 +02:00
|
|
|
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
|
2021-10-12 06:36:31 +02:00
|
|
|
#define STR2(a) #a
|
2021-10-12 07:43:08 +02:00
|
|
|
#define STR(a) STR2(a)
|
2021-10-13 03:07:55 +02:00
|
|
|
#define BLOCK(cmd, interval, signal) {"echo \"" STR(__COUNTER__) "$(" cmd ")\"", interval, signal},
|
2021-10-12 06:36:31 +02:00
|
|
|
typedef struct {
|
|
|
|
const char *command;
|
|
|
|
const unsigned int interval;
|
|
|
|
const unsigned int signal;
|
|
|
|
} Block;
|
2021-03-20 10:52:45 +01:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
static Display *dpy;
|
|
|
|
static int screen;
|
|
|
|
static Window root;
|
2021-10-12 16:53:42 +02:00
|
|
|
static char outputs[LEN(blocks)][CMDLENGTH + 2];
|
|
|
|
static char statusBar[2][LEN(blocks) * ((LEN(outputs[0]) - 1) + (LEN(DELIMITER) - 1)) + 1];
|
2021-03-20 10:52:45 +01:00
|
|
|
static int statusContinue = 1;
|
2021-10-14 11:13:07 +02:00
|
|
|
static volatile sig_atomic_t updatedBlocks = 0;
|
2021-04-17 12:17:40 +02:00
|
|
|
void (*writeStatus)();
|
2021-10-12 06:36:31 +02:00
|
|
|
static int pipeFD[2];
|
2021-04-13 07:35:21 +02:00
|
|
|
|
|
|
|
int gcd(int a, int b) {
|
|
|
|
int temp;
|
|
|
|
while (b > 0) {
|
|
|
|
temp = a % b;
|
|
|
|
a = b;
|
|
|
|
b = temp;
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
2021-04-13 07:35:21 +02:00
|
|
|
return a;
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
|
|
|
|
2021-04-13 07:35:21 +02:00
|
|
|
void replace(char *str, char old, char new) {
|
|
|
|
for (char *ch = str; *ch; ch++)
|
|
|
|
if (*ch == old)
|
|
|
|
*ch = new;
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 12:17:40 +02:00
|
|
|
void getCommand(int i, const char *button) {
|
|
|
|
if (fork() == 0) {
|
2021-10-12 06:36:31 +02:00
|
|
|
dup2(pipeFD[1], STDOUT_FILENO);
|
|
|
|
close(pipeFD[0]);
|
|
|
|
|
|
|
|
if (button)
|
|
|
|
setenv("BLOCK_BUTTON", button, 1);
|
2021-10-13 17:58:49 +02:00
|
|
|
execl("/bin/sh", "sh", "-c", blocks[i].command);
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void getCommands(int time) {
|
2021-04-17 12:17:40 +02:00
|
|
|
for (int i = 0; i < LEN(blocks); i++)
|
|
|
|
if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0))
|
|
|
|
getCommand(i, NULL);
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 12:17:40 +02:00
|
|
|
void getSignalCommand(int signal) {
|
|
|
|
for (int i = 0; i < LEN(blocks); i++)
|
|
|
|
if (blocks[i].signal == signal)
|
|
|
|
getCommand(i, NULL);
|
2021-04-13 07:35:21 +02:00
|
|
|
}
|
|
|
|
|
2021-03-20 10:52:45 +01:00
|
|
|
int getStatus(char *new, char *old) {
|
|
|
|
strcpy(old, new);
|
2021-10-12 06:36:31 +02:00
|
|
|
new[0] = 0;
|
2021-10-13 17:58:49 +02:00
|
|
|
|
2021-04-17 12:17:40 +02:00
|
|
|
for (int i = 0; i < LEN(blocks); i++) {
|
2021-10-13 17:58:49 +02:00
|
|
|
#ifdef TRAILING_DELIMITER
|
|
|
|
if (strlen(outputs[i]) > (blocks[i].signal > 0))
|
|
|
|
#else
|
|
|
|
if (strlen(new) && strlen(outputs[i]) > (blocks[i].signal > 0))
|
|
|
|
#endif
|
2021-04-17 12:17:40 +02:00
|
|
|
strcat(new, DELIMITER);
|
2021-10-12 06:36:31 +02:00
|
|
|
strcat(new, outputs[i]);
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
2021-10-12 06:36:31 +02:00
|
|
|
new[strlen(new)] = 0;
|
2021-03-20 10:52:45 +01:00
|
|
|
return strcmp(new, old);
|
|
|
|
}
|
|
|
|
|
2021-04-13 07:35:21 +02:00
|
|
|
void debug() {
|
|
|
|
// Only write out if text has changed
|
2021-10-12 06:36:31 +02:00
|
|
|
if (!getStatus(statusBar[0], statusBar[1]))
|
|
|
|
return;
|
2021-04-13 07:35:21 +02:00
|
|
|
|
2021-04-17 12:17:40 +02:00
|
|
|
write(STDOUT_FILENO, statusBar[0], strlen(statusBar[0]));
|
2021-04-13 07:35:21 +02:00
|
|
|
write(STDOUT_FILENO, "\n", 1);
|
|
|
|
}
|
|
|
|
|
2021-03-20 10:52:45 +01:00
|
|
|
void setRoot() {
|
2021-03-22 06:42:52 +01:00
|
|
|
// Only set root if text has changed
|
2021-10-12 06:36:31 +02:00
|
|
|
if (!getStatus(statusBar[0], statusBar[1]))
|
|
|
|
return;
|
2021-03-20 10:52:45 +01:00
|
|
|
|
|
|
|
Display *d = XOpenDisplay(NULL);
|
2021-10-12 06:36:31 +02:00
|
|
|
if (d)
|
|
|
|
dpy = d;
|
2021-03-20 10:52:45 +01:00
|
|
|
screen = DefaultScreen(dpy);
|
|
|
|
root = RootWindow(dpy, screen);
|
2021-04-17 12:17:40 +02:00
|
|
|
XStoreName(dpy, root, statusBar[0]);
|
2021-03-20 10:52:45 +01:00
|
|
|
XCloseDisplay(dpy);
|
|
|
|
}
|
|
|
|
|
2021-08-31 13:48:19 +02:00
|
|
|
void signalHandler(int sig, siginfo_t *si, void *ucontext) {
|
|
|
|
sig -= SIGRTMIN;
|
2021-04-17 12:17:40 +02:00
|
|
|
int i = 0;
|
2021-10-12 06:36:31 +02:00
|
|
|
while (blocks[i].signal != sig)
|
|
|
|
i++;
|
|
|
|
const char button[2] = {'0' + si->si_value.sival_int & 0xff, 0};
|
2021-04-17 12:17:40 +02:00
|
|
|
getCommand(i, button);
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
|
|
|
|
2021-04-17 12:17:40 +02:00
|
|
|
void termHandler(int signal) {
|
2021-03-20 10:52:45 +01:00
|
|
|
statusContinue = 0;
|
2021-04-17 12:17:40 +02:00
|
|
|
exit(EXIT_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
void childHandler() {
|
2021-10-12 06:36:31 +02:00
|
|
|
char i;
|
|
|
|
read(pipeFD[0], &i, 1);
|
|
|
|
i -= '0';
|
|
|
|
|
|
|
|
char ch;
|
2021-10-13 03:07:55 +02:00
|
|
|
char buffer[LEN(outputs[0]) - 1];
|
2021-10-12 06:36:31 +02:00
|
|
|
int j = 0;
|
2021-10-13 03:07:55 +02:00
|
|
|
while (j < LEN(buffer) - 1 && read(pipeFD[0], &ch, 1) == 1 && ch != '\n')
|
2021-10-12 06:36:31 +02:00
|
|
|
buffer[j++] = ch;
|
2021-10-13 03:07:55 +02:00
|
|
|
buffer[j] = 0;
|
|
|
|
|
|
|
|
// Clear the pipe until newline
|
|
|
|
while (ch != '\n' && read(pipeFD[0], &ch, 1) == 1);
|
2021-10-12 06:36:31 +02:00
|
|
|
|
|
|
|
char *output = outputs[i];
|
2021-10-13 17:58:49 +02:00
|
|
|
if (blocks[i].signal > 0) {
|
|
|
|
output[0] = blocks[i].signal;
|
2021-10-12 06:36:31 +02:00
|
|
|
output++;
|
|
|
|
}
|
2021-10-14 11:13:07 +02:00
|
|
|
|
|
|
|
// 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);
|
2021-03-20 10:52:45 +01:00
|
|
|
}
|
|
|
|
|
2021-04-13 07:35:21 +02:00
|
|
|
void setupSignals() {
|
2021-04-17 12:17:40 +02:00
|
|
|
signal(SIGTERM, termHandler);
|
|
|
|
signal(SIGINT, termHandler);
|
2021-04-13 07:35:21 +02:00
|
|
|
|
|
|
|
// Handle block update signals
|
2021-04-17 12:17:40 +02:00
|
|
|
struct sigaction sa;
|
2021-08-31 13:48:19 +02:00
|
|
|
sa.sa_flags = SA_SIGINFO;
|
|
|
|
sa.sa_sigaction = signalHandler;
|
2021-04-17 12:17:40 +02:00
|
|
|
for (int i = 0; i < LEN(blocks); i++) {
|
2021-08-31 13:48:19 +02:00
|
|
|
if (blocks[i].signal > 0)
|
|
|
|
sigaction(SIGRTMIN + blocks[i].signal, &sa, NULL);
|
2021-04-13 07:35:21 +02:00
|
|
|
}
|
|
|
|
|
2021-04-17 12:17:40 +02:00
|
|
|
// Handle exit of forks
|
2021-10-12 06:36:31 +02:00
|
|
|
signal(SIGCHLD, childHandler);
|
2021-04-13 07:35:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void statusLoop() {
|
2021-04-17 12:17:40 +02:00
|
|
|
getCommands(0);
|
2021-04-13 07:35:21 +02:00
|
|
|
|
|
|
|
unsigned int sleepInterval = -1;
|
2021-04-17 12:17:40 +02:00
|
|
|
for (int i = 0; i < LEN(blocks); i++)
|
2021-04-13 07:35:21 +02:00
|
|
|
if (blocks[i].interval)
|
|
|
|
sleepInterval = gcd(blocks[i].interval, sleepInterval);
|
|
|
|
|
|
|
|
unsigned int i = 0;
|
|
|
|
struct timespec sleepTime = {sleepInterval, 0};
|
|
|
|
struct timespec toSleep = sleepTime;
|
|
|
|
|
|
|
|
while (statusContinue) {
|
|
|
|
// Sleep for `sleepTime` even on being interrupted
|
2021-10-12 06:36:31 +02:00
|
|
|
if (nanosleep(&toSleep, &toSleep) == -1)
|
|
|
|
continue;
|
2021-04-13 07:35:21 +02:00
|
|
|
|
|
|
|
// Write to status after sleeping
|
|
|
|
getCommands(i);
|
|
|
|
|
|
|
|
// After sleep, reset timer and update counter
|
|
|
|
toSleep = sleepTime;
|
|
|
|
i += sleepInterval;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-20 10:52:45 +01:00
|
|
|
int main(int argc, char **argv) {
|
2021-10-12 06:36:31 +02:00
|
|
|
pipe(pipeFD);
|
2021-04-17 12:17:40 +02:00
|
|
|
writeStatus = setRoot;
|
2021-03-20 10:52:45 +01:00
|
|
|
for (int i = 0; i < argc; i++)
|
2021-10-12 06:36:31 +02:00
|
|
|
if (!strcmp("-d", argv[i]))
|
|
|
|
writeStatus = debug;
|
2021-04-17 12:17:40 +02:00
|
|
|
|
|
|
|
setupSignals();
|
2021-03-20 10:52:45 +01:00
|
|
|
statusLoop();
|
|
|
|
}
|