Compare commits

..

No commits in common. "7dd9a3bddcdfbf92dd43696bfb4356d591b1a482" and "d58dfbb32237d688955ab7cddfe83ed391e48dc2" have entirely different histories.

4 changed files with 197 additions and 202 deletions

View File

@ -1,2 +0,0 @@
BasedOnStyle: Google
IndentWidth: 4

View File

@ -1,21 +1,19 @@
.POSIX: .POSIX:
PREFIX = /usr/local PREFIX = /usr/local
CFLAGS = -Ofast CC = gcc
LDLIBS = -lX11
BIN = dwmblocks
$(BIN): main.o
$(CC) $^ -o $@ $(LDLIBS)
dwmblocks: main.o
$(CC) main.o -lX11 -Ofast -o dwmblocks
main.o: main.c config.h
$(CC) -Ofast -c main.c
clean: clean:
$(RM) *.o $(BIN) rm -f *.o *.gch dwmblocks
install: dwmblocks
install: $(BIN) mkdir -p $(DESTDIR)$(PREFIX)/bin
install -D -m 755 $(BIN) $(DESTDIR)$(PREFIX)/bin/$(BIN) cp -f dwmblocks $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dwmblocks
uninstall: uninstall:
$(RM) $(DESTDIR)$(PREFIX)/bin/$(BIN) rm -f $(DESTDIR)$(PREFIX)/bin/dwmblocks
.PHONY: clean install uninstall .PHONY: clean install uninstall

View File

