diff --git a/README.md b/README.md index 23b362e..6c13433 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t ### Changelog: +2020-06-21 - Added floatpos patch + 2020-06-19 - Added tagothermonitor patch 2020-06-15 - Added sizehints patch @@ -232,6 +234,12 @@ Refer to [https://dwm.suckless.org/](https://dwm.suckless.org/) for details on t - [floatbordercolor](https://dwm.suckless.org/patches/float_border_color/) - this patch allows a different border color to be chosen for floating windows + - [floatpos](https://github.com/bakkeby/patches/wiki/floatpos) + - adds a float rule allowing the size and position of floating windows to be specified + - control the size and position of floating windows similar to exresize, moveresize, moveplace patches + - specify size and position using absolute, relative or fixed co-ordinates or + - position floating windows in a grid-like manner + - [focusadjacenttag](https://dwm.suckless.org/patches/focusadjacenttag/) - provides the ability to focus the tag on the immediate left or right of the currently focused tag - it also allows to send the focused window either on the left or the right tag diff --git a/config.def.h b/config.def.h index 0a43c48..34850a3 100644 --- a/config.def.h +++ b/config.def.h @@ -28,6 +28,10 @@ static const int sidepad = 10; /* horizontal padding of bar */ #if FOCUSONCLICK_PATCH static const int focusonwheel = 0; #endif // FOCUSONCLICK_PATCH +#if FLOATPOS_PATCH +static int floatposgrid_x = 5; /* float grid columns */ +static int floatposgrid_y = 5; /* float grid rows */ +#endif // FLOATPOS_PATCH #if STATUSPADDING_PATCH static const int horizpadbar = 2; /* horizontal padding for statusbar */ static const int vertpadbar = 0; /* vertical padding for statusbar */ @@ -1033,6 +1037,48 @@ static Key keys[] = { { MODKEY|ControlMask, XK_KP_7, togglemaximize, {.i = +1} }, /* XK_KP_Home, */ { MODKEY|ControlMask, XK_KP_5, togglemaximize, {.i = 0} }, /* XK_KP_Begin, */ #endif // EXRESIZE_PATCH + #if FLOATPOS_PATCH + /* Note that due to key limitations the below example kybindings are defined with a Mod3Mask, + * which is not always readily available. Refer to the patch wiki for more details. */ + /* Client position is limited to monitor window area */ + { Mod3Mask, XK_u, floatpos, {.v = "-26x -26y" } }, + { Mod3Mask, XK_i, floatpos, {.v = " 0x -26y" } }, + { Mod3Mask, XK_o, floatpos, {.v = " 26x -26y" } }, + { Mod3Mask, XK_j, floatpos, {.v = "-26x 0y" } }, + { Mod3Mask, XK_l, floatpos, {.v = " 26x 0y" } }, + { Mod3Mask, XK_m, floatpos, {.v = "-26x 26y" } }, + { Mod3Mask, XK_comma, floatpos, {.v = " 0x 26y" } }, + { Mod3Mask, XK_period, floatpos, {.v = " 26x 26y" } }, + /* Absolute positioning (allows moving windows between monitors) */ + { Mod3Mask|ControlMask, XK_u, floatpos, {.v = "-26a -26a" } }, + { Mod3Mask|ControlMask, XK_i, floatpos, {.v = " 0a -26a" } }, + { Mod3Mask|ControlMask, XK_o, floatpos, {.v = " 26a -26a" } }, + { Mod3Mask|ControlMask, XK_j, floatpos, {.v = "-26a 0a" } }, + { Mod3Mask|ControlMask, XK_l, floatpos, {.v = " 26a 0a" } }, + { Mod3Mask|ControlMask, XK_m, floatpos, {.v = "-26a 26a" } }, + { Mod3Mask|ControlMask, XK_comma, floatpos, {.v = " 0a 26a" } }, + { Mod3Mask|ControlMask, XK_period, floatpos, {.v = " 26a 26a" } }, + /* Resize client, client center position is fixed which means that client expands in all directions */ + { Mod3Mask|ShiftMask, XK_u, floatpos, {.v = "-26w -26h" } }, + { Mod3Mask|ShiftMask, XK_i, floatpos, {.v = " 0w -26h" } }, + { Mod3Mask|ShiftMask, XK_o, floatpos, {.v = " 26w -26h" } }, + { Mod3Mask|ShiftMask, XK_j, floatpos, {.v = "-26w 0h" } }, + { Mod3Mask|ShiftMask, XK_k, floatpos, {.v = "800W 800H" } }, + { Mod3Mask|ShiftMask, XK_l, floatpos, {.v = " 26w 0h" } }, + { Mod3Mask|ShiftMask, XK_m, floatpos, {.v = "-26w 26h" } }, + { Mod3Mask|ShiftMask, XK_comma, floatpos, {.v = " 0w 26h" } }, + { Mod3Mask|ShiftMask, XK_period, floatpos, {.v = " 26w 26h" } }, + /* Client is positioned in a floating grid, movement is relative to client's current position */ + { Mod3Mask|Mod1Mask, XK_u, floatpos, {.v = "-1p -1p" } }, + { Mod3Mask|Mod1Mask, XK_i, floatpos, {.v = " 0p -1p" } }, + { Mod3Mask|Mod1Mask, XK_o, floatpos, {.v = " 1p -1p" } }, + { Mod3Mask|Mod1Mask, XK_j, floatpos, {.v = "-1p 0p" } }, + { Mod3Mask|Mod1Mask, XK_k, floatpos, {.v = " 0p 0p" } }, + { Mod3Mask|Mod1Mask, XK_l, floatpos, {.v = " 1p 0p" } }, + { Mod3Mask|Mod1Mask, XK_m, floatpos, {.v = "-1p 1p" } }, + { Mod3Mask|Mod1Mask, XK_comma, floatpos, {.v = " 0p 1p" } }, + { Mod3Mask|Mod1Mask, XK_period, floatpos, {.v = " 1p 1p" } }, + #endif // FLOATPOS_PATCH #if SETBORDERPX_PATCH { MODKEY|ControlMask, XK_minus, setborderpx, {.i = -1 } }, { MODKEY|ControlMask, XK_plus, setborderpx, {.i = +1 } }, diff --git a/dwm.c b/dwm.c index 3d1d1c6..e4fccd9 100644 --- a/dwm.c +++ b/dwm.c @@ -319,6 +319,9 @@ typedef struct { int isterminal; int noswallow; #endif // SWALLOW_PATCH + #if FLOATPOS_PATCH + const char *floatpos; + #endif // FLOATPOS_PATCH int monitor; } Rule; @@ -610,6 +613,11 @@ applyrules(Client *c) c->y = c->mon->wy + (c->mon->wh / 2 - HEIGHT(c) / 2); } #endif // SCRATCHPADS_PATCH | SCRATCHPAD_KEEP_POSITION_AND_SIZE_PATCH + #if FLOATPOS_PATCH + if (c->isfloating && r->floatpos) + setfloatpos(c, r->floatpos); + #endif // FLOATPOS_PATCH + for (m = mons; m && m->num != r->monitor; m = m->next); if (m) c->mon = m; @@ -2230,6 +2238,13 @@ manage(Window w, XWindowAttributes *wa) c->tags = t->tags; } else { c->mon = selmon; + #if FLOATPOS_PATCH + #if SETBORDERPX_PATCH + c->bw = c->mon->borderpx; + #else + c->bw = borderpx; + #endif // SETBORDERPX_PATCH + #endif // FLOATPOS_PATCH applyrules(c); #if SWALLOW_PATCH term = termforwin(c); @@ -2244,11 +2259,13 @@ manage(Window w, XWindowAttributes *wa) /* only fix client y-offset, if the client center might cover the bar */ c->y = MAX(c->y, ((c->mon->by == c->mon->my) && (c->x + (c->w / 2) >= c->mon->wx) && (c->x + (c->w / 2) < c->mon->wx + c->mon->ww)) ? bh : c->mon->my); + #if !FLOATPOS_PATCH #if SETBORDERPX_PATCH c->bw = c->mon->borderpx; #else c->bw = borderpx; #endif // SETBORDERPX_PATCH + #endif // FLOATPOS_PATCH wc.border_width = c->bw; XConfigureWindow(dpy, w, CWBorderWidth, &wc); @@ -2266,8 +2283,8 @@ manage(Window w, XWindowAttributes *wa) updatewmhints(c); #if CENTER_PATCH if (c->iscentered) { - c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; - c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; + c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; + c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; } #endif // CENTER_PATCH #if SAVEFLOATS_PATCH || EXRESIZE_PATCH diff --git a/patch/floatpos.c b/patch/floatpos.c new file mode 100644 index 0000000..65fec9f --- /dev/null +++ b/patch/floatpos.c @@ -0,0 +1,331 @@ +void +floatpos(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!c || !c->isfloating) + return; + + setfloatpos(c, (char *)arg->v); + resizeclient(c, c->x, c->y, c->w, c->h); + + #if !FOCUSONCLICK_PATCH + XRaiseWindow(dpy, c->win); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2); + #endif // FOCUSONCLICK_PATCH +} + +void +setfloatpos(Client *c, const char *floatpos) +{ + char xCh, yCh, wCh, hCh; + int x, y, w, h, cx, cy, cw, ch, wx, ww, wy, wh, bw; + unsigned int i; + #if FLOATPOS_RESPECT_GAPS_PATCH + int oh, ov, ih, iv; + unsigned int n; + #endif // FLOATPOS_RESPECT_GAPS_PATCH + int absx, absy, absw, absh, delta, rest; + + if (!c || !floatpos) + return; + if (selmon->lt[selmon->sellt]->arrange && !c->isfloating) + return; + switch(sscanf(floatpos, "%d%c %d%c %d%c %d%c", &x, &xCh, &y, &yCh, &w, &wCh, &h, &hCh)) { + case 4: + if (xCh == 'w' || xCh == 'W') { + w = x; wCh = xCh; + h = y; hCh = yCh; + x = 0; xCh = 'x'; + y = 0; yCh = 'y'; + } else if (xCh == 'p' || xCh == 'P') { + w = x; wCh = xCh; + h = y; hCh = yCh; + x = 0; xCh = 'G'; + y = 0; yCh = 'G'; + } else { + w = -1; wCh = 'C'; + h = -1; hCh = 'C'; + } + break; + case 8: + break; + default: + return; + } + + absx = xCh == 'A' || xCh == 'a'; + absy = yCh == 'A' || yCh == 'a'; + absw = wCh == 'A' || wCh == 'a'; + absh = hCh == 'A' || hCh == 'a'; + + #if FLOATPOS_RESPECT_GAPS_PATCH + getgaps(c->mon, &oh, &ov, &ih, &iv, &n); + wx = c->mon->wx + ov; + wy = c->mon->wy + oh; + ww = c->mon->ww - 2*ov; + wh = c->mon->wh - 2*oh; + #else + wx = c->mon->wx; + wy = c->mon->wy; + ww = c->mon->ww; + wh = c->mon->wh; + #endif // FLOATPOS_RESPECT_GAPS_PATCH + + cx = (!absx && c->x < wx ? wx : c->x); + cy = (!absy && c->y < wy ? wy : c->y); + cw = (!absw && c->w > ww ? ww : c->w); + ch = (!absh && c->h > wh ? wh : c->h); + + switch(xCh) { + case 'A': // absolute position + cx = x; + break; + case 'a': // absolute relative position + cx += x; + break; + case 'x': // client relative position + if (cx + x > ww) + cx = ww; + else + cx += x; + break; + case 'X': // client position relative to monitor + if (x > ww) + x = ww; + cx = wx + x; + break; + case 'S': // fixed client position (sticky) + case 'C': // fixed client position (center) + case 'Z': // fixed client right-hand position (position + width) + if (x == -1) + break; + if (x > ww) + x = ww; + if (x < 0) + x = 0; + if (xCh == 'Z') + cw = abs((cx + cw) - (wx + x)); + else if (xCh == 'C') + cw = abs((cx + cw / 2) - (wx + x)); + else + cw = abs(cx - (wx + x)); + cx = wx + x; + wCh = 0; // size determined by position, override defined width + break; + case 'G': // grid + if (x <= 0) + x = floatposgrid_x; // default configurable + if (w == 0 || x < 2 || (wCh != 'p' && wCh != 'P')) + break; + delta = (ww - cw - 2*c->bw) / (x - 1); + rest = ww - cw - 2*c->bw - delta * (x - 1); + if (wCh == 'P') { + if (w < 1 || w > x) + break; + cx = wx + delta * (w - 1); + } else { + for (i = 0; i < x -1 && cx >= wx + delta * i + (i > x - rest ? i + rest - x + 1 : 0); i++); + cx = wx + delta * (MAX(MIN(i + w, x), 1) - 1) + (i > x - rest ? i + rest - x + 1 : 0); + } + break; + } + + switch(yCh) { + case 'A': // absolute position + cy = y; + break; + case 'a': // absolute relative position + cy += y; + break; + case 'y': // client relative position + if (cy + y > wh) + cy = wh; + else + cy += y; + break; + case 'Y': // client position relative to monitor + if (y > wh) + y = wh; + cy = wy + y; + break; + case 'S': // fixed client y position (sticky) + case 'C': // fixed client position (center) + case 'Z': // fixed client right-hand position (position + height) + if (y == -1) + break; + if (y > wh) + y = wh; + if (y < 0) + y = 0; + if (yCh == 'Z') + ch = abs((cy + ch) - (wy + y)); + else if (yCh == 'C') + ch = abs((cy + ch / 2) - (wy + y)); + else + ch = abs(cy - (wy + y)); + cy = wy + y; + hCh = 0; // size determined by position, override defined width + break; + case 'G': // grid + if (y <= 0) + y = floatposgrid_y; // default configurable + if (h == 0 || y < 1 || (hCh != 'p' && hCh != 'P')) + break; + delta = (wh - ch - 2*c->bw) / (y - 1); + rest = wh - ch - 2*c->bw - delta * (y - 1); + if (hCh == 'P') { + if (h < 1 || h > y) + break; + cy = wy + delta * (h - 1); + } else { + for (i = 0; i < y - 1 && cy >= wy + delta * i + (i > y - rest ? i + rest - y + 1: 0); i++); + cy = wy + delta * (MAX(MIN(i + h, y), 1) - 1) + (i > y - rest ? i + rest - y + 1: 0); + } + hCh = 0; // size determined by position, override defined width + break; + } + + bw = 2*c->bw; + switch(wCh) { + case 'A': // absolute width + cw = w - 2*c->bw; + break; + case 'a': // absolute relative width + if (cw + w < 1) + cw = 1; + else + cw += w; + break; + case '%': // client width percentage in relation to monitor window area width + if (w > 100) + w = 100; + if (w <= 0) + break; + w = ww * w / 100; + /* falls through */ + case 'w': // width relative to client + if (wCh == 'w') { + if (w == 0) + break; + else { + w += cw; + bw = 0; + } + } + /* falls through */ + case 'W': // normal width, position takes precedence + if (xCh == 'S' && cx + w > wx + ww) + w = wx + ww - cx; + else if (xCh == 'C' && cx + cw / 2 + w / 2 > wx + ww) + w = wx + ww - cx + cw / 2; + else if (xCh == 'Z' && w > cx - ww) + w = cx - wx; + else if (w > ww) + w = ww; + + if (xCh == 'C') { // fixed client center, expand or contract client + delta = w - bw - cw; + if (delta < 0 || (cx - delta / 2 + w + 2*c->bw <= wx + ww)) + cx -= delta / 2; + else if (cx - delta / 2 < wx) + cx = wx; + else if (delta) + cx = wx + ww - cw - bw; + } + + cw = w - bw; + break; + } + + bw = 2*c->bw; + switch(hCh) { + case 'A': // absolute height + ch = h - 2*c->bw; + break; + case 'a': // absolute relative height + if (ch + h < 1) + ch = 1; + else + ch += h; + break; + case '%': // client height percentage in relation to monitor window area height + if (h > 100) + h = 100; + if (h <= 0) + break; + h = wh * h / 100; + /* falls through */ + case 'h': // height relative to client + if (hCh == 'h') { + if (h == 0) + break; + else { + h += ch; + bw = 0; + } + } + /* falls through */ + case 'H': /* normal height, position takes precedence */ + if (yCh == 'S' && cy + h > wy + wh) + h = wy + wh - cy; + else if (yCh == 'C' && cy + ch / 2 + h / 2 > wy + wh) + h = wy + wh - cy + ch / 2; + else if (yCh == 'Z' && h > cy - wh) + h = cy - wy; + else if (h > wh) + h = wh; + + if (yCh == 'C') { // fixed client center, expand or contract client + delta = h - bw - ch; + if (delta < 0 || (cy - delta / 2 + ch + 2*c->bw <= wy + wh)) + cy -= delta / 2; + else if (cy - delta / 2 < wy) + cy = wy; + else if (delta) + cy = wy + wh - ch - bw; + } + + ch = h - bw; + break; + } + + if (xCh == '%') { // client mid-point position in relation to monitor window area width + if (x > 100) + x = 100; + if (x < 0) + x = 0; + cx = wx + ww * x / 100 - (cw + 2*c->bw) / 2; + } + + if (yCh == '%') { // client mid-point position in relation to monitor window area height + if (y > 100) + y = 100; + if (y < 0) + y = 0; + cy = wy + wh * y / 100 - (ch + 2*c->bw) / 2; + } + + if (!absx && cx < wx) + cx = wx; + if (cx + cw + 2*c->bw > wx + ww && !(absx && absw)) { + if (absx || cx == wx) + cw = ww - cx + wx - 2*c->bw; + else + cx = wx + ww - cw - 2*c->bw; + } + + if (!absy && cy < wy) + cy = wy; + if (cy + ch + 2*c->bw > wy + wh && !(absy && absh)) { + if (absy || cy == wy) + ch = wh - cy + wy - 2*c->bw; + else + cy = wy + wh - ch - 2*c->bw; + } + + c->x = cx; + c->y = cy; + c->w = MAX(cw, 1); + c->h = MAX(ch, 1); +} \ No newline at end of file diff --git a/patch/floatpos.h b/patch/floatpos.h new file mode 100644 index 0000000..c0a5853 --- /dev/null +++ b/patch/floatpos.h @@ -0,0 +1,2 @@ +static void floatpos(const Arg *arg); +static void setfloatpos(Client *c, const char *floatpos); diff --git a/patch/include.c b/patch/include.c index 47136f2..d574fd6 100644 --- a/patch/include.c +++ b/patch/include.c @@ -46,6 +46,9 @@ #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH #include "fakefullscreenclient.c" #endif +#if FLOATPOS_PATCH +#include "floatpos.c" +#endif #if FOCUSADJACENTTAG_PATCH #include "focusadjacenttag.c" #endif diff --git a/patch/include.h b/patch/include.h index dca2120..97628aa 100644 --- a/patch/include.h +++ b/patch/include.h @@ -49,6 +49,9 @@ #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH #include "fakefullscreenclient.h" #endif +#if FLOATPOS_PATCH +#include "floatpos.h" +#endif #if FOCUSADJACENTTAG_PATCH #include "focusadjacenttag.h" #endif diff --git a/patches.def.h b/patches.def.h index d7f4e4b..5bf94d3 100644 --- a/patches.def.h +++ b/patches.def.h @@ -219,6 +219,17 @@ */ #define FLOAT_BORDER_COLOR_PATCH 0 +/* This patch adds a float rule allowing the size and position of floating windows to be specified + * It also allows the size and position of floating windows to be controlled similar to the + * exresize, moveresize, and moveplace patches. + * The size and position can be specified using absolute, relative or fixed co-ordinates and + * https://github.com/bakkeby/patches/wiki/floatpos/ + */ +#define FLOATPOS_PATCH 0 + +/* Add-on functionality for the above: make the float positions respect outer (vanity)gaps. */ +#define FLOATPOS_RESPECT_GAPS_PATCH 0 + /* This patch provides the ability to focus the tag on the immediate left or right of the * currently focused tag. It also allows to send the focused window either on the left or * the right tag.