[hackers] [libgrapheme] Add lg_utf8_*() manuals to Makefile MAN3-variable || Laslo Hunhold

2021-12-17 Thread git
commit 29c6958306523d0370488403717047ceb960bc69
Author: Laslo Hunhold 
AuthorDate: Sat Dec 18 01:44:28 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sat Dec 18 01:44:28 2021 +0100

Add lg_utf8_*() manuals to Makefile MAN3-variable

Signed-off-by: Laslo Hunhold 

diff --git a/Makefile b/Makefile
index 104d805..64327f3 100644
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,8 @@ TEST =\
 MAN3 =\
man/lg_grapheme_isbreak.3\
man/lg_grapheme_nextbreak.3\
+   man/lg_utf8_decode.3\
+   man/lg_utf8_encode.3\
 
 MAN7 = man/libgrapheme.7
 



[hackers] [libgrapheme] Add manual pages for lg_utf8_*() and refactor lg_grapheme_nextbreak() || Laslo Hunhold

2021-12-17 Thread git
commit 4aa9cbec9fa8cc9faeddadac5f4108c367d40718
Author: Laslo Hunhold 
AuthorDate: Sat Dec 18 01:26:53 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sat Dec 18 01:26:53 2021 +0100

Add manual pages for lg_utf8_*() and refactor lg_grapheme_nextbreak()

Officially document how to treat null-terminated strings and use
(size_t)-1 instead of some magic number 5. Using the maximum allowed
size indicates clearly that len is not used at all within the decoder.

Signed-off-by: Laslo Hunhold 

