layout.c 16 KB

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