diff --git a/README.org b/README.org index 938536e..777c550 100644 --- a/README.org +++ b/README.org @@ -12,6 +12,7 @@ - [[#auto-start][Auto Start]] - [[#float-unfocused-border-color][Float Unfocused Border Color]] - [[#foreign-toplevel-management][Foreign Toplevel Management]] + - [[#gapless-grid][Gapless Grid]] - [[#ipc][IPC]] - [[#natural-scroll-trackpad][Natural Scroll Trackpad]] - [[#numlock-capslock][Numlock Capslock]] @@ -129,6 +130,16 @@ Implement ~foreign-toplevel-management~, it add handlers for activate, close, fu #define FOREIGN_TOPLEVEL_MANAGEMENT_PATCH 1 #+END_SRC +*** [[https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/gaplessgrid][Gapless Grid]] + +Arranges windows in a grid. Except it adjusts the number of windows in the first few columns to avoid empty cells. + +On widescreens (w > 2*h), it splits to three columns before splitting rows. + +#+BEGIN_SRC c :tangle patches.def.h +#define GAPLESSGRID_PATCH 1 +#+END_SRC + *** [[https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/ipc][IPC]] Largely based on [[https://sr.ht/~raphi/][raphi]]'s [[https://sr.ht/~raphi/somebar/][somebar]], this patch provides an ipc for wayland clients to get and set dwl state. The ipc is intended for status bars, but can also be scripted with tools like [[https://codeberg.org/notchoc/dwlmsg][dwlmsg]]. @@ -301,6 +312,9 @@ static const Layout layouts[] = { { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, +#if GAPLESSGRID_PATCH + { "###", gaplessgrid }, +#endif // GAPLESSGRID_PATCH }; #+END_SRC @@ -453,6 +467,9 @@ static const Key keys[] = { { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, +#if GAPLESSGRID_PATCH + { MODKEY, XKB_KEY_g, setlayout, {.v = &layouts[3]} }, +#endif // GAPLESSGRID_PATCH { MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, diff --git a/config.def.h b/config.def.h index e7a3b91..b38efd1 100644 --- a/config.def.h +++ b/config.def.h @@ -62,6 +62,9 @@ static const Layout layouts[] = { { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, +#if GAPLESSGRID_PATCH + { "###", gaplessgrid }, +#endif // GAPLESSGRID_PATCH }; /* monitors */ @@ -197,6 +200,9 @@ static const Key keys[] = { { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, +#if GAPLESSGRID_PATCH + { MODKEY, XKB_KEY_g, setlayout, {.v = &layouts[3]} }, +#endif // GAPLESSGRID_PATCH { MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, diff --git a/config.h b/config.h index a7248d4..3e7c45f 100644 --- a/config.h +++ b/config.h @@ -58,6 +58,9 @@ static const Layout layouts[] = { { "[]=", tile }, { "><>", NULL }, /* no layout function means floating behavior */ { "[M]", monocle }, +#if GAPLESSGRID_PATCH + { "###", gaplessgrid }, +#endif // GAPLESSGRID_PATCH }; /* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator @@ -190,6 +193,9 @@ static const Key keys[] = { { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, +#if GAPLESSGRID_PATCH + { MODKEY, XKB_KEY_g, setlayout, {.v = &layouts[3]} }, +#endif // GAPLESSGRID_PATCH { MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, diff --git a/dwl.c b/dwl.c index 07f6436..4fc6bcb 100644 --- a/dwl.c +++ b/dwl.c @@ -360,6 +360,9 @@ static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); static void fullscreennotify(struct wl_listener *listener, void *data); static void gpureset(struct wl_listener *listener, void *data); +#if GAPLESSGRID_PATCH +static void gaplessgrid(Monitor *m); +#endif // GAPLESSGRID_PATCH #if UNCLUTTER_PATCH static void handlecursoractivity(void); static int hidecursor(void *data); @@ -2048,6 +2051,58 @@ gpureset(struct wl_listener *listener, void *data) wlr_renderer_destroy(old_drw); } +#if GAPLESSGRID_PATCH +void +gaplessgrid(Monitor *m) +{ + unsigned int n = 0, i = 0, ch, cw, cn, rn, rows, cols; + Client *c; + + wl_list_for_each(c, &clients, link) + if (VISIBLEON(c, m) && !c->isfloating) + n++; + if (n == 0) + return; + + /* grid dimensions */ + for (cols = 0; cols <= (n / 2); cols++) + if ((cols * cols) >= n) + break; + + if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ + cols = 2; + + /* widescreen is better if 3 columns */ + if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1) + cols = 3; + + rows = n / cols; + + /* window geometries */ + cw = cols ? m->w.width / cols : m->w.width; + cn = 0; /* current column number */ + rn = 0; /* current row number */ + wl_list_for_each(c, &clients, link) { + unsigned int cx, cy; + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + continue; + + if ((i / rows + 1) > (cols - n % cols)) + rows = n / cols + 1; + ch = rows ? m->w.height / rows : m->w.height; + cx = m->w.x + cn * cw; + cy = m->w.y + rn * ch; + resize(c, (struct wlr_box) { cx, cy, cw, ch}, 0); + rn++; + if (rn >= rows) { + rn = 0; + cn++; + } + i++; + } +} +#endif // GAPLESSGRID_PATCH + void handlesig(int signo) { diff --git a/patches.def.h b/patches.def.h index 694ca89..bd26e69 100644 --- a/patches.def.h +++ b/patches.def.h @@ -8,6 +8,8 @@ #define FOREIGN_TOPLEVEL_MANAGEMENT_PATCH 1 +#define GAPLESSGRID_PATCH 1 + #define IPC_PATCH 1 #define NATURALSCROLLTRACKPAD_PATCH 1 diff --git a/patches/gaplessgrid-20240918.patch b/patches/gaplessgrid-20240918.patch new file mode 100644 index 0000000..2af1072 --- /dev/null +++ b/patches/gaplessgrid-20240918.patch @@ -0,0 +1,89 @@ +diff --git a/config.def.h b/config.def.h +index 22d2171..014a909 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -34,6 +34,7 @@ static const Layout layouts[] = { + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "###", gaplessgrid }, + }; + + /* monitors */ +@@ -139,6 +140,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, ++ { MODKEY, XKB_KEY_g, setlayout, {.v = &layouts[3]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, +diff --git a/dwl.c b/dwl.c +index dc0c861..875d8cd 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -292,6 +292,7 @@ static void focusstack(const Arg *arg); + static Client *focustop(Monitor *m); + static void fullscreennotify(struct wl_listener *listener, void *data); + static void gpureset(struct wl_listener *listener, void *data); ++static void gaplessgrid(Monitor *m); + static void handlesig(int signo); + static void incnmaster(const Arg *arg); + static void inputdevice(struct wl_listener *listener, void *data); +@@ -1510,6 +1511,56 @@ handlesig(int signo) + } + } + ++void ++gaplessgrid(Monitor *m) ++{ ++ unsigned int n = 0, i = 0, ch, cw, cn, rn, rows, cols; ++ Client *c; ++ ++ wl_list_for_each(c, &clients, link) ++ if (VISIBLEON(c, m) && !c->isfloating) ++ n++; ++ if (n == 0) ++ return; ++ ++ /* grid dimensions */ ++ for (cols = 0; cols <= (n / 2); cols++) ++ if ((cols * cols) >= n) ++ break; ++ ++ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */ ++ cols = 2; ++ ++ /* widescreen is better if 3 columns */ ++ if (n >= 3 && n <= 6 && (m->w.width / m->w.height) > 1) ++ cols = 3; ++ ++ rows = n / cols; ++ ++ /* window geometries */ ++ cw = cols ? m->w.width / cols : m->w.width; ++ cn = 0; /* current column number */ ++ rn = 0; /* current row number */ ++ wl_list_for_each(c, &clients, link) { ++ unsigned int cx, cy; ++ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) ++ continue; ++ ++ if ((i / rows + 1) > (cols - n % cols)) ++ rows = n / cols + 1; ++ ch = rows ? m->w.height / rows : m->w.height; ++ cx = m->w.x + cn * cw; ++ cy = m->w.y + rn * ch; ++ resize(c, (struct wlr_box) { cx, cy, cw, ch}, 0); ++ rn++; ++ if (rn >= rows) { ++ rn = 0; ++ cn++; ++ } ++ i++; ++ } ++} ++ + void + incnmaster(const Arg *arg) + {