dwmblocks/dwmblocks.c

374 lines
12 KiB
C
Raw Normal View History

2020-07-06 21:23:31 +00:00
#include <errno.h>
#include <fcntl.h>
2020-07-09 20:10:34 +00:00
#include <limits.h>
2020-07-06 21:23:31 +00:00
#include <signal.h>
2020-07-03 21:04:11 +00:00
#include <stdio.h>
2020-07-06 21:23:31 +00:00
#include <stdlib.h>
2020-07-03 21:04:11 +00:00
#include <string.h>
2020-12-04 06:49:12 +00:00
#include <time.h>
2020-07-03 21:04:11 +00:00
#include <unistd.h>
#include <X11/Xlib.h>
2020-11-12 08:20:26 +00:00
#define CMDLENGTH 50
2020-08-22 17:43:56 +00:00
#define STTLENGTH 256
#define NILL INT_MIN
#define LOCKFILE "/tmp/dwmblocks.pid"
2020-07-03 21:04:11 +00:00
2020-12-04 21:37:20 +00:00
#define LENGTH(X) (sizeof X / sizeof X[0])
2020-07-03 21:04:11 +00:00
typedef struct {
2020-07-24 21:33:29 +00:00
char *pathu;
char *pathc;
const int interval;
const int signal;
2020-07-03 21:04:11 +00:00
char cmdoutcur[CMDLENGTH];
char cmdoutprv[CMDLENGTH];
} Block;
2020-07-07 10:17:13 +00:00
#include "blocks.h"
2020-07-03 21:04:11 +00:00
static void buttonhandler(int signal, siginfo_t *si, void *ucontext);
static void cleanup();
2020-07-09 20:10:34 +00:00
static void getcmd(Block *block, int sigval);
static void setroot();
2020-07-03 21:04:11 +00:00
static void setupsignals();
2020-07-06 20:28:41 +00:00
static void sighandler(int signal, siginfo_t *si, void *ucontext);
2020-07-03 21:04:11 +00:00
static void statusloop();
static void termhandler(int signum);
static int updatestatus();
static void writepid();
2020-11-24 20:07:36 +00:00
static char statustext[STTLENGTH];
2020-12-04 21:37:20 +00:00
static char *delim;
2020-07-03 21:04:11 +00:00
static size_t delimlength;
static Display *dpy;
static sigset_t blocksigmask;
2020-07-03 21:04:11 +00:00
void
buttonhandler(int signal, siginfo_t *si, void *ucontext)
{
2020-07-24 21:33:29 +00:00
signal = si->si_value.sival_int >> 8;
2020-11-15 07:54:15 +00:00
for (Block *block = blocks; block->pathu; block++)
if (block->signal == signal)
2020-10-03 10:32:07 +00:00
switch (fork()) {
case -1:
perror("buttonhandler - fork");
break;
case 0:
{
2020-07-03 21:04:11 +00:00
char button[] = { '0' + (si->si_value.sival_int & 0xff), '\0' };
2020-11-15 07:54:15 +00:00
char *arg[] = { block->pathc, button, NULL };
2020-07-03 21:04:11 +00:00
2020-10-03 10:32:07 +00:00
close(ConnectionNumber(dpy));
2020-07-03 21:04:11 +00:00
setsid();
execv(arg[0], arg);
2020-07-08 17:25:19 +00:00
perror("buttonhandler - child - execv");
2020-07-03 21:04:11 +00:00
_exit(127);
}
}
}
void
cleanup()
{
unlink(LOCKFILE);
XStoreName(dpy, DefaultRootWindow(dpy), "");
XCloseDisplay(dpy);
}
2020-07-03 21:04:11 +00:00
void
2020-07-09 20:10:34 +00:00
getcmd(Block *block, int sigval)
2020-07-03 21:04:11 +00:00
{
int fd[2];
if (pipe(fd) == -1) {
perror("getcmd - pipe");
exit(1);
}
switch (fork()) {
case -1:
perror("getcmd - fork");
exit(1);
case 0:
close(ConnectionNumber(dpy));
2020-07-03 21:04:11 +00:00
close(fd[0]);
if (fd[1] != STDOUT_FILENO) {
if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
perror("getcmd - child - dup2");
exit(1);
}
close(fd[1]);
2020-07-03 21:04:11 +00:00
}
2020-07-09 20:10:34 +00:00
if (sigval == NILL) {
char *arg[] = { block->pathu, NULL };
2020-07-03 21:04:11 +00:00
execv(arg[0], arg);
} else {
2020-07-09 20:10:34 +00:00
char buf[12];
char *arg[] = { block->pathu, buf, NULL };
2020-07-03 21:04:11 +00:00
2020-07-09 20:10:34 +00:00
snprintf(buf, sizeof buf, "%d", sigval);
2020-07-03 21:04:11 +00:00
execv(arg[0], arg);
}
2020-07-08 17:25:19 +00:00
perror("getcmd - child - execv");
2020-07-03 21:04:11 +00:00
_exit(127);
default:
close(fd[1]);
if (read(fd[0], block->cmdoutcur, CMDLENGTH) == -1) {
perror("getcmd - read");
exit(1);
}
close(fd[0]);
}
}
void
setroot()
{
if (updatestatus()) {
2020-11-24 20:07:36 +00:00
XStoreName(dpy, DefaultRootWindow(dpy), statustext);
XSync(dpy, False);
}
}
2020-07-03 21:04:11 +00:00
void
setupsignals()
{
struct sigaction sa;
/* populate blocksigmask */
sigemptyset(&blocksigmask);
sigaddset(&blocksigmask, SIGHUP);
sigaddset(&blocksigmask, SIGINT);
sigaddset(&blocksigmask, SIGTERM);
for (Block *block = blocks; block->pathu; block++)
if (block->signal > 0)
sigaddset(&blocksigmask, SIGRTMIN + block->signal);
/* setup signal handlers */
/* to handle HUP, INT and TERM */
2020-07-03 21:04:11 +00:00
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
2020-07-03 21:04:11 +00:00
sa.sa_handler = termhandler;
2020-07-24 21:33:29 +00:00
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
2020-07-24 21:33:29 +00:00
sigaction(SIGTERM, &sa, NULL);
2020-07-03 21:04:11 +00:00
/* to ignore unused realtime signals */
// sa.sa_flags = SA_RESTART;
// sigemptyset(&sa.sa_mask);
2020-07-03 21:04:11 +00:00
sa.sa_handler = SIG_IGN;
for (int i = SIGRTMIN + 1; i <= SIGRTMAX; i++)
2020-07-03 21:04:11 +00:00
sigaction(i, &sa, NULL);
/* to prevent forked children from becoming zombies */
sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART;
// sigemptyset(&sa.sa_mask);
sa.sa_handler = SIG_DFL;
sigaction(SIGCHLD, &sa, NULL);
2020-07-03 21:04:11 +00:00
/* to handle signals generated by dwm on click events */
2020-07-24 21:33:29 +00:00
sa.sa_flags = SA_RESTART | SA_SIGINFO;
// sigemptyset(&sa.sa_mask);
2020-07-24 21:33:29 +00:00
sa.sa_sigaction = buttonhandler;
sigaction(SIGRTMIN, &sa, NULL);
2020-07-03 21:04:11 +00:00
/* to handle update signals for individual blocks */
sa.sa_flags |= SA_NODEFER;
sa.sa_mask = blocksigmask;
2020-07-03 21:04:11 +00:00
sa.sa_sigaction = sighandler;
2020-11-15 07:54:15 +00:00
for (Block *block = blocks; block->pathu; block++)
if (block->signal > 0)
sigaction(SIGRTMIN + block->signal, &sa, NULL);
2020-07-03 21:04:11 +00:00
}
void
2020-07-06 20:28:41 +00:00
sighandler(int signal, siginfo_t *si, void *ucontext)
2020-07-03 21:04:11 +00:00
{
2020-07-24 21:33:29 +00:00
signal -= SIGRTMIN;
2020-11-15 07:54:15 +00:00
for (Block *block = blocks; block->pathu; block++)
if (block->signal == signal)
getcmd(block, si->si_value.sival_int);
setroot();
2020-07-03 21:04:11 +00:00
}
void
statusloop()
{
int i;
2020-12-04 06:49:12 +00:00
struct timespec t;
2020-07-03 21:04:11 +00:00
/* first run */
sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
2020-11-15 07:54:15 +00:00
for (Block *block = blocks; block->pathu; block++)
if (block->interval >= 0)
getcmd(block, NILL);
setroot();
sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL);
/* main loop */
2020-12-04 06:49:12 +00:00
for (i = 1;; t.tv_sec = INTERVALs, t.tv_nsec = INTERVALn, i += 1) {
while (nanosleep(&t, &t) == -1)
if (errno != EINTR) {
perror("statusloop - nanosleep");
exit(1);
}
sigprocmask(SIG_BLOCK, &blocksigmask, NULL);
2020-11-15 07:54:15 +00:00
for (Block *block = blocks; block->pathu; block++)
if (block->interval > 0 && i % block->interval == 0)
getcmd(block, NILL);
setroot();
2020-07-29 17:07:58 +00:00
sigprocmask(SIG_UNBLOCK, &blocksigmask, NULL);
2020-07-24 21:33:29 +00:00
}
2020-07-03 21:04:11 +00:00
}
void
termhandler(int signum)
{
cleanup();
exit(0);
2020-07-03 21:04:11 +00:00
}
2020-11-24 20:07:36 +00:00
/* returns whether block outputs have changed and updates statustext if they have */
2020-07-03 21:04:11 +00:00
int
updatestatus()
{
2020-11-24 20:07:36 +00:00
char *s = statustext;
char *c, *p; /* for cmdoutcur and cmdoutprv */
const char *d; /* for delimiter */
2020-11-15 07:54:15 +00:00
Block *block = blocks;
2020-07-03 21:04:11 +00:00
/* checking half of the function */
/* find the first non-empty block */
2020-11-15 07:54:15 +00:00
for (;; block++) {
/* all blocks are empty */
2020-11-15 07:54:15 +00:00
if (!block->pathu)
return 0;
2020-11-15 07:54:15 +00:00
/* contents of the block changed */
if (*block->cmdoutcur != *block->cmdoutprv)
goto update0;
2020-10-23 13:27:13 +00:00
/* skip delimiter handler for the first non-empty block */
2020-11-15 07:54:15 +00:00
if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0')
goto skipdelimc;
}
/* main loop */
2020-11-15 07:54:15 +00:00
for (; block->pathu; block++) {
/* contents of the block changed */
if (*block->cmdoutcur != *block->cmdoutprv)
goto update1;
/* delimiter handler */
2020-11-15 07:54:15 +00:00
if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0')
s += delimlength;
/* skip over empty blocks */
else
continue;
skipdelimc:
/* checking for the first byte has been done */
2020-11-15 07:54:15 +00:00
c = block->cmdoutcur + 1, p = block->cmdoutprv + 1;
for (; *c != '\n' && *c != '\0'; c++, p++)
2020-11-15 07:54:15 +00:00
/* contents of the block changed */
if (*c != *p) {
2020-11-15 07:54:15 +00:00
s += c - block->cmdoutcur;
goto update2;
2020-07-03 21:04:11 +00:00
}
2020-11-15 07:54:15 +00:00
s += c - block->cmdoutcur;
/* byte containing info about signal number for the block */
2020-11-15 07:54:15 +00:00
if (block->pathc && block->signal)
s++;
2020-07-03 21:04:11 +00:00
}
2020-07-25 11:50:09 +00:00
return 0;
/* updating half of the function */
/* find the first non-empty block */
2020-11-15 07:54:15 +00:00
for (;; block++) {
/* all blocks are empty */
2020-11-15 07:54:15 +00:00
if (!block->pathu)
return 1;
update0:
2020-10-23 13:27:13 +00:00
/* don't add delimiter before the first non-empty block */
2020-11-15 07:54:15 +00:00
if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0')
goto skipdelimu;
2020-11-15 07:54:15 +00:00
*block->cmdoutprv = *block->cmdoutcur;
}
/* main loop */
2020-11-15 07:54:15 +00:00
for (; block->pathu; block++) {
update1:
/* delimiter handler */
2020-11-15 07:54:15 +00:00
if (*block->cmdoutcur != '\n' && *block->cmdoutcur != '\0') {
d = delim;
2020-08-12 07:20:04 +00:00
while (*d != '\0')
*(s++) = *(d++);
*(s++) = '\n'; /* to mark the end of delimiter */
/* skip over empty blocks */
} else {
2020-11-15 07:54:15 +00:00
*block->cmdoutprv = *block->cmdoutcur;
continue;
}
skipdelimu:
2020-11-15 07:54:15 +00:00
c = block->cmdoutcur, p = block->cmdoutprv;
update2:
2020-07-03 21:04:11 +00:00
do {
*(s++) = *c;
*p = *c;
c++, p++;
} while (*c != '\n' && *c != '\0');
2020-11-15 07:54:15 +00:00
if (block->pathc && block->signal)
*(s++) = block->signal;
2020-07-03 21:04:11 +00:00
}
*s = '\0';
2020-07-25 11:50:09 +00:00
return 1;
2020-07-03 21:04:11 +00:00
}
void
writepid()
{
int fd;
struct flock fl;
fd = open(LOCKFILE, O_RDWR|O_CREAT, 0644);
if (fd == -1) {
2020-10-14 19:13:31 +00:00
perror("writepid - open");
2020-07-03 21:04:11 +00:00
exit(1);
}
fl.l_type = F_WRLCK;
fl.l_start = 0;
fl.l_whence = SEEK_SET;
fl.l_len = 0;
if (fcntl(fd, F_SETLK, &fl) == -1) {
if (errno == EACCES || errno == EAGAIN) {
fputs("Error: another instance of dwmblocks is already running.\n", stderr);
exit(2);
}
2020-07-24 16:25:56 +00:00
perror("writepid - fcntl");
2020-07-03 21:04:11 +00:00
exit(1);
}
if (ftruncate(fd, 0) == -1) {
perror("writepid - ftruncate");
exit(1);
}
2020-07-24 20:31:56 +00:00
if (dprintf(fd, "%ld", (long)getpid()) < 0) {
perror("writepid - dprintf");
2020-07-03 21:04:11 +00:00
exit(1);
}
}
int
main(int argc, char *argv[])
{
writepid();
2020-12-04 21:37:20 +00:00
if (argc == 3 && strcmp(argv[1], "-d") == 0) {
delim = argv[2];
delimlength = strlen(delim) + 1;
} else {
delim = DELIMITER;
delimlength = LENGTH(DELIMITER);
}
2020-07-03 21:04:11 +00:00
if (!(dpy = XOpenDisplay(NULL))) {
fputs("Error: could not open display.\n", stderr);
return 1;
}
setupsignals();
2020-07-24 21:33:29 +00:00
statusloop();
cleanup();
2020-07-03 21:04:11 +00:00
return 0;
}