dwmblocks-custom/main.c

259 lines
5.6 KiB
C
Raw Normal View History

2021-10-13 03:07:55 +02:00
#define _GNU_SOURCE
2021-03-20 10:52:45 +01:00
#include <X11/Xlib.h>
#include <fcntl.h>
2021-03-20 10:52:45 +01:00
#include <signal.h>
#include <stdbool.h>
2021-03-20 10:52:45 +01:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
2021-03-20 10:52:45 +01:00
#include <time.h>
#include <unistd.h>
#define POLL_INTERVAL 50
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
#define BLOCK(cmd, interval, signal) {"echo \"_$(" cmd ")\"", interval, signal},
2021-10-12 06:36:31 +02:00
typedef struct {
const char* command;
2021-10-12 06:36:31 +02:00
const unsigned int interval;
const unsigned int signal;
} Block;
2021-03-20 10:52:45 +01:00
#include "config.h"
static Display* dpy;
2021-03-20 10:52:45 +01:00
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 struct epoll_event event, events[LEN(blocks) + 2];
static int pipes[LEN(blocks)][2];
static int timerFD[2];
static int signalFD;
static int epollFD;
2021-04-17 12:17:40 +02:00
void (*writeStatus)();
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
}
void execBlock(int i, const char* button) {
2021-04-17 12:17:40 +02:00
if (fork() == 0) {
dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][0]);
2021-10-12 06:36:31 +02:00
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 execBlocks(unsigned long long 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))
execBlock(i, NULL);
2021-04-13 07:35:21 +02:00
}
int getStatus(char* new, char* old) {
2021-03-20 10:52:45 +01:00
strcpy(old, new);
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++) {
#ifdef TRAILING_DELIMITER
2021-10-15 11:56:08 +02:00
if (strlen(outputs[i]))
#else
2021-10-15 11:56:08 +02:00
if (strlen(new) && strlen(outputs[i]))
#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
}
return strcmp(new, old);
}
void updateBlock(int i) {
2021-10-15 11:56:08 +02:00
char* output = outputs[i];
char buffer[LEN(outputs[0])];
int bytesRead = read(pipes[i][0], buffer, LEN(buffer));
buffer[bytesRead - 1] = '\0';
if (bytesRead == LEN(buffer)) {
2021-10-15 11:56:08 +02:00
// Clear the pipe
char ch;
while (read(pipes[i][0], &ch, 1) == 1 && ch != '\n')
;
2021-10-15 11:56:08 +02:00
} else if (bytesRead == 2) {
output[0] = '\0';
return;
}
if (blocks[i].signal > 0) {
output[0] = blocks[i].signal;
output++;
}
strcpy(output, buffer + 1);
}
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);
}
void signalHandler() {
struct signalfd_siginfo info;
read(signalFD, &info, sizeof(info));
2021-03-20 10:52:45 +01:00
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;
}
2021-10-12 06:36:31 +02:00
}
// Clear the pipe after each poll to limit number of signals handled
while (read(signalFD, &info, sizeof(info)) != -1);
}
void termHandler() {
statusContinue = 0;
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
// Avoid zombie subprocesses
2021-04-17 12:17:40 +02:00
struct sigaction sa;
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++)
2021-08-31 13:48:19 +02:00
if (blocks[i].signal > 0)
sigaddset(&sigset, SIGRTMIN + blocks[i].signal);
signalFD = signalfd(-1, &sigset, 0);
fcntl(signalFD, F_SETFL, O_NONBLOCK);
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);
2021-04-13 07:35:21 +02:00
}
pipe(timerFD);
event.data.u32 = LEN(blocks);
epoll_ctl(epollFD, EPOLL_CTL_ADD, timerFD[0], &event);
setupSignals();
2021-04-13 07:35:21 +02:00
}
void statusLoop() {
execBlocks(0);
while (statusContinue) {
int eventCount = epoll_wait(epollFD, events, LEN(events), 10);
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();
2021-10-15 07:21:47 +02:00
// Poll every `POLL_INTERVAL` milliseconds
struct timespec toSleep = {.tv_nsec = POLL_INTERVAL * 1000000UL};
2021-10-15 07:21:47 +02:00
nanosleep(&toSleep, &toSleep);
}
}
void timerLoop() {
close(timerFD[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 long long int i = 0;
2021-04-13 07:35:21 +02:00
struct timespec sleepTime = {sleepInterval, 0};
struct timespec toSleep = sleepTime;
while (1) {
2021-04-13 07:35:21 +02:00
// 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
// Notify parent to update blocks
write(timerFD[1], &i, sizeof(i));
2021-04-13 07:35:21 +02:00
// After sleep, reset timer and update counter
toSleep = sleepTime;
i += sleepInterval;
}
}
int main(const int argc, const char* argv[]) {
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
init();
if (fork() == 0)
timerLoop();
else
statusLoop();
close(epollFD);
return 0;
2021-03-20 10:52:45 +01:00
}