swm

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

lyt.c (9164B)


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