From c720d07df8c031586df1cbe599104e40439a291d Mon Sep 17 00:00:00 2001 From: Utkarsh Verma Date: Sat, 26 Feb 2022 08:55:50 +0530 Subject: [PATCH 1/6] Handle the bytesRead = CMDLENGTH case properly --- main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main.c b/main.c index ece2ce0..a3375ad 100644 --- a/main.c +++ b/main.c @@ -114,6 +114,9 @@ void updateBlock(int i) { int j = bytesRead - 1; while ((buffer[j] & 0b11000000) == 0x80) j--; + + // Cache last character and replace it with a trailing space + char ch = buffer[j]; buffer[j] = ' '; // Trim trailing spaces @@ -121,10 +124,9 @@ void updateBlock(int i) { j--; buffer[j + 1] = '\0'; + // Clear the pipe if (bytesRead == LEN(buffer)) { - // Clear the pipe - char ch; - while (read(pipes[i][0], &ch, 1) == 1 && ch != '\n') + while (ch != '\n' && read(pipes[i][0], &ch, 1) == 1) ; } From f04d21fa22dd730e8db06dc93c23cd5f8334bf5a Mon Sep 17 00:00:00 2001 From: Utkarsh Verma Date: Tue, 19 Apr 2022 16:48:17 +0530 Subject: [PATCH 2/6] Handle UTF-8 characters properly for CMDLENGTH --- config.h | 2 +- main.c | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/config.h b/config.h index 40d5a96..3e07365 100644 --- a/config.h +++ b/config.h @@ -1,4 +1,4 @@ -#define CMDLENGTH 60 +#define CMDLENGTH 45 #define DELIMITER " " #define CLICKABLE_BLOCKS diff --git a/main.c b/main.c index a3375ad..b594f36 100644 --- a/main.c +++ b/main.c @@ -39,14 +39,17 @@ static Display* dpy; static int screen; static Window root; static unsigned short statusContinue = 1; -static char outputs[LEN(blocks)][CMDLENGTH + 1 + CLICKABLE_BLOCKS]; -static char statusBar[2][LEN(blocks) * (LEN(outputs[0]) - 1) + (LEN(blocks) - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1) + 1]; static struct epoll_event event, events[LEN(blocks) + 2]; static int pipes[LEN(blocks)][2]; static int timerPipe[2]; static int signalFD; static int epollFD; static int execLock = 0; + +// Longest UTF-8 character is 4 bytes long +static char outputs[LEN(blocks)][CMDLENGTH * 4 + 1 + CLICKABLE_BLOCKS]; +static char statusBar[2][LEN(blocks) * (LEN(outputs[0]) - 1) + (LEN(blocks) - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1) + 1]; + void (*writeStatus)(); int gcd(int a, int b) { @@ -110,19 +113,21 @@ void updateBlock(int i) { char buffer[LEN(outputs[0]) - CLICKABLE_BLOCKS]; int bytesRead = read(pipes[i][0], buffer, LEN(buffer)); - // Trim UTF-8 characters properly - int j = bytesRead - 1; - while ((buffer[j] & 0b11000000) == 0x80) - j--; + // Trim UTF-8 string to desired length + int count = 0, j = 0; + while (buffer[j] != '\n' && count <= CMDLENGTH) { + // Increment count for non-continuation bytes + if ((buffer[j++] & 0xc0) != 0x80) + count++; + } // Cache last character and replace it with a trailing space char ch = buffer[j]; buffer[j] = ' '; // Trim trailing spaces - while (buffer[j] == ' ') - j--; - buffer[j + 1] = '\0'; + while (j >= 0 && buffer[j] == ' ') j--; + buffer[j + 1] = 0; // Clear the pipe if (bytesRead == LEN(buffer)) { From 23e1d5732a8c9eb0d811a49cea151c066627edcb Mon Sep 17 00:00:00 2001 From: Utkarsh Verma Date: Thu, 21 Apr 2022 18:37:00 +0530 Subject: [PATCH 3/6] Use SIGALRM for timer and get rid of the child process --- README.md | 16 +------ main.c | 123 ++++++++++++++++++++++-------------------------------- 2 files changed, 50 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 758a061..3bae144 100644 --- a/README.md +++ b/README.md @@ -125,22 +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/). -Because `dwmblocks-async` creates a child process, it messes up the way the original `statuscmd` patch gets the PID of statusbar. It is necessary to modify the following lines in the definition of `getstatusbarpid()`. +> 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. -```diff - return statuspid; - } - } -- if (!(fp = popen("pidof -s "STATUSBAR, "r"))) -+ if (!(fp = popen("pgrep -o "STATUSBAR, "r"))) - return -1; - fgets(buf, sizeof(buf), fp); - pclose(fp); -``` - -This modification is backwards-compatible with other versions of `dwmblocks` as well. - -Please note that if you are using [`dwm-flexipatch`](https://github.com/bakkeby/dwm-flexipatch), then you only need to enable the `statuscmd` patch through `patches.h`. There is no need to edit change the definition of `getstatusbarpid()` as the changes have already been applied there through [dwm-flexipatch#190](https://github.com/bakkeby/dwm-flexipatch/pull/190). ## 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/). diff --git a/main.c b/main.c index b594f36..1be0f8d 100644 --- a/main.c +++ b/main.c @@ -39,9 +39,9 @@ static Display* dpy; static int screen; static Window root; static unsigned short statusContinue = 1; -static struct epoll_event event, events[LEN(blocks) + 2]; +static struct epoll_event event; static int pipes[LEN(blocks)][2]; -static int timerPipe[2]; +static int timer = 0, timerTick = 0, maxInterval = 0; static int signalFD; static int epollFD; static int execLock = 0; @@ -176,8 +176,17 @@ void signalHandler() { read(signalFD, &info, sizeof(info)); unsigned int signal = info.ssi_signo; - // Update all blocks on receiving SIGUSR1 - if (signal == SIGUSR1) { + switch (signal) { + case SIGALRM: + // Schedule the next timer event and execute blocks + alarm(timerTick); + execBlocks(timer); + + // Wrap `timer` to the interval [1, `maxInterval`] + timer = (timer + timerTick - 1) % maxInterval + 1; + return; + case SIGUSR1: + // Update all blocks on receiving SIGUSR1 execBlocks(0); return; } @@ -196,13 +205,25 @@ void termHandler() { } void setupSignals() { - // Ignore SIGUSR1 and all realtime signals - sigset_t ignoredSignals; - sigemptyset(&ignoredSignals); - sigaddset(&ignoredSignals, SIGUSR1); + sigset_t handledSignals; + sigemptyset(&handledSignals); + sigaddset(&handledSignals, SIGUSR1); + sigaddset(&handledSignals, SIGALRM); + + // Append all block signals to `handledSignals` + for (int i = 0; i < LEN(blocks); i++) + if (blocks[i].signal > 0) + sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal); + + // Create a signal file descriptor for epoll to watch + signalFD = signalfd(-1, &handledSignals, 0); + event.data.u32 = LEN(blocks); + epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event); + + // Block all realtime and handled signals for (int i = SIGRTMIN; i <= SIGRTMAX; i++) - sigaddset(&ignoredSignals, i); - sigprocmask(SIG_BLOCK, &ignoredSignals, NULL); + sigaddset(&handledSignals, i); + sigprocmask(SIG_BLOCK, &handledSignals, NULL); // Handle termination signals signal(SIGINT, termHandler); @@ -214,85 +235,44 @@ void setupSignals() { sigemptyset(&sa.sa_mask); sa.sa_flags = SA_NOCLDWAIT; sigaction(SIGCHLD, &sa, 0); - - // Handle block update signals - sigset_t handledSignals; - sigemptyset(&handledSignals); - sigaddset(&handledSignals, SIGUSR1); - for (int i = 0; i < LEN(blocks); i++) - if (blocks[i].signal > 0) - sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal); - signalFD = signalfd(-1, &handledSignals, 0); - event.data.u32 = LEN(blocks) + 1; - epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event); } void statusLoop() { + // Update all blocks initially + kill(0, SIGALRM); + + struct epoll_event events[LEN(blocks) + 1]; while (statusContinue) { int eventCount = epoll_wait(epollFD, events, LEN(events), -1); for (int i = 0; i < eventCount; i++) { unsigned short id = events[i].data.u32; - - if (id == LEN(blocks)) { - unsigned int j = 0; - read(timerPipe[0], &j, sizeof(j)); - execBlocks(j); - } else if (id < LEN(blocks)) { - updateBlock(events[i].data.u32); - } else { + if (id < LEN(blocks)) + updateBlock(id); + else signalHandler(); - } } + if (eventCount != -1) writeStatus(); } } -void timerLoop() { - close(timerPipe[0]); - - unsigned int sleepInterval = 0; - unsigned int maxInterval = 0; - for (int i = 0; i < LEN(blocks); i++) - if (blocks[i].interval) { - maxInterval = MAX(blocks[i].interval, maxInterval); - sleepInterval = gcd(blocks[i].interval, sleepInterval); - } - - unsigned int i = 0; - struct timespec sleepTime = {sleepInterval, 0}; - struct timespec toSleep = sleepTime; - - while (statusContinue) { - // Notify parent to update blocks - write(timerPipe[1], &i, sizeof(i)); - - // Wrap `i` to the interval [1, `maxInterval`] - i = (i + sleepInterval - 1) % maxInterval + 1; - - // Sleep for `sleepTime` even on being interrupted - while (nanosleep(&toSleep, &toSleep) == -1) - ; - toSleep = sleepTime; - } - - close(timerPipe[1]); - _exit(0); -} - void init() { - epollFD = epoll_create(LEN(blocks) + 1); + epollFD = epoll_create(LEN(blocks)); event.events = EPOLLIN; for (int i = 0; i < LEN(blocks); i++) { + // Append each block's pipe to `epollFD` pipe(pipes[i]); event.data.u32 = i; epoll_ctl(epollFD, EPOLL_CTL_ADD, pipes[i][0], &event); - } - pipe(timerPipe); - event.data.u32 = LEN(blocks); - epoll_ctl(epollFD, EPOLL_CTL_ADD, timerPipe[0], &event); + // Calculate the max interval and tick size for the timer + if (blocks[i].interval) { + maxInterval = MAX(blocks[i].interval, maxInterval); + timerTick = gcd(blocks[i].interval, timerTick); + } + } setupSignals(); } @@ -304,17 +284,12 @@ int main(const int argc, const char* argv[]) { writeStatus = debug; init(); - - // Ensure that `timerLoop()` only runs in the fork - if (fork() == 0) - timerLoop(); - else - statusLoop(); + statusLoop(); close(epollFD); close(signalFD); - closePipe(timerPipe); for (int i = 0; i < LEN(pipes); i++) closePipe(pipes[i]); + return 0; } From 09e72e590d5ebae06459a45ed1a14449126ee0ea Mon Sep 17 00:00:00 2001 From: Utkarsh Verma Date: Thu, 21 Apr 2022 19:04:41 +0530 Subject: [PATCH 4/6] maxInterval should bet at least 1 --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 1be0f8d..97ea568 100644 --- a/main.c +++ b/main.c @@ -41,7 +41,7 @@ static Window root; static unsigned short statusContinue = 1; static struct epoll_event event; static int pipes[LEN(blocks)][2]; -static int timer = 0, timerTick = 0, maxInterval = 0; +static int timer = 0, timerTick = 0, maxInterval = 1; static int signalFD; static int epollFD; static int execLock = 0; From 6cdd6f7ff254cbeff1d94f404772384909648b0f Mon Sep 17 00:00:00 2001 From: Utkarsh Verma Date: Fri, 22 Apr 2022 06:19:39 +0530 Subject: [PATCH 5/6] Fix crashing of dwmblocks --- main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.c b/main.c index 97ea568..f70522b 100644 --- a/main.c +++ b/main.c @@ -239,7 +239,7 @@ void setupSignals() { void statusLoop() { // Update all blocks initially - kill(0, SIGALRM); + raise(SIGALRM); struct epoll_event events[LEN(blocks) + 1]; while (statusContinue) { From 65bfd0eef541502e01e6b9501d841777ac2f6d19 Mon Sep 17 00:00:00 2001 From: Utkarsh Verma Date: Sun, 1 May 2022 15:27:30 +0530 Subject: [PATCH 6/6] Improve UTF8 trimming algorithm --- main.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index f70522b..f101146 100644 --- a/main.c +++ b/main.c @@ -115,10 +115,15 @@ void updateBlock(int i) { // Trim UTF-8 string to desired length int count = 0, j = 0; - while (buffer[j] != '\n' && count <= CMDLENGTH) { - // Increment count for non-continuation bytes - if ((buffer[j++] & 0xc0) != 0x80) - count++; + while (buffer[j] != '\n' && count < CMDLENGTH) { + count++; + + // Skip continuation bytes, if any. + char ch = buffer[j]; + int skip = 1; + while ((ch & 0xc0) > 0x80) + ch <<= 1, skip++; + j += skip; } // Cache last character and replace it with a trailing space @@ -126,7 +131,8 @@ void updateBlock(int i) { buffer[j] = ' '; // Trim trailing spaces - while (j >= 0 && buffer[j] == ' ') j--; + while (j >= 0 && buffer[j] == ' ') + j--; buffer[j + 1] = 0; // Clear the pipe