demwm/dwm-integrated-status-text-...

540 lines
16 KiB
Diff

From 02f1f07ee4460787c971bd28e934cb5fc319253d Mon Sep 17 00:00:00 2001
From: explosion-mental <explosion0mental@gmail.com>
Date: Thu, 26 May 2022 22:34:14 -0500
Subject: [PATCH] [PATCH] Allows dwm to handle the text by itself. You can
think of it like a dwmblocks integration into dwm itself. This is extracted
from my dwm build[0] in which you can find even more information.
Example:
```
/* fg command interval signal */
{ "#000000", "echo 'dwm block!", 10, 3},
```
- fg: the foreground color of the individual block, for the background it
uses the bg of SchemeStatus.
- command: it uses the output of the commands for the status text
interval: in seconds, how much does it have to pass before updating the
block.
- interval: in seconds, how many seconds until the block it's updated
- signal: have to be less than 30. This lets you update the block with
`kill` by adding 35 to this value.
For the block above it would be 34 + 3 = 37 -> `kill -37 $(pidof dwm)`.
These signals are linux dependant.
You can change `$(pidof dwm)` with `$STATUSBAR` to 'fix' signaling
multiple instances of dwm, since this patch also wraps the PID of dwm
into the `$STATUSBAR` enviromental variable.
Last thing, mouse actions. For this you need to handle the env variable
`$BLOCK_BUTTON` in a script, this is so you can easily reuse the scripts
used in dwmblocks. And remember that mouse actions update the block.
[0] https://github.com/explosion-mental/Dwm or
https://codeberg.org/explosion-mental/Dwm
---
config.def.h | 39 ++++++-
dwm.c | 298 +++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 318 insertions(+), 19 deletions(-)
diff --git a/config.def.h b/config.def.h
index a2ac963..cad178c 100644
--- a/config.def.h
+++ b/config.def.h
@@ -16,8 +16,38 @@ static const char *colors[][3] = {
/* fg bg border */
[SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
[SchemeSel] = { col_gray4, col_cyan, col_cyan },
+ [SchemeStatus]={ col_cyan, col_gray1, NULL },
};
+
+/* status bar */
+static const Block blocks[] = {
+ /* fg command interval signal */
+ { col_gray3, "sb-clock", 20, 1},
+ { col_gray1, "sb-disk", 9000, 2},
+ { col_gray2, "sb-battery", 10, 3},
+ { col_gray3, "sb-internet", 10, 4},
+ { col_cyan, "sb-mailbox", 0, 5},
+ { "#000001", "sb-moonphase", 0, 6},
+ { "#1F0077", "sb-forecast", 0, 7},
+ { "#000077", "sb-volume", 0, 8},
+ { "#F77000", "sb-pacpackages", 0, 9},
+ { "#177000", "sb-sync", 0, 10},
+// { col_gray1, "sb-mpc", 0, 26},
+ { col_gray2, "sb-music", 0, 11},
+// { col_gray3, "sb-tasks", 10, 12},
+ { col_gray4, "sb-notes", 0, 13},
+ { col_cyan, "echo '';cat /tmp/recordingicon", 0, 14},
+};
+
+/* inverse the order of the blocks, comment to disable */
+#define INVERSED 1
+/* delimeter between blocks commands. NULL character ('\0') means no delimeter. */
+static char delimiter[] = " ";
+/* max number of character that one block command can output */
+#define CMDLENGTH 50
+
+
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
@@ -104,7 +134,14 @@ static Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+
+ { ClkStatusText, 0, Button1, sendstatusbar, {.i = 1 } },
+ { ClkStatusText, 0, Button2, sendstatusbar, {.i = 2 } },
+ { ClkStatusText, 0, Button3, sendstatusbar, {.i = 3 } },
+ { ClkStatusText, 0, Button4, sendstatusbar, {.i = 4 } },
+ { ClkStatusText, 0, Button5, sendstatusbar, {.i = 5 } },
+ { ClkStatusText, ShiftMask, Button1, sendstatusbar, {.i = 6 } },
+
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
diff --git a/dwm.c b/dwm.c
index a96f33c..5789f72 100644
--- a/dwm.c
+++ b/dwm.c
@@ -28,6 +28,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <poll.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <X11/cursorfont.h>
@@ -59,7 +60,7 @@
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
-enum { SchemeNorm, SchemeSel }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeStatus }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
@@ -141,6 +142,13 @@ typedef struct {
int monitor;
} Rule;
+typedef struct {
+ const char *color;
+ const char *command;
+ const unsigned int interval;
+ const unsigned int signal;
+} Block;
+
/* function declarations */
static void applyrules(Client *c);
static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@@ -172,6 +180,11 @@ static void focusstack(const Arg *arg);
static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
+static void getcmd(int i, char *button);
+static void getcmds(int time);
+static void getsigcmds(int signal);
+static int gcd(int a, int b);
+static int getstatus(int width);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, int focused);
static void grabkeys(void);
@@ -197,14 +210,17 @@ static void run(void);
static void scan(void);
static int sendevent(Client *c, Atom proto);
static void sendmon(Client *c, Monitor *m);
+static void sendstatusbar(const Arg *arg);
static void setclientstate(Client *c, long state);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg);
static void setup(void);
+static void setsignal(int sig, void (*handler)(int sig));
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
+static void sigalrm(int unused);
static void sigchld(int unused);
static void spawn(const Arg *arg);
static void tag(const Arg *arg);
@@ -237,13 +253,16 @@ static void zoom(const Arg *arg);
/* variables */
static const char broken[] = "broken";
-static char stext[256];
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh, blw = 0; /* bar geometry */
static int lrpad; /* sum of left and right padding for text */
static int (*xerrorxlib)(Display *, XErrorEvent *);
+static unsigned int blocknum; /* blocks idx in mouse click */
+static unsigned int stsw = 0; /* status width */
static unsigned int numlockmask = 0;
+static unsigned int sleepinterval = 0, maxinterval = 0, count = 0;
+static unsigned int execlock = 0; /* ensure only one child process exists per block at an instance */
static void (*handler[LASTEvent]) (XEvent *) = {
[ButtonPress] = buttonpress,
[ClientMessage] = clientmessage,
@@ -272,6 +291,9 @@ static Window root, wmcheckwin;
/* configuration, allows nested code to access above variables */
#include "config.h"
+static char blockoutput[LENGTH(blocks)][CMDLENGTH + 1] = {0};
+static int pipes[LENGTH(blocks)][2];
+
/* compile-time check if all tags fit into an unsigned int bit array. */
struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
@@ -440,9 +462,26 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + blw)
click = ClkLtSymbol;
- else if (ev->x > selmon->ww - (int)TEXTW(stext))
+ else if (ev->x > (x = selmon->ww - stsw)) {
click = ClkStatusText;
- else
+ int len, i;
+
+ #if INVERSED
+ for (i = LENGTH(blocks) - 1; i >= 0; i--)
+ #else
+ for (i = 0; i < LENGTH(blocks); i++)
+ #endif /* INVERSED */
+ {
+ if (*blockoutput[i] == '\0') /* ignore command that output NULL or '\0' */
+ continue;
+ len = TEXTW(blockoutput[i]) - lrpad + TEXTW(delimiter) - lrpad;
+ x += len;
+ if (ev->x <= x && ev->x >= x - len) { /* if the mouse is between the block area */
+ blocknum = i; /* store what block the mouse is clicking */
+ break;
+ }
+ }
+ } else
click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) {
focus(c);
@@ -706,11 +745,8 @@ drawbar(Monitor *m)
return;
/* draw status first so it can be overdrawn by tags later */
- if (m == selmon) { /* status is only drawn on selected monitor */
- drw_setscheme(drw, scheme[SchemeNorm]);
- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
- }
+ if (m == selmon) /* status is only drawn on selected monitor */
+ tw = getstatus(m->ww);
for (c = m->clients; c; c = c->next) {
occ |= c->tags;
@@ -903,6 +939,106 @@ getstate(Window w)
return result;
}
+void
+getcmd(int i, char *button)
+{
+ if (!selmon->showbar)
+ return;
+
+ if (execlock & 1 << i) { /* block is already running */
+ //fprintf(stderr, "dwm: ignoring block %d, command %s\n", i, blocks[i].command);
+ return;
+ }
+
+ /* lock execution of block until current instance finishes execution */
+ execlock |= 1 << i;
+
+ if (fork() == 0) {
+ if (dpy)
+ close(ConnectionNumber(dpy));
+ dup2(pipes[i][1], STDOUT_FILENO);
+ close(pipes[i][0]);
+ close(pipes[i][1]);
+
+ if (button)
+ setenv("BLOCK_BUTTON", button, 1);
+ execlp("/bin/sh", "sh", "-c", blocks[i].command, (char *) NULL);
+ fprintf(stderr, "dwm: block %d, execlp %s", i, blocks[i].command);
+ perror(" failed");
+ exit(EXIT_SUCCESS);
+ }
+}
+
+void
+getcmds(int time)
+{
+ int i;
+ for (i = 0; i < LENGTH(blocks); i++)
+ if ((blocks[i].interval != 0 && time % blocks[i].interval == 0) || time == -1)
+ getcmd(i, NULL);
+}
+
+void
+getsigcmds(int signal)
+{
+ int i;
+ unsigned int sig = signal - SIGRTMIN;
+ for (i = 0; i < LENGTH(blocks); i++)
+ if (blocks[i].signal == sig)
+ getcmd(i, NULL);
+}
+
+int
+getstatus(int width)
+{
+ int i, len, all = width, delimlen = TEXTW(delimiter) - lrpad;
+ char fgcol[8];
+ /* fg bg */
+ const char *cols[8] = { fgcol, colors[SchemeStatus][ColBg] };
+ //uncomment to inverse the colors
+ //const char *cols[8] = { colors[SchemeStatus][ColBg], fgcol };
+
+ #if INVERSED
+ for (i = 0; i < LENGTH(blocks); i++)
+ #else
+ for (i = LENGTH(blocks) - 1; i >= 0; i--)
+ #endif /* INVERSED */
+ {
+ if (*blockoutput[i] == '\0') /* ignore command that output NULL or '\0' */
+ continue;
+ strncpy(fgcol, blocks[i].color, 8);
+ /* re-load the scheme with the new colors */
+ scheme[SchemeStatus] = drw_scm_create(drw, cols, 3);
+ drw_setscheme(drw, scheme[SchemeStatus]); /* 're-set' the scheme */
+ len = TEXTW(blockoutput[i]) - lrpad;
+ all -= len;
+ drw_text(drw, all, 0, len, bh, 0, blockoutput[i], 0);
+ /* draw delimiter */
+ if (*delimiter == '\0') /* ignore no delimiter */
+ continue;
+ drw_setscheme(drw, scheme[SchemeNorm]);
+ all -= delimlen;
+ drw_text(drw, all, 0, delimlen, bh, 0, delimiter, 0);
+ }
+
+ return stsw = width - all;
+}
+
+int
+gcd(int a, int b)
+{
+ int temp;
+
+ while (b > 0) {
+ temp = a % b;
+ a = b;
+ b = temp;
+ }
+
+ return a;
+}
+
+
int
gettextprop(Window w, Atom atom, char *text, unsigned int size)
{
@@ -1376,12 +1512,99 @@ restack(Monitor *m)
void
run(void)
{
+ int i;
XEvent ev;
+ struct pollfd fds[LENGTH(blocks) + 1] = {0};
+
+ fds[0].fd = ConnectionNumber(dpy);
+ fds[0].events = POLLIN;
+
+ #if INVERSED
+ for (i = LENGTH(blocks) - 1; i >= 0; i--)
+ #else
+ for (i = 0; i < LENGTH(blocks); i++)
+ #endif /* INVERSED */
+ {
+ pipe(pipes[i]);
+ fds[i + 1].fd = pipes[i][0];
+ fds[i + 1].events = POLLIN;
+ getcmd(i, NULL);
+ if (blocks[i].interval) {
+ maxinterval = MAX(blocks[i].interval, maxinterval);
+ sleepinterval = gcd(blocks[i].interval, sleepinterval);
+ }
+ }
+
+ alarm(sleepinterval);
/* main event loop */
XSync(dpy, False);
- while (running && !XNextEvent(dpy, &ev))
- if (handler[ev.type])
- handler[ev.type](&ev); /* call handler */
+ while (running) {
+
+ /* bar hidden, then skip poll */
+ if (!selmon->showbar) {
+ XNextEvent(dpy, &ev);
+ if (handler[ev.type])
+ handler[ev.type](&ev); /* call handler */
+ continue;
+ }
+
+ if ((poll(fds, LENGTH(blocks) + 1, -1)) == -1) {
+ /* FIXME other than SIGALRM and the real time signals,
+ * there seems to be a signal being que if using
+ * 'xsetroot -name' sutff */
+ if (errno == EINTR) /* signal caught */
+ continue;
+ fprintf(stderr, "dwm: poll ");
+ perror("failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* handle display fd */
+ if (fds[0].revents & POLLIN) {
+ while (running && XPending(dpy)) {
+ XNextEvent(dpy, &ev);
+ if (handler[ev.type])
+ handler[ev.type](&ev); /* call handler */
+ }
+ } else if (fds[0].revents & POLLHUP) {
+ fprintf(stderr, "dwm: main event loop, hang up");
+ perror(" failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* handle blocks */
+ for (i = 0; i < LENGTH(blocks); i++) {
+ if (fds[i + 1].revents & POLLIN) {
+ /* empty buffer with CMDLENGTH + 1 byte for the null terminator */
+ int bt = read(fds[i + 1].fd, blockoutput[i], CMDLENGTH);
+ /* remove lock for the current block */
+ execlock &= ~(1 << i);
+
+ if (bt == -1) { /* if read failed */
+ fprintf(stderr, "dwm: read failed in block %s\n", blocks[i].command);
+ perror(" failed");
+ continue;
+ }
+
+ if (blockoutput[i][bt - 1] == '\n') /* chop off ending new line, if one is present */
+ blockoutput[i][bt - 1] = '\0';
+ else /* NULL terminate the string */
+ blockoutput[i][bt++] = '\0';
+
+ drawbar(selmon);
+ } else if (fds[i + 1].revents & POLLHUP) {
+ fprintf(stderr, "dwm: block %d hangup", i);
+ perror(" failed");
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* close the pipes after running */
+ for (i = 0; i < LENGTH(blocks); i++) {
+ close(pipes[i][0]);
+ close(pipes[i][1]);
+ }
}
void
@@ -1427,6 +1650,13 @@ sendmon(Client *c, Monitor *m)
arrange(NULL);
}
+void
+sendstatusbar(const Arg *arg)
+{
+ char button[2] = { '0' + arg->i & 0xff, '\0' };
+ getcmd(blocknum, button);
+}
+
void
setclientstate(Client *c, long state)
{
@@ -1537,8 +1767,20 @@ setup(void)
XSetWindowAttributes wa;
Atom utf8string;
- /* clean up any zombies immediately */
- sigchld(0);
+ setsignal(SIGCHLD, sigchld); /* zombies */
+ setsignal(SIGALRM, sigalrm); /* timer */
+
+ #ifdef __linux__
+ /* handle defined real time signals (linux only) */
+ for (i = 0; i < LENGTH(blocks); i++)
+ if (blocks[i].signal)
+ setsignal(SIGRTMIN + blocks[i].signal, getsigcmds);
+ #endif /* __linux__ */
+
+ /* pid as an enviromental variable */
+ char envpid[16];
+ snprintf(envpid, LENGTH(envpid), "%d", getpid());
+ setenv("STATUSBAR", envpid, 1);
/* init screen */
screen = DefaultScreen(dpy);
@@ -1600,6 +1842,21 @@ setup(void)
focus(NULL);
}
+void
+setsignal(int sig, void (*handler)(int unused))
+{
+ struct sigaction sa;
+
+ sa.sa_handler = handler;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
+
+ if (sigaction(sig, &sa, 0) == -1) {
+ fprintf(stderr, "signal %d ", sig);
+ perror("failed to setup");
+ exit(EXIT_FAILURE);
+ }
+}
void
seturgent(Client *c, int urg)
@@ -1632,11 +1889,18 @@ showhide(Client *c)
}
}
+
+void
+sigalrm(int unused)
+{
+ getcmds(count);
+ alarm(sleepinterval);
+ count = (count + sleepinterval - 1) % maxinterval + 1;
+}
+
void
sigchld(int unused)
{
- if (signal(SIGCHLD, sigchld) == SIG_ERR)
- die("can't install SIGCHLD handler:");
while (0 < waitpid(-1, NULL, WNOHANG));
}
@@ -1993,8 +2257,6 @@ updatesizehints(Client *c)
void
updatestatus(void)
{
- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
- strcpy(stext, "dwm-"VERSION);
drawbar(selmon);
}
--
2.36.1