Add split layout
This commit is contained in:
parent
60bd64e59e
commit
961e57f876
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
|
||||
* For license, see COPYING.
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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 */
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -292,7 +292,7 @@ void
|
||||
uicb_reload(Uicb cmd)
|
||||
{
|
||||
(void)cmd;
|
||||
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
[themes]
|
||||
|
||||
|
||||
[theme]
|
||||
# name = "default"
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user