swm

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

misc.c (11237B)


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