layout.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. /*
  2. * wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
  3. * For license, see COPYING.
  4. */
  5. #include <X11/Xutil.h>
  6. #include "layout.h"
  7. #include "config.h"
  8. #include "client.h"
  9. #include "draw.h"
  10. #include "event.h"
  11. #include "util.h"
  12. /* Shift in client split to keep clients's parent at close arrange */
  13. static int shiftv = 1, shifth = 1;
  14. void
  15. layout_save_set(struct tag *t)
  16. {
  17. struct client *c;
  18. struct layout_set *l;
  19. struct geo_list *g, *gp;
  20. int n = 1;
  21. l = xcalloc(1, sizeof(struct layout_set));
  22. SLIST_INIT(&l->geos);
  23. FOREACH_NFCLIENT(c, &t->clients, tnext)
  24. {
  25. g = xcalloc(1, sizeof(struct geo_list));
  26. g->geo = c->geo;
  27. if(!SLIST_FIRST(&l->geos))
  28. SLIST_INSERT_HEAD(&l->geos, g, next);
  29. else
  30. SLIST_INSERT_AFTER(gp, g, next);
  31. ++n;
  32. gp = g;
  33. }
  34. l->n = n;
  35. TAILQ_INSERT_TAIL(&t->sets, l, next);
  36. }
  37. static void
  38. layout_apply_set(struct tag *t, struct layout_set *l)
  39. {
  40. struct geo_list *g;
  41. struct client *c;
  42. int nc = 1;
  43. FOREACH_NFCLIENT(c, &t->clients, tnext)
  44. ++nc;
  45. /* TODO: Adapt different client number case */
  46. if(l->n != nc)
  47. return;
  48. for(g = SLIST_FIRST(&l->geos), c = SLIST_FIRST(&t->clients);
  49. c; c = SLIST_NEXT(c, tnext))
  50. {
  51. if(g)
  52. {
  53. client_moveresize(c, &g->geo);
  54. g = SLIST_NEXT(g, next);
  55. }
  56. /* TODO
  57. * Not enough geos in the set;
  58. * then integrate remains of client
  59. *
  60. else
  61. layout_split_integrate(c, SLIST_FIRST(&t->clients));
  62. */
  63. }
  64. /* TODO
  65. * Not enough clients for geos in set;
  66. * arrange clients with not set geo.
  67. *
  68. if((g = SLIST_NEXT(g, next)))
  69. for(cc.tag = t; g; g = SLIST_NEXT(g, next))
  70. {
  71. cc.geo = g->geo;
  72. layout_split_arrange_closed(&cc);
  73. }
  74. */
  75. /* Re-insert set in historic */
  76. layout_save_set(t);
  77. }
  78. void
  79. layout_free_set(struct tag *t)
  80. {
  81. struct layout_set *l;
  82. while(!TAILQ_EMPTY(&t->sets))
  83. {
  84. l = TAILQ_FIRST(&t->sets);
  85. TAILQ_REMOVE(&t->sets, l, next);
  86. FREE_LIST(geo_list, l->geos);
  87. free(l);
  88. }
  89. }
  90. #define _REV_BORDER() \
  91. SLIST_FOREACH(g, &l->geos, next) { \
  92. cd.geo = g->geo; \
  93. draw_reversed_rect(W->root, &cd, false); \
  94. }
  95. static void
  96. _historic_set(struct tag *t, bool prev)
  97. {
  98. struct keybind *k;
  99. struct layout_set *l;
  100. struct geo_list *g;
  101. struct client cd = { .screen = t->screen, .theme = THEME_DEFAULT };
  102. bool b = true;
  103. XEvent ev;
  104. KeySym keysym;
  105. if(TAILQ_EMPTY(&t->sets))
  106. return;
  107. l = TAILQ_LAST(&t->sets, ssub);
  108. if(prev)
  109. l = TAILQ_PREV(l, ssub, next);
  110. if(!l)
  111. return;
  112. /* TODO
  113. if(option_simple_manual_resize)
  114. {
  115. layout_set_apply(l);
  116. return;
  117. */
  118. XGrabKeyboard(W->dpy, W->root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
  119. _REV_BORDER();
  120. do
  121. {
  122. XMaskEvent(W->dpy, KeyPressMask, &ev);
  123. if(ev.type == KeyPress)
  124. {
  125. XKeyPressedEvent *ke = &ev.xkey;
  126. keysym = XKeycodeToKeysym(W->dpy, (KeyCode)ke->keycode, 0);
  127. _REV_BORDER();
  128. SLIST_FOREACH(k, &W->h.keybind, next)
  129. if(k->keysym == keysym && KEYPRESS_MASK(k->mod) == KEYPRESS_MASK(ke->state)
  130. && k->func)
  131. {
  132. if(k->func == uicb_layout_prev_set)
  133. {
  134. if(!(l = TAILQ_PREV(l, ssub, next)))
  135. l = TAILQ_LAST(&t->sets, ssub);
  136. }
  137. else if(k->func == uicb_layout_next_set)
  138. {
  139. if(!(l = TAILQ_NEXT(l, next)))
  140. l = TAILQ_FIRST(&t->sets);
  141. }
  142. else
  143. {
  144. k->func(k->cmd);
  145. keysym = XK_Escape;
  146. }
  147. }
  148. if(!l)
  149. l = TAILQ_LAST(&t->sets, ssub);
  150. _REV_BORDER();
  151. /* Gtfo of this loop */
  152. if(keysym == XK_Return)
  153. break;
  154. else if(keysym == XK_Escape)
  155. {
  156. b = false;
  157. break;
  158. }
  159. XSync(W->dpy, False);
  160. }
  161. XNextEvent(W->dpy, &ev);
  162. } while(ev.type != KeyPress);
  163. _REV_BORDER();
  164. if(b)
  165. layout_apply_set(t, l);
  166. XUngrabServer(W->dpy);
  167. XUngrabKeyboard(W->dpy, CurrentTime);
  168. }
  169. void
  170. uicb_layout_prev_set(Uicb cmd)
  171. {
  172. (void)cmd;
  173. _historic_set(W->screen->seltag, true);
  174. }
  175. void
  176. uicb_layout_next_set(Uicb cmd)
  177. {
  178. (void)cmd;
  179. _historic_set(W->screen->seltag, false);
  180. }
  181. static struct geo
  182. layout_split(struct client *c, bool vertical)
  183. {
  184. struct geo og, geo;
  185. geo = og = c->geo;
  186. if(vertical)
  187. {
  188. c->geo.w >>= 1;
  189. c->geo.w += shiftv;
  190. geo.x = c->geo.x + c->geo.w;
  191. geo.w >>= 1;
  192. /* Remainder */
  193. geo.w += (og.x + og.w) - (geo.x + geo.w);
  194. shiftv = -shiftv;
  195. }
  196. else
  197. {
  198. c->geo.h >>= 1;
  199. c->geo.h += shifth;
  200. geo.y = c->geo.y + c->geo.h;
  201. geo.h >>= 1;
  202. /* Remainder */
  203. geo.h += (og.y + og.h) - (geo.y + geo.h);
  204. shifth = -shifth;
  205. }
  206. return geo;
  207. }
  208. static inline void
  209. layout_split_arrange_size(struct geo *g, struct client *c, enum position p)
  210. {
  211. if(LDIR(p))
  212. {
  213. c->geo.w += g->w;
  214. if(p == Right)
  215. c->geo.x = g->x;
  216. }
  217. else
  218. {
  219. c->geo.h += g->h;
  220. if(p == Bottom)
  221. c->geo.y = g->y;
  222. }
  223. client_moveresize(c, &c->geo);
  224. }
  225. static inline bool
  226. layout_split_check_row_dir(struct client *c, struct client *g, enum position p)
  227. {
  228. struct geo cgeo = c->geo;
  229. struct client *cc;
  230. int s = 0, cs = (LDIR(p) ? g->geo.h : g->geo.w);
  231. FOREACH_NFCLIENT(cc, &c->tag->clients, tnext)
  232. if(GEO_PARENTROW(cgeo, cc->geo, RPOS(p))
  233. && GEO_CHECK_ROW(cc->geo, g->geo, p))
  234. {
  235. s += (LDIR(p) ? cc->geo.h : cc->geo.w);
  236. if(s == cs)
  237. return true;
  238. if(s > cs)
  239. return false;
  240. }
  241. return false;
  242. }
  243. /* Use ghost client properties to fix holes in tile
  244. *
  245. * ~ .--. ~ ~
  246. *_____ ~ /xx \ ~ ~
  247. * |>>| ~\O _ (____ ~
  248. * | |__.| .--'-==~ ~
  249. * |>>'---\ '. ~ , ~
  250. *__|__| '. '-.___.-'/ ~
  251. * '-.__ _.' ~
  252. * ````` ~
  253. */
  254. #define _ARRANGE_SINGLE_PARENT(p) \
  255. do { \
  256. if((c = client_next_with_pos(ghost, p))) \
  257. if(GEO_CHECK2(ghost->geo, c->geo, p)) \
  258. { \
  259. layout_split_arrange_size(&ghost->geo, c, p); \
  260. layout_save_set(ghost->tag); \
  261. return; \
  262. } \
  263. } while(/* CONSTCOND */ 0);
  264. void
  265. layout_split_arrange_closed(struct client *ghost)
  266. {
  267. struct client *c, *cc;
  268. struct geo g;
  269. bool b = false;
  270. enum position p;
  271. if(!(ghost->flags & CLIENT_TILED))
  272. return;
  273. /* Search for single parent for easy resize
  274. * Example case:
  275. * ___________ ___________
  276. * | | B | -> -> | | |
  277. * | A |_____| -> Close -> | A | B |
  278. * | | C | -> C -> | |v v v|
  279. * |_____|_____| -> -> |_____|_____|
  280. */
  281. _ARRANGE_SINGLE_PARENT(Right);
  282. _ARRANGE_SINGLE_PARENT(Left);
  283. _ARRANGE_SINGLE_PARENT(Top);
  284. _ARRANGE_SINGLE_PARENT(Bottom);
  285. /* Check row parents for full resize
  286. * Example case:
  287. * ___________ ___________
  288. * | | B | -> -> | << B |
  289. * | A |_____| -> Close -> |___________|
  290. * | | C | -> A -> | << C |
  291. * |_____|_____| -> -> |___________|
  292. */
  293. for(p = Right; p < Center && !b; ++p)
  294. {
  295. if((c = client_next_with_pos(ghost, p))
  296. && layout_split_check_row_dir(c, ghost, p))
  297. {
  298. g = c->geo;
  299. FOREACH_NFCLIENT(cc, &c->tag->clients, tnext)
  300. if(GEO_PARENTROW(g, cc->geo, RPOS(p))
  301. && GEO_CHECK_ROW(cc->geo, ghost->geo, p))
  302. {
  303. layout_split_arrange_size(&ghost->geo, cc, p);
  304. b = true;
  305. }
  306. }
  307. }
  308. layout_save_set(ghost->tag);
  309. }
  310. /*
  311. * Integrate a client in split layout:
  312. * - Check if there is no sc
  313. * - Check if sc is on a different tag than c
  314. * - Check if sc is not in free mode
  315. *
  316. * So from there, sc is not compatible, so we will integrate
  317. * c in the larger tiled client of c tag:
  318. * - Check if the larger client is correct
  319. *
  320. * Checks all failed? Get first tiled client of the tag to integrate in.
  321. * Still no client, it means that c is the first tiled client of the tag, then maximize it.
  322. */
  323. void
  324. layout_split_integrate(struct client *c, struct client *sc)
  325. {
  326. struct geo g;
  327. bool f = false;
  328. /* No sc or not compatible, get the largest of the tag */
  329. if(!sc
  330. || sc == c
  331. || sc->tag != c->tag
  332. || (sc->flags & CLIENT_FREE)
  333. || !COMPCLIENT(c, sc))
  334. sc = client_get_larger(c->tag, c->flags & CLIENT_IGNORE_TAG);
  335. /* Largest not correct */
  336. if(!sc || sc == c)
  337. {
  338. FOREACH_NFCLIENT(sc, &c->tag->clients, tnext)
  339. if(sc != c && !(sc->flags & CLIENT_TABBED))
  340. {
  341. f = true;
  342. break;
  343. }
  344. /* Ok there is no client to integrate in */
  345. if(!f)
  346. {
  347. client_maximize(c);
  348. c->flags |= CLIENT_TILED;
  349. W->flags &= ~WMFS_TABNOC;
  350. return;
  351. }
  352. }
  353. /* Tab Next Opened Client option */
  354. if(W->flags & WMFS_TABNOC && COMPCLIENT(c, sc))
  355. {
  356. W->flags ^= WMFS_TABNOC;
  357. _client_tab(c, sc);
  358. return;
  359. }
  360. /* If there are clients but we can tab with them, split the screen. */
  361. c->flags |= CLIENT_TILED;
  362. g = layout_split(sc, (sc->geo.h < sc->geo.w));
  363. client_moveresize(c, &g);
  364. client_moveresize(sc, &sc->geo);
  365. client_fac_hint(c);
  366. client_fac_hint(sc);
  367. layout_save_set(c->tag);
  368. W->flags &= ~WMFS_TABNOC;
  369. }
  370. /* Arrange inter-clients holes:
  371. * ___________ ___________
  372. * | || | -> | | |
  373. * | A || B | -> | A >| B |
  374. * | || | -> | >| |
  375. * |_____||____| -> |______|____|
  376. * ^ void
  377. *
  378. * and client-screen edge holes
  379. * ___________ ___________
  380. * | | || -> | | |
  381. * | A | B || -> | A | B >|
  382. * | | || -> | | >|
  383. * |_____|----'| -> |_____|__v__|
  384. * ^^^ void
  385. */
  386. void
  387. layout_fix_hole(struct client *c)
  388. {
  389. struct client *cr = client_next_with_pos(c, Right);
  390. struct client *cb = client_next_with_pos(c, Bottom);
  391. c->geo.w += (cr ? cr->geo.x : c->screen->ugeo.w) - (c->geo.x + c->geo.w);
  392. c->geo.h += (cb ? cb->geo.y : c->screen->ugeo.h) - (c->geo.y + c->geo.h);
  393. client_moveresize(c, &c->geo);
  394. }
  395. /* Layout rotation: Rotate 90° all client to right or left.
  396. * Avoid if(left) condition in layout_rotate loop; use func ptr
  397. *
  398. * Right rotation
  399. * ____________ ____________
  400. * | | B | -> | | A |
  401. * | A |_______| -> |__|_________|
  402. * |____| C | D | -> |_____| B |
  403. * |____|___|___| -> |_____|______|
  404. *
  405. * Left rotation
  406. * ____________ ____________
  407. * | | B | -> | B |_____|
  408. * | A |_______| -> |______|_____|
  409. * |____| C | D | -> | A | |
  410. * |____|___|___| -> |_________|__|
  411. *
  412. */
  413. static inline void
  414. _pos_rotate_left(struct geo *g, struct geo *ug, struct geo *og)
  415. {
  416. g->x = (ug->h - (og->y + og->h));
  417. g->y = og->x;
  418. }
  419. static inline void
  420. _pos_rotate_right(struct geo *g, struct geo *ug, struct geo *og)
  421. {
  422. g->x = og->y;
  423. g->y = (ug->w - (og->x + og->w));
  424. }
  425. static void
  426. layout_rotate(struct tag *t, void (*pfunc)(struct geo*, struct geo*, struct geo*))
  427. {
  428. struct client *c;
  429. struct geo g, *ug = &t->screen->ugeo;
  430. float f1 = (float)t->screen->ugeo.w / (float)t->screen->ugeo.h;
  431. float f2 = 1 / f1;
  432. FOREACH_NFCLIENT(c, &t->clients, tnext)
  433. {
  434. pfunc(&g, ug, &c->geo);
  435. g.x *= f1;
  436. g.y *= f2;
  437. g.w = c->geo.h * f1;
  438. g.h = c->geo.w * f2;
  439. client_moveresize(c, &g);
  440. }
  441. /* Rotate sometimes do not set back perfect size.. */
  442. FOREACH_NFCLIENT(c, &t->clients, tnext)
  443. layout_fix_hole(c);
  444. layout_save_set(t);
  445. }
  446. void
  447. uicb_layout_rotate_left(Uicb cmd)
  448. {
  449. (void)cmd;
  450. layout_rotate(W->screen->seltag, _pos_rotate_left);
  451. }
  452. void
  453. uicb_layout_rotate_right(Uicb cmd)
  454. {
  455. (void)cmd;
  456. layout_rotate(W->screen->seltag, _pos_rotate_right);
  457. }
  458. /*
  459. * Really simple functions, don't need static no-uicb backend
  460. * so we avoid the use of if(vertical) .. else
  461. *
  462. * Vertical mirror
  463. * ____________ ____________
  464. * | | B | -> | B | |
  465. * | A |_______| -> |_______| A |
  466. * | | C | D | -> | D | C | |
  467. * |____|___|___| -> |___|___|____|
  468. *
  469. * Horizontal mirror
  470. * ____________ ____________
  471. * | | B | -> | | C | D |
  472. * | A |_______| -> | A |___|___|
  473. * | | C | D | -> | | B |
  474. * |____|___|___| -> |____|_______|
  475. */
  476. void
  477. uicb_layout_vmirror(Uicb cmd)
  478. {
  479. (void)cmd;
  480. struct client *c;
  481. FOREACH_NFCLIENT(c, &W->screen->seltag->clients, tnext)
  482. {
  483. c->geo.x = W->screen->ugeo.w - (c->geo.x + c->geo.w);
  484. client_moveresize(c, &c->geo);
  485. }
  486. layout_save_set(W->screen->seltag);
  487. }
  488. void
  489. uicb_layout_hmirror(Uicb cmd)
  490. {
  491. (void)cmd;
  492. struct client *c;
  493. FOREACH_NFCLIENT(c, &W->screen->seltag->clients, tnext)
  494. {
  495. c->geo.y = W->screen->ugeo.h - (c->geo.y + c->geo.h);
  496. client_moveresize(c, &c->geo);
  497. }
  498. layout_save_set(W->screen->seltag);
  499. }
  500. #define LAYOUT_INTEGRATE_DIR(D) \
  501. void uicb_layout_integrate_##D(Uicb cmd) \
  502. { \
  503. (void)cmd; \
  504. if(W->client) \
  505. layout_integrate(W->client, D); \
  506. }
  507. static void
  508. layout_integrate(struct client *c, enum position p)
  509. {
  510. struct client *n;
  511. struct client ghost = *c;
  512. if(!(c->flags & CLIENT_TILED))
  513. return;
  514. if((n = client_next_with_pos(c, p))
  515. && (n->flags & CLIENT_TILED))
  516. {
  517. layout_split_integrate(c, n);
  518. layout_split_arrange_closed(&ghost);
  519. }
  520. }
  521. LAYOUT_INTEGRATE_DIR(Left);
  522. LAYOUT_INTEGRATE_DIR(Right);
  523. LAYOUT_INTEGRATE_DIR(Top);
  524. LAYOUT_INTEGRATE_DIR(Bottom);
  525. void
  526. layout_client(struct client *c)
  527. {
  528. if(c->flags & (CLIENT_IGNORE_LAYOUT | CLIENT_FULLSCREEN))
  529. {
  530. c->flags &= ~CLIENT_IGNORE_LAYOUT;
  531. return;
  532. }
  533. if(c->flags & (CLIENT_FREE | CLIENT_STICKY))
  534. {
  535. layout_split_arrange_closed(c);
  536. c->flags ^= CLIENT_TILED;
  537. client_moveresize(c, &c->geo);
  538. XRaiseWindow(W->dpy, c->frame);
  539. }
  540. else if(!(c->flags & CLIENT_TABBED))
  541. layout_split_integrate(c, c->tag->sel);
  542. }