123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- /*
- * wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
- * For license, see COPYING.
- */
- #include <X11/Xutil.h>
- #include <X11/XKBlib.h>
- #include "layout.h"
- #include "config.h"
- #include "client.h"
- #include "draw.h"
- #include "event.h"
- #include "util.h"
- /* Shift in client split to keep clients's parent at close arrange */
- static int shiftv = 1, shifth = 1;
- void
- layout_save_set(struct tag *t)
- {
- struct client *c;
- struct layout_set *l;
- struct geo_list *g, *gp;
- int n = 1;
- l = xcalloc(1, sizeof(struct layout_set));
- SLIST_INIT(&l->geos);
- FOREACH_NFCLIENT(c, &t->clients, tnext)
- {
- g = xcalloc(1, sizeof(struct geo_list));
- g->geo = c->geo;
- if(!SLIST_FIRST(&l->geos))
- SLIST_INSERT_HEAD(&l->geos, g, next);
- else
- SLIST_INSERT_AFTER(gp, g, next);
- ++n;
- gp = g;
- }
- l->n = n;
- TAILQ_INSERT_TAIL(&t->sets, l, next);
- }
- static void
- layout_apply_set(struct tag *t, struct layout_set *l)
- {
- struct geo_list *g;
- struct client *c;
- int nc = 1;
- FOREACH_NFCLIENT(c, &t->clients, tnext)
- ++nc;
- /* TODO: Adapt different client number case */
- if(l->n != nc)
- return;
- for(g = SLIST_FIRST(&l->geos), c = SLIST_FIRST(&t->clients);
- c; c = SLIST_NEXT(c, tnext))
- {
- if(g)
- {
- client_moveresize(c, &g->geo);
- g = SLIST_NEXT(g, next);
- }
- /* TODO
- * Not enough geos in the set;
- * then integrate remains of client
- *
- else
- layout_split_integrate(c, SLIST_FIRST(&t->clients));
- */
- }
- /* TODO
- * Not enough clients for geos in set;
- * arrange clients with not set geo.
- *
- if((g = SLIST_NEXT(g, next)))
- for(cc.tag = t; g; g = SLIST_NEXT(g, next))
- {
- cc.geo = g->geo;
- layout_split_arrange_closed(&cc);
- }
- */
- /* Re-insert set in historic */
- layout_save_set(t);
- }
- void
- layout_free_set(struct tag *t)
- {
- struct layout_set *l;
- while(!TAILQ_EMPTY(&t->sets))
- {
- l = TAILQ_FIRST(&t->sets);
- TAILQ_REMOVE(&t->sets, l, next);
- FREE_LIST(geo_list, l->geos);
- free(l);
- }
- }
- #define _REV_BORDER() \
- SLIST_FOREACH(g, &l->geos, next) { \
- cd.geo = g->geo; \
- draw_reversed_rect(W->root, &cd, false); \
- }
- static void
- _historic_set(struct tag *t, bool prev)
- {
- struct keybind *k;
- struct layout_set *l;
- struct geo_list *g;
- struct client cd = { .screen = t->screen, .theme = THEME_DEFAULT };
- bool b = true;
- XEvent ev;
- KeySym keysym;
- if(TAILQ_EMPTY(&t->sets))
- return;
- l = TAILQ_LAST(&t->sets, ssub);
- if(prev)
- l = TAILQ_PREV(l, ssub, next);
- if(!l)
- return;
- /* TODO
- if(option_simple_manual_resize)
- {
- layout_set_apply(l);
- return;
- */
- XGrabKeyboard(W->dpy, W->root, True, GrabModeAsync, GrabModeAsync, CurrentTime);
- _REV_BORDER();
- do
- {
- XMaskEvent(W->dpy, KeyPressMask, &ev);
- if(ev.type == KeyPress)
- {
- XKeyPressedEvent *ke = &ev.xkey;
- keysym = XkbKeycodeToKeysym(W->dpy, (KeyCode)ke->keycode, 0, 0);
- _REV_BORDER();
- SLIST_FOREACH(k, &W->h.keybind, next)
- if(k->keysym == keysym && KEYPRESS_MASK(k->mod) == KEYPRESS_MASK(ke->state)
- && k->func)
- {
- if(k->func == uicb_layout_prev_set)
- {
- if(!(l = TAILQ_PREV(l, ssub, next)))
- l = TAILQ_LAST(&t->sets, ssub);
- }
- else if(k->func == uicb_layout_next_set)
- {
- if(!(l = TAILQ_NEXT(l, next)))
- l = TAILQ_FIRST(&t->sets);
- }
- else
- {
- k->func(k->cmd);
- keysym = XK_Escape;
- }
- }
- if(!l)
- l = TAILQ_LAST(&t->sets, ssub);
- _REV_BORDER();
- /* Gtfo of this loop */
- if(keysym == XK_Return)
- break;
- else if(keysym == XK_Escape)
- {
- b = false;
- break;
- }
- XSync(W->dpy, False);
- }
- XNextEvent(W->dpy, &ev);
- } while(ev.type != KeyPress);
- _REV_BORDER();
- if(b)
- layout_apply_set(t, l);
- XUngrabServer(W->dpy);
- XUngrabKeyboard(W->dpy, CurrentTime);
- }
- void
- uicb_layout_prev_set(Uicb cmd)
- {
- (void)cmd;
- _historic_set(W->screen->seltag, true);
- }
- void
- uicb_layout_next_set(Uicb cmd)
- {
- (void)cmd;
- _historic_set(W->screen->seltag, false);
- }
- static struct geo
- layout_split(struct client *c, bool vertical)
- {
- struct geo og, geo;
- geo = og = c->geo;
- if(vertical)
- {
- c->geo.w >>= 1;
- c->geo.w += shiftv;
- geo.x = c->geo.x + c->geo.w;
- geo.w >>= 1;
- /* Remainder */
- geo.w += (og.x + og.w) - (geo.x + geo.w);
- shiftv = -shiftv;
- }
- else
- {
- c->geo.h >>= 1;
- c->geo.h += shifth;
- geo.y = c->geo.y + c->geo.h;
- geo.h >>= 1;
- /* Remainder */
- geo.h += (og.y + og.h) - (geo.y + geo.h);
- shifth = -shifth;
- }
- return geo;
- }
- static inline void
- layout_split_arrange_size(struct geo *g, struct client *c, enum position p)
- {
- if(LDIR(p))
- {
- c->geo.w += g->w;
- if(p == Right)
- c->geo.x = g->x;
- }
- else
- {
- c->geo.h += g->h;
- if(p == Bottom)
- c->geo.y = g->y;
- }
- client_moveresize(c, &c->geo);
- }
- static inline bool
- layout_split_check_row_dir(struct client *c, struct client *g, enum position p)
- {
- struct geo cgeo = c->geo;
- struct client *cc;
- int s = 0, cs = (LDIR(p) ? g->geo.h : g->geo.w);
- FOREACH_NFCLIENT(cc, &c->tag->clients, tnext)
- if(GEO_PARENTROW(cgeo, cc->geo, RPOS(p))
- && GEO_CHECK_ROW(cc->geo, g->geo, p))
- {
- s += (LDIR(p) ? cc->geo.h : cc->geo.w);
- if(s == cs)
- return true;
- if(s > cs)
- return false;
- }
- return false;
- }
- /* Use ghost client properties to fix holes in tile
- *
- * ~ .--. ~ ~
- *_____ ~ /xx \ ~ ~
- * |>>| ~\O _ (____ ~
- * | |__.| .--'-==~ ~
- * |>>'---\ '. ~ , ~
- *__|__| '. '-.___.-'/ ~
- * '-.__ _.' ~
- * ````` ~
- */
- #define _ARRANGE_SINGLE_PARENT(p) \
- do { \
- if((c = client_next_with_pos(ghost, p))) \
- if(GEO_CHECK2(ghost->geo, c->geo, p)) \
- { \
- layout_split_arrange_size(&ghost->geo, c, p); \
- layout_save_set(ghost->tag); \
- return; \
- } \
- } while(/* CONSTCOND */ 0);
- void
- layout_split_arrange_closed(struct client *ghost)
- {
- struct client *c, *cc;
- struct geo g;
- bool b = false;
- enum position p;
- if(!(ghost->flags & CLIENT_TILED))
- return;
- /* Search for single parent for easy resize
- * Example case:
- * ___________ ___________
- * | | B | -> -> | | |
- * | A |_____| -> Close -> | A | B |
- * | | C | -> C -> | |v v v|
- * |_____|_____| -> -> |_____|_____|
- */
- _ARRANGE_SINGLE_PARENT(Right);
- _ARRANGE_SINGLE_PARENT(Left);
- _ARRANGE_SINGLE_PARENT(Top);
- _ARRANGE_SINGLE_PARENT(Bottom);
- /* Check row parents for full resize
- * Example case:
- * ___________ ___________
- * | | B | -> -> | << B |
- * | A |_____| -> Close -> |___________|
- * | | C | -> A -> | << C |
- * |_____|_____| -> -> |___________|
- */
- for(p = Right; p < Center && !b; ++p)
- {
- if((c = client_next_with_pos(ghost, p))
- && layout_split_check_row_dir(c, ghost, p))
- {
- g = c->geo;
- FOREACH_NFCLIENT(cc, &c->tag->clients, tnext)
- if(GEO_PARENTROW(g, cc->geo, RPOS(p))
- && GEO_CHECK_ROW(cc->geo, ghost->geo, p))
- {
- layout_split_arrange_size(&ghost->geo, cc, p);
- b = true;
- }
- }
- }
- layout_save_set(ghost->tag);
- }
- /*
- * Integrate a client in split layout:
- * - Check if there is no sc
- * - Check if sc is on a different tag than c
- * - Check if sc is not in free mode
- *
- * So from there, sc is not compatible, so we will integrate
- * c in the larger tiled client of c tag:
- * - Check if the larger client is correct
- *
- * Checks all failed? Get first tiled client of the tag to integrate in.
- * Still no client, it means that c is the first tiled client of the tag, then maximize it.
- */
- void
- layout_split_integrate(struct client *c, struct client *sc)
- {
- struct geo g;
- bool f = false;
- /* No sc or not compatible, get the largest of the tag */
- if(!sc
- || sc == c
- || sc->tag != c->tag
- || (sc->flags & CLIENT_FREE)
- || !COMPCLIENT(c, sc))
- sc = client_get_larger(c->tag, c->flags & CLIENT_IGNORE_TAG);
- /* Largest not correct */
- if(!sc || sc == c)
- {
- FOREACH_NFCLIENT(sc, &c->tag->clients, tnext)
- if(sc != c && !(sc->flags & CLIENT_TABBED))
- {
- f = true;
- break;
- }
- /* Ok there is no client to integrate in */
- if(!f)
- {
- client_maximize(c);
- c->flags |= CLIENT_TILED;
- W->flags &= ~WMFS_TABNOC;
- return;
- }
- }
- /* Tab Next Opened Client option */
- if(W->flags & WMFS_TABNOC && COMPCLIENT(c, sc))
- {
- W->flags ^= WMFS_TABNOC;
- _client_tab(c, sc);
- return;
- }
- /* If there are clients but we can tab with them, split the screen. */
- c->flags |= CLIENT_TILED;
- g = layout_split(sc, (sc->geo.h < sc->geo.w));
- client_moveresize(c, &g);
- client_moveresize(sc, &sc->geo);
- client_fac_hint(c);
- client_fac_hint(sc);
- layout_save_set(c->tag);
- W->flags &= ~WMFS_TABNOC;
- }
- /* Arrange inter-clients holes:
- * ___________ ___________
- * | || | -> | | |
- * | A || B | -> | A >| B |
- * | || | -> | >| |
- * |_____||____| -> |______|____|
- * ^ void
- *
- * and client-screen edge holes
- * ___________ ___________
- * | | || -> | | |
- * | A | B || -> | A | B >|
- * | | || -> | | >|
- * |_____|----'| -> |_____|__v__|
- * ^^^ void
- */
- void
- layout_fix_hole(struct client *c)
- {
- struct client *cr = client_next_with_pos(c, Right);
- struct client *cb = client_next_with_pos(c, Bottom);
- c->geo.w += (cr ? cr->geo.x : c->screen->ugeo.w) - (c->geo.x + c->geo.w);
- c->geo.h += (cb ? cb->geo.y : c->screen->ugeo.h) - (c->geo.y + c->geo.h);
- client_moveresize(c, &c->geo);
- }
- /* Layout rotation: Rotate 90° all client to right or left.
- * Avoid if(left) condition in layout_rotate loop; use func ptr
- *
- * Right rotation
- * ____________ ____________
- * | | B | -> | | A |
- * | A |_______| -> |__|_________|
- * |____| C | D | -> |_____| B |
- * |____|___|___| -> |_____|______|
- *
- * Left rotation
- * ____________ ____________
- * | | B | -> | B |_____|
- * | A |_______| -> |______|_____|
- * |____| C | D | -> | A | |
- * |____|___|___| -> |_________|__|
- *
- */
- static inline void
- _pos_rotate_left(struct geo *g, struct geo *ug, struct geo *og)
- {
- g->x = (ug->h - (og->y + og->h));
- g->y = og->x;
- }
- static inline void
- _pos_rotate_right(struct geo *g, struct geo *ug, struct geo *og)
- {
- g->x = og->y;
- g->y = (ug->w - (og->x + og->w));
- }
- static void
- layout_rotate(struct tag *t, void (*pfunc)(struct geo*, struct geo*, struct geo*))
- {
- struct client *c;
- struct geo g, *ug = &t->screen->ugeo;
- float f1 = (float)t->screen->ugeo.w / (float)t->screen->ugeo.h;
- float f2 = 1 / f1;
- FOREACH_NFCLIENT(c, &t->clients, tnext)
- {
- pfunc(&g, ug, &c->geo);
- g.x *= f1;
- g.y *= f2;
- g.w = c->geo.h * f1;
- g.h = c->geo.w * f2;
- client_moveresize(c, &g);
- }
- /* Rotate sometimes do not set back perfect size.. */
- FOREACH_NFCLIENT(c, &t->clients, tnext)
- layout_fix_hole(c);
- layout_save_set(t);
- }
- void
- uicb_layout_rotate_left(Uicb cmd)
- {
- (void)cmd;
- layout_rotate(W->screen->seltag, _pos_rotate_left);
- }
- void
- uicb_layout_rotate_right(Uicb cmd)
- {
- (void)cmd;
- layout_rotate(W->screen->seltag, _pos_rotate_right);
- }
- /*
- * Really simple functions, don't need static no-uicb backend
- * so we avoid the use of if(vertical) .. else
- *
- * Vertical mirror
- * ____________ ____________
- * | | B | -> | B | |
- * | A |_______| -> |_______| A |
- * | | C | D | -> | D | C | |
- * |____|___|___| -> |___|___|____|
- *
- * Horizontal mirror
- * ____________ ____________
- * | | B | -> | | C | D |
- * | A |_______| -> | A |___|___|
- * | | C | D | -> | | B |
- * |____|___|___| -> |____|_______|
- */
- void
- uicb_layout_vmirror(Uicb cmd)
- {
- (void)cmd;
- struct client *c;
- FOREACH_NFCLIENT(c, &W->screen->seltag->clients, tnext)
- {
- c->geo.x = W->screen->ugeo.w - (c->geo.x + c->geo.w);
- client_moveresize(c, &c->geo);
- }
- layout_save_set(W->screen->seltag);
- }
- void
- uicb_layout_hmirror(Uicb cmd)
- {
- (void)cmd;
- struct client *c;
- FOREACH_NFCLIENT(c, &W->screen->seltag->clients, tnext)
- {
- c->geo.y = W->screen->ugeo.h - (c->geo.y + c->geo.h);
- client_moveresize(c, &c->geo);
- }
- layout_save_set(W->screen->seltag);
- }
- #define LAYOUT_INTEGRATE_DIR(D) \
- void uicb_layout_integrate_##D(Uicb cmd) \
- { \
- (void)cmd; \
- if(W->client) \
- layout_integrate(W->client, D); \
- }
- static void
- layout_integrate(struct client *c, enum position p)
- {
- struct client *n;
- struct client ghost = *c;
- if(!(c->flags & CLIENT_TILED))
- return;
- if((n = client_next_with_pos(c, p))
- && (n->flags & CLIENT_TILED))
- {
- layout_split_integrate(c, n);
- layout_split_arrange_closed(&ghost);
- }
- }
- LAYOUT_INTEGRATE_DIR(Left);
- LAYOUT_INTEGRATE_DIR(Right);
- LAYOUT_INTEGRATE_DIR(Top);
- LAYOUT_INTEGRATE_DIR(Bottom);
- void
- layout_client(struct client *c)
- {
- if(c->flags & (CLIENT_IGNORE_LAYOUT | CLIENT_FULLSCREEN))
- {
- c->flags &= ~CLIENT_IGNORE_LAYOUT;
- return;
- }
- if(c->flags & CLIENT_FREE)
- {
- layout_split_arrange_closed(c);
- c->flags ^= CLIENT_TILED;
- client_moveresize(c, &c->geo);
- XRaiseWindow(W->dpy, c->frame);
- }
- else if(!(c->flags & CLIENT_TABBED))
- layout_split_integrate(c, c->tag->sel);
- }
|