swm

sigma window manager
git clone git://wolog.xyz/swm
Log | Files | Refs | README | LICENSE

misc.c (11047B)


      1 /*
      2  * Copyright (C) Raymond Cole <rc@wolog.xyz>
      3  * License: GPL-3.0-or-later
      4  *
      5  * Portions of the code are derived from suckless dwm.  See file
      6  * LICENSE-dwm for copyright and license details of those portions.
      7  */
      8 #include <stdarg.h>
      9 #include <signal.h>
     10 #include <stdio.h>
     11 #include <stdlib.h>
     12 #include <unistd.h>
     13 #include <X11/Xlib.h>
     14 
     15 #include "swm.h"
     16 
     17 #define MOUSEMASK (ButtonPressMask|ButtonReleaseMask|PointerMotionMask)
     18 
     19 static LytNode *master(LytNode *lyt);
     20 
     21 static const unsigned ctrlshift[] = {
     22 	0, ControlMask, ShiftMask, ControlMask|ShiftMask
     23 };
     24 static FILE *menufpr, *menufpw;
     25 
     26 int
     27 depart(int *fds)
     28 {
     29 	int fds1[2], fds2[2];
     30 	struct sigaction sa;
     31 
     32 	if (fds) {
     33 		if (pipe(fds1) < 0 || pipe(fds2) < 0) {
     34 			warn("pipe:");
     35 			return -1;
     36 		}
     37 	}
     38 	switch (fork()) {
     39 	case -1:
     40 		if (fds) {
     41 			close(fds1[0]);
     42 			close(fds1[1]);
     43 			close(fds2[0]);
     44 			close(fds2[1]);
     45 		}
     46 		return -1;
     47 	case 0:
     48 		if (dpy)
     49 			close(ConnectionNumber(dpy));
     50 		if (fds) {
     51 			close(fds1[1]);
     52 			close(fds2[0]);
     53 			if (fds1[0] != 0) {
     54 				if (dup2(fds1[0], 0) < 0)
     55 					warn("dup2:");
     56 				close(fds1[0]);
     57 			}
     58 			if (fds2[1] != 1) {
     59 				if (dup2(fds2[1], 1) < 0)
     60 					warn("dup2:");
     61 				close(fds2[1]);
     62 			}
     63 		}
     64 		sigemptyset(&sa.sa_mask);
     65 		sa.sa_flags = 0;
     66 		sa.sa_handler = SIG_DFL;
     67 		if (sigaction(SIGCHLD, &sa, NULL) < 0)
     68 			warn("sigaction:");
     69 		if (setsid() < 0)
     70 			warn("setsid:");
     71 		return 0;
     72 	default:
     73 		if (fds) {
     74 			close(fds1[0]);
     75 			close(fds2[1]);
     76 			fds[0] = fds2[0];
     77 			fds[1] = fds1[1];
     78 		}
     79 		return 1;
     80 	}
     81 }
     82 
     83 void
     84 spawn(char **cmd)
     85 {
     86 	if (depart(NULL) == 0) {
     87 		execvp(cmd[0], cmd);
     88 		die("execvp '%s':", cmd[0]);
     89 	}
     90 }
     91 
     92 int
     93 getrootptr(int *x, int *y)
     94 {
     95 	int di;
     96 	unsigned dui;
     97 	Window dw;
     98 
     99 	return XQueryPointer(dpy, root, &dw, &dw, x, y, &di, &di, &dui);
    100 }
    101 
    102 long
    103 getstate(Window w)
    104 {
    105 	int format;
    106 	long result = -1;
    107 	unsigned char *p = NULL;
    108 	unsigned long n, extra;
    109 	Atom real;
    110 
    111 	if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False,
    112 	                       wmatom[WMState], &real, &format, &n,
    113 	                       &extra, (unsigned char **)&p) != Success)
    114 		return -1;
    115 	if (n != 0)
    116 		result = *p;
    117 	XFree(p);
    118 	return result;
    119 }
    120 
    121 void
    122 grabbuttons(Client *c, int focused)
    123 {
    124 	int i, j;
    125 
    126 	XUngrabButton(dpy, AnyButton, AnyModifier, c->win);
    127 	if (!focused)
    128 		XGrabButton(dpy, AnyButton, AnyModifier, c->win,
    129 		            False, ButtonPressMask|ButtonReleaseMask,
    130 		            GrabModeSync, GrabModeSync, None, None);
    131 	for (i = 0; i < 4; ++i)
    132 		for (j = 0; j < 4; ++j)
    133 			XGrabButton(dpy, AnyButton,
    134 			            MODMASK|lockmasks[i]|ctrlshift[j],
    135 			            c->win, False,
    136 			            ButtonPressMask|ButtonReleaseMask,
    137 			            GrabModeAsync, GrabModeSync, None, None);
    138 }
    139 
    140 void
    141 grabkeys(void)
    142 {
    143 	int i, j;
    144 
    145 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    146 	for (i = 0; i < 4; ++i)
    147 		for (j = 0; j < 4; ++j)
    148 			XGrabKey(dpy, AnyKey,
    149 			         MODMASK|lockmasks[i]|ctrlshift[j],
    150 			         root, True, GrabModeAsync, GrabModeAsync);
    151 }
    152 
    153 void
    154 swap(Client *c, Client *d)
    155 {
    156 	Client **tc, **td;
    157 	Monitor *m;
    158 	Client *t;
    159 
    160 	if (!c || !d || c == d)
    161 		return;
    162 	tc = &c->mon->clients;
    163 	while (*tc && *tc != c && *tc != d)
    164 		tc = &(*tc)->next;
    165 	if (!*tc)
    166 		return;
    167 	if (*tc == d) {
    168 		t = c;
    169 		c = d;
    170 		d = t;
    171 	}
    172 	if (c->next == d) {
    173 		c->next = d->next;
    174 		d->next = c;
    175 		*tc = d;
    176 	} else {
    177 		td = c->mon == d->mon ? &c->next : &d->mon->clients;
    178 		while (*td && *td != d)
    179 			td = &(*td)->next;
    180 		if (!*td)
    181 			return;
    182 		*tc = d;
    183 		*td = c;
    184 		t = c->next;
    185 		c->next = d->next;
    186 		d->next = t;
    187 		if (c->mon != d->mon) {
    188 			m = c->mon;
    189 			c->mon = d->mon;
    190 			d->mon = m;
    191 		}
    192 	}
    193 	parrange(c->mon);
    194 }
    195 
    196 void
    197 tag(unsigned ts)
    198 {
    199 	if (selmon->sel && (ts &= TAGMASK)) {
    200 		selmon->sel->tags = ts;
    201 		focus(selmon->stack);
    202 		parrange(selmon);
    203 		pdrawbar(selmon);
    204 	}
    205 }
    206 
    207 void
    208 view(unsigned ts)
    209 {
    210 	selmon->tagset = ts & TAGMASK;
    211 	focus(selmon->stack);
    212 	parrange(selmon);
    213 	pdrawbar(selmon);
    214 }
    215 
    216 void
    217 setworkspace(unsigned ws)
    218 {
    219 	if (ws >= LENGTH(tags))
    220 		return;
    221 	wssync(selmon);
    222 	selmon->altws = selmon->selws;
    223 	selmon->selws = ws;
    224 	wsload(selmon);
    225 	focus(selmon->stack);
    226 	parrange(selmon);
    227 	pdrawbar(selmon);
    228 }
    229 
    230 void
    231 setlayout(const char *desc)
    232 {
    233 	LytNode *lyt = NULL;
    234 
    235 	if (desc && !(lyt = lytparse(desc))) {
    236 		warn("failed to parse layout `%s'", desc);
    237 		return;
    238 	}
    239 	lytfree(selmon->lyt);
    240 	selmon->lyt = lyt;
    241 	updatelytdesc(selmon);
    242 	parrange(selmon);
    243 	pdrawbar(selmon);
    244 }
    245 
    246 LytNode *
    247 master(LytNode *lyt)
    248 {
    249 	LytNode *n = lyt;
    250 
    251 	if (!n)
    252 		return NULL;
    253 	while (n->fact < 0) {
    254 		if (n->type == '*' || n->p.chain == n)
    255 			return NULL;
    256 		n = n->p.chain;
    257 	}
    258 	return n;
    259 }
    260 
    261 void
    262 incnmaster(int i)
    263 {
    264 	LytNode *p, *n, *nn;
    265 
    266 	if (!(p = master(selmon->lyt)))
    267 		return;
    268 	if (i > 0) {
    269 		n = p->p.chain;
    270 		if (n != p)
    271 			while (!n->ending)
    272 				n = n->next;
    273 		while (i--) {
    274 			if (!(nn = malloc(sizeof(*nn)))) {
    275 				warn("error while adding masters:");
    276 				break;
    277 			}
    278 			nn->type = '*';
    279 			nn->ending = 1;
    280 			nn->trailing = 0;
    281 			nn->fact = -1;
    282 			nn->p.client = NULL;
    283 			nn->next = p;
    284 			if (n == p) {
    285 				n = p->p.chain = nn;
    286 			} else {
    287 				n->ending = 0;
    288 				n = n->next = nn;
    289 			}
    290 		}
    291 	} else {
    292 		while (p->p.chain != p && i++) {
    293 			n = p->p.chain;
    294 			if (n->ending) {
    295 				if (n->type != '*')
    296 					break;
    297 				free(n);
    298 				p->p.chain = p;
    299 			} else {
    300 				while (!n->next->ending)
    301 					n = n->next;
    302 				if (n->next->type != '*')
    303 					break;
    304 				free(n->next);
    305 				n->next = p;
    306 				n->ending = 1;
    307 			}
    308 		}
    309 	}
    310 	updatelytdesc(selmon);
    311 	lyttile(selmon);
    312 	pdrawbar(selmon);
    313 }
    314 
    315 void
    316 incmfact(float f)
    317 {
    318 	LytNode *p;
    319 
    320 	if (!(p = master(selmon->lyt)))
    321 		return;
    322 	f += p->fact;
    323 	p->fact = f < 0 ? 0 : f > 1 ? 1 : f;
    324 	updatelytdesc(selmon);
    325 	lyttile(selmon);
    326 	pdrawbar(selmon);
    327 }
    328 
    329 void
    330 movebymouse(void)
    331 {
    332 	int opx, opy, ocx, ocy;
    333 	int npx, npy, ncx, ncy;
    334 	Client *c = selmon->sel;
    335 	Time lasttime = 0;
    336 	Monitor *m;
    337 	XEvent ev;
    338 
    339 	if (!c || c->isfullscreen)
    340 		return;
    341 	ocx = c->x;
    342 	ocy = c->y;
    343 	if (XGrabPointer(dpy, root, False, MOUSEMASK,
    344 	                 GrabModeAsync, GrabModeAsync,
    345 	                 None, curmove, CurrentTime) != GrabSuccess)
    346 		return;
    347 	if (!getrootptr(&opx, &opy))
    348 		return;
    349 	npx = opx;
    350 	npy = opy;
    351 	do {
    352 		XMaskEvent(dpy, MOUSEMASK|ExposureMask
    353 		                |SubstructureRedirectMask, &ev);
    354 		switch(ev.type) {
    355 		case ConfigureRequest:
    356 		case Expose:
    357 		case MapRequest:
    358 			handler[ev.type](&ev);
    359 			break;
    360 		case MotionNotify:
    361 			if ((ev.xmotion.time - lasttime) <= (1000 / 60))
    362 				continue;
    363 			lasttime = ev.xmotion.time;
    364 			npx = ev.xmotion.x;
    365 			npy = ev.xmotion.y;
    366 			ncx = ocx - opx + npx;
    367 			ncy = ocy - opy + npy;
    368 			if (abs(selmon->x - ncx) < snap)
    369 				ncx = selmon->x;
    370 			else if (abs((selmon->x + selmon->w)
    371 			             - (ncx + WIDTH(c))) < snap)
    372 				ncx = selmon->x + selmon->w - WIDTH(c);
    373 			if (abs(WY(selmon) - ncy) < snap)
    374 				ncy = WY(selmon);
    375 			else if (abs((WY(selmon) + WH(selmon))
    376 			             - (ncy + HEIGHT(c))) < snap)
    377 				ncy = WY(selmon) + WH(selmon) - HEIGHT(c);
    378 			if (!c->isfloating && selmon->lyt
    379 			&& (abs(ncx - c->x) > snap || abs(ncy - c->y) > snap))
    380 				setfloating(c, 1);
    381 			if (!selmon->lyt || c->isfloating) {
    382 				if (c->x == ncx && c->y == ncy)
    383 					break;
    384 				c->x = ncx;
    385 				c->y = ncy;
    386 				configure(c, 0);
    387 				XSync(dpy, False);
    388 			} else if (abs(ncx - c->x) > snap
    389 			        || abs(ncy - c->y) > snap) {
    390 				setfloating(c, 1);
    391 			}
    392 			break;
    393 		}
    394 	} while (ev.type != ButtonRelease);
    395 	XUngrabPointer(dpy, CurrentTime);
    396 	if (!(m = pttomon(c->x + WIDTH(c) / 2, c->y + HEIGHT(c) / 2)))
    397 		m = pttomon(npx, npy);
    398 	if (m && m != selmon) {
    399 		sendtomon(c, m);
    400 		focus(c);
    401 	}
    402 }
    403 
    404 void
    405 resizebymouse(void)
    406 {
    407 	int nw, nh;
    408 	Client *c = selmon->sel;
    409 	Time lasttime = 0;
    410 	Monitor *m;
    411 	XEvent ev;
    412 
    413 	if (!c || c->isfullscreen)
    414 		return;
    415 	if (XGrabPointer(dpy, root, False, MOUSEMASK,
    416 	                 GrabModeAsync, GrabModeAsync,
    417 	                 None, curresize, CurrentTime) != GrabSuccess)
    418 		return;
    419 	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
    420 	             c->w + borderw - 1, c->h + borderw - 1);
    421 	do {
    422 		XMaskEvent(dpy, MOUSEMASK|ExposureMask
    423 		                |SubstructureRedirectMask, &ev);
    424 		switch(ev.type) {
    425 		case ConfigureRequest:
    426 		case Expose:
    427 		case MapRequest:
    428 			handler[ev.type](&ev);
    429 			break;
    430 		case MotionNotify:
    431 			if ((ev.xmotion.time - lasttime) <= (1000 / 60))
    432 				continue;
    433 			lasttime = ev.xmotion.time;
    434 
    435 			nw = MAX(ev.xmotion.x - c->x - 2 * borderw + 1, 1);
    436 			nh = MAX(ev.xmotion.y - c->y - 2 * borderw + 1, 1);
    437 			if (!selmon->lyt || c->isfloating) {
    438 				applysizehints(c, &nw, &nh);
    439 				if (c->w == nw && c->h == nh)
    440 					break;
    441 				c->w = nw;
    442 				c->h = nh;
    443 				configure(c, 1);
    444 				XSync(dpy, False);
    445 			} else if (abs(nw - c->w) > snap
    446 			        || abs(nh - c->h) > snap) {
    447 				setfloating(c, 1);
    448 			}
    449 			break;
    450 		}
    451 	} while (ev.type != ButtonRelease);
    452 	XWarpPointer(dpy, None, c->win, 0, 0, 0, 0,
    453 	             c->w + borderw - 1, c->h + borderw - 1);
    454 	XUngrabPointer(dpy, CurrentTime);
    455 	while (XCheckMaskEvent(dpy, EnterWindowMask, &ev))
    456 		;
    457 	m = pttomon(c->x + WIDTH(c) / 2, c->y + HEIGHT(c) / 2);
    458 	if (m && m != selmon) {
    459 		sendtomon(c, m);
    460 		focus(c);
    461 	}
    462 }
    463 
    464 void
    465 togglescratch(char **cmd)
    466 {
    467 	Client *c, *d;
    468 
    469 	for (c = selmon->stack; c && !c->isscratch; c = c->snext)
    470 		;
    471 	if (!c) {
    472 		if (cmd)
    473 			spawn(cmd);
    474 		return;
    475 	}
    476 	for (d = c; d && (!d->isscratch || !ISVISIBLE(d)); d = d->snext)
    477 		;
    478 	if (d) {
    479 		d->tags &= ~selmon->tagset;
    480 		if (selmon->sel == d)
    481 			focus(selmon->stack);
    482 		if (d != c) {
    483 			detachstack(d);
    484 			attachstack(d);
    485 		}
    486 	} else {
    487 		if (!(c->tags |= selmon->tagset))
    488 			return;
    489 		focus(c);
    490 	}
    491 	parrange(selmon);
    492 	pdrawbar(selmon);
    493 }
    494 
    495 void
    496 cyclescratch(void)
    497 {
    498 	Client *c;
    499 
    500 	if (!selmon->sel || !selmon->sel->isscratch) {
    501 		c = selmon->clients;
    502 		while (c && (!c->isscratch || ISVISIBLE(c)))
    503 			c = c->next;
    504 		if (!c)
    505 			return;
    506 	} else {
    507 		c = selmon->sel;
    508 		do {
    509 			c = c->next ? c->next : selmon->clients;
    510 		} while (c != selmon->sel && (!c->isscratch || ISVISIBLE(c)));
    511 		if (c == selmon->sel)
    512 			return;
    513 		selmon->sel->tags &= ~selmon->tagset;
    514 	}
    515 	if (!(c->tags |= selmon->tagset))
    516 		return;
    517 	focus(c);
    518 	parrange(selmon);
    519 	pdrawbar(selmon);
    520 }
    521 
    522 void
    523 newmenu(void)
    524 {
    525 	int fds[2];
    526 
    527 	if (menufpw)
    528 		fclose(menufpw);
    529 	if (menufpr)
    530 		fclose(menufpr);
    531 	menufpw = menufpr = NULL;
    532 	switch (depart(fds)) {
    533 	case -1:
    534 		warn("failed to start a menu");
    535 		return;
    536 	case 0:
    537 		execmenu();
    538 		return;
    539 	default:
    540 		break;
    541 	}
    542 	if (!(menufpr = fdopen(fds[0], "r"))
    543 	 || !(menufpw = fdopen(fds[1], "w"))) {
    544 		warn("failed to start a menu: fdopen:");
    545 		if (menufpr)
    546 			fclose(menufpr);
    547 		else
    548 			close(fds[0]);
    549 		close(fds[1]);
    550 		return;
    551 	}
    552 }
    553 
    554 void
    555 menuput(const char *fmt, ...)
    556 {
    557 	va_list ap;
    558 
    559 	if (!menufpw)
    560 		return;
    561 	va_start(ap, fmt);
    562 	vfprintf(menufpw, fmt, ap);
    563 	fputc('\n', menufpw);
    564 	va_end(ap);
    565 }
    566 
    567 char *
    568 menuget(void)
    569 {
    570 	static char *line = NULL;
    571 	static size_t n;
    572 	ssize_t len;
    573 
    574 	if (menufpw) {
    575 		fclose(menufpw);
    576 		menufpw = NULL;
    577 	}
    578 	if ((len = getline(&line, &n, menufpr)) != -1) {
    579 		if (len && line[len - 1] == '\n')
    580 			line[len - 1] = '\0';
    581 		return line;
    582 	}
    583 	fclose(menufpr);
    584 	menufpr = NULL;
    585 	free(line);
    586 	line = NULL;
    587 	n = 0;
    588 	return NULL;
    589 }