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