dwm-integrated-status-text-6.3.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
dwm-integrated-status-text-6.3.diff (16210B)
---
1 From 02f1f07ee4460787c971bd28e934cb5fc319253d Mon Sep 17 00:00:00 2001
2 From: explosion-mental <explosion0mental@gmail.com>
3 Date: Thu, 26 May 2022 22:34:14 -0500
4 Subject: [PATCH] [PATCH] Allows dwm to handle the text by itself. You can
5 think of it like a dwmblocks integration into dwm itself. This is extracted
6 from my dwm build[0] in which you can find even more information.
7
8 Example:
9 ```
10 /* fg command interval signal */
11 { "#000000", "echo 'dwm block!", 10, 3},
12 ```
13
14 - fg: the foreground color of the individual block, for the background it
15 uses the bg of SchemeStatus.
16
17 - command: it uses the output of the commands for the status text
18 interval: in seconds, how much does it have to pass before updating the
19 block.
20
21 - interval: in seconds, how many seconds until the block it's updated
22
23 - signal: have to be less than 30. This lets you update the block with
24 `kill` by adding 35 to this value.
25 For the block above it would be 34 + 3 = 37 -> `kill -37 $(pidof dwm)`.
26 These signals are linux dependant.
27
28 You can change `$(pidof dwm)` with `$STATUSBAR` to 'fix' signaling
29 multiple instances of dwm, since this patch also wraps the PID of dwm
30 into the `$STATUSBAR` enviromental variable.
31
32 Last thing, mouse actions. For this you need to handle the env variable
33 `$BLOCK_BUTTON` in a script, this is so you can easily reuse the scripts
34 used in dwmblocks. And remember that mouse actions update the block.
35
36 [0] https://github.com/explosion-mental/Dwm or
37 https://codeberg.org/explosion-mental/Dwm
38 ---
39 config.def.h | 39 ++++++-
40 dwm.c | 298 +++++++++++++++++++++++++++++++++++++++++++++++----
41 2 files changed, 318 insertions(+), 19 deletions(-)
42
43 diff --git a/config.def.h b/config.def.h
44 index a2ac963..cad178c 100644
45 --- a/config.def.h
46 +++ b/config.def.h
47 @@ -16,8 +16,38 @@ static const char *colors[][3] = {
48 /* fg bg border */
49 [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
50 [SchemeSel] = { col_gray4, col_cyan, col_cyan },
51 + [SchemeStatus]={ col_cyan, col_gray1, NULL },
52 };
53
54 +
55 +/* status bar */
56 +static const Block blocks[] = {
57 + /* fg command interval signal */
58 + { col_gray3, "sb-clock", 20, 1},
59 + { col_gray1, "sb-disk", 9000, 2},
60 + { col_gray2, "sb-battery", 10, 3},
61 + { col_gray3, "sb-internet", 10, 4},
62 + { col_cyan, "sb-mailbox", 0, 5},
63 + { "#000001", "sb-moonphase", 0, 6},
64 + { "#1F0077", "sb-forecast", 0, 7},
65 + { "#000077", "sb-volume", 0, 8},
66 + { "#F77000", "sb-pacpackages", 0, 9},
67 + { "#177000", "sb-sync", 0, 10},
68 +// { col_gray1, "sb-mpc", 0, 26},
69 + { col_gray2, "sb-music", 0, 11},
70 +// { col_gray3, "sb-tasks", 10, 12},
71 + { col_gray4, "sb-notes", 0, 13},
72 + { col_cyan, "echo '';cat /tmp/recordingicon", 0, 14},
73 +};
74 +
75 +/* inverse the order of the blocks, comment to disable */
76 +#define INVERSED 1
77 +/* delimeter between blocks commands. NULL character ('\0') means no delimeter. */
78 +static char delimiter[] = " ";
79 +/* max number of character that one block command can output */
80 +#define CMDLENGTH 50
81 +
82 +
83 /* tagging */
84 static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
85
86 @@ -104,7 +134,14 @@ static Button buttons[] = {
87 { ClkLtSymbol, 0, Button1, setlayout, {0} },
88 { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
89 { ClkWinTitle, 0, Button2, zoom, {0} },
90 - { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
91 +
92 + { ClkStatusText, 0, Button1, sendstatusbar, {.i = 1 } },
93 + { ClkStatusText, 0, Button2, sendstatusbar, {.i = 2 } },
94 + { ClkStatusText, 0, Button3, sendstatusbar, {.i = 3 } },
95 + { ClkStatusText, 0, Button4, sendstatusbar, {.i = 4 } },
96 + { ClkStatusText, 0, Button5, sendstatusbar, {.i = 5 } },
97 + { ClkStatusText, ShiftMask, Button1, sendstatusbar, {.i = 6 } },
98 +
99 { ClkClientWin, MODKEY, Button1, movemouse, {0} },
100 { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
101 { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
102 diff --git a/dwm.c b/dwm.c
103 index a96f33c..5789f72 100644
104 --- a/dwm.c
105 +++ b/dwm.c
106 @@ -28,6 +28,7 @@
107 #include <stdlib.h>
108 #include <string.h>
109 #include <unistd.h>
110 +#include <poll.h>
111 #include <sys/types.h>
112 #include <sys/wait.h>
113 #include <X11/cursorfont.h>
114 @@ -59,7 +60,7 @@
115
116 /* enums */
117 enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
118 -enum { SchemeNorm, SchemeSel }; /* color schemes */
119 +enum { SchemeNorm, SchemeSel, SchemeStatus }; /* color schemes */
120 enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
121 NetWMFullscreen, NetActiveWindow, NetWMWindowType,
122 NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
123 @@ -141,6 +142,13 @@ typedef struct {
124 int monitor;
125 } Rule;
126
127 +typedef struct {
128 + const char *color;
129 + const char *command;
130 + const unsigned int interval;
131 + const unsigned int signal;
132 +} Block;
133 +
134 /* function declarations */
135 static void applyrules(Client *c);
136 static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
137 @@ -172,6 +180,11 @@ static void focusstack(const Arg *arg);
138 static Atom getatomprop(Client *c, Atom prop);
139 static int getrootptr(int *x, int *y);
140 static long getstate(Window w);
141 +static void getcmd(int i, char *button);
142 +static void getcmds(int time);
143 +static void getsigcmds(int signal);
144 +static int gcd(int a, int b);
145 +static int getstatus(int width);
146 static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
147 static void grabbuttons(Client *c, int focused);
148 static void grabkeys(void);
149 @@ -197,14 +210,17 @@ static void run(void);
150 static void scan(void);
151 static int sendevent(Client *c, Atom proto);
152 static void sendmon(Client *c, Monitor *m);
153 +static void sendstatusbar(const Arg *arg);
154 static void setclientstate(Client *c, long state);
155 static void setfocus(Client *c);
156 static void setfullscreen(Client *c, int fullscreen);
157 static void setlayout(const Arg *arg);
158 static void setmfact(const Arg *arg);
159 static void setup(void);
160 +static void setsignal(int sig, void (*handler)(int sig));
161 static void seturgent(Client *c, int urg);
162 static void showhide(Client *c);
163 +static void sigalrm(int unused);
164 static void sigchld(int unused);
165 static void spawn(const Arg *arg);
166 static void tag(const Arg *arg);
167 @@ -237,13 +253,16 @@ static void zoom(const Arg *arg);
168
169 /* variables */
170 static const char broken[] = "broken";
171 -static char stext[256];
172 static int screen;
173 static int sw, sh; /* X display screen geometry width, height */
174 static int bh, blw = 0; /* bar geometry */
175 static int lrpad; /* sum of left and right padding for text */
176 static int (*xerrorxlib)(Display *, XErrorEvent *);
177 +static unsigned int blocknum; /* blocks idx in mouse click */
178 +static unsigned int stsw = 0; /* status width */
179 static unsigned int numlockmask = 0;
180 +static unsigned int sleepinterval = 0, maxinterval = 0, count = 0;
181 +static unsigned int execlock = 0; /* ensure only one child process exists per block at an instance */
182 static void (*handler[LASTEvent]) (XEvent *) = {
183 [ButtonPress] = buttonpress,
184 [ClientMessage] = clientmessage,
185 @@ -272,6 +291,9 @@ static Window root, wmcheckwin;
186 /* configuration, allows nested code to access above variables */
187 #include "config.h"
188
189 +static char blockoutput[LENGTH(blocks)][CMDLENGTH + 1] = {0};
190 +static int pipes[LENGTH(blocks)][2];
191 +
192 /* compile-time check if all tags fit into an unsigned int bit array. */
193 struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; };
194
195 @@ -440,9 +462,26 @@ buttonpress(XEvent *e)
196 arg.ui = 1 << i;
197 } else if (ev->x < x + blw)
198 click = ClkLtSymbol;
199 - else if (ev->x > selmon->ww - (int)TEXTW(stext))
200 + else if (ev->x > (x = selmon->ww - stsw)) {
201 click = ClkStatusText;
202 - else
203 + int len, i;
204 +
205 + #if INVERSED
206 + for (i = LENGTH(blocks) - 1; i >= 0; i--)
207 + #else
208 + for (i = 0; i < LENGTH(blocks); i++)
209 + #endif /* INVERSED */
210 + {
211 + if (*blockoutput[i] == '\0') /* ignore command that output NULL or '\0' */
212 + continue;
213 + len = TEXTW(blockoutput[i]) - lrpad + TEXTW(delimiter) - lrpad;
214 + x += len;
215 + if (ev->x <= x && ev->x >= x - len) { /* if the mouse is between the block area */
216 + blocknum = i; /* store what block the mouse is clicking */
217 + break;
218 + }
219 + }
220 + } else
221 click = ClkWinTitle;
222 } else if ((c = wintoclient(ev->window))) {
223 focus(c);
224 @@ -706,11 +745,8 @@ drawbar(Monitor *m)
225 return;
226
227 /* draw status first so it can be overdrawn by tags later */
228 - if (m == selmon) { /* status is only drawn on selected monitor */
229 - drw_setscheme(drw, scheme[SchemeNorm]);
230 - tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
231 - drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
232 - }
233 + if (m == selmon) /* status is only drawn on selected monitor */
234 + tw = getstatus(m->ww);
235
236 for (c = m->clients; c; c = c->next) {
237 occ |= c->tags;
238 @@ -903,6 +939,106 @@ getstate(Window w)
239 return result;
240 }
241
242 +void
243 +getcmd(int i, char *button)
244 +{
245 + if (!selmon->showbar)
246 + return;
247 +
248 + if (execlock & 1 << i) { /* block is already running */
249 + //fprintf(stderr, "dwm: ignoring block %d, command %s\n", i, blocks[i].command);
250 + return;
251 + }
252 +
253 + /* lock execution of block until current instance finishes execution */
254 + execlock |= 1 << i;
255 +
256 + if (fork() == 0) {
257 + if (dpy)
258 + close(ConnectionNumber(dpy));
259 + dup2(pipes[i][1], STDOUT_FILENO);
260 + close(pipes[i][0]);
261 + close(pipes[i][1]);
262 +
263 + if (button)
264 + setenv("BLOCK_BUTTON", button, 1);
265 + execlp("/bin/sh", "sh", "-c", blocks[i].command, (char *) NULL);
266 + fprintf(stderr, "dwm: block %d, execlp %s", i, blocks[i].command);
267 + perror(" failed");
268 + exit(EXIT_SUCCESS);
269 + }
270 +}
271 +
272 +void
273 +getcmds(int time)
274 +{
275 + int i;
276 + for (i = 0; i < LENGTH(blocks); i++)
277 + if ((blocks[i].interval != 0 && time % blocks[i].interval == 0) || time == -1)
278 + getcmd(i, NULL);
279 +}
280 +
281 +void
282 +getsigcmds(int signal)
283 +{
284 + int i;
285 + unsigned int sig = signal - SIGRTMIN;
286 + for (i = 0; i < LENGTH(blocks); i++)
287 + if (blocks[i].signal == sig)
288 + getcmd(i, NULL);
289 +}
290 +
291 +int
292 +getstatus(int width)
293 +{
294 + int i, len, all = width, delimlen = TEXTW(delimiter) - lrpad;
295 + char fgcol[8];
296 + /* fg bg */
297 + const char *cols[8] = { fgcol, colors[SchemeStatus][ColBg] };
298 + //uncomment to inverse the colors
299 + //const char *cols[8] = { colors[SchemeStatus][ColBg], fgcol };
300 +
301 + #if INVERSED
302 + for (i = 0; i < LENGTH(blocks); i++)
303 + #else
304 + for (i = LENGTH(blocks) - 1; i >= 0; i--)
305 + #endif /* INVERSED */
306 + {
307 + if (*blockoutput[i] == '\0') /* ignore command that output NULL or '\0' */
308 + continue;
309 + strncpy(fgcol, blocks[i].color, 8);
310 + /* re-load the scheme with the new colors */
311 + scheme[SchemeStatus] = drw_scm_create(drw, cols, 3);
312 + drw_setscheme(drw, scheme[SchemeStatus]); /* 're-set' the scheme */
313 + len = TEXTW(blockoutput[i]) - lrpad;
314 + all -= len;
315 + drw_text(drw, all, 0, len, bh, 0, blockoutput[i], 0);
316 + /* draw delimiter */
317 + if (*delimiter == '\0') /* ignore no delimiter */
318 + continue;
319 + drw_setscheme(drw, scheme[SchemeNorm]);
320 + all -= delimlen;
321 + drw_text(drw, all, 0, delimlen, bh, 0, delimiter, 0);
322 + }
323 +
324 + return stsw = width - all;
325 +}
326 +
327 +int
328 +gcd(int a, int b)
329 +{
330 + int temp;
331 +
332 + while (b > 0) {
333 + temp = a % b;
334 + a = b;
335 + b = temp;
336 + }
337 +
338 + return a;
339 +}
340 +
341 +
342 int
343 gettextprop(Window w, Atom atom, char *text, unsigned int size)
344 {
345 @@ -1376,12 +1512,99 @@ restack(Monitor *m)
346 void
347 run(void)
348 {
349 + int i;
350 XEvent ev;
351 + struct pollfd fds[LENGTH(blocks) + 1] = {0};
352 +
353 + fds[0].fd = ConnectionNumber(dpy);
354 + fds[0].events = POLLIN;
355 +
356 + #if INVERSED
357 + for (i = LENGTH(blocks) - 1; i >= 0; i--)
358 + #else
359 + for (i = 0; i < LENGTH(blocks); i++)
360 + #endif /* INVERSED */
361 + {
362 + pipe(pipes[i]);
363 + fds[i + 1].fd = pipes[i][0];
364 + fds[i + 1].events = POLLIN;
365 + getcmd(i, NULL);
366 + if (blocks[i].interval) {
367 + maxinterval = MAX(blocks[i].interval, maxinterval);
368 + sleepinterval = gcd(blocks[i].interval, sleepinterval);
369 + }
370 + }
371 +
372 + alarm(sleepinterval);
373 /* main event loop */
374 XSync(dpy, False);
375 - while (running && !XNextEvent(dpy, &ev))
376 - if (handler[ev.type])
377 - handler[ev.type](&ev); /* call handler */
378 + while (running) {
379 +
380 + /* bar hidden, then skip poll */
381 + if (!selmon->showbar) {
382 + XNextEvent(dpy, &ev);
383 + if (handler[ev.type])
384 + handler[ev.type](&ev); /* call handler */
385 + continue;
386 + }
387 +
388 + if ((poll(fds, LENGTH(blocks) + 1, -1)) == -1) {
389 + /* FIXME other than SIGALRM and the real time signals,
390 + * there seems to be a signal being que if using
391 + * 'xsetroot -name' sutff */
392 + if (errno == EINTR) /* signal caught */
393 + continue;
394 + fprintf(stderr, "dwm: poll ");
395 + perror("failed");
396 + exit(EXIT_FAILURE);
397 + }
398 +
399 + /* handle display fd */
400 + if (fds[0].revents & POLLIN) {
401 + while (running && XPending(dpy)) {
402 + XNextEvent(dpy, &ev);
403 + if (handler[ev.type])
404 + handler[ev.type](&ev); /* call handler */
405 + }
406 + } else if (fds[0].revents & POLLHUP) {
407 + fprintf(stderr, "dwm: main event loop, hang up");
408 + perror(" failed");
409 + exit(EXIT_FAILURE);
410 + }
411 +
412 + /* handle blocks */
413 + for (i = 0; i < LENGTH(blocks); i++) {
414 + if (fds[i + 1].revents & POLLIN) {
415 + /* empty buffer with CMDLENGTH + 1 byte for the null terminator */
416 + int bt = read(fds[i + 1].fd, blockoutput[i], CMDLENGTH);
417 + /* remove lock for the current block */
418 + execlock &= ~(1 << i);
419 +
420 + if (bt == -1) { /* if read failed */
421 + fprintf(stderr, "dwm: read failed in block %s\n", blocks[i].command);
422 + perror(" failed");
423 + continue;
424 + }
425 +
426 + if (blockoutput[i][bt - 1] == '\n') /* chop off ending new line, if one is present */
427 + blockoutput[i][bt - 1] = '\0';
428 + else /* NULL terminate the string */
429 + blockoutput[i][bt++] = '\0';
430 +
431 + drawbar(selmon);
432 + } else if (fds[i + 1].revents & POLLHUP) {
433 + fprintf(stderr, "dwm: block %d hangup", i);
434 + perror(" failed");
435 + exit(EXIT_FAILURE);
436 + }
437 + }
438 + }
439 +
440 + /* close the pipes after running */
441 + for (i = 0; i < LENGTH(blocks); i++) {
442 + close(pipes[i][0]);
443 + close(pipes[i][1]);
444 + }
445 }
446
447 void
448 @@ -1427,6 +1650,13 @@ sendmon(Client *c, Monitor *m)
449 arrange(NULL);
450 }
451
452 +void
453 +sendstatusbar(const Arg *arg)
454 +{
455 + char button[2] = { '0' + arg->i & 0xff, '\0' };
456 + getcmd(blocknum, button);
457 +}
458 +
459 void
460 setclientstate(Client *c, long state)
461 {
462 @@ -1537,8 +1767,20 @@ setup(void)
463 XSetWindowAttributes wa;
464 Atom utf8string;
465
466 - /* clean up any zombies immediately */
467 - sigchld(0);
468 + setsignal(SIGCHLD, sigchld); /* zombies */
469 + setsignal(SIGALRM, sigalrm); /* timer */
470 +
471 + #ifdef __linux__
472 + /* handle defined real time signals (linux only) */
473 + for (i = 0; i < LENGTH(blocks); i++)
474 + if (blocks[i].signal)
475 + setsignal(SIGRTMIN + blocks[i].signal, getsigcmds);
476 + #endif /* __linux__ */
477 +
478 + /* pid as an enviromental variable */
479 + char envpid[16];
480 + snprintf(envpid, LENGTH(envpid), "%d", getpid());
481 + setenv("STATUSBAR", envpid, 1);
482
483 /* init screen */
484 screen = DefaultScreen(dpy);
485 @@ -1600,6 +1842,21 @@ setup(void)
486 focus(NULL);
487 }
488
489 +void
490 +setsignal(int sig, void (*handler)(int unused))
491 +{
492 + struct sigaction sa;
493 +
494 + sa.sa_handler = handler;
495 + sigemptyset(&sa.sa_mask);
496 + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
497 +
498 + if (sigaction(sig, &sa, 0) == -1) {
499 + fprintf(stderr, "signal %d ", sig);
500 + perror("failed to setup");
501 + exit(EXIT_FAILURE);
502 + }
503 +}
504
505 void
506 seturgent(Client *c, int urg)
507 @@ -1632,11 +1889,18 @@ showhide(Client *c)
508 }
509 }
510
511 +
512 +void
513 +sigalrm(int unused)
514 +{
515 + getcmds(count);
516 + alarm(sleepinterval);
517 + count = (count + sleepinterval - 1) % maxinterval + 1;
518 +}
519 +
520 void
521 sigchld(int unused)
522 {
523 - if (signal(SIGCHLD, sigchld) == SIG_ERR)
524 - die("can't install SIGCHLD handler:");
525 while (0 < waitpid(-1, NULL, WNOHANG));
526 }
527
528 @@ -1993,8 +2257,6 @@ updatesizehints(Client *c)
529 void
530 updatestatus(void)
531 {
532 - if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
533 - strcpy(stext, "dwm-"VERSION);
534 drawbar(selmon);
535 }
536
537 --
538 2.36.1
539