Browse Source

Add split layout

Martin Duquesnoy 12 years ago
parent
commit
961e57f876
12 changed files with 299 additions and 19 deletions
  1. 1 3
      wmfs2/src/barwin.c
  2. 78 3
      wmfs2/src/client.c
  3. 3 0
      wmfs2/src/client.h
  4. 9 3
      wmfs2/src/config.c
  5. 1 1
      wmfs2/src/event.c
  6. 120 0
      wmfs2/src/layout.c
  7. 76 0
      wmfs2/src/layout.h
  8. 4 2
      wmfs2/src/tag.c
  9. 3 4
      wmfs2/src/util.h
  10. 1 1
      wmfs2/src/wmfs.c
  11. 2 2
      wmfs2/src/wmfs.h
  12. 1 0
      wmfs2/wmfsrc2

+ 1 - 3
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;

+ 78 - 3
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);
 

+ 3 - 0
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);
 

+ 9 - 3
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);

+ 1 - 1
wmfs2/src/event.c

@@ -1,4 +1,4 @@
-  /*
+/*
  *  wmfs2 by Martin Duquesnoy <xorg62@gmail.com> { for(i = 2011; i < 2111; ++i) ©(i); }
  *  For license, see COPYING.
  */

+ 120 - 0
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);
+}
 

+ 76 - 0
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 */
 

+ 4 - 2
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);
 }
 

+ 3 - 4
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
  */

+ 1 - 1
wmfs2/src/wmfs.c

@@ -292,7 +292,7 @@ void
 uicb_reload(Uicb cmd)
 {
      (void)cmd;
-
+     /* TODO */
 }
 
 void

+ 2 - 2
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;

+ 1 - 0
wmfs2/wmfsrc2

@@ -4,6 +4,7 @@
 
 [themes]
 
+
   [theme]
      # name = "default"