sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | 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