diff --git a/wmfs2/src/barwin.c b/wmfs2/src/barwin.c index a72f220..9c473c1 100644 --- a/wmfs2/src/barwin.c +++ b/wmfs2/src/barwin.c @@ -98,9 +98,7 @@ barwin_resize(struct barwin *b, int w, int h) void barwin_mousebind_new(struct barwin *b, unsigned int button, bool u, struct geo a, void (*func)(Uicb), Uicb cmd) { - struct mousebind *m; - - m = xcalloc(1, sizeof(struct mousebind)); + struct mousebind *m = (struct mousebind*)xcalloc(1, sizeof(struct mousebind)); m->button = button; m->use_area = u; diff --git a/wmfs2/src/client.c b/wmfs2/src/client.c index e8b7c92..a728190 100644 --- a/wmfs2/src/client.c +++ b/wmfs2/src/client.c @@ -10,6 +10,7 @@ #include "util.h" #include "barwin.h" #include "ewmh.h" +#include "layout.h" #define CLIENT_MOUSE_MOD Mod1Mask @@ -47,6 +48,54 @@ client_gb_win(Window w) return c; } +static struct client* +client_gb_pos(struct tag *t, int x, int y) +{ + struct client *c; + + SLIST_FOREACH(c, &t->clients, tnext) + if(INAREA(x, y, c->geo)) + return c; + + return NULL; +} + +/** Get client left/right/top/bottom of selected client + *\param bc Base client + *\param pos Position + *\return Client found or NULL +*/ +struct client* +client_next_with_pos(struct client *bc, Position p) +{ + struct client *c; + int x, y; + const static char scanfac[4][2] = + { + { 2, 0 }, { -2, 0 }, /* Right, Left */ + { 0, -2 }, { 0, 2 } /* Top, Bottom */ + }; + + /* + * Set start place of pointer (edge of base client) + * for faster scanning + */ + x = bc->geo.x + ((p == Right) ? bc->geo.w : 0); + y = bc->geo.y + ((p == Bottom) ? bc->geo.h : 0); + y += ((LDIR(p)) ? (bc->geo.h >> 1) : 0); + x += ((p > Left) ? (bc->geo.w >> 1) : 0); + + /* Scan in right direction to next(p) physical client */ + + while((c = client_gb_pos(bc->tag, x, y)) == bc) + { + x += scanfac[p][0]; + y += scanfac[p][1]; + } + + return c; +} + /** Map a client * \param c struct client pointer */ @@ -191,14 +240,15 @@ client_new(Window w, XWindowAttributes *wa) c->flags = 0; c->tag = NULL; - /* Set tag */ - tag_client(W->screen->seltag, c); - /* struct geometry */ c->geo.x = wa->x; c->geo.y = wa->y; c->geo.w = wa->width; c->geo.h = wa->height; + c->cgeo = c->geo; + + /* Set tag */ + tag_client(W->screen->seltag, c); /* X window attributes */ XSelectInput(W->dpy, w, EnterWindowMask | FocusChangeMask @@ -219,6 +269,29 @@ client_new(Window w, XWindowAttributes *wa) return c; } +void +client_moveresize(struct client *c, struct geo g) +{ + c->geo = c->cgeo = g; + + c->cgeo.w += THEME_DEFAULT->client_border_width << 1; + c->cgeo.h += THEME_DEFAULT->client_border_width << 1; + + XMoveResizeWindow(W->dpy, c->win, g.x, g.y, g.w, g.h); +} + +void +client_maximize(struct client *c) +{ + c->geo = c->tag->screen->ugeo; + + c->geo.x = c->geo.y = 0; /* Frame x/y, not screen geo */ + c->geo.w = c->tag->screen->ugeo.w - (THEME_DEFAULT->client_border_width << 1); + c->geo.h = c->tag->screen->ugeo.h - (THEME_DEFAULT->client_border_width << 1); + + client_moveresize(c, c->geo); +} + void client_remove(struct client *c) { @@ -232,6 +305,8 @@ client_remove(struct client *c) XSetInputFocus(W->dpy, W->root, RevertToPointerRoot, CurrentTime); } + layout_split_arrange_closed(c); + /* Remove from global client list */ SLIST_REMOVE(&W->h.client, c, client, next); diff --git a/wmfs2/src/client.h b/wmfs2/src/client.h index ae81087..06df224 100644 --- a/wmfs2/src/client.h +++ b/wmfs2/src/client.h @@ -10,10 +10,13 @@ void client_configure(struct client *c); struct client *client_gb_win(Window w); +struct client *client_next_with_pos(struct client *bc, Position p); void client_focus(struct client *c); void client_get_name(struct client *c); void client_close(struct client *c); struct client *client_new(Window w, XWindowAttributes *wa); +void client_moveresize(struct client *c, struct geo g); +void client_maximize(struct client *c); void client_remove(struct client *c); void client_free(void); diff --git a/wmfs2/src/config.c b/wmfs2/src/config.c index f037e1c..18334ea 100644 --- a/wmfs2/src/config.c +++ b/wmfs2/src/config.c @@ -16,7 +16,7 @@ static void config_theme(void) { - struct theme *t; + struct theme *t, *p; size_t i, n; struct conf_sec *sec, **ks; @@ -63,7 +63,13 @@ config_theme(void) t->client_titlebar_width = fetch_opt_first(ks[i], "12", "client_titlebar_width").num; t->client_border_width = fetch_opt_first(ks[i], "1", "client_border_width").num; - SLIST_INSERT_HEAD(&W->h.theme, t, next); + /* insert_tail with SLIST */ + if(SLIST_EMPTY(&W->h.theme)) + SLIST_INSERT_HEAD(&W->h.theme, t, next); + else + SLIST_INSERT_AFTER(p, t, next); + + p = t; } free(ks); @@ -162,7 +168,7 @@ config_keybind(void) /* mod = {} */ opt = fetch_opt(ks[i], "", "mod"); - for(j = 0; j < fetch_opt_count(opt); ++j) + for(j = k->mod = 0; j < fetch_opt_count(opt); ++j) k->mod |= modkey_keysym(opt[j].str); free(opt); diff --git a/wmfs2/src/event.c b/wmfs2/src/event.c index b5d4352..ec5c144 100644 --- a/wmfs2/src/event.c +++ b/wmfs2/src/event.c @@ -1,4 +1,4 @@ - /* +/* * wmfs2 by Martin Duquesnoy { for(i = 2011; i < 2111; ++i) ©(i); } * For license, see COPYING. */ diff --git a/wmfs2/src/layout.c b/wmfs2/src/layout.c index ae951c4..91ad0d8 100644 --- a/wmfs2/src/layout.c +++ b/wmfs2/src/layout.c @@ -4,5 +4,125 @@ */ #include "layout.h" +#include "config.h" +#include "client.h" +#include "util.h" +static struct geo +layout_split(struct client *c, bool vertical) +{ + struct geo og, geo; + int bord = THEME_DEFAULT->client_border_width; + + geo = og = c->geo; + + if(vertical) + { + c->geo.w >>= 1; + geo.x = (c->geo.x + bord) + (c->geo.w + bord); + geo.w >>= 1; + + /* Remainder */ + geo.w += (og.x + og.w) - (geo.x + geo.w); + } + else + { + c->geo.h >>= 1; + geo.y = (c->geo.y + bord) + (c->geo.h + bord); + geo.h >>= 1; + + /* Remainder */ + geo.h += (og.y + og.h) - (geo.y + geo.h); + } + + client_moveresize(c, c->geo); + + return geo; +} + +/* Use ghost client properties to fix holes in tile + * .--. ~ ~ + * /xx \ ~ ~ + * ~~\O _ (____ ~ + * __.| .--'-==~ ~ + * '---\ '. ~ , ~ + * '. '-.___.-'/ ~ + * '-.__ _.' ~ + * ````` ~ + */ +void +layout_split_arrange_closed(struct client *ghost) +{ + struct client *c, *cc; + struct geo g; + bool b; + Position p; + + /* Search for single parent for easy resize + * Example case: + * ___________ ___________ + * | | B | -> -> | | | + * | A |_____| -> Close -> | A | B | + * | | C | -> C -> | |v v v| + * |_____|_____| -> -> |_____|_____| + */ + for(p = Right; p < Center; ++p) /* Check every direction */ + { + if((c = client_next_with_pos(ghost, p))) + if(GEO_CHECK2(ghost->geo, c->geo, p)) + { + layout_split_arrange_size(ghost->geo, c, p); + return; + } + } + + /* 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; + SLIST_FOREACH(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; + } + } + } + +} + +/* Integrate a client in split layout: split sc and fill c in new geo */ +void +layout_split_integrate(struct client *c, struct client *sc) +{ + struct geo g; + + /* No sc */ + if(!sc || sc == c || sc->tag != c->tag) + { + /* + * Not even a first client in list, then + * maximize the lonely client + */ + if(!(sc = SLIST_FIRST(&c->tag->clients))) + { + client_maximize(c); + return; + } + } + + g = layout_split(sc, (sc->geo.h < sc->geo.w)); + client_moveresize(c, g); +} diff --git a/wmfs2/src/layout.h b/wmfs2/src/layout.h index d10e13e..2305e6e 100644 --- a/wmfs2/src/layout.h +++ b/wmfs2/src/layout.h @@ -7,6 +7,82 @@ #define LAYOUT_H #include "wmfs.h" +#include "config.h" +#include "client.h" + +/* Check lateral direction (if p is Right or Left) */ +#define LDIR(P) (P < Top) + +/* Reverse position */ +#define RPOS(P) (P & 1 ? P - 1 : P + 1) + +/* geo comparaison */ +#define GEO_CHECK2(g1, g2, p) (LDIR(p) ? (g1.h == g2.h) : (g1.w == g2.w)) +#define GEO_CHECK_ROW(g1, g2, p) \ + (LDIR(p) \ + ? (g1.y >= g2.y && (g1.y + g1.h) <= (g2.y + g2.h)) \ + : (g1.x >= g2.x && (g1.x + g1.w) <= (g2.x + g2.w))) +#define GEO_PARENTROW(g1, g2, p) \ + (LDIR(p) \ + ? (p == Left ? (g1.x == g2.x) : (g1.x + g1.w == g2.x + g2.w)) \ + : (p == Top ? (g1.y == g2.y) : (g1.y + g1.h == g2.y + g2.h))) + + +/* Debug */ +#define DGEO(G) printf(": %d %d %d %d\n", G.x, G.y, G.w, G.h) + + +void layout_split_integrate(struct client *c, struct client *sc); +void layout_split_arrange_closed(struct client *ghost); + +static inline void +layout_split_arrange_size(struct geo g, struct client *c, Position p) +{ + if(LDIR(p)) + { + c->geo.w += g.w + (THEME_DEFAULT->client_border_width << 1); + + if(p == Right) + c->geo.x = g.x; + } + else + { + c->geo.h += g.h + (THEME_DEFAULT->client_border_width << 1); + + 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, Position p) +{ + struct geo cgeo = c->geo; + struct client *cc; + int bord = THEME_DEFAULT->client_border_width; + int i = 0, s = 0, cs = (LDIR(p) ? g->geo.h : g->geo.w ); + + + SLIST_FOREACH(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 + bord + : cc->geo.w + bord) + (i > 1 ? bord : 0);; + + if(s == cs) + return true; + if(s > cs) + return false; + + ++i; + } + + return false; +} #endif /* LAYOUT_H */ diff --git a/wmfs2/src/tag.c b/wmfs2/src/tag.c index cf1c899..5235c08 100644 --- a/wmfs2/src/tag.c +++ b/wmfs2/src/tag.c @@ -12,6 +12,7 @@ #include "config.h" #include "barwin.h" #include "ewmh.h" +#include "layout.h" struct tag* tag_new(struct screen *s, char *name) @@ -89,7 +90,6 @@ tag_client(struct tag *t, struct client *c) return; SLIST_REMOVE(&c->tag->clients, c, client, tnext); - /* TODO: Focus next client */ if(c->tag->sel == c) c->tag->sel = NULL; @@ -117,7 +117,7 @@ tag_client(struct tag *t, struct client *c) /* Reparent client win in frame win */ XReparentWindow(W->dpy, c->win, t->frame, 0, 0); - /* tag_frame_client */ + layout_split_integrate(c, t->sel); /* Insert in new tag list */ SLIST_INSERT_HEAD(&t->clients, c, tnext); @@ -178,7 +178,9 @@ static void tag_remove(struct tag *t) { free(t->name); + XDestroyWindow(W->dpy, t->frame); + free(t); } diff --git a/wmfs2/src/util.h b/wmfs2/src/util.h index 982e0a0..df751c6 100644 --- a/wmfs2/src/util.h +++ b/wmfs2/src/util.h @@ -11,11 +11,11 @@ /* Todo FREE_LIST(type, head, function_remove) */ #define FREE_LIST(type, head) \ do { \ - struct type *t; \ + struct type *Z; \ while(!SLIST_EMPTY(&head)) { \ - t = SLIST_FIRST(&head); \ + Z = SLIST_FIRST(&head); \ SLIST_REMOVE_HEAD(&head, next); \ - free(t); /* function_remove(t)*/ \ + free(Z); /* function_remove(t)*/ \ } \ } while(/* CONSTCOND */ 0); @@ -33,7 +33,6 @@ #define ABS(j) (j < 0 ? -j : j) #define INAREA(i, j, a) ((i) >= (a).x && (i) <= (a).x + (a).w && (j) >= (a).y && (j) <= (a).y + (a).h) - /* * "#RRGGBB" -> 0xRRGGBB */ diff --git a/wmfs2/src/wmfs.c b/wmfs2/src/wmfs.c index 3e3dc57..00f728f 100644 --- a/wmfs2/src/wmfs.c +++ b/wmfs2/src/wmfs.c @@ -292,7 +292,7 @@ void uicb_reload(Uicb cmd) { (void)cmd; - + /* TODO */ } void diff --git a/wmfs2/src/wmfs.h b/wmfs2/src/wmfs.h index 972b873..23a4123 100644 --- a/wmfs2/src/wmfs.h +++ b/wmfs2/src/wmfs.h @@ -104,7 +104,7 @@ struct client struct tag *tag; struct screen *screen; struct barwin *titlebar; - struct geo geo; + struct geo geo, cgeo; /* Complete geo: + border */ char *title; Flags flags; Window win; @@ -124,7 +124,7 @@ struct keybind struct mousebind { struct geo area; - unsigned int button; + int button; bool use_area; void (*func)(Uicb); Uicb cmd; diff --git a/wmfs2/wmfsrc2 b/wmfs2/wmfsrc2 index a203ed4..ea7d220 100644 --- a/wmfs2/wmfsrc2 +++ b/wmfs2/wmfsrc2 @@ -4,6 +4,7 @@ [themes] + [theme] # name = "default"