layout.c 15 KB

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