diff --git a/man/lg_utf8_decode.3 b/man/lg_utf8_decode.3
new file mode 100644
index 000..5dfb50a
--- /dev/null
+++ b/man/lg_utf8_decode.3
@@ -0,0 +1,101 @@
+.Dd 2021-12-17
+.Dt LG_UTF8_DECODE 3
+.Os suckless.org
+.Sh NAME
+.Nm lg_utf8_decode
+.Nd decode first code point in UTF-8-encoded string
+.Sh SYNOPSIS
+.In grapheme.h
+.Ft size_t
+.Fn lg_utf8_decode "const char *str" "size_t len" "uint_least32_t *cp"
+.Sh DESCRIPTION
+The
+.Fn lg_utf8_decode
+function decodes the next code point in the UTF-8-encoded string
+.Va str
+of length
+.Va len .
+If the UTF-8-sequence is invalid (overlong encoding, unexpected byte,
+string ends unexpectedly, empty string, etc.) the decoding is stopped
+at the last processed byte and the decoded code point set to
+.Dv LG_INVALID_CODE_POINT.
+.Pp
+If
+.Va cp
+is not
+.Dv NULL
+the decoded code point is stored in the memory pointed to by
+.Va cp .
+.Pp
+Given NUL has a unique 1 byte representation, it is safe to operate on
+NUL-terminated strings by setting
+.Va len
+to
+.Dv (size_t)-1
+and terminating when
+.Va cp
+is 0 (see
+.Sx EXAMPLES
+for an example).
+.Sh RETURN VALUES
+The
+.Fn lg_utf8_decode
+function returns the number of processed bytes and 0 if
+.Va str
+is
+.Dv NULL
+or
+.Va len
+is 0.
+If the string ends unexpectedly in a multibyte sequence, the desired
+length (that is larger than
+.Va len )
+is returned.
+.Sh EXAMPLES
+.Bd -literal
+/* cc (-static) -o example example.c -lgrapheme */
+#include 
+#include 
+#include 
+
+void
+print_cps(const char *str, size_t len)
+{
+   size_t ret, off;
+   uint_least32_t cp;
+
+   for (off = 0; off < len; off += ret) {
+   if ((ret = lg_utf8_decode(str + off,
+ len - off, )) > (len - off)) { 
+   /*
+* string ended unexpectedly in the middle of a
+* multibyte sequence and we have the choice
+* here to possibly expand str by ret - len + off
+* bytes to get a full sequence, but we just
+* bail out in this case.
+*/
+   break;
+   }
+   printf("%"PRIxLEAST32"\\n", cp);
+   }
+}
+
+void
+print_cps_nul_terminated(const char *str)
+{
+   size_t ret, off;
+   uint_least32_t cp;
+
+   for (off = 0; (ret = lg_utf8_decode(str + off,
+   (size_t)-1, )) > 0 &&
+cp != 0; off += ret) {
+   printf("%"PRIxLEAST32"\\n", cp);
+   }
+}
+.Ed
+.Sh SEE ALSO
+.Xr lg_grapheme_encode 3 ,
+.Xr lg_grapheme_isbreak 3 ,
+.Xr libgrapheme 7
+.Sh AUTHORS
+.An Laslo Hunhold Aq Mt d...@frign.de
diff --git a/man/lg_utf8_encode.3 b/man/lg_utf8_encode.3
new file mode 100644
index 000..542cdae
--- /dev/null
+++ b/man/lg_utf8_encode.3
@@ -0,0 +1,98 @@
+.Dd 2021-12-17
+.Dt LG_UTF8_ENCODE 3
+.Os suckless.org
+.Sh NAME
+.Nm lg_utf8_encode
+.Nd encode code point into UTF-8 string
+.Sh SYNOPSIS
+.In grapheme.h
+.Ft size_t
+.Fn lg_utf8_encode "uint_least32_t cp" "char *" "size_t"
+.Sh DESCRIPTION
+The
+.Fn lg_utf8_encode
+function encodes the code point
+.Va cp
+into a UTF-8-string.
+If
+.Va str
+is not
+.Dv NULL
+and
+.Va len
+is large enough it writes the UTF-8-string to the memory pointed to by
+.Va str .
+.Sh RETURN VALUES
+The
+.Fn lg_utf8_encode
+function returns the length (in bytes) of the UTF-8-string resulting
+from encoding
+.Va cp .
+When the returned value is larger than
+.Va len
+it is indicated that the output string is too small and no data has been
+written.
+.Sh EXAMPLES
+.Bd -literal
+/* cc (-static) -o example example.c -lgrapheme */
+#include 
+#include 
+#include 
+
+size_t
+cps_to_utf8(const uint_least32_t *cp, size_t cplen, char *str, size_t len)
+{
+   size_t i, off, ret;
+
+   for (i = 0, off = 0; i < cplen; i++, off += ret) {
+   if ((ret = lg_utf8_encode(cp[i], str + off,
+ len - off)) > (len - off)) {
+   /* buffer too small */
+   break;
+   }
+   }
+   
+   return off;
+}
+
+size_t
+cps_bytelen(const uint_least32_t *cp, size_t cplen)
+{
+   size_t i, len;
+
+   for (i = 0, len = 0; i < cplen; i++) {
+   len += lg_utf8_encode(cp[i], NULL, 0);
+   }
+
+   return len;
+}
+
+char *
+cps_to_utf8_alloc(const uint_least32_t 

[hackers] [libgrapheme] Refactor manual pages for lg_grapheme_*() || Laslo Hunhold

2021-12-17 Thread git
commit 031a47497bd4ef470bd48b8c9455ae4ce9d88121
Author: Laslo Hunhold 
AuthorDate: Sat Dec 18 01:25:49 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sat Dec 18 01:25:49 2021 +0100

Refactor manual pages for lg_grapheme_*()

Signed-off-by: Laslo Hunhold 

diff --git a/man/lg_grapheme_isbreak.3 b/man/lg_grapheme_isbreak.3
index 2570b2f..2d975dd 100644
--- a/man/lg_grapheme_isbreak.3
+++ b/man/lg_grapheme_isbreak.3
@@ -1,4 +1,4 @@
-.Dd 2021-12-15
+.Dd 2021-12-18
 .Dt LG_GRAPHEME_ISBREAK 3
 .Os suckless.org
 .Sh NAME
@@ -7,16 +7,16 @@
 .Sh SYNOPSIS
 .In grapheme.h
 .Ft size_t
-.Fn lg_grapheme_isbreak "uint_least32_t a, uint_least32_t b, 
LG_SEGMENTATION_STATE *state"
+.Fn lg_grapheme_isbreak "uint_least32_t cp1" "uint_least32_t cp2" 
"LG_SEGMENTATION_STATE *state"
 .Sh DESCRIPTION
 The
 .Fn lg_grapheme_isbreak
 function determines if there is a grapheme cluster break (see
 .Xr libgrapheme 7 )
 between the two code points
-.Va a
+.Va cp1
 and
-.Va b .
+.Va cp2 .
 By specification this decision depends on a
 .Va state
 that can at most be completely reset after detecting a break and must
@@ -29,13 +29,14 @@ is
 .Fn lg_grapheme_isbreak
 behaves as if it was called with a fully reset state.
 .Sh RETURN VALUES
+The
 .Fn lg_grapheme_isbreak
-returns
+function returns
 .Va true
 if there is a grapheme cluster break between the code points
-.Va a
+.Va cp1
 and
-.Va b
+.Va cp2
 and
 .Va false
 if there is not.
diff --git a/man/lg_grapheme_nextbreak.3 b/man/lg_grapheme_nextbreak.3
index ff78395..cad7faf 100644
--- a/man/lg_grapheme_nextbreak.3
+++ b/man/lg_grapheme_nextbreak.3
@@ -1,4 +1,4 @@
-.Dd 2021-12-15
+.Dd 2021-12-18
 .Dt LG_GRAPHEME_NEXTBREAK 3
 .Os suckless.org
 .Sh NAME
@@ -9,8 +9,9 @@
 .Ft size_t
 .Fn lg_grapheme_nextbreak "const char *str"
 .Sh DESCRIPTION
+The
 .Fn lg_grapheme_nextbreak
-computes the offset (in bytes) to the next grapheme
+function computes the offset (in bytes) to the next grapheme
 cluster break (see
 .Xr libgrapheme 7 )
 in the UTF-8-encoded NUL-terminated string
@@ -23,8 +24,9 @@ For non-UTF-8 input data
 .Xr lg_grapheme_isbreak 3
 can be used instead.
 .Sh RETURN VALUES
+The
 .Fn lg_grapheme_nextbreak
-returns the offset (in bytes) to the next grapheme cluster
+function returns the offset (in bytes) to the next grapheme cluster
 break in
 .Va str
 or 0 if



[hackers] [libgrapheme] Improve a small edge-case in lg_utf8_decode() || Laslo Hunhold

2021-12-17 Thread git
commit faeaa564686873e4720a0c1ef9879f58347d754e
Author: Laslo Hunhold 
AuthorDate: Sat Dec 18 01:04:37 2021 +0100
Commit: Laslo Hunhold 
CommitDate: Sat Dec 18 01:23:32 2021 +0100

Improve a small edge-case in lg_utf8_decode()

Okay, this case is really crazy but possible: Before this change,
when we encountered e.g. a 0xF0 (which indicates a 4-byte-UTF-8
sequence and implies 3 subsequent continuation bytes) but have a
string-length of e.g. 2, we would automatically return 4 (> 2) no matter
how the following bytes look like to indicate that we need a larger
buffer.

However, it's actually necessary to check the subsequent bytes until
the buffer-end as we might have a case like

   0xF0 0x80 0x00

where 0xF0 is followed by a single continuation byte but then the
continuation stops and we have a NUL-byte. It's more expected to
return 2 in such a situation because we obtain more information about
the string by inspecting the continuation bytes instead of throwing
our hands up so early.

Also add this to the test-cases of the decoder to prevent any
regressions.

Signed-off-by: Laslo Hunhold 

diff --git a/src/utf8.c b/src/utf8.c
index c04fc0b..efd6068 100644
--- a/src/utf8.c
+++ b/src/utf8.c
@@ -84,11 +84,29 @@ lg_utf8_decode(const char *s, size_t n, uint_least32_t *cp)
}
if (1 + off > n) {
/*
-* input is not long enough, set cp as invalid and
-* return number of bytes needed
+* input is not long enough, set cp as invalid
 */
*cp = LG_INVALID_CODE_POINT;
-   return 1 + off;
+
+   /*
+* count the following continuation bytes, but nothing
+* else in case we have a "rogue" case where e.g. such a
+* sequence starter occurs right before a NUL-byte.
+*/
+   for (i = 0; 1 + i < n; i++) {
+   if(!BETWEEN(((const unsigned char *)s)[1 + i],
+   0x80, 0xBF)) {
+   break;
+   }
+   }
+
+   /*
+* if the continuation bytes do not continue until
+* the end, return the incomplete sequence length.
+* Otherwise return the number of bytes we actually
+* expected, which is larger than n.
+*/
+   return ((1 + i) < n) ? (1 + i) : (1 + off);
}
 
