layout.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  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. /* Tab Next Opened Client */
  332. if(W->flags & WMFS_TABNOC)
  333. {
  334. W->flags ^= WMFS_TABNOC;
  335. _client_tab(c, sc);
  336. return;
  337. }
  338. c->flags |= CLIENT_TILED;
  339. g = layout_split(sc, (sc->geo.h < sc->geo.w));
  340. client_moveresize(c, &g);
  341. client_moveresize(sc, &sc->geo);
  342. client_fac_hint(c);
  343. client_fac_hint(sc);
  344. layout_save_set(c->tag);
  345. }
  346. /* Arrange inter-clients holes:
  347. * ___________ ___________
  348. * | || | -> | | |
  349. * | A || B | -> | A >| B |
  350. * | || | -> | >| |
  351. * |_____||____| -> |______|____|
  352. * ^ void
  353. *
  354. * and client-screen edge holes
  355. * ___________ ___________
  356. * | | || -> | | |
  357. * | A | B || -> | A | B >|
  358. * | | || -> | | >|
  359. * |_____|----'| -> |_____|__v__|
  360. * ^^^ void
  361. */
  362. void
  363. layout_fix_hole(struct client *c)
  364. {
  365. struct client *cr = client_next_with_pos(c, Right);
  366. struct client *cb = client_next_with_pos(c, Bottom);
  367. c->geo.w += (cr ? cr->geo.x : c->screen->ugeo.w) - (c->geo.x + c->geo.w);
  368. c->geo.h += (cb ? cb->geo.y : c->screen->ugeo.h) - (c->geo.y + c->geo.h);
  369. client_moveresize(c, &c->geo);
  370. }
  371. /* Layout rotation: Rotate 90° all client to right or left.
  372. * Avoid if(left) condition in layout_rotate loop; use func ptr
  373. *
  374. * Right rotation
  375. * ____________ ____________
  376. * | | B | -> | | A |
  377. * | A |_______| -> |__|_________|
  378. * |____| C | D | -> |_____| B |
  379. * |____|___|___| -> |_____|______|
  380. *
  381. * Left rotation
  382. * ____________ ____________
  383. * | | B | -> | B |_____|
  384. * | A |_______| -> |______|_____|
  385. * |____| C | D | -> | A | |
  386. * |____|___|___| -> |_________|__|
  387. *
  388. */
  389. static inline void
  390. _pos_rotate_left(struct geo *g, struct geo *ug, struct geo *og)
  391. {
  392. g->x = (ug->h - (og->y + og->h));
  393. g->y = og->x;
  394. }
  395. static inline void
  396. _pos_rotate_right(struct geo *g, struct geo *ug, struct geo *og)
  397. {
  398. g->x = og->y;
  399. g->y = (ug->w - (og->x + og->w));
  400. }
  401. static void
  402. layout_rotate(struct tag *t, void (*pfunc)(struct geo*, struct geo*, struct geo*))
  403. {
  404. struct client *c;
  405. struct geo g, *ug = &t->screen->ugeo;
  406. float f1 = (float)t->screen->ugeo.w / (float)t->screen->ugeo.h;
  407. float f2 = 1 / f1;
  408. FOREACH_NFCLIENT(c, &t->clients, tnext)
  409. {
  410. pfunc(&g, ug, &c->geo);
  411. g.x *= f1;
  412. g.y *= f2;
  413. g.w = c->geo.h * f1;
  414. g.h = c->geo.w * f2;
  415. client_moveresize(c, &g);
  416. }
  417. /* Rotate sometimes do not set back perfect size.. */
  418. FOREACH_NFCLIENT(c, &t->clients, tnext)
  419. layout_fix_hole(c);
  420. layout_save_set(t);
  421. }
  422. void
  423. uicb_layout_rotate_left(Uicb cmd)
  424. {
  425. (void)cmd;
  426. layout_rotate(W->screen->seltag, _pos_rotate_left);
  427. }
  428. void
  429. uicb_layout_rotate_right(Uicb cmd)
  430. {
  431. (void)cmd;
  432. layout_rotate(W->screen->seltag, _pos_rotate_right);
  433. }
  434. /*
  435. * Really simple functions, don't need static no-uicb backend
  436. * so we avoid the use of if(vertical) .. else
  437. *
  438. * Vertical mirror
  439. * ____________ ____________
  440. * | | B | -> | B | |
  441. * | A |_______| -> |_______| A |
  442. * | | C | D | -> | D | C | |
  443. * |____|___|___| -> |___|___|____|
  444. *
  445. * Horizontal mirror
  446. * ____________ ____________
  447. * | | B | -> | | C | D |
  448. * | A |_______| -> | A |___|___|
  449. * | | C | D | -> | | B |
  450. * |____|___|___| -> |____|_______|
  451. */
  452. void
  453. uicb_layout_vmirror(Uicb cmd)
  454. {
  455. (void)cmd;
  456. struct client *c;
  457. FOREACH_NFCLIENT(c, &W->screen->seltag->clients, tnext)
  458. {
  459. c->geo.x = W->screen->ugeo.w - (c->geo.x + c->geo.w);
  460. client_moveresize(c, &c->geo);
  461. }
  462. layout_save_set(W->screen->seltag);
  463. }
  464. void
  465. uicb_layout_hmirror(Uicb cmd)
  466. {
  467. (void)cmd;
  468. struct client *c;
  469. FOREACH_NFCLIENT(c, &W->screen->seltag->clients, tnext)
  470. {
  471. c->geo.y = W->screen->ugeo.h - (c->geo.y + c->geo.h);
  472. client_moveresize(c, &c->geo);
  473. }
  474. layout_save_set(W->screen->seltag);
  475. }
  476. #define LAYOUT_INTEGRATE_DIR(D) \
  477. void uicb_layout_integrate_##D(Uicb cmd) \
  478. { \
  479. (void)cmd; \
  480. if(W->client) \
  481. layout_integrate(W->client, D); \
  482. }
  483. static void
  484. layout_integrate(struct client *c, enum position p)
  485. {
  486. struct client *n;
  487. struct client ghost = *c;
  488. if(!(c->flags & CLIENT_TILED))
  489. return;
  490. if((n = client_next_with_pos(c, p))
  491. && (n->flags & CLIENT_TILED))
  492. {
  493. layout_split_integrate(c, n);
  494. layout_split_arrange_closed(&ghost);
  495. }
  496. }
  497. LAYOUT_INTEGRATE_DIR(Left);
  498. LAYOUT_INTEGRATE_DIR(Right);
  499. LAYOUT_INTEGRATE_DIR(Top);
  500. LAYOUT_INTEGRATE_DIR(Bottom);
  501. void
  502. layout_client(struct client *c)
  503. {
  504. if(c->flags & CLIENT_IGNORE_LAYOUT)
  505. {
  506. c->flags ^= CLIENT_IGNORE_LAYOUT;
  507. return;
  508. }
  509. if(c->flags & CLIENT_FREE)
  510. {
  511. layout_split_arrange_closed(c);
  512. c->flags ^= CLIENT_TILED;
  513. client_moveresize(c, &c->geo);
  514. XRaiseWindow(W->dpy, c->frame);
  515. }
  516. else if(!(c->flags & CLIENT_TABBED))
  517. layout_split_integrate(c, c->tag->sel);
  518. }