commit 1a1b74d2027adfc7c073ea87f8dcc85d213bc44d
parent 4b91716720161902e3235c67c7813983d114d56e
Author: phillbush <phillbush@cock.li>
Date: Mon, 27 Sep 2021 09:59:39 -0300
add support for docks/panels/bars
Diffstat:
M | README | | | 1 | - |
M | shod.1 | | | 26 | +++++++++++++++++++++----- |
M | shod.c | | | 213 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- |
3 files changed, 222 insertions(+), 18 deletions(-)
diff --git a/README b/README
@@ -39,5 +39,4 @@ Screenshots:
TODO:
• Add support for dropdown (quake-like) windows.
-• Add support for docked windows (for panels/bars).
• Add support for window rules.
diff --git a/shod.1 b/shod.1
@@ -400,6 +400,9 @@ One of the monitors is the focused one, where new windows go to when they are cr
Each monitor contains a different set of virtual desktops (or "desktop", for short).
One of the desktops of a monitor is the focused desktop for that monitor.
.PP
+Each monitor has an area called container area, within containers are spawned and can be maximized.
+The size and position of a monitor's container area can be changed by dock windows.
+.PP
Most client windows are displayed in containers;
but some windows are special and are displayed in different ways.
.SH CONTAINERS
@@ -618,7 +621,8 @@ Each container can or not have each one of the following states.
By default, a container has no state.
.TP
Maximized
-Maximized containers occupies the entire space on the monitor available for windows.
+Maximized containers occupies the entire space on the monitor available for windows
+(a region called container area).
When a maximized container is unmaximized,
it restores its previous size and position.
.TP
@@ -685,10 +689,22 @@ command-line option.
.PP
.B shod
can change the size and the position of notification windows.
-.SH DROPDOWN WINDOWS
-TODO
-.SH DOCKED WINDOWS
-TODO
+.SH DOCKS
+Windows of type
+.B _NET_WM_WINDOW_TYPE_DOCK
+(called docks, panels, or bars)
+are mapped on a side of a monitor.
+Dock windows cannot be manipulated, have no decoration and do not receive input focus.
+.PP
+A dock window can change the size of a region of the monitor called container area.
+The container area is the region of the monitor that a maximized container occupies.
+The container area is also the region of the monitor inside which containers are spawned.
+.PP
+Example of dock are a taskbar (that shows which programs are currently running),
+and a statusbar (that shows information about the system, such as memory usage and system time).
+.PP
+.B shod
+does not change the size nor the position of dock windows.
.SH ENVIRONMENT
The following environment variables affect the execution of
.B shod
diff --git a/shod.c b/shod.c
@@ -99,6 +99,7 @@ enum {
LAYER_BELOW,
LAYER_NORMAL,
LAYER_ABOVE,
+ LAYER_DOCK,
LAYER_FULLSCREEN,
LAYER_LAST
};
@@ -155,6 +156,8 @@ enum {
_NET_WM_STATE_BELOW,
_NET_WM_STATE_FOCUSED,
_NET_WM_STATE_DEMANDS_ATTENTION,
+ _NET_WM_STRUT,
+ _NET_WM_STRUT_PARTIAL,
_NET_REQUEST_FRAME_EXTENTS,
_NET_FRAME_EXTENTS,
@@ -198,6 +201,23 @@ enum {
_SHOD_FOCUS_NEXT_WINDOW = 12,
};
+/* strut elements */
+enum {
+ STRUT_LEFT = 0,
+ STRUT_RIGHT = 1,
+ STRUT_TOP = 2,
+ STRUT_BOTTOM = 3,
+ STRUT_LEFT_START_Y = 4,
+ STRUT_LEFT_END_Y = 5,
+ STRUT_RIGHT_START_Y = 6,
+ STRUT_RIGHT_END_Y = 7,
+ STRUT_TOP_START_X = 8,
+ STRUT_TOP_END_X = 9,
+ STRUT_BOTTOM_START_X = 10,
+ STRUT_BOTTOM_END_X = 11,
+ STRUT_LAST = 12,
+};
+
/* window eight sections (aka octants) */
enum Octant {
C = 0,
@@ -213,6 +233,7 @@ enum Octant {
/* struct returned by getwindow */
struct Winres {
+ struct Dock *dock; /* dock of window */
struct Notification *n; /* notification of window */
struct Container *c; /* container of window */
struct Row *row; /* row (with buttons) of window */
@@ -310,6 +331,13 @@ struct Desktop {
int n; /* desktop number */
};
+struct Dock {
+ struct Dock *prev, *next; /* pointers for list of docks */
+ Window win; /* dock window */
+ int strut[STRUT_LAST]; /* strut values */
+ int partial; /* strut has 12 elements rather than 4 */
+};
+
/* data of a monitor */
struct Monitor {
struct Monitor *prev, *next; /* pointers for list of monitors */
@@ -378,6 +406,7 @@ struct Visual {
/* window manager stuff */
struct WM {
+ struct Dock *docks; /* list of docks */
struct Monitor *monhead, *montail; /* list of monitors */
struct Notification *nhead, *ntail; /* list of notifications */
struct Container *c; /* list of containers */
@@ -731,6 +760,8 @@ initatoms(void)
[_NET_WM_STATE_BELOW] = "_NET_WM_STATE_BELOW",
[_NET_WM_STATE_FOCUSED] = "_NET_WM_STATE_FOCUSED",
[_NET_WM_STATE_DEMANDS_ATTENTION] = "_NET_WM_STATE_DEMANDS_ATTENTION",
+ [_NET_WM_STRUT] = "_NET_WM_STRUT",
+ [_NET_WM_STRUT_PARTIAL] = "_NET_WM_STRUT_PARTIAL",
[_NET_REQUEST_FRAME_EXTENTS] = "_NET_REQUEST_FRAME_EXTENTS",
[_NET_FRAME_EXTENTS] = "_NET_FRAME_EXTENTS",
[_SHOD_GROUP_TAB] = "_SHOD_GROUP_TAB",
@@ -883,6 +914,7 @@ static struct Winres
getwin(Window win)
{
struct Winres res;
+ struct Dock *dock;
struct Container *c;
struct Column *col;
struct Row *row;
@@ -890,11 +922,18 @@ getwin(Window win)
struct Dialog *d;
struct Notification *n;
+ res.dock = NULL;
res.row = NULL;
res.n = NULL;
res.c = NULL;
res.t = NULL;
res.d = NULL;
+ for (dock = wm.docks; dock != NULL; dock = dock->next) {
+ if (win == dock->win) {
+ res.dock = dock;
+ return res;
+ }
+ }
for (n = wm.nhead; n != NULL; n = n->next) {
if (win == n->frame || win == n->win) {
res.n = n;
@@ -990,6 +1029,24 @@ getatomprop(Window win, Atom prop)
return atom;
}
+/* get atom property from window */
+static unsigned long
+getcardprop(Window win, Atom prop, unsigned long **array)
+{
+ int di;
+ unsigned long len;
+ unsigned long dl;
+ unsigned char *p = NULL;
+ Atom da = None;
+
+ if (XGetWindowProperty(dpy, win, prop, 0L, 1024, False, XA_CARDINAL, &da, &di, &len, &dl, &p) != Success || p == NULL) {
+ *array = NULL;
+ return 0;
+ }
+ *array = (unsigned long *)p;
+ return len;
+}
+
/* get window's WM_STATE property */
static long
getstate(Window w)
@@ -3376,6 +3433,70 @@ monupdate(void)
free(unique);
}
+/* update window area and dock area of monitor */
+static void
+monupdatearea(void)
+{
+ struct Monitor *mon;
+ struct Dock *dock;
+ struct Container *c;
+ int t, b, l, r;
+
+ for (mon = wm.monhead; mon != NULL; mon = mon->next) {
+ mon->wx = mon->mx;
+ mon->wy = mon->my;
+ mon->ww = mon->mw;
+ mon->wh = mon->mh;
+ t = b = l = r = 0;
+ for (dock = wm.docks; dock != NULL; dock = dock->next) {
+ if (dock->strut[STRUT_TOP] != 0) {
+ if (dock->strut[STRUT_TOP] >= mon->my &&
+ dock->strut[STRUT_TOP] < mon->my + mon->mh &&
+ (!dock->partial ||
+ (dock->strut[STRUT_TOP_START_X] >= mon->mx &&
+ dock->strut[STRUT_TOP_END_X] < mon->mx + mon->mw))) {
+ t = max(t, dock->strut[STRUT_TOP] - mon->my);
+ }
+ } else if (dock->strut[STRUT_BOTTOM] != 0) {
+ if (screenh - dock->strut[STRUT_BOTTOM] <= mon->my + mon->mh &&
+ screenh - dock->strut[STRUT_BOTTOM] > mon->my &&
+ (!dock->partial ||
+ (dock->strut[STRUT_BOTTOM_START_X] >= mon->mx &&
+ dock->strut[STRUT_BOTTOM_END_X] < mon->mx + mon->mw))) {
+ b = max(b, dock->strut[STRUT_BOTTOM] - (screenh - (mon->my + mon->mh)));
+ }
+ } else if (dock->strut[STRUT_LEFT] != 0) {
+ if (dock->strut[STRUT_LEFT] >= mon->mx &&
+ dock->strut[STRUT_LEFT] < mon->mx + mon->mw &&
+ (!dock->partial ||
+ (dock->strut[STRUT_LEFT_START_Y] >= mon->my &&
+ dock->strut[STRUT_LEFT_END_Y] < mon->my + mon->mh))) {
+ l = max(l, dock->strut[STRUT_LEFT] - mon->mx);
+ }
+ } else if (dock->strut[STRUT_RIGHT] != 0) {
+ if (screenw - dock->strut[STRUT_RIGHT] <= mon->mx + mon->mw &&
+ screenw - dock->strut[STRUT_RIGHT] > mon->mx &&
+ (!dock->partial ||
+ (dock->strut[STRUT_RIGHT_START_Y] >= mon->my &&
+ dock->strut[STRUT_RIGHT_END_Y] < mon->my + mon->mh))) {
+ r = max(r, dock->strut[STRUT_RIGHT] - (screenw - (mon->mx + mon->mw)));
+ }
+ }
+ }
+ mon->wy += t;
+ mon->wh -= t + b;
+ mon->wx += l;
+ mon->ww -= l + r;
+ }
+ for (c = wm.c; c != NULL; c = c->next) {
+ if (c->ismaximized) {
+ containercalccols(c, 1);
+ containermoveresize(c);
+ containerredecorate(c, NULL, NULL, 0);
+ }
+ }
+}
+
/* select window input events, grab mouse button presses, and clear its border */
static void
preparewin(Window win)
@@ -3620,11 +3741,49 @@ notifdel(struct Notification *n)
wm.nhead = n->next;
if (n->pix != None)
XFreePixmap(dpy, n->pix);
+ XReparentWindow(dpy, n->win, root, 0, 0);
XDestroyWindow(dpy, n->frame);
free(n);
notifplace();
}
+/* fill strut array of dock */
+static void
+dockstrut(struct Dock *dock)
+{
+ unsigned long *arr;
+ unsigned long l, i;
+
+ for (i = 0; i < STRUT_LAST; i++)
+ dock->strut[i] = 0;
+ dock->partial = 1;
+ l = getcardprop(dock->win, atoms[_NET_WM_STRUT_PARTIAL], &arr);
+ if (arr == NULL) {
+ dock->partial = 0;
+ l = getcardprop(dock->win, atoms[_NET_WM_STRUT], &arr);
+ if (arr == NULL) {
+ return;
+ }
+ }
+ for (i = 0; i < STRUT_LAST && i < l; i++)
+ dock->strut[i] = arr[i];
+ XFree(arr);
+}
+
+/* delete dock */
+static void
+dockdel(struct Dock *dock)
+{
+ if (dock->next != NULL)
+ dock->next->prev = dock->prev;
+ if (dock->prev != NULL)
+ dock->prev->next = dock->next;
+ else
+ wm.docks = dock->next;
+ free(dock);
+ monupdatearea();
+}
+
/* call the proper decorate function */
static void
decorate(struct Winres *res)
@@ -3770,6 +3929,26 @@ managecontainer(struct Container *c, struct Tab *t, struct Desktop *desk, int us
ewmhsetclientsstacking();
}
+/* map dock window */
+static void
+managedock(Window win)
+{
+ struct Dock *dock;
+ Window wins[2] = {win, wm.layerwins[LAYER_DOCK]};
+
+ dock = emalloc(sizeof(*dock));
+ dock->prev = dock->next = NULL;
+ dock->win = win;
+ if (wm.docks != NULL)
+ wm.docks->prev = dock;
+ dock->next = wm.docks;
+ wm.docks = dock;
+ XRestackWindows(dpy, wins, sizeof wins);
+ XMapWindow(dpy, win);
+ dockstrut(dock);
+ monupdatearea();
+}
+
/* call one of the manage- functions */
static void
manage(Window win, XWindowAttributes *wa, int ignoreunmap)
@@ -3789,6 +3968,8 @@ manage(Window win, XWindowAttributes *wa, int ignoreunmap)
t = getdialogfor(win);
if (prop == atoms[_NET_WM_WINDOW_TYPE_DESKTOP]) {
managedesktop(win);
+ } else if (prop == atoms[_NET_WM_WINDOW_TYPE_DOCK]) {
+ managedock(win);
} else if (prop == atoms[_NET_WM_WINDOW_TYPE_NOTIFICATION]) {
managenotif(win, wa->width, wa->height);
} else if (prop == atoms[_NET_WM_WINDOW_TYPE_PROMPT]) {
@@ -4748,6 +4929,9 @@ xeventdestroynotify(XEvent *e)
if (res.n && ev->window == res.n->win) {
notifdel(res.n);
return;
+ } else if (res.dock != NULL && ev->window == res.dock->win) {
+ dockdel(res.dock);
+ return;
} else if (res.d && ev->window == res.d->win) {
dialogdel(res.d);
} else if (res.t && ev->window == res.t->win) {
@@ -4892,6 +5076,9 @@ xeventpropertynotify(XEvent *e)
tabupdateclass(res.t);
} else if (ev->atom == XA_WM_HINTS) {
tabupdateurgency(res.t, winisurgent(res.t->win));
+ } else if (res.dock != NULL && (ev->atom == _NET_WM_STRUT_PARTIAL || ev->atom == _NET_WM_STRUT)) {
+ dockstrut(res.dock);
+ monupdatearea();
}
}
@@ -4907,6 +5094,9 @@ xeventunmapnotify(XEvent *e)
if (res.n && ev->window == res.n->win) {
notifdel(res.n);
return;
+ } else if (res.dock != NULL && ev->window == res.dock->win) {
+ dockdel(res.dock);
+ return;
} else if (res.d && ev->window == res.d->win) {
if (res.d->ignoreunmap) {
res.d->ignoreunmap--;
@@ -4950,20 +5140,20 @@ cleancursors(void)
}
}
-/* clean clients */
+/* clean window manager structures */
static void
-cleancontainers(void)
+cleanwm(void)
{
- while (wm.c) {
+ while (wm.c != NULL) {
containerdel(wm.c);
}
-}
-
-/* clean monitors */
-static void
-cleanmonitors(void)
-{
- while (wm.monhead) {
+ while (wm.nhead != NULL) {
+ notifdel(wm.nhead);
+ }
+ while (wm.docks != NULL) {
+ dockdel(wm.docks);
+ }
+ while (wm.monhead != NULL) {
mondel(wm.monhead);
}
}
@@ -5078,10 +5268,9 @@ main(int argc, char *argv[])
/* clean up */
cleandummywindows();
cleancursors();
- cleancontainers();
- cleanmonitors();
cleanpixmaps();
cleanfontset();
+ cleanwm();
/* clear ewmh hints */
ewmhsetclients();