floatpos: Control the size and position of floating windows

This patch offers a comprehensive and monitor size agnostic way
of positioning new and existing floating windows.

Refer to:
https://github.com/bakkeby/patches/wiki/floatpos/
This commit is contained in:
Bakkeby
2022-01-10 11:37:31 +01:00
committed by Zoe Roux
parent 1f809e3f81
commit d8d3b7abb4
3 changed files with 240 additions and 12 deletions

View File

@@ -9,6 +9,8 @@ static const int topbar = 1; /* 0 means bottom bar */
static const char statussep = ';'; /* separator between status bars */
static const unsigned int systrayspacing = 2; /* systray spacing */
static const int showsystray = 1; /* 0 means no systray */
static int floatposgrid_x = 5; /* float grid columns */
static int floatposgrid_y = 5; /* float grid rows */
static const char *fonts[] = { "monospace:size=10" };
static const char dmenufont[] = "monospace:size=10";
static const char col_gray1[] = "#222222";
@@ -43,12 +45,12 @@ static const Rule rules[] = {
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
{ NULL, "spterm", NULL, SPTAG(0), 1, -1 },
{ NULL, "spfm", NULL, SPTAG(1), 1, -1 },
{ NULL, "keepassxc", NULL, SPTAG(2), 0, -1 },
/* class instance title tags mask isfloating floatpos monitor */
{ "Gimp", NULL, NULL, 0, 1, NULL, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, NULL, -1 },
{ NULL, "spterm", NULL, SPTAG(0), 1, NULL, -1 },
{ NULL, "spfm", NULL, SPTAG(1), 1, NULL, -1 },
{ NULL, "keepassxc", NULL, SPTAG(2), 0, NULL, -1 },
};
/* Bar rules allow you to configure what is shown where on the bar, as well as
@@ -70,7 +72,6 @@ static const BarRule barrules[] = {
{ 'A', 0, BAR_ALIGN_RIGHT, width_status2d, draw_status2d, click_statuscmd, "status2d" },
{ 'A', 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, "systray" },
{ -1, 0, BAR_ALIGN_NONE, width_wintitle, draw_wintitle, click_wintitle, "wintitle" },
};
/* layout(s) */
static const float mfact = 0.55; /* factor of master area size [0.05..0.95] */
@@ -130,6 +131,44 @@ static Key keys[] = {
{ MODKEY, XK_y, togglescratch, {.ui = 0 } },
{ MODKEY, XK_u, togglescratch, {.ui = 1 } },
{ MODKEY, XK_x, togglescratch, {.ui = 2 } },
/* Client position is limited to monitor window area */
{ Mod4Mask, XK_u, floatpos, {.v = "-26x -26y" } }, // ↖
{ Mod4Mask, XK_i, floatpos, {.v = " 0x -26y" } }, // ↑
{ Mod4Mask, XK_o, floatpos, {.v = " 26x -26y" } }, // ↗
{ Mod4Mask, XK_j, floatpos, {.v = "-26x 0y" } }, // ←
{ Mod4Mask, XK_l, floatpos, {.v = " 26x 0y" } }, // →
{ Mod4Mask, XK_m, floatpos, {.v = "-26x 26y" } }, // ↙
{ Mod4Mask, XK_comma, floatpos, {.v = " 0x 26y" } }, // ↓
{ Mod4Mask, XK_period, floatpos, {.v = " 26x 26y" } }, // ↘
/* Absolute positioning (allows moving windows between monitors) */
{ Mod4Mask|ControlMask, XK_u, floatpos, {.v = "-26a -26a" } }, // ↖
{ Mod4Mask|ControlMask, XK_i, floatpos, {.v = " 0a -26a" } }, // ↑
{ Mod4Mask|ControlMask, XK_o, floatpos, {.v = " 26a -26a" } }, // ↗
{ Mod4Mask|ControlMask, XK_j, floatpos, {.v = "-26a 0a" } }, // ←
{ Mod4Mask|ControlMask, XK_l, floatpos, {.v = " 26a 0a" } }, // →
{ Mod4Mask|ControlMask, XK_m, floatpos, {.v = "-26a 26a" } }, // ↙
{ Mod4Mask|ControlMask, XK_comma, floatpos, {.v = " 0a 26a" } }, // ↓
{ Mod4Mask|ControlMask, XK_period, floatpos, {.v = " 26a 26a" } }, // ↘
/* Resize client, client center position is fixed which means that client expands in all directions */
{ Mod4Mask|ShiftMask, XK_u, floatpos, {.v = "-26w -26h" } }, // ↖
{ Mod4Mask|ShiftMask, XK_i, floatpos, {.v = " 0w -26h" } }, // ↑
{ Mod4Mask|ShiftMask, XK_o, floatpos, {.v = " 26w -26h" } }, // ↗
{ Mod4Mask|ShiftMask, XK_j, floatpos, {.v = "-26w 0h" } }, // ←
{ Mod4Mask|ShiftMask, XK_k, floatpos, {.v = "800W 800H" } }, // ·
{ Mod4Mask|ShiftMask, XK_l, floatpos, {.v = " 26w 0h" } }, // →
{ Mod4Mask|ShiftMask, XK_m, floatpos, {.v = "-26w 26h" } }, // ↙
{ Mod4Mask|ShiftMask, XK_comma, floatpos, {.v = " 0w 26h" } }, // ↓
{ Mod4Mask|ShiftMask, XK_period, floatpos, {.v = " 26w 26h" } }, // ↘
/* Client is positioned in a floating grid, movement is relative to client's current position */
{ Mod4Mask|Mod1Mask, XK_u, floatpos, {.v = "-1p -1p" } }, // ↖
{ Mod4Mask|Mod1Mask, XK_i, floatpos, {.v = " 0p -1p" } }, // ↑
{ Mod4Mask|Mod1Mask, XK_o, floatpos, {.v = " 1p -1p" } }, // ↗
{ Mod4Mask|Mod1Mask, XK_j, floatpos, {.v = "-1p 0p" } }, // ←
{ Mod4Mask|Mod1Mask, XK_k, floatpos, {.v = " 0p 0p" } }, // ·
{ Mod4Mask|Mod1Mask, XK_l, floatpos, {.v = " 1p 0p" } }, // →
{ Mod4Mask|Mod1Mask, XK_m, floatpos, {.v = "-1p 1p" } }, // ↙
{ Mod4Mask|Mod1Mask, XK_comma, floatpos, {.v = " 0p 1p" } }, // ↓
{ Mod4Mask|Mod1Mask, XK_period, floatpos, {.v = " 1p 1p" } }, // ↘
TAGKEYS( XK_1, 0)
TAGKEYS( XK_2, 1)
TAGKEYS( XK_3, 2)

View File

@@ -11,6 +11,9 @@ static const int vertpadbar = 10; /* vertical padding for statusb
static const unsigned int systrayspacing = 2; /* systray spacing */
static const int showsystray = 1; /* 0 means no systray */
static int floatposgrid_x = 5; /* float grid columns */
static int floatposgrid_y = 5; /* float grid rows */
static const char *fonts[] = { "monospace:size=10" };
static const char col_gray1[] = "#222222";
static const char col_gray2[] = "#444444";
@@ -43,9 +46,7 @@ static const Rule rules[] = {
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
{ NULL, "kitty-sp", NULL, SPTAG(0), 1, -1 },
{ NULL, "kitty-sp", NULL, SPTAG(0), 1, NULL, -1 },
};
/* layout(s) */

192
dwm.c
View File

@@ -152,6 +152,7 @@ struct Client {
int bw, oldbw;
unsigned int tags;
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
int ignoresizehints;
Client *next;
Client *snext;
Monitor *mon;
@@ -194,6 +195,7 @@ typedef struct {
const char *title;
unsigned int tags;
int isfloating;
const char *floatpos;
int monitor;
} Rule;
@@ -228,11 +230,13 @@ static void drawbars(void);
static void drawbarwin(Bar *bar);
static void enternotify(XEvent *e);
static void expose(XEvent *e);
static void floatpos(const Arg *arg);
static void focus(Client *c);
static void focusin(XEvent *e);
static void focusmon(const Arg *arg);
static void focusstack(const Arg *arg);
static Atom getatomprop(Client *c, Atom prop);
static void getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
@@ -262,6 +266,7 @@ static void scan(void);
static int sendevent(Window w, Atom proto, int m, long d0, long d1, long d2, long d3, long d4);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
static void setfloatpos(Client *c, const char *floatpos);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
@@ -384,6 +389,8 @@ applyrules(Client *c)
for (m = mons; m && m->num != r->monitor; m = m->next);
if (m)
c->mon = m;
if (c->isfloating && r->floatpos)
setfloatpos(c, r->floatpos);
}
}
if (ch.res_class)
@@ -425,7 +432,7 @@ applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact)
*h = bh;
if (*w < bh)
*w = bh;
if (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) {
if (!c->ignoresizehints && (resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange)) {
/* see last two sentences in ICCCM 4.1.2.3 */
baseismin = c->basew == c->minw && c->baseh == c->minh;
if (!baseismin) { /* temporarily remove base dimensions */
@@ -1077,6 +1084,21 @@ expose(XEvent *e)
drawbar(m);
}
void
floatpos(const Arg *arg)
{
Client *c = selmon->sel;
if (!c || (selmon->lt[selmon->sellt]->arrange && !c->isfloating))
return;
setfloatpos(c, (char *)arg->v);
resizeclient(c, c->x, c->y, c->w, c->h);
XRaiseWindow(dpy, c->win);
XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w/2, c->h/2);
}
void
focus(Client *c)
{
@@ -1179,6 +1201,124 @@ getatomprop(Client *c, Atom prop)
return atom;
}
void
getfloatpos(int pos, char pCh, int size, char sCh, int min_p, int max_s, int cp, int cs, int cbw, int defgrid, int *out_p, int *out_s)
{
int abs_p, abs_s, i, delta, rest;
abs_p = pCh == 'A' || pCh == 'a';
abs_s = sCh == 'A' || sCh == 'a';
cs += 2*cbw;
switch(pCh) {
case 'A': // absolute position
cp = pos;
break;
case 'a': // absolute relative position
cp += pos;
break;
case 'y':
case 'x': // client relative position
cp = MIN(cp + pos, min_p + max_s);
break;
case 'Y':
case 'X': // client position relative to monitor
cp = min_p + MIN(pos, max_s);
break;
case 'S': // fixed client position (sticky)
case 'C': // fixed client position (center)
case 'Z': // fixed client right-hand position (position + size)
if (pos == -1)
break;
pos = MAX(MIN(pos, max_s), 0);
if (pCh == 'Z')
cs = abs((cp + cs) - (min_p + pos));
else if (pCh == 'C')
cs = abs((cp + cs / 2) - (min_p + pos));
else
cs = abs(cp - (min_p + pos));
cp = min_p + pos;
sCh = 0; // size determined by position, override defined size
break;
case 'G': // grid
if (pos <= 0)
pos = defgrid; // default configurable
if (size == 0 || pos < 2 || (sCh != 'p' && sCh != 'P'))
break;
delta = (max_s - cs) / (pos - 1);
rest = max_s - cs - delta * (pos - 1);
if (sCh == 'P') {
if (size < 1 || size > pos)
break;
cp = min_p + delta * (size - 1);
} else {
for (i = 0; i < pos && cp >= min_p + delta * i + (i > pos - rest ? i + rest - pos + 1 : 0); i++);
cp = min_p + delta * (MAX(MIN(i + size, pos), 1) - 1) + (i > pos - rest ? i + rest - pos + 1 : 0);
}
break;
}
switch(sCh) {
case 'A': // absolute size
cs = size;
break;
case 'a': // absolute relative size
cs = MAX(1, cs + size);
break;
case '%': // client size percentage in relation to monitor window area size
if (size <= 0)
break;
size = max_s * MIN(size, 100) / 100;
/* falls through */
case 'h':
case 'w': // size relative to client
if (sCh == 'w' || sCh == 'h') {
if (size == 0)
break;
size += cs;
}
/* falls through */
case 'H':
case 'W': // normal size, position takes precedence
if (pCh == 'S' && cp + size > min_p + max_s)
size = min_p + max_s - cp;
else if (size > max_s)
size = max_s;
if (pCh == 'C') { // fixed client center, expand or contract client
delta = size - cs;
if (delta < 0 || (cp - delta / 2 + size <= min_p + max_s))
cp -= delta / 2;
else if (cp - delta / 2 < min_p)
cp = min_p;
else if (delta)
cp = min_p + max_s;
} else if (pCh == 'Z')
cp -= size - cs;
cs = size;
break;
}
if (pCh == '%') // client mid-point position in relation to monitor window area size
cp = min_p + max_s * MAX(MIN(pos, 100), 0) / 100 - (cs) / 2;
if (pCh == 'm' || pCh == 'M')
cp = pos - cs / 2;
if (!abs_p && cp < min_p)
cp = min_p;
if (cp + cs > min_p + max_s && !(abs_p && abs_s)) {
if (abs_p || cp == min_p)
cs = min_p + max_s - cp;
else
cp = min_p + max_s - cs;
}
*out_p = cp;
*out_s = MAX(cs - 2*cbw, 1);
}
int
getrootptr(int *x, int *y)
{
@@ -1346,8 +1486,10 @@ manage(Window w, XWindowAttributes *wa)
c->w = c->oldw = wa->width;
c->h = c->oldh = wa->height;
c->oldbw = wa->border_width;
c->ignoresizehints = 0;
updatetitle(c);
c->bw = borderpx;
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
c->mon = t->mon;
c->tags = t->tags;
@@ -1364,7 +1506,6 @@ 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->bar->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);
c->bw = borderpx;
wc.border_width = c->bw;
XConfigureWindow(dpy, w, CWBorderWidth, &wc);
@@ -1811,6 +1952,53 @@ sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, lo
return exists;
}
void
setfloatpos(Client *c, const char *floatpos)
{
char xCh, yCh, wCh, hCh;
int x, y, w, h, wx, ww, wy, wh;
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 = -1; xCh = 'C';
y = -1; yCh = 'C';
} else if (xCh == 'p' || xCh == 'P') {
w = x; wCh = xCh;
h = y; hCh = yCh;
x = 0; xCh = 'G';
y = 0; yCh = 'G';
} else if (xCh == 'm' || xCh == 'M') {
getrootptr(&x, &y);
} else {
w = 0; wCh = 0;
h = 0; hCh = 0;
}
break;
case 8:
if (xCh == 'm' || xCh == 'M')
getrootptr(&x, &y);
break;
default:
return;
}
wx = c->mon->wx;
wy = c->mon->wy;
ww = c->mon->ww;
wh = c->mon->wh;
c->ignoresizehints = 1;
getfloatpos(x, xCh, w, wCh, wx, ww, c->x, c->w, c->bw, floatposgrid_x, &c->x, &c->w);
getfloatpos(y, yCh, h, hCh, wy, wh, c->y, c->h, c->bw, floatposgrid_y, &c->y, &c->h);
}
void
setfocus(Client *c)
{