/*
diff --git a/test/utf8-decode.c b/test/utf8-decode.c
index 0749688..d98314c 100644
--- a/test/utf8-decode.c
+++ b/test/utf8-decode.c
@@ -113,6 +113,16 @@ static const struct {
.exp_len = 1,
.exp_cp  = LG_INVALID_CODE_POINT,
},
+   {
+   /* invalid 3-byte sequence (short string, second byte malformed)
+* [ 1110 0111 ] ->
+* INVALID
+*/
+   .arr = (char *)(unsigned char[]){ 0xE0, 0x7F },
+   .len = 2,
+   .exp_len = 1,
+   .exp_cp  = LG_INVALID_CODE_POINT,
+   },
{
/* invalid 3-byte sequence (third byte missing)
 * [ 1110 1011 ] ->
@@ -183,6 +193,27 @@ static const struct {
.exp_len = 1,
.exp_cp  = LG_INVALID_CODE_POINT,
},
+   {
+   /* invalid 4-byte sequence (short string 1, second byte 
malformed)
+* [ 0011 0 ] ->
+* INVALID
+*/
+   .arr = (char *)(unsigned char[]){ 0xF3, 0x7F },
+   .len = 2,
+   .exp_len = 1,
+   .exp_cp  = LG_INVALID_CODE_POINT,
+   },
+   {
+   /* invalid 4-byte sequence (short string 2, second byte 
malformed)
+* [ 0011 0 1011 ] ->
+* INVALID
+*/
+   .arr = (char *)(unsigned char[]){ 0xF3, 0x7F, 0xBF },
+   .len = 3,
+   .exp_len = 1,
+   .exp_cp  = LG_INVALID_CODE_POINT,
+   },
+
{
/* invalid 4-byte sequence (third byte missing)
 * [ 0011 1011 ] ->
@@ -203,6 +234,16 @@ static const struct {
.exp_len = 2,
.exp_cp  = LG_INVALID_CODE_POINT,
},
+   {
+   /* invalid 4-byte sequence (short string, third byte malformed)
+* [ 0011 1011 0111 ] ->
+* INVALID
+*/
+   .arr = (char *)(unsigned char[]){ 0xF3, 0xBF, 0x7F },
+   .len = 3,
+   .exp_len = 2,
+   .exp_cp  = LG_INVALID_CODE_POINT,
+   },
{
/* invalid 4-byte sequence (fourth byte missing)
 * [ 

Re: [hackers] [dwm][patch] gaplessgrid focus left/right

2021-12-17 Thread שלומי אקנין
Thanks for letting me know.

Best regards

On Fri, Dec 17, 2021, 11:53 Hiltjo Posthuma  wrote:

> You send it to the wrong mailinglist.
>
> If its a wiki patch you can upload it, see the wiki guidelines.
>
> Thanks,
>
> On Fri, Dec 17, 2021 at 02:03:15AM +0200, שלומי אקנין wrote:
> > From 76d72e24117a5626827cfb0ef49515ec7f7dd4fe Mon Sep 17 00:00:00 2001
> > From: shlomi-aknin 
> > Date: Thu, 16 Dec 2021 14:53:02 +0200
> > Subject: [PATCH] This patch incorporates gaplessgrid patch. This patch
> adds
> >  the ability to focus on left or right window of the current window
> (works
> > in
> >  gaplessgrid layout only)
> >
> > ---
> >  config.def.h | 5 +++-
> >  dwm.c | 74 +++-
> >  2 files changed, 77 insertions(+), 2 deletions(-)
> >
> > diff --git a/config.def.h b/config.def.h
> > index a2ac963..c63a640 100644
> > --- a/config.def.h
> > +++ b/config.def.h
> > @@ -42,6 +42,7 @@ static const Layout layouts[] = {
> >   { "[]=", tile }, /* first entry is default */
> >   { "><>", NULL }, /* no layout function means floating behavior */
> >   { "[M]", monocle },
> > + { "###", gaplessgrid },
> >  };
> >
> >  /* key definitions */
> > @@ -77,6 +78,7 @@ static Key keys[] = {
> >   { MODKEY, XK_t, setlayout, {.v = [0]} },
> >   { MODKEY, XK_f, setlayout, {.v = [1]} },
> >   { MODKEY, XK_m, setlayout, {.v = [2]} },
> > + { MODKEY, XK_g, setlayout, {.v = [3]} },
> >   { MODKEY, XK_space, setlayout, {0} },
> >   { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
> >   { MODKEY, XK_0, view, {.ui = ~0 } },
> > @@ -85,6 +87,8 @@ static Key keys[] = {
> >   { MODKEY, XK_period, focusmon, {.i = +1 } },
> >   { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
> >   { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
> > + { MODKEY|ControlMask, XK_l, gaplessgridleftright, {.i = +1 } },
> > + { MODKEY|ControlMask, XK_h, gaplessgridleftright, {.i = -1 } },
> >   TAGKEYS( XK_1, 0)
> >   TAGKEYS( XK_2, 1)
> >   TAGKEYS( XK_3, 2)
> > @@ -113,4 +117,3 @@ static Button buttons[] = {
> >   { ClkTagBar, MODKEY, Button1, tag, {0} },
> >   { ClkTagBar, MODKEY, Button3, toggletag, {0} },
> >  };
> > -
> > diff --git a/dwm.c b/dwm.c
> > index 5e4d494..a7fb265 100644
> > --- a/dwm.c
> > +++ b/dwm.c
> > @@ -91,6 +91,7 @@ struct Client {
> >   int oldx, oldy, oldw, oldh;
> >   int basew, baseh, incw, inch, maxw, maxh, minw, minh;
> >   int bw, oldbw;
> > + int gridrow, gridcol;
> >   unsigned int tags;
> >   int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
> >   Client *next;
> > @@ -169,6 +170,8 @@ static void focus(Client *c);
> >  static void focusin(XEvent *e);
> >  static void focusmon(const Arg *arg);
> >  static void focusstack(const Arg *arg);
> > +static void gaplessgrid(Monitor *m);
> > +static void gaplessgridleftright(const Arg *arg);
> >  static Atom getatomprop(Client *c, Atom prop);
> >  static int getrootptr(int *x, int *y);
> >  static long getstate(Window w);
> > @@ -856,6 +859,76 @@ focusstack(const Arg *arg)
> >   }
> >  }
> >
> > +void
> > +gaplessgrid(Monitor *m)
> > +{
> > + unsigned int n, cols, rows, cn, rn, i, cx, cy, cw, ch;
> > + Client *c;
> > +
> > + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), 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;
> > + rows = n/cols;
> > +
> > + /* window geometries */
> > + cw = cols ? m->ww / cols : m->ww;
> > + cn = 0; /* current column number */
> > + rn = 0; /* current row number */
> > + for(i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) {
> > + if(i/rows + 1 > cols - n%cols)
> > + rows = n/cols + 1;
> > + ch = rows ? m->wh / rows : m->wh;
> > + cx = m->wx + cn*cw;
> > + cy = m->wy + rn*ch;
> > + c->gridrow = rn;
> > + c->gridcol = cn;
> > + resize(c, cx, cy, cw - 2 * c->bw, ch - 2 * c->bw, False);
> > + rn++;
> > + if(rn >= rows) {
> > + rn = 0;
> > + cn++;
> > + }
> > + }
> > +}
> > +
> > +void
> > +gaplessgridleftright(const Arg *arg)
> > +{
> > + Client *c = selmon->sel;
> > + Client *t = NULL;
> > + int found = 0;
> > + if(selmon->lt[selmon->sellt]->arrange != gaplessgrid || !ISVISIBLE(c))
> > return;
> > +
> > + if (arg->i > 0) {
> > + for(t = selmon->sel->next; t; t = t->next) {
> > + if (t->gridcol == c->gridcol + 1 && t->gridrow == c->gridrow) break;
> > + }
> > + } else {
> > + for(t = selmon->clients; t; t = t->next) {
> > + if (t->gridcol == c->gridcol - 1 && t->gridrow == c->gridrow) {
> > + found = 1;
> > + break;
> > + }
> > + }
> > +
> > + if (found == 0) {
> > + for(t = selmon->clients; t; t = t->next) {
> > + if (t->gridcol == c->gridcol - 1 && t->gridrow == c->gridrow - 1)
> break;
> > + }
> > + }
> > + }
> > +
> > + focus(t);
> > + arrange(selmon);
> > +}
> > +
> >  Atom
> >  getatomprop(Client *c, Atom prop)
> >  {
> > @@ -1597,7 +1670,6 

Re: [hackers] [dwm][patch] gaplessgrid focus left/right

2021-12-17 Thread Hiltjo Posthuma
You send it to the wrong mailinglist.

If its a wiki patch you can upload it, see the wiki guidelines.

Thanks,

On Fri, Dec 17, 2021 at 02:03:15AM +0200, שלומי אקנין wrote:
> From 76d72e24117a5626827cfb0ef49515ec7f7dd4fe Mon Sep 17 00:00:00 2001
> From: shlomi-aknin 
> Date: Thu, 16 Dec 2021 14:53:02 +0200
> Subject: [PATCH] This patch incorporates gaplessgrid patch. This patch adds
>  the ability to focus on left or right window of the current window (works
> in
>  gaplessgrid layout only)
> 
> ---
>  config.def.h | 5 +++-
>  dwm.c | 74 +++-
>  2 files changed, 77 insertions(+), 2 deletions(-)
> 
> diff --git a/config.def.h b/config.def.h
> index a2ac963..c63a640 100644
> --- a/config.def.h
> +++ b/config.def.h
> @@ -42,6 +42,7 @@ static const Layout layouts[] = {
>   { "[]=", tile }, /* first entry is default */
>   { "><>", NULL }, /* no layout function means floating behavior */
>   { "[M]", monocle },
> + { "###", gaplessgrid },
>  };
> 
>  /* key definitions */
> @@ -77,6 +78,7 @@ static Key keys[] = {
>   { MODKEY, XK_t, setlayout, {.v = [0]} },
>   { MODKEY, XK_f, setlayout, {.v = [1]} },
>   { MODKEY, XK_m, setlayout, {.v = [2]} },
> + { MODKEY, XK_g, setlayout, {.v = [3]} },
>   { MODKEY, XK_space, setlayout, {0} },
>   { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
>   { MODKEY, XK_0, view, {.ui = ~0 } },
> @@ -85,6 +87,8 @@ static Key keys[] = {
>   { MODKEY, XK_period, focusmon, {.i = +1 } },
>   { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
>   { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
> + { MODKEY|ControlMask, XK_l, gaplessgridleftright, {.i = +1 } },
> + { MODKEY|ControlMask, XK_h, gaplessgridleftright, {.i = -1 } },
>   TAGKEYS( XK_1, 0)
>   TAGKEYS( XK_2, 1)
>   TAGKEYS( XK_3, 2)
> @@ -113,4 +117,3 @@ static Button buttons[] = {
>   { ClkTagBar, MODKEY, Button1, tag, {0} },
>   { ClkTagBar, MODKEY, Button3, toggletag, {0} },
>  };
> -
> diff --git a/dwm.c b/dwm.c
> index 5e4d494..a7fb265 100644
> --- a/dwm.c
> +++ b/dwm.c
> @@ -91,6 +91,7 @@ struct Client {
>   int oldx, oldy, oldw, oldh;
>   int basew, baseh, incw, inch, maxw, maxh, minw, minh;
>   int bw, oldbw;
> + int gridrow, gridcol;
>   unsigned int tags;
>   int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
>   Client *next;
> @@ -169,6 +170,8 @@ static void focus(Client *c);
>  static void focusin(XEvent *e);
>  static void focusmon(const Arg *arg);
>  static void focusstack(const Arg *arg);
> +static void gaplessgrid(Monitor *m);
> +static void gaplessgridleftright(const Arg *arg);
>  static Atom getatomprop(Client *c, Atom prop);
>  static int getrootptr(int *x, int *y);
>  static long getstate(Window w);
> @@ -856,6 +859,76 @@ focusstack(const Arg *arg)
>   }
>  }
> 
> +void
> +gaplessgrid(Monitor *m)
> +{
> + unsigned int n, cols, rows, cn, rn, i, cx, cy, cw, ch;
> + Client *c;
> +
> + for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), 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;
> + rows = n/cols;
> +
> + /* window geometries */
> + cw = cols ? m->ww / cols : m->ww;
> + cn = 0; /* current column number */
> + rn = 0; /* current row number */
> + for(i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) {
> + if(i/rows + 1 > cols - n%cols)
> + rows = n/cols + 1;
> + ch = rows ? m->wh / rows : m->wh;
> + cx = m->wx + cn*cw;
> + cy = m->wy + rn*ch;
> + c->gridrow = rn;
> + c->gridcol = cn;
> + resize(c, cx, cy, cw - 2 * c->bw, ch - 2 * c->bw, False);
> + rn++;
> + if(rn >= rows) {
> + rn = 0;
> + cn++;
> + }
> + }
> +}
> +
> +void
> +gaplessgridleftright(const Arg *arg)
> +{
> + Client *c = selmon->sel;
> + Client *t = NULL;
> + int found = 0;
> + if(selmon->lt[selmon->sellt]->arrange != gaplessgrid || !ISVISIBLE(c))
> return;
> +
> + if (arg->i > 0) {
> + for(t = selmon->sel->next; t; t = t->next) {
> + if (t->gridcol == c->gridcol + 1 && t->gridrow == c->gridrow) break;
> + }
> + } else {
> + for(t = selmon->clients; t; t = t->next) {
> + if (t->gridcol == c->gridcol - 1 && t->gridrow == c->gridrow) {
> + found = 1;
> + break;
> + }
> + }
> +
> + if (found == 0) {
> + for(t = selmon->clients; t; t = t->next) {
> + if (t->gridcol == c->gridcol - 1 && t->gridrow == c->gridrow - 1) break;
> + }
> + }
> + }
> +
> + focus(t);
> + arrange(selmon);
> +}
> +
>  Atom
>  getatomprop(Client *c, Atom prop)
>  {
> @@ -1597,7 +1670,6 @@ setup(void)
>   focus(NULL);
>  }
> 
> -
>  void
>  seturgent(Client *c, int urg)
>  {
> --
> 2.34.1

> From 76d72e24117a5626827cfb0ef49515ec7f7dd4fe Mon Sep 17 00:00:00 2001
> From: shlomi-aknin 
> Date: Thu, 16 Dec 2021 14:53:02 +0200
> Subject: [PATCH] This patch incorporates gaplessgrid patch. This patch adds
>  the ability to focus on left or right window of the current window (works in
>