swm

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

lyt.c (9354B)


      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 <errno.h>
     13 #include <stdio.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 #include <X11/Xlib.h>
     17 
     18 #include "swm.h"
     19 #include "util.h"
     20 
     21 enum { W, H, X, Y };
     22 
     23 static LytNode *parse(const char **ps, LytNode *parent, int rev);
     24 static Client *nexttiled(Client *c);
     25 static void makeplans(Monitor *m, LytNode *p, int area[2]);
     26 static void tile(Monitor *m, LytNode *p, int area[4]);
     27 static void getdesc(LytNode *n, int rev, char **ps, size_t *psize);
     28 
     29 LytNode *
     30 parse(const char **ps, LytNode *parent, int rev)
     31 {
     32 	LytNode *lyt = NULL, *n = NULL;
     33 	LytNode **end = &lyt;
     34 	const char *s = *ps, *num;
     35 	char b, d;
     36 	int rev2;
     37 
     38 	s += strspn(s, " \t");
     39 	while (*s && !strchr("}])", *s)) {
     40 		if (!(n = malloc(sizeof(*n)))) {
     41 			warn("malloc:");
     42 			goto fail;
     43 		}
     44 		n->type = '*';
     45 		n->trailing = 0;
     46 		n->fact = -1;
     47 		n->next = *end;
     48 		*end = n;
     49 		num = NULL;
     50 		rev2 = 0;
     51 		switch (*s) {
     52 		case '*':
     53 			++s;
     54 			break;
     55 		case '-':
     56 			rev2 = 1;
     57 			/* FALLTHROUGH */
     58 		case '+':
     59 			if (!strchr("{[(", s[1])) {
     60 				num = s;
     61 				break;
     62 			}
     63 			++s;
     64 			/* FALLTHROUGH */
     65 		case '{':
     66 		case '[':
     67 		case '(':
     68 			b = *s++;
     69 			d = strchr("{}[]()", b)[1];
     70 			n->type = rev2 ? d : b;
     71 			if (!(n->p.chain = parse(&s, n, rev2)))
     72 				n->p.chain = n;
     73 			if (*s++ != d) {
     74 				warn("`%c' mismatched in layout description", b);
     75 				goto fail;
     76 			}
     77 			s += strspn(s, " \t");
     78 			if (*s == ':')
     79 				num = s + 1;
     80 			break;
     81 		default:
     82 			num = s;
     83 			break;
     84 		}
     85 		if (num) {
     86 			errno = 0;
     87 			n->fact = strtof(num, (char **)&s);
     88 			if (errno || n->fact < 0 || n->fact > 1) {
     89 				if (errno) {
     90 					s = num + 1;
     91 					s += strcspn(s, " \t*+-{}[]()");
     92 				}
     93 				warn("invalid value `%.*s' in layout description",
     94 				     (int)(s - num), num);
     95 				goto fail;
     96 			}
     97 		}
     98 		s += strspn(s, " \t");
     99 		if (!rev)
    100 			end = &n->next;
    101 	}
    102 	if (lyt) {
    103 		for (n = lyt; n->next; n = n->next)
    104 			n->ending = n->loaded = 0;
    105 		n->next = parent;
    106 		n->ending = 1;
    107 	}
    108 	*ps = s;
    109 	return lyt;
    110 fail:
    111 	lytfree(lyt);
    112 	return NULL;
    113 }
    114 
    115 LytNode *
    116 lytparse(const char *s)
    117 {
    118 	LytNode *r = parse(&s, NULL, 0);
    119 
    120 	if (*s) {
    121 		warn("trailing characters `%s' in layout description", s);
    122 		lytfree(r);
    123 		r = NULL;
    124 	}
    125 	return r;
    126 }
    127 
    128 void
    129 lytfree(LytNode *lyt)
    130 {
    131 	LytNode *n = lyt, *nx;
    132 
    133 	if (!n)
    134 		return;
    135 	do {
    136 		if (n->type != '*' && n->p.chain != n)
    137 			lytfree(n->p.chain);
    138 		nx = n->ending ? NULL : n->next;
    139 		free(n);
    140 		n = nx;
    141 	} while (n);
    142 }
    143 
    144 Client *
    145 nexttiled(Client *c)
    146 {
    147 	for (; c; c = c->next)
    148 		if (ISVISIBLE(c) && !c->isfullscreen && !c->isfloating)
    149 			return c;
    150 	return NULL;
    151 }
    152 
    153 void
    154 makeplans(Monitor *m, LytNode *p, int area[2])
    155 {
    156 	int w, h, u, v, t;
    157 	LytNode *n, *o;
    158 	Client *c;
    159 	int rect[2];
    160 
    161 	switch (p->type) {
    162 	case '*':
    163 		if (!RESIZEHINTS)
    164 			return;
    165 		c = p->p.client;
    166 		u = 2 * borderw;
    167 		v = 2 * borderw;
    168 		if (!m->nogaps) {
    169 			u += m->gapih;
    170 			v += m->gapiv;
    171 		}
    172 		w = c->maxw;
    173 		h = c->maxh;
    174 		c->maxw = area[W] - u;
    175 		c->maxh = area[H] - v;
    176 		if (!m->nogaps) {
    177 			c->maxw += MIN(m->gapih, m->gapoh);
    178 			c->maxh += MIN(m->gapiv, m->gapov);
    179 		}
    180 		if (w && w < c->maxw)
    181 			c->maxw = w;
    182 		if (h && h < c->maxh)
    183 			c->maxh = h;
    184 		p->plan[W] -= u;
    185 		p->plan[H] -= v;
    186 		if (p->plan[W] > 0 && p->plan[H] > 0)
    187 			applysizehints(c, &p->plan[W], &p->plan[H]);
    188 		p->plan[W] += u;
    189 		p->plan[H] += v;
    190 		c->maxw = w;
    191 		c->maxh = h;
    192 		return;
    193 	case '{':
    194 	case '}':
    195 		w = W, h = H;
    196 		break;
    197 	case '[':
    198 	case ']':
    199 		w = H, h = W;
    200 		break;
    201 	default:
    202 		w = h = 0;
    203 		for (n = p->p.chain; n != p && n->loaded; n = n->next) {
    204 			if (n->fact < 0) {
    205 				n->plan[W] = p->plan[W];
    206 				n->plan[H] = p->plan[H];
    207 			} else {
    208 				n->plan[W] = p->plan[W] * n->fact;
    209 				n->plan[H] = p->plan[H] * n->fact;
    210 			}
    211 			makeplans(m, n, area);
    212 			if (w < n->plan[W])
    213 				w = n->plan[W];
    214 			if (h < n->plan[H])
    215 				h = n->plan[H];
    216 		}
    217 		p->plan[W] = w;
    218 		p->plan[H] = h;
    219 		return;
    220 	}
    221 	u = p->plan[w];
    222 	v = 0;
    223 	for (n = p->p.chain; n != p && n->loaded; n = n->next) {
    224 		if (n->fact < 0) {
    225 			++v;
    226 		} else {
    227 			n->plan[w] = p->plan[w] * n->fact;
    228 			if (n->plan[w] > u)
    229 				n->plan[w] = u;
    230 			u -= n->plan[w];
    231 		}
    232 		n->plan[h] = p->plan[h];
    233 	}
    234 	if (v) {
    235 		for (n = p->p.chain; v; n = n->next) {
    236 			if (n->fact < 0) {
    237 				u -= n->plan[w] = (u + v - 1) / v;
    238 				--v;
    239 			}
    240 		}
    241 	} else {
    242 		p->p.chain->plan[w] += u;
    243 	}
    244 	p->plan[w] = p->plan[h] = 0;
    245 	for (n = p->p.chain; n != p && n->loaded; n = n->next) {
    246 		rect[w] = area[w] - p->plan[w];
    247 		rect[h] = area[h];
    248 		t = n->plan[w];
    249 		makeplans(m, n, rect);
    250 		p->plan[w] += n->plan[w];
    251 		if ((u = t - n->plan[w])) {
    252 			v = 0;
    253 			for (o = n->next; o != p && o->loaded; o = o->next)
    254 				v += o->plan[w];
    255 			for (o = n->next; v > 0; o = o->next) {
    256 				t = u < 0 ? (u * o->plan[w] - v + 1) / v
    257 				          : (u * o->plan[w] + v - 1) / v;
    258 				u -= t;
    259 				v -= o->plan[w];
    260 				if ((o->plan[w] += t) < 0)
    261 					o->plan[w] = 0;
    262 			}
    263 		}
    264 		if (p->plan[h] < n->plan[h])
    265 			p->plan[h] = n->plan[h];
    266 	}
    267 }
    268 
    269 void
    270 tile(Monitor *m, LytNode *p, int area[4])
    271 {
    272 	int x, y, w, h, t, rev;
    273 	int gih, giv;
    274 	LytNode *n;
    275 	Client *c;
    276 	int rect[4];
    277 
    278 	if (m->nogaps) {
    279 		gih = giv = 0;
    280 	} else {
    281 		gih = m->gapih;
    282 		giv = m->gapiv;
    283 	}
    284 	switch (p->type) {
    285 	case '*':
    286 		c = p->p.client;
    287 		w = p->plan[W];
    288 		h = p->plan[H];
    289 		x = area[X] + (area[W] - w) / 2;
    290 		y = area[Y] + (area[H] - h) / 2;
    291 		w -= 2 * borderw + gih;
    292 		h -= 2 * borderw + giv;
    293 		x += gih / 2;
    294 		y += giv / 2;
    295 		if (w <= 0)
    296 			w = 1;
    297 		if (h <= 0)
    298 			h = 1;
    299 		if ((t = c->w != w || c->h != h)) {
    300 			c->w = w;
    301 			c->h = h;
    302 		}
    303 		if (t || c->x != x || c->y != y) {
    304 			c->x = x;
    305 			c->y = y;
    306 			configure(c, t);
    307 		}
    308 		return;
    309 	case '{':
    310 	case '}':
    311 		w = W, h = H;
    312 		x = X, y = Y;
    313 		rev = p->type == '}';
    314 		break;
    315 	case '[':
    316 	case ']':
    317 		w = H, h = W;
    318 		x = Y, y = X;
    319 		rev = p->type == ']';
    320 		break;
    321 	default:
    322 		for (n = p->p.chain; n != p && n->loaded; n = n->next)
    323 			tile(m, n, area);
    324 		return;
    325 	}
    326 	t = (area[w] - p->plan[w]) / 2;
    327 	rect[x] = rev ? area[x] + area[w] - t : area[x] + t;
    328 	rect[y] = area[y];
    329 	rect[h] = area[h];
    330 	for (n = p->p.chain; n != p && n->loaded; n = n->next) {
    331 		rect[w] = n->plan[w];
    332 		if (rev)
    333 			rect[x] -= rect[w];
    334 		tile(m, n, rect);
    335 		if (!rev)
    336 			rect[x] += rect[w];
    337 	}
    338 }
    339 
    340 void
    341 lyttile(Monitor *m)
    342 {
    343 	int u, v;
    344 	int area[4];
    345 	LytNode *n, *t;
    346 	Client *c;
    347 	static LytNode r = {
    348 		.type = '{',
    349 		.ending = 1,
    350 		.trailing = 0,
    351 		.fact = -1,
    352 		.next = NULL,
    353 	};
    354 
    355 	if (!(n = m->lyt) || !(c = nexttiled(m->clients)))
    356 		return;
    357 	do {
    358 		n->loaded = !!c;
    359 		t = n;
    360 		if (n->type != '*' && n->p.chain != n) {
    361 			n = n->p.chain;
    362 		} else {
    363 			if (n->type == '*') {
    364 				n->p.client = c;
    365 				if (c)
    366 					c = nexttiled(c->next);
    367 			}
    368 			while (n->ending && (n = n->next))
    369 				;
    370 			if (!n)
    371 				break;
    372 			n = n->next;
    373 		}
    374 	} while (n);
    375 	if (c) {
    376 		do {
    377 			if (!(n = malloc(sizeof(*n)))) {
    378 				warn("some windows won't be tiled:");
    379 				break;
    380 			}
    381 			n->type = '*';
    382 			n->ending = 1;
    383 			n->loaded = 1;
    384 			n->trailing = 1;
    385 			n->fact = -1;
    386 			n->p.client = c;
    387 			if (t->type == '*') {
    388 				n->next = t->next;
    389 				t->ending = 0;
    390 				t->next = n;
    391 			} else {
    392 				n->next = t;
    393 				t->p.chain = n;
    394 			}
    395 			t = n;
    396 		} while ((c = nexttiled(c->next)));
    397 	}
    398 	if (m->nogaps) {
    399 		u = v = 0;
    400 	} else {
    401 		u = 2 * m->gapoh - m->gapih;
    402 		v = 2 * m->gapov - m->gapiv;
    403 	}
    404 	if (m->lyt->type != '*' && m->lyt->ending) {
    405 		n = m->lyt;
    406 	} else {
    407 		n = &r;
    408 		n->p.chain = m->lyt;
    409 		for (t = m->lyt; !t->ending; t = t->next)
    410 			;
    411 		t->next = n;
    412 	}
    413 	n->plan[W] = area[W] = m->w  - u;
    414 	n->plan[H] = area[H] = WH(m) - v;
    415 	area[X] = m->x  + u / 2;
    416 	area[Y] = WY(m) + v / 2;
    417 	makeplans(m, n, area);
    418 	tile(m, n, area);
    419 	if (n == &r) {
    420 		n->p.chain = NULL;
    421 		for (t = m->lyt; !t->ending; t = t->next)
    422 			;
    423 		t->next = NULL;
    424 	}
    425 }
    426 
    427 void
    428 getdesc(LytNode *n, int rev, char **ps, size_t *psize)
    429 {
    430 	char b, d;
    431 	int t;
    432 
    433 	if (rev && !n->ending && !n->next->trailing) {
    434 		getdesc(n->next, 1, ps, psize);
    435 		if (*psize < 1)
    436 			return;
    437 		*(*ps)++ = ' ';
    438 		--*psize;
    439 	}
    440 	if (n->type != '*') {
    441 		if (strchr("{}", n->type))
    442 			b = '{', d = '}';
    443 		else if (strchr("[]", n->type))
    444 			b = '[', d = ']';
    445 		else
    446 			b = '(', d = ')';
    447 		if (n->type == d) {
    448 			if (*psize < 1)
    449 				return;
    450 			*(*ps)++ = '-';
    451 			++*psize;
    452 		}
    453 		if (*psize < 1)
    454 			return;
    455 		*(*ps)++ = b;
    456 		--*psize;
    457 		if (n->p.chain != n && !n->p.chain->trailing)
    458 			getdesc(n->p.chain, n->type == d, ps, psize);
    459 		if (*psize < 1)
    460 			return;
    461 		*(*ps)++ = d;
    462 		--*psize;
    463 		if (n->fact >= 0) {
    464 			if (*psize < 1)
    465 				return;
    466 			*(*ps)++ = ':';
    467 			--*psize;
    468 		}
    469 	} else if (n->fact < 0) {
    470 		if (*psize < 1)
    471 			return;
    472 		*(*ps)++ = '*';
    473 		--*psize;
    474 	}
    475 	if (n->fact >= 0) {
    476 		if (*psize < 3)
    477 			return;
    478 		snprintf(*ps, *psize, ".%d", (int)(n->fact * 100 + .5));
    479 		*ps += (t = strlen(*ps));
    480 		*psize -= t;
    481 	}
    482 	if (!rev && !n->ending && !n->next->trailing) {
    483 		if (*psize < 1)
    484 			return;
    485 		*(*ps)++ = ' ';
    486 		--*psize;
    487 		getdesc(n->next, 0, ps, psize);
    488 	}
    489 }
    490 
    491 void
    492 updatelytdesc(Monitor *m)
    493 {
    494 	char *buf = m->lytdesc;
    495 	size_t size = sizeof(m->lytdesc);
    496 
    497 	if (!m->lyt) {
    498 		*buf = '\0';
    499 		return;
    500 	}
    501 	getdesc(m->lyt, 0, &buf, &size);
    502 	if (!size)
    503 		--buf;
    504 	*buf = '\0';
    505 }