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 }