Hello all, I've been using dwm for a bit on my laptop in both a docked and mobile configuration, which involves frequent switching between portrait and landscape monitor layouts. The default tiling mode isn't quite optimal for this. As a result I've done a bit of work reviving the original flextile patch by jo...@freenet.de.
This wasn't a pure update on the patch against 5.8.2. As-is, I manually took code chunks from the original patch, rewrote it a bit to use the now-included nmaster code, and rolled the per-tag attributes into a separate struct (much like the pertag patch) to avoid moving the Monitor struct definition out of dwm.c. I've attached the current diff, but have not submitted this patch update to the original wiki entry as I'm unsure of best practice in this case, and I may experiment with some enhancements. Best regards, Jon
diff --git a/config.def.h b/config.def.h index 875885b..1e941a1 100644 --- a/config.def.h +++ b/config.def.h @@ -30,6 +30,11 @@ static const Rule rules[] = { static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */ static const int nmaster = 1; /* number of clients in master area */ static const Bool resizehints = True; /* True means respect size hints in tiled resizals */ +static const int layoutaxis[] = { + 1, /* layout axis: 1 = x, 2 = y; negative values mirror the layout */ + 2, /* master axis: 1 = x (left to right), 2 = y (top to bottom), 3 = z (monocle) */ + 2, /* stack axis: 1 = x (left to right), 2 = y (top to bottom), 3 = z (monocle) */ +}; static const Layout layouts[] = { /* symbol arrange function */ @@ -79,6 +84,10 @@ static Key keys[] = { { MODKEY, XK_period, focusmon, {.i = +1 } }, { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_Tab, rotatelayoutaxis, {.i = 0} }, /* 0 = layout axis */ + { MODKEY|ControlMask, XK_Tab, rotatelayoutaxis, {.i = 1} }, /* 1 = master axis */ + { MODKEY|ControlMask|ShiftMask, XK_Tab, rotatelayoutaxis, {.i = 2} }, /* 2 = stack axis */ + { MODKEY|ControlMask, XK_Return, mirrorlayout, {0} }, TAGKEYS( XK_1, 0) TAGKEYS( XK_2, 1) TAGKEYS( XK_3, 2) diff --git a/dwm.c b/dwm.c index 1bbb4b3..f758b4e 100644 --- a/dwm.c +++ b/dwm.c @@ -110,8 +110,10 @@ typedef struct { void (*arrange)(Monitor *); } Layout; +typedef struct Pertag Pertag; struct Monitor { char ltsymbol[16]; + int ltaxis[3]; float mfact; int nmaster; int num; @@ -129,6 +131,7 @@ struct Monitor { Monitor *next; Window barwin; const Layout *lt[2]; + Pertag *pertag; }; typedef struct { @@ -180,6 +183,7 @@ static void killclient(const Arg *arg); static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); static void maprequest(XEvent *e); +static void mirrorlayout(const Arg *arg); static void monocle(Monitor *m); static void motionnotify(XEvent *e); static void movemouse(const Arg *arg); @@ -192,6 +196,7 @@ static void resize(Client *c, int x, int y, int w, int h, Bool interact); static void resizeclient(Client *c, int x, int y, int w, int h); static void resizemouse(const Arg *arg); static void restack(Monitor *m); +static void rotatelayoutaxis(const Arg *arg); static void run(void); static void scan(void); static Bool sendevent(Client *c, Atom proto); @@ -270,6 +275,15 @@ static Window root; /* configuration, allows nested code to access above variables */ #include "config.h" +struct Pertag { + unsigned int curtag, prevtag; + int ltaxes[LENGTH(tags) + 1][3]; + int nmasters[LENGTH(tags) + 1]; + float mfacts[LENGTH(tags) + 1]; + const Layout *lts[LENGTH(tags) + 1]; + Bool showbars[LENGTH(tags) + 1]; +}; + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -631,6 +645,7 @@ configurerequest(XEvent *e) { Monitor * createmon(void) { Monitor *m; + unsigned int i; if(!(m = (Monitor *)calloc(1, sizeof(Monitor)))) die("fatal: could not malloc() %u bytes\n", sizeof(Monitor)); @@ -642,6 +657,22 @@ createmon(void) { m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->ltaxis[0] = layoutaxis[0]; + m->ltaxis[1] = layoutaxis[1]; + m->ltaxis[2] = layoutaxis[2]; + if(!(m->pertag = (Pertag *)calloc(1, sizeof(Pertag)))) + die("fatal: could not malloc() %u bytes\n", sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + /* init tags, bars, layouts, axes, nmasters, and mfacts */ + for(i = 0; i < LENGTH(tags) + 1; i++){ + m->pertag->showbars[i] = m->showbar; + m->pertag->lts[i] = &layouts[0]; + m->pertag->mfacts[i] = m->mfact; + m->pertag->ltaxes[i][0] = m->ltaxis[0]; + m->pertag->ltaxes[i][1] = m->ltaxis[1]; + m->pertag->ltaxes[i][2] = m->ltaxis[2]; + m->pertag->nmasters[i] = m->nmaster; + } return m; } @@ -958,7 +989,13 @@ grabkeys(void) { void incnmaster(const Arg *arg) { - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + unsigned int n; + Client *c; + + for(n = 0, c = nexttiled(selmon->clients); c; c = nexttiled(c->next), n++); + if(!arg || !selmon->lt[selmon->sellt]->arrange || selmon->nmaster + arg->i < 1 || selmon->nmaster + arg->i > n) + return; + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); arrange(selmon); } @@ -1088,6 +1125,15 @@ maprequest(XEvent *e) { } void +mirrorlayout(const Arg *arg) { + if(!selmon->lt[selmon->sellt]->arrange) + return; + selmon->ltaxis[0] *= -1; + selmon->pertag->ltaxes[selmon->pertag->curtag][0] = selmon->ltaxis[0]; + arrange(selmon); +} + +void monocle(Monitor *m) { unsigned int n = 0; Client *c; @@ -1339,6 +1385,22 @@ restack(Monitor *m) { } void +rotatelayoutaxis(const Arg *arg) { + if(!selmon->lt[selmon->sellt]->arrange) + return; + if(arg->i == 0) { + if(selmon->ltaxis[0] > 0) + selmon->ltaxis[0] = selmon->ltaxis[0] + 1 > 2 ? 1 : selmon->ltaxis[0] + 1; + else + selmon->ltaxis[0] = selmon->ltaxis[0] - 1 < -2 ? -1 : selmon->ltaxis[0] - 1; + } + else + selmon->ltaxis[arg->i] = selmon->ltaxis[arg->i] + 1 > 3 ? 1 : selmon->ltaxis[arg->i] + 1; + selmon->pertag->ltaxes[selmon->pertag->curtag][arg->i] = selmon->ltaxis[arg->i]; + arrange(selmon); +} + +void run(void) { XEvent ev; /* main event loop */ @@ -1465,7 +1527,7 @@ setlayout(const Arg *arg) { if(!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) selmon->sellt ^= 1; if(arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->lts[selmon->pertag->curtag] = (Layout *)arg->v; strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); if(selmon->sel) arrange(selmon); @@ -1483,7 +1545,7 @@ setmfact(const Arg *arg) { f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if(f < 0.1 || f > 0.9) return; - selmon->mfact = f; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; arrange(selmon); } @@ -1601,33 +1663,93 @@ tagmon(const Arg *arg) { void tile(Monitor *m) { - unsigned int i, n, h, mw, my, ty; + char sym1 = 61, sym2 = 93, sym3 = 61, sym; + int x1 = m->wx, y1 = m->wy, h1 = m->wh, w1 = m->ww, X1 = x1 + w1, Y1 = y1 + h1; + int x2 = m->wx, y2 = m->wy, h2 = m->wh, w2 = m->ww, X2 = x2 + w2, Y2 = y2 + h2; + unsigned int i, n, n1, n2; Client *c; for(n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); + if(m->nmaster > n) + m->nmaster = (n == 0) ? 1 : n; + /* layout symbol */ + if(abs(m->ltaxis[0]) == m->ltaxis[1]) /* explicitly: ((abs(m->ltaxis[0]) == 1 && m->ltaxis[1] == 1) || (abs(m->ltaxis[0]) == 2 && m->ltaxis[1] == 2)) */ + sym1 = 124; + if(abs(m->ltaxis[0]) == m->ltaxis[2]) + sym3 = 124; + if(m->ltaxis[1] == 3) + sym1 = (n == 0) ? 0 : m->nmaster; + if(m->ltaxis[2] == 3) + sym3 = (n == 0) ? 0 : n - m->nmaster; + if(m->ltaxis[0] < 0) { + sym = sym1; + sym1 = sym3; + sym2 = 91; + sym3 = sym; + } + if(m->nmaster == 1) { + if(m->ltaxis[0] > 0) + sym1 = 91; + else + sym3 = 93; + } + if(m->nmaster > 1 && m->ltaxis[1] == 3 && m->ltaxis[2] == 3) + snprintf(m->ltsymbol, sizeof m->ltsymbol, "%d%c%d", sym1, sym2, sym3); + else if((m->nmaster > 1 && m->ltaxis[1] == 3 && m->ltaxis[0] > 0) || (m->ltaxis[2] == 3 && m->ltaxis[0] < 0)) + snprintf(m->ltsymbol, sizeof m->ltsymbol, "%d%c%c", sym1, sym2, sym3); + else if((m->ltaxis[2] == 3 && m->ltaxis[0] > 0) || (m->nmaster > 1 && m->ltaxis[1] == 3 && m->ltaxis[0] < 0)) + snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%d", sym1, sym2, sym3); + else + snprintf(m->ltsymbol, sizeof m->ltsymbol, "%c%c%c", sym1, sym2, sym3); if(n == 0) return; - - if(n > m->nmaster) - mw = m->nmaster ? m->ww * m->mfact : 0; - else - mw = m->ww; - for(i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) - if(i < m->nmaster) { - h = (m->wh - my) / (MIN(n, m->nmaster) - i); - resize(c, m->wx, m->wy + my, mw - (2*c->bw), h - (2*c->bw), False); - my += HEIGHT(c); - } - else { - h = (m->wh - ty) / (n - i); - resize(c, m->wx + mw, m->wy + ty, m->ww - mw - (2*c->bw), h - (2*c->bw), False); - ty += HEIGHT(c); + /* master and stack area */ + if(abs(m->ltaxis[0]) == 1 && n > m->nmaster) { + w1 *= m->mfact; + w2 -= w1; + x1 += (m->ltaxis[0] < 0) ? w2 : 0; + x2 += (m->ltaxis[0] < 0) ? 0 : w1; + X1 = x1 + w1; + X2 = x2 + w2; + } else if(abs(m->ltaxis[0]) == 2 && n > m->nmaster) { + h1 *= m->mfact; + h2 -= h1; + y1 += (m->ltaxis[0] < 0) ? h2 : 0; + y2 += (m->ltaxis[0] < 0) ? 0 : h1; + Y1 = y1 + h1; + Y2 = y2 + h2; + } + /* master */ + n1 = (m->ltaxis[1] != 1 || w1 / m->nmaster < bh) ? 1 : m->nmaster; + n2 = (m->ltaxis[1] != 2 || h1 / m->nmaster < bh) ? 1 : m->nmaster; + for(i = 0, c = nexttiled(m->clients); i < m->nmaster; c = nexttiled(c->next), i++) { + resize(c, x1, y1, + (m->ltaxis[1] == 1 && i + 1 == m->nmaster) ? X1 - x1 - 2 * c->bw : w1 / n1 - 2 * c->bw, + (m->ltaxis[1] == 2 && i + 1 == m->nmaster) ? Y1 - y1 - 2 * c->bw : h1 / n2 - 2 * c->bw, False); + if(n1 > 1) + x1 = c->x + WIDTH(c); + if(n2 > 1) + y1 = c->y + HEIGHT(c); + } + /* stack */ + if(n > m->nmaster) { + n1 = (m->ltaxis[2] != 1 || w2 / (n - m->nmaster) < bh) ? 1 : n - m->nmaster; + n2 = (m->ltaxis[2] != 2 || h2 / (n - m->nmaster) < bh) ? 1 : n - m->nmaster; + for(i = 0; c; c = nexttiled(c->next), i++) { + resize(c, x2, y2, + (m->ltaxis[2] == 1 && i + 1 == n - m->nmaster) ? X2 - x2 - 2 * c->bw : w2 / n1 - 2 * c->bw, + (m->ltaxis[2] == 2 && i + 1 == n - m->nmaster) ? Y2 - y2 - 2 * c->bw : h2 / n2 - 2 * c->bw, False); + if(n1 > 1) + x2 = c->x + WIDTH(c); + if(n2 > 1) + y2 = c->y + HEIGHT(c); } + } } void togglebar(const Arg *arg) { - selmon->showbar = !selmon->showbar; + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; updatebarpos(selmon); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); arrange(selmon); @@ -1649,12 +1771,30 @@ togglefloating(const Arg *arg) { void toggletag(const Arg *arg) { unsigned int newtags; + unsigned int i; if(!selmon->sel) return; newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); if(newtags) { selmon->sel->tags = newtags; + if(newtags == ~0) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + if(!(newtags & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtags & 1 << i); i++); /* get first new tag */ + selmon->pertag->curtag = i + 1; + } + selmon->lt[selmon->sellt] = selmon->pertag->lts[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->ltaxis[0] = selmon->pertag->ltaxes[selmon->pertag->curtag][0]; + selmon->ltaxis[1] = selmon->pertag->ltaxes[selmon->pertag->curtag][1]; + selmon->ltaxis[2] = selmon->pertag->ltaxes[selmon->pertag->curtag][2]; + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); focus(NULL); arrange(selmon); } @@ -1960,11 +2100,30 @@ updatewmhints(Client *c) { void view(const Arg *arg) { + unsigned int i; if((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ - if(arg->ui & TAGMASK) + if(arg->ui & TAGMASK) { selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + selmon->pertag->prevtag = selmon->pertag->curtag; + if(arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for(i = 0; !(arg->ui & 1 << i); i++); + selmon->pertag->curtag = i + 1; + } + } else { + selmon->pertag->prevtag = selmon->pertag->curtag ^ selmon->pertag->prevtag; + selmon->pertag->curtag ^= selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag ^ selmon->pertag->prevtag; + } + selmon->lt[selmon->sellt] = selmon->pertag->lts[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->ltaxis[0] = selmon->pertag->ltaxes[selmon->pertag->curtag][0]; + selmon->ltaxis[1] = selmon->pertag->ltaxes[selmon->pertag->curtag][1]; + selmon->ltaxis[2] = selmon->pertag->ltaxes[selmon->pertag->curtag][2]; focus(NULL); arrange(selmon); }