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 }