mirror of
https://github.com/bakkeby/patches
synced 2024-11-09 13:10:41 +00:00
350 lines
13 KiB
Diff
350 lines
13 KiB
Diff
|
From 01c0dae986d6a070beb9e3947f2bb34bd41d1478 Mon Sep 17 00:00:00 2001
|
||
|
From: bakkeby <bakkeby@gmail.com>
|
||
|
Date: Tue, 23 Jun 2020 11:59:12 +0200
|
||
|
Subject: [PATCH] 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/
|
||
|
---
|
||
|
config.def.h | 46 ++++++++++++-
|
||
|
dwm.c | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++-
|
||
|
2 files changed, 224 insertions(+), 5 deletions(-)
|
||
|
|
||
|
diff --git a/config.def.h b/config.def.h
|
||
|
index 1c0b587..8605e95 100644
|
||
|
--- a/config.def.h
|
||
|
+++ b/config.def.h
|
||
|
@@ -5,6 +5,8 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
|
||
|
static const unsigned int snap = 32; /* snap pixel */
|
||
|
static const int showbar = 1; /* 0 means no bar */
|
||
|
static const int topbar = 1; /* 0 means bottom bar */
|
||
|
+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";
|
||
|
@@ -26,9 +28,9 @@ 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 },
|
||
|
+ /* class instance title tags mask isfloating floatpos monitor */
|
||
|
+ { "Gimp", NULL, NULL, 0, 1, NULL, -1 },
|
||
|
+ { "Firefox", NULL, NULL, 1 << 8, 0, NULL, -1 },
|
||
|
};
|
||
|
|
||
|
/* layout(s) */
|
||
|
@@ -84,6 +86,44 @@ static Key keys[] = {
|
||
|
{ MODKEY, XK_period, focusmon, {.i = +1 } },
|
||
|
{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
|
||
|
{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
|
||
|
+ /* 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)
|
||
|
diff --git a/dwm.c b/dwm.c
|
||
|
index 4465af1..c8ca124 100644
|
||
|
--- a/dwm.c
|
||
|
+++ b/dwm.c
|
||
|
@@ -138,6 +138,7 @@ typedef struct {
|
||
|
const char *title;
|
||
|
unsigned int tags;
|
||
|
int isfloating;
|
||
|
+ const char *floatpos;
|
||
|
int monitor;
|
||
|
} Rule;
|
||
|
|
||
|
@@ -165,10 +166,12 @@ static void drawbar(Monitor *m);
|
||
|
static void drawbars(void);
|
||
|
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 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);
|
||
|
@@ -197,6 +200,7 @@ static void scan(void);
|
||
|
static int sendevent(Client *c, Atom proto);
|
||
|
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);
|
||
|
@@ -302,6 +306,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)
|
||
|
@@ -780,6 +786,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)
|
||
|
{
|
||
|
@@ -871,6 +892,123 @@ 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 (pCh == 'Z' && size > cp - max_s)
|
||
|
+ size = cp - min_p;
|
||
|
+ 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;
|
||
|
+ }
|
||
|
+
|
||
|
+ 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 (!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)
|
||
|
{
|
||
|
@@ -1031,6 +1169,7 @@ manage(Window w, XWindowAttributes *wa)
|
||
|
c->oldbw = wa->border_width;
|
||
|
|
||
|
updatetitle(c);
|
||
|
+ c->bw = borderpx;
|
||
|
if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) {
|
||
|
c->mon = t->mon;
|
||
|
c->tags = t->tags;
|
||
|
@@ -1047,14 +1186,12 @@ 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);
|
||
|
- c->bw = borderpx;
|
||
|
|
||
|
wc.border_width = c->bw;
|
||
|
XConfigureWindow(dpy, w, CWBorderWidth, &wc);
|
||
|
XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel);
|
||
|
configure(c); /* propagates border_width, if size doesn't change */
|
||
|
updatewindowtype(c);
|
||
|
- updatesizehints(c);
|
||
|
updatewmhints(c);
|
||
|
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
|
||
|
grabbuttons(c, 0);
|
||
|
@@ -1457,6 +1594,48 @@ sendevent(Client *c, Atom proto)
|
||
|
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 {
|
||
|
+ w = 0; wCh = 0;
|
||
|
+ h = 0; hCh = 0;
|
||
|
+ }
|
||
|
+ break;
|
||
|
+ case 8:
|
||
|
+ break;
|
||
|
+ default:
|
||
|
+ return;
|
||
|
+ }
|
||
|
+
|
||
|
+ wx = c->mon->wx;
|
||
|
+ wy = c->mon->wy;
|
||
|
+ ww = c->mon->ww;
|
||
|
+ wh = c->mon->wh;
|
||
|
+
|
||
|
+ 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)
|
||
|
{
|
||
|
--
|
||
|
2.19.1
|
||
|
|