@ -125,6 +125,8 @@ To use this feature, define the `CLICKABLE_BLOCKS` feature macro in your `config
Apart from that, you need `dwm` to be patched with [statuscmd](https://dwm.suckless.org/patches/statuscmd/). Apart from that, you need `dwm` to be patched with [statuscmd](https://dwm.suckless.org/patches/statuscmd/).
> Earlier, `dwmblocks-async` used to require a patch to be applied to `dwm`. However, the code has been redone so there's no need to apply that patch anymore.
## Credits ## Credits
This work would not have been possible without [Luke's build of dwmblocks](https://github.com/LukeSmithxyz/dwmblocks) and [Daniel Bylinka's statuscmd patch](https://dwm.suckless.org/patches/statuscmd/). This work would not have been possible without [Luke's build of dwmblocks](https://github.com/LukeSmithxyz/dwmblocks) and [Daniel Bylinka's statuscmd patch](https://dwm.suckless.org/patches/statuscmd/).

371
main.c
View File

@ -12,12 +12,12 @@
#define LEN(arr) (sizeof(arr) / sizeof(arr[0])) #define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
#define MAX(a, b) (a > b ? a : b) #define MAX(a, b) (a > b ? a : b)
#define BLOCK(cmd, interval, signal) \ #define BLOCK(cmd, interval, signal) \
{ "echo \"$(" cmd ")\"", interval, signal } { "echo \"$(" cmd ")\"", interval, signal }
typedef const struct { typedef const 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"
@ -35,7 +35,7 @@ typedef const struct {
#define LEADING_DELIMITER 0 #define LEADING_DELIMITER 0
#endif #endif
static Display *dpy; static Display* dpy;
static int screen; static int screen;
static Window root; static Window root;
static unsigned short statusContinue = 1; static unsigned short statusContinue = 1;
@ -48,257 +48,254 @@ static int execLock = 0;
// Longest UTF-8 character is 4 bytes long // Longest UTF-8 character is 4 bytes long
static char outputs[LEN(blocks)][CMDLENGTH * 4 + 1 + CLICKABLE_BLOCKS]; static char outputs[LEN(blocks)][CMDLENGTH * 4 + 1 + CLICKABLE_BLOCKS];
static char static char statusBar[2][LEN(blocks) * (LEN(outputs[0]) - 1) + (LEN(blocks) - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1) + 1];
statusBar[2]
[LEN(blocks) * (LEN(outputs[0]) - 1) +
(LEN(blocks) - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1) + 1];
void (*writeStatus)(); void (*writeStatus)();
int gcd(int a, int b) { int gcd(int a, int b) {
int temp; int temp;
while (b > 0) { while (b > 0) {
temp = a % b; temp = a % b;
a = b; a = b;
b = temp; b = temp;
} }
return a; return a;
} }
void closePipe(int *pipe) { void closePipe(int* pipe) {
close(pipe[0]); close(pipe[0]);
close(pipe[1]); close(pipe[1]);
} }
void execBlock(int i, const char *button) { void execBlock(int i, const char* button) {
// Ensure only one child process exists per block at an instance // Ensure only one child process exists per block at an instance
if (execLock & 1 << i) return; if (execLock & 1 << i)
// Lock execution of block until current instance finishes execution return;
execLock |= 1 << i; // Lock execution of block until current instance finishes execution
execLock |= 1 << i;
if (fork() == 0) { if (fork() == 0) {
close(pipes[i][0]); close(pipes[i][0]);
dup2(pipes[i][1], STDOUT_FILENO); dup2(pipes[i][1], STDOUT_FILENO);
close(pipes[i][1]); close(pipes[i][1]);
if (button) setenv("BLOCK_BUTTON", button, 1); if (button)
execl("/bin/sh", "sh", "-c", blocks[i].command, (char *)NULL); setenv("BLOCK_BUTTON", button, 1);
exit(EXIT_FAILURE); execl("/bin/sh", "sh", "-c", blocks[i].command, (char*)NULL);
} _exit(1);
}
} }
void execBlocks(unsigned int time) { void execBlocks(unsigned int time) {
for (int i = 0; i < LEN(blocks); i++) for (int i = 0; i < LEN(blocks); i++)
if (time == 0 || if (time == 0 || (blocks[i].interval != 0 && time % blocks[i].interval == 0))
(blocks[i].interval != 0 && time % blocks[i].interval == 0)) execBlock(i, NULL);
execBlock(i, NULL);
} }
int getStatus(char *new, char *old) { 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++) {
#if LEADING_DELIMITER #if LEADING_DELIMITER
if (strlen(outputs[i])) if (strlen(outputs[i]))
#else #else
if (strlen(new) && strlen(outputs[i])) if (strlen(new) && strlen(outputs[i]))
#endif #endif
strcat(new, DELIMITER); strcat(new, DELIMITER);
strcat(new, outputs[i]); strcat(new, outputs[i]);
} }
return strcmp(new, old); return strcmp(new, old);
} }
void updateBlock(int i) { void updateBlock(int i) {
char *output = outputs[i]; char* output = outputs[i];
char buffer[LEN(outputs[0]) - CLICKABLE_BLOCKS]; char buffer[LEN(outputs[0]) - CLICKABLE_BLOCKS];
int bytesRead = read(pipes[i][0], buffer, LEN(buffer)); int bytesRead = read(pipes[i][0], buffer, LEN(buffer));
// Trim UTF-8 string to desired length // Trim UTF-8 string to desired length
int count = 0, j = 0; int count = 0, j = 0;
while (buffer[j] != '\n' && count < CMDLENGTH) { while (buffer[j] != '\n' && count < CMDLENGTH) {
count++; count++;
// Skip continuation bytes, if any // Skip continuation bytes, if any.
char ch = buffer[j]; char ch = buffer[j];
int skip = 1; int skip = 1;
while ((ch & 0xc0) > 0x80) ch <<= 1, skip++; while ((ch & 0xc0) > 0x80)
j += skip; ch <<= 1, skip++;
} j += skip;
}
// Cache last character and replace it with a trailing space // Cache last character and replace it with a trailing space
char ch = buffer[j]; char ch = buffer[j];
buffer[j] = ' '; buffer[j] = ' ';
// Trim trailing spaces // Trim trailing spaces
while (j >= 0 && buffer[j] == ' ') j--; while (j >= 0 && buffer[j] == ' ')
buffer[j + 1] = 0; j--;
buffer[j + 1] = 0;
// Clear the pipe // Clear the pipe
if (bytesRead == LEN(buffer)) { if (bytesRead == LEN(buffer)) {
while (ch != '\n' && read(pipes[i][0], &ch, 1) == 1) while (ch != '\n' && read(pipes[i][0], &ch, 1) == 1)
; ;
} }
#if CLICKABLE_BLOCKS #if CLICKABLE_BLOCKS
if (bytesRead > 1 && blocks[i].signal > 0) { if (bytesRead > 1 && blocks[i].signal > 0) {
output[0] = blocks[i].signal; output[0] = blocks[i].signal;
output++; output++;
} }
#endif #endif
strcpy(output, buffer); strcpy(output, buffer);
// Remove execution lock for the current block // Remove execution lock for the current block
execLock &= ~(1 << i); execLock &= ~(1 << i);
} }
void debug() { void debug() {
// Only write out if text has changed // Only write out if text has changed
if (!getStatus(statusBar[0], statusBar[1])) return; if (!getStatus(statusBar[0], statusBar[1]))
return;
write(STDOUT_FILENO, statusBar[0], strlen(statusBar[0])); write(STDOUT_FILENO, statusBar[0], strlen(statusBar[0]));
write(STDOUT_FILENO, "\n", 1); write(STDOUT_FILENO, "\n", 1);
}
int setupX() {
dpy = XOpenDisplay(NULL);
if (!dpy) return 1;
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
return 0;
} }
void setRoot() { void setRoot() {
// Only set root if text has changed // Only set root if text has changed
if (!getStatus(statusBar[0], statusBar[1])) return; if (!getStatus(statusBar[0], statusBar[1]))
return;
XStoreName(dpy, root, statusBar[0]); Display* d = XOpenDisplay(NULL);
XFlush(dpy); if (d)
dpy = d;
screen = DefaultScreen(dpy);
root = RootWindow(dpy, screen);
XStoreName(dpy, root, statusBar[0]);
XCloseDisplay(dpy);
} }
void signalHandler() { void signalHandler() {
struct signalfd_siginfo info; struct signalfd_siginfo info;
read(signalFD, &info, sizeof(info)); read(signalFD, &info, sizeof(info));
unsigned int signal = info.ssi_signo; unsigned int signal = info.ssi_signo;
switch (signal) { switch (signal) {
case SIGALRM: case SIGALRM:
// Schedule the next timer event and execute blocks // Schedule the next timer event and execute blocks
alarm(timerTick); alarm(timerTick);
execBlocks(timer); execBlocks(timer);
// Wrap `timer` to the interval [1, `maxInterval`] // Wrap `timer` to the interval [1, `maxInterval`]
timer = (timer + timerTick - 1) % maxInterval + 1; timer = (timer + timerTick - 1) % maxInterval + 1;
return; return;
case SIGUSR1: case SIGUSR1:
// Update all blocks on receiving SIGUSR1 // Update all blocks on receiving SIGUSR1
execBlocks(0); execBlocks(0);
return; return;
} }
for (int j = 0; j < LEN(blocks); j++) { for (int j = 0; j < LEN(blocks); j++) {
if (blocks[j].signal == signal - SIGRTMIN) { if (blocks[j].signal == signal - SIGRTMIN) {
char button[4]; // value can't be more than 255; char button[] = {'0' + info.ssi_int & 0xff, 0};
sprintf(button, "%d", info.ssi_int & 0xff); execBlock(j, button);
execBlock(j, button); break;
break; }
} }
}
} }
void termHandler() { statusContinue = 0; } void termHandler() {
statusContinue = 0;
}
void setupSignals() { void setupSignals() {
sigset_t handledSignals; sigset_t handledSignals;
sigemptyset(&handledSignals); sigemptyset(&handledSignals);
sigaddset(&handledSignals, SIGUSR1); sigaddset(&handledSignals, SIGUSR1);
sigaddset(&handledSignals, SIGALRM); sigaddset(&handledSignals, SIGALRM);
// Append all block signals to `handledSignals` // Append all block signals to `handledSignals`
for (int i = 0; i < LEN(blocks); i++) for (int i = 0; i < LEN(blocks); i++)
if (blocks[i].signal > 0) if (blocks[i].signal > 0)
sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal); sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal);
// Create a signal file descriptor for epoll to watch // Create a signal file descriptor for epoll to watch
signalFD = signalfd(-1, &handledSignals, 0); signalFD = signalfd(-1, &handledSignals, 0);
event.data.u32 = LEN(blocks); event.data.u32 = LEN(blocks);
epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event); epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event);
// Block all realtime and handled signals // Block all realtime and handled signals
for (int i = SIGRTMIN; i <= SIGRTMAX; i++) sigaddset(&handledSignals, i); for (int i = SIGRTMIN; i <= SIGRTMAX; i++)
sigprocmask(SIG_BLOCK, &handledSignals, NULL); sigaddset(&handledSignals, i);
sigprocmask(SIG_BLOCK, &handledSignals, NULL);
// Handle termination signals // Handle termination signals
signal(SIGINT, termHandler); signal(SIGINT, termHandler);
signal(SIGTERM, termHandler); signal(SIGTERM, termHandler);
// Avoid zombie subprocesses // Avoid zombie subprocesses
struct sigaction sa; struct sigaction sa;
sa.sa_handler = SIG_DFL; sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask); sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NOCLDWAIT; sa.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &sa, 0); sigaction(SIGCHLD, &sa, 0);
} }
void statusLoop() { void statusLoop() {
// Update all blocks initially // Update all blocks initially
raise(SIGALRM); raise(SIGALRM);
struct epoll_event events[LEN(blocks) + 1]; struct epoll_event events[LEN(blocks) + 1];
while (statusContinue) { while (statusContinue) {
int eventCount = epoll_wait(epollFD, events, LEN(events), -1); int eventCount = epoll_wait(epollFD, events, LEN(events), -1);
for (int i = 0; i < eventCount; i++) { for (int i = 0; i < eventCount; i++) {
unsigned short id = events[i].data.u32; unsigned short id = events[i].data.u32;
if (id < LEN(blocks)) if (id < LEN(blocks))
updateBlock(id); updateBlock(id);
else else
signalHandler(); signalHandler();
} }
if (eventCount != -1) writeStatus(); if (eventCount != -1)
} writeStatus();
}
} }
void init() { void init() {
epollFD = epoll_create(LEN(blocks)); epollFD = epoll_create(LEN(blocks));
event.events = EPOLLIN; event.events = EPOLLIN;
for (int i = 0; i < LEN(blocks); i++) { for (int i = 0; i < LEN(blocks); i++) {
// Append each block's pipe to `epollFD` // Append each block's pipe to `epollFD`
pipe(pipes[i]); pipe(pipes[i]);
event.data.u32 = i; event.data.u32 = i;
epoll_ctl(epollFD, EPOLL_CTL_ADD, pipes[i][0], &event); epoll_ctl(epollFD, EPOLL_CTL_ADD, pipes[i][0], &event);
// Calculate the max interval and tick size for the timer // Calculate the max interval and tick size for the timer
if (blocks[i].interval) { if (blocks[i].interval) {
maxInterval = MAX(blocks[i].interval, maxInterval); maxInterval = MAX(blocks[i].interval, maxInterval);
timerTick = gcd(blocks[i].interval, timerTick); timerTick = gcd(blocks[i].interval, timerTick);
} }
} }
setupSignals(); setupSignals();
} }
int main(const int argc, const char *argv[]) { int main(const int argc, const char* argv[]) {
if (setupX()) { writeStatus = setRoot;
fprintf(stderr, "dwmblocks: Failed to open display\n"); for (int i = 0; i < argc; i++)
return 1; if (!strcmp("-d", argv[i]))
} writeStatus = debug;
writeStatus = setRoot; init();
for (int i = 0; i < argc; i++) statusLoop();
if (!strcmp("-d", argv[i])) writeStatus = debug;
init(); close(epollFD);
statusLoop(); close(signalFD);
for (int i = 0; i < LEN(pipes); i++)
closePipe(pipes[i]);
XCloseDisplay(dpy); return 0;
close(epollFD);
close(signalFD);
for (int i = 0; i < LEN(pipes); i++) closePipe(pipes[i]);
return 0;
} }