summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Trzeciak <jptrzy@gmail.com>2023-09-11 17:18:18 +0200
committerJakub Trzeciak <jptrzy@gmail.com>2023-09-11 17:18:18 +0200
commit2028e66f39b0b24b17c37a6b67cbb5e48ed06883 (patch)
treeb85a39399803a0f2c5f88c05db2f9c0a1dc67c1f
parente81f17d4c196aaed6893fd4beed49991caa3e2a4 (diff)
downloaddwm-2028e66f39b0b24b17c37a6b67cbb5e48ed06883.tar.gz
dwm-2028e66f39b0b24b17c37a6b67cbb5e48ed06883.tar.bz2
dwm-2028e66f39b0b24b17c37a6b67cbb5e48ed06883.zip
Jptrzy's patches
-rw-r--r--README48
-rw-r--r--README.md11
-rw-r--r--config.def.h24
-rw-r--r--config.h193
-rw-r--r--config.mk3
-rw-r--r--dwm.13
-rw-r--r--dwm.c823
-rw-r--r--vanitygaps.c809
8 files changed, 1797 insertions, 117 deletions
diff --git a/README b/README
deleted file mode 100644
index 95d4fd0..0000000
--- a/README
+++ /dev/null
@@ -1,48 +0,0 @@
-dwm - dynamic window manager
-============================
-dwm is an extremely fast, small, and dynamic window manager for X.
-
-
-Requirements
-------------
-In order to build dwm you need the Xlib header files.
-
-
-Installation
-------------
-Edit config.mk to match your local setup (dwm is installed into
-the /usr/local namespace by default).
-
-Afterwards enter the following command to build and install dwm (if
-necessary as root):
-
- make clean install
-
-
-Running dwm
------------
-Add the following line to your .xinitrc to start dwm using startx:
-
- exec dwm
-
-In order to connect dwm to a specific display, make sure that
-the DISPLAY environment variable is set correctly, e.g.:
-
- DISPLAY=foo.bar:1 exec dwm
-
-(This will start dwm on display :1 of the host foo.bar.)
-
-In order to display status info in the bar, you can do something
-like this in your .xinitrc:
-
- while xsetroot -name "`date` `uptime | sed 's/.*,//'`"
- do
- sleep 1
- done &
- exec dwm
-
-
-Configuration
--------------
-The configuration of dwm is done by creating a custom config.h
-and (re)compiling the source code.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..100232f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+# Jp3's dwm-6.4
+
+## Patches
+* preserveonrestart
+* vanitygaps
+* actualfullscreen
+* systrays
+* swallowing
+* leftstack
+
+
diff --git a/config.def.h b/config.def.h
index 9efa774..cd79b7c 100644
--- a/config.def.h
+++ b/config.def.h
@@ -3,6 +3,12 @@
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
+static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
+static const unsigned int gappih = 20; /* horiz inner gap between windows */
+static const unsigned int gappiv = 10; /* vert inner gap between windows */
+static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */
+static const unsigned int gappov = 30; /* vert outer gap between windows and screen edge */
+static int smartgaps = 0; /* 1 means no outer gap when there is only one window */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=10" };
@@ -26,9 +32,11 @@ 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 isterminal noswallow monitor */
+ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 },
+ { "St", NULL, NULL, 0, 0, 1, 0, -1 },
+ { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */
};
/* layout(s) */
@@ -55,13 +63,18 @@ static const Layout layouts[] = {
/* helper for spawning shell commands in the pre dwm-5.0 fashion */
#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+#define STATUSBAR "dwmblocks"
+
/* commands */
static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
+#include "selfrestart.c"
+
static const Key keys[] = {
/* modifier key function argument */
+ { MODKEY|ShiftMask, XK_r, self_restart, {0} },
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
{ MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } },
{ MODKEY, XK_b, togglebar, {0} },
@@ -79,6 +92,7 @@ static const Key keys[] = {
{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
{ MODKEY, XK_space, setlayout, {0} },
{ MODKEY|ShiftMask, XK_space, togglefloating, {0} },
+ { MODKEY|ShiftMask, XK_f, togglefullscr, {0} },
{ MODKEY, XK_0, view, {.ui = ~0 } },
{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
{ MODKEY, XK_comma, focusmon, {.i = -1 } },
@@ -104,7 +118,9 @@ static const Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} },
+ { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} },
+ { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..4d5a8fe
--- /dev/null
+++ b/config.h
@@ -0,0 +1,193 @@
+/* See LICENSE file for copyright and license details. */
+
+/* appearance */
+static const unsigned int borderpx = 1; /* border pixel of windows */
+static const unsigned int snap = 32; /* snap pixel */
+static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
+
+static const unsigned int gappih = 10; /* horiz inner gap between windows */
+static const unsigned int gappiv = 10; /* vert inner gap between windows */
+static const unsigned int gappoh = 5; /* horiz outer gap between windows and screen edge */
+static const unsigned int gappov = 5; /* vert outer gap between windows and screen edge */
+static int smartgaps = 1; /* 1 means no outer gap when there is only one window */
+
+static const unsigned int systraypinning = 0; /* 0: sloppy systray follows selected monitor, >0: pin systray to monitor X */
+static const unsigned int systrayonleft = 0; /* 0: systray in the right corner, >0: systray on left of status text */
+static const unsigned int systrayspacing = 2; /* systray spacing */
+static const int systraypinningfailfirst = 1; /* 1: if pinning fails, display systray on the first monitor, False: display systray on the last monitor*/
+static const int showsystray = 1; /* 0 means no systray */
+
+static const int showbar = 1; /* 0 means no bar */
+static const int topbar = 1; /* 0 means bottom bar */
+static const char *fonts[] = {
+ "Neo Comic Mono:pixelsize=14",
+ //"Noto Sans Mono:pixelsize=12",
+ "Font Awesome 6 Free Solid:size=10",
+};
+static const char dmenufont[] = "Noto Sans Mono:size=12";
+static const char col_gray1[] = "#222222";
+static const char col_gray2[] = "#444444";
+static const char col_gray3[] = "#bbbbbb";
+static const char col_gray4[] = "#eeeeee";
+static const char col_cyan[] = "#af18db";
+static const char *colors[][3] = {
+ /* fg bg border */
+ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 },
+ [SchemeSel] = { col_gray4, col_cyan, col_cyan },
+};
+
+/* tagging */
+static const char *tags[] = { "\uf015", "\uf120", "3", "4", "5", "6", "7", "\uf8d7", "\uf269" };
+
+static const Rule rules[] = {
+ /* xprop(1):
+ * WM_CLASS(STRING) = instance, class
+ * WM_NAME(STRING) = title
+ */
+ /* class instance title tags mask isfloating isterminal noswallow monitor */
+ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 },
+ { "Alacritty",NULL, NULL, 0, 0, 1, 1, -1 },
+ { "Z-Space", NULL, NULL, 0, 0, 0, -1, -1 },
+ { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */
+};
+
+/* layout(s) */
+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 int resizehints = 0; /* 1 means respect size hints in tiled resizals */
+static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */
+
+#define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */
+#include "vanitygaps.c"
+
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile }, /* first entry is default */
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* key definitions */
+#define MODKEY Mod1Mask
+#define TAGKEYS(KEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|ShiftMask, KEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|ControlMask|ShiftMask, KEY, toggletag, {.ui = 1 << TAG} },
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+#define STATUSBAR "dwmblocks"
+
+/* commands */
+static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */
+static const char *dmenucmd[] = { "dmenu_run", "-l", "16", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+static const char *passmenu_cmd[] = { "passmenu", "-l", "16", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+static const char *emojis_cmd[] = { "dmenu-emoji", "-l", "16", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
+static const char *trans_cmd[] = { "translate", NULL };
+
+static const char *termcmd[] = { "alacritty", NULL };
+
+static const char *brupcmd[] = { "light", "-A", "10", NULL };
+static const char *brdowncmd[] = { "light", "-U", "10", NULL };
+
+static const char *fmcmd[] = { "dbus-launch", "nautilus", "-w", NULL };
+
+static const char *vol_up_cmd[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "+5%", NULL };
+static const char *vol_mute_cmd[] = { "pactl", "set-sink-mute", "@DEFAULT_SINK@", "toggle", NULL };
+static const char *vol_down_cmd[] = { "pactl", "set-sink-volume", "@DEFAULT_SINK@", "-5%", NULL };
+
+static const char *mic_down_cmd[] = { "pactl", "set-source-mute", "@DEFAULT_SOURCE@", "toggle", NULL };
+
+#include <X11/XF86keysym.h>
+
+static const Key keys[] = {
+ /* modifier key function argument */
+
+ { MODKEY, XK_u, spawn, {.v = trans_cmd } },
+ { MODKEY, XK_d, spawn, {.v = dmenucmd } },
+ { MODKEY, XK_p, spawn, {.v = passmenu_cmd } },
+ { MODKEY, XK_o, spawn, {.v = emojis_cmd } },
+ { MODKEY, XK_Return, spawn, {.v = termcmd } },
+
+ { MODKEY, XK_f, togglefullscr, {0} },
+
+ { MODKEY|ShiftMask, XK_q, killclient, {0} },
+ { MODKEY|ShiftMask, XK_e, quit, {0} },
+
+ { MODKEY, XK_m, spawn, {.v = fmcmd } },
+
+ { 0, XF86XK_MonBrightnessUp, spawn, {.v = brupcmd} },
+ { 0, XF86XK_MonBrightnessDown, spawn, {.v = brdowncmd} },
+
+ { 0, XF86XK_AudioRaiseVolume, spawn, {.v = vol_up_cmd} },
+ { 0, XF86XK_AudioMute, spawn, {.v = vol_mute_cmd} },
+ { 0, XF86XK_AudioLowerVolume, spawn, {.v = vol_down_cmd} },
+
+ { 0, XF86XK_AudioMicMute, spawn, {.v = mic_down_cmd} },
+
+
+ //{ MODKEY, XK_t, setlayout, {.v = &layouts[0]} },
+ //{ MODKEY, XK_f, setlayout, {.v = &layouts[1]} },
+ //{ MODKEY, XK_m, setlayout, {.v = &layouts[2]} },
+ //{ MODKEY, XK_space, setlayout, {0} },
+
+ //{ MODKEY, XK_b, togglebar, {0} },
+ //{ MODKEY, XK_Return, zoom, {0} },
+ //{ MODKEY, XK_Tab, view, {0} },
+ //{ MODKEY, XK_0, view, {.ui = ~0 } },
+ //{ MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } },
+
+// Monitors
+ //{ MODKEY, XK_comma, focusmon, {.i = -1 } },
+ //{ MODKEY, XK_period, focusmon, {.i = +1 } },
+ //{ MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } },
+ //{ MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } },
+
+ { MODKEY, XK_space, zoom, {0} },
+ { MODKEY|ShiftMask, XK_space, togglefloating, {0} },
+
+ { MODKEY, XK_j, focusstack, {.i = +1 } },
+ { MODKEY, XK_k, focusstack, {.i = -1 } },
+ { MODKEY|ShiftMask, XK_k, incnmaster, {.i = +1 } },
+ { MODKEY|ShiftMask, XK_j, incnmaster, {.i = -1 } },
+ { MODKEY, XK_h, setmfact, {.f = -0.05} },
+ { MODKEY, XK_l, setmfact, {.f = +0.05} },
+
+ TAGKEYS( XK_1, 0)
+ TAGKEYS( XK_2, 1)
+ TAGKEYS( XK_3, 2)
+ TAGKEYS( XK_4, 3)
+ TAGKEYS( XK_5, 4)
+ TAGKEYS( XK_6, 5)
+ TAGKEYS( XK_7, 6)
+ TAGKEYS( XK_8, 7)
+ TAGKEYS( XK_9, 8)
+};
+
+/* button definitions */
+/* click can be ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, ClkClientWin, or ClkRootWin */
+static const Button buttons[] = {
+ /* click event mask button function argument */
+ { ClkLtSymbol, 0, Button1, setlayout, {0} },
+ { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
+ { ClkWinTitle, 0, Button2, zoom, {0} },
+
+// { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+
+ /* DwmBlocks Patch */
+ { ClkStatusText, 0, Button1, sigstatusbar, {.i = 1} },
+ { ClkStatusText, 0, Button2, sigstatusbar, {.i = 2} },
+ { ClkStatusText, 0, Button3, sigstatusbar, {.i = 3} },
+
+ { ClkClientWin, MODKEY, Button1, movemouse, {0} },
+ { ClkClientWin, MODKEY, Button2, togglefloating, {0} },
+ { ClkClientWin, MODKEY, Button3, resizemouse, {0} },
+ { ClkTagBar, 0, Button1, view, {0} },
+ { ClkTagBar, 0, Button3, toggleview, {0} },
+ { ClkTagBar, MODKEY, Button1, tag, {0} },
+ { ClkTagBar, MODKEY, Button3, toggletag, {0} },
+};
+
diff --git a/config.mk b/config.mk
index ba64d3d..1c9d2e3 100644
--- a/config.mk
+++ b/config.mk
@@ -20,10 +20,11 @@ FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
+#KVMLIB = -lkvm
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res ${KVMLIB}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
diff --git a/dwm.1 b/dwm.1
index ddc8321..3d310ac 100644
--- a/dwm.1
+++ b/dwm.1
@@ -116,6 +116,9 @@ Zooms/cycles focused window to/from master area (tiled layouts only).
.B Mod1\-Shift\-c
Close focused window.
.TP
+.B Mod1\-Shift\-f
+Toggle fullscreen for focused window.
+.TP
.B Mod1\-Shift\-space
Toggle focused window between tiled and floating state.
.TP
diff --git a/dwm.c b/dwm.c
index f1d86b2..db52dce 100644
--- a/dwm.c
+++ b/dwm.c
@@ -40,6 +40,12 @@
#include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */
#include <X11/Xft/Xft.h>
+#include <X11/Xlib-xcb.h>
+#include <xcb/res.h>
+#ifdef __OpenBSD__
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif /* __OpenBSD */
#include "drw.h"
#include "util.h"
@@ -57,12 +63,27 @@
#define TAGMASK ((1 << LENGTH(tags)) - 1)
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+#define SYSTEM_TRAY_REQUEST_DOCK 0
+/* XEMBED messages */
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_IN 4
+#define XEMBED_MODALITY_ON 10
+#define XEMBED_MAPPED (1 << 0)
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_WINDOW_DEACTIVATE 2
+#define VERSION_MAJOR 0
+#define VERSION_MINOR 0
+#define XEMBED_EMBEDDED_VERSION (VERSION_MAJOR << 16) | VERSION_MINOR
+
/* enums */
enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
+ NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+ NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetLast }; /* EWMH atoms */
+enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@@ -92,9 +113,11 @@ struct Client {
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
int bw, oldbw;
unsigned int tags;
- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow;
+ pid_t pid;
Client *next;
Client *snext;
+ Client *swallowing;
Monitor *mon;
Window win;
};
@@ -119,6 +142,10 @@ struct Monitor {
int by; /* bar geometry */
int mx, my, mw, mh; /* screen size */
int wx, wy, ww, wh; /* window area */
+ int gappih; /* horizontal gap between windows */
+ int gappiv; /* vertical gap between windows */
+ int gappoh; /* horizontal outer gaps */
+ int gappov; /* vertical outer gaps */
unsigned int seltags;
unsigned int sellt;
unsigned int tagset[2];
@@ -138,9 +165,17 @@ typedef struct {
const char *title;
unsigned int tags;
int isfloating;
+ int isterminal;
+ int noswallow;
int monitor;
} Rule;
+typedef struct Systray Systray;
+struct Systray {
+ Window win;
+ Client *icons;
+};
+
/* function declarations */
static void applyrules(Client *c);
static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@@ -172,6 +207,8 @@ static void focusstack(const Arg *arg);
static Atom getatomprop(Client *c, Atom prop);
static int getrootptr(int *x, int *y);
static long getstate(Window w);
+static unsigned int getsystraywidth();
+static pid_t getstatusbarpid();
static int gettextprop(Window w, Atom atom, char *text, unsigned int size);
static void grabbuttons(Client *c, int focused);
static void grabkeys(void);
@@ -189,15 +226,19 @@ static void pop(Client *c);
static void propertynotify(XEvent *e);
static void quit(const Arg *arg);
static Monitor *recttomon(int x, int y, int w, int h);
+static void removesystrayicon(Client *i);
static void resize(Client *c, int x, int y, int w, int h, int interact);
+static void resizebarwin(Monitor *m);
static void resizeclient(Client *c, int x, int y, int w, int h);
static void resizemouse(const Arg *arg);
+static void resizerequest(XEvent *e);
static void restack(Monitor *m);
static void run(void);
static void scan(void);
-static int sendevent(Client *c, Atom proto);
+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 setclienttagprop(Client *c);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
@@ -205,12 +246,14 @@ static void setmfact(const Arg *arg);
static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
+static void sigstatusbar(const Arg *arg);
static void spawn(const Arg *arg);
+static Monitor *systraytomon(Monitor *m);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
-static void tile(Monitor *m);
static void togglebar(const Arg *arg);
static void togglefloating(const Arg *arg);
+static void togglefullscr(const Arg *arg);
static void toggletag(const Arg *arg);
static void toggleview(const Arg *arg);
static void unfocus(Client *c, int setfocus);
@@ -223,20 +266,34 @@ static int updategeom(void);
static void updatenumlockmask(void);
static void updatesizehints(Client *c);
static void updatestatus(void);
+static void updatesystray(void);
+static void updatesystrayicongeom(Client *i, int w, int h);
+static void updatesystrayiconstate(Client *i, XPropertyEvent *ev);
static void updatetitle(Client *c);
static void updatewindowtype(Client *c);
static void updatewmhints(Client *c);
static void view(const Arg *arg);
static Client *wintoclient(Window w);
static Monitor *wintomon(Window w);
+static Client *wintosystrayicon(Window w);
static int xerror(Display *dpy, XErrorEvent *ee);
static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg);
+static pid_t getparentprocess(pid_t p);
+static int isdescprocess(pid_t p, pid_t c);
+static Client *swallowingclient(Window w);
+static Client *termforwin(const Client *c);
+static pid_t winpid(Window w);
+
/* variables */
+static Systray *systray = NULL;
static const char broken[] = "broken";
static char stext[256];
+static int statusw;
+static int statussig;
+static pid_t statuspid = -1;
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar height */
@@ -257,9 +314,10 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[MapRequest] = maprequest,
[MotionNotify] = motionnotify,
[PropertyNotify] = propertynotify,
+ [ResizeRequest] = resizerequest,
[UnmapNotify] = unmapnotify
};
-static Atom wmatom[WMLast], netatom[NetLast];
+static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast];
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
@@ -268,6 +326,8 @@ static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
+static xcb_connection_t *xcon;
+
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -297,6 +357,8 @@ applyrules(Client *c)
&& (!r->class || strstr(class, r->class))
&& (!r->instance || strstr(instance, r->instance)))
{
+ c->isterminal = r->isterminal;
+ c->noswallow = r->noswallow;
c->isfloating = r->isfloating;
c->tags |= r->tags;
for (m = mons; m && m->num != r->monitor; m = m->next);
@@ -416,6 +478,53 @@ attachstack(Client *c)
}
void
+swallow(Client *p, Client *c)
+{
+
+ if (c->noswallow || c->isterminal)
+ return;
+ if (c->noswallow && !swallowfloating && c->isfloating)
+ return;
+
+ detach(c);
+ detachstack(c);
+
+ setclientstate(c, WithdrawnState);
+ XUnmapWindow(dpy, p->win);
+
+ p->swallowing = c;
+ c->mon = p->mon;
+
+ Window w = p->win;
+ p->win = c->win;
+ c->win = w;
+ updatetitle(p);
+ XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
+ arrange(p->mon);
+ configure(p);
+ updateclientlist();
+}
+
+void
+unswallow(Client *c)
+{
+ c->win = c->swallowing->win;
+
+ free(c->swallowing);
+ c->swallowing = NULL;
+
+ /* unfullscreen the client */
+ setfullscreen(c, 0);
+ updatetitle(c);
+ arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ setclientstate(c, NormalState);
+ focus(NULL);
+ arrange(c->mon);
+}
+
+void
buttonpress(XEvent *e)
{
unsigned int i, x, click;
@@ -423,6 +532,7 @@ buttonpress(XEvent *e)
Client *c;
Monitor *m;
XButtonPressedEvent *ev = &e->xbutton;
+ char *text, *s, ch;
click = ClkRootWin;
/* focus monitor if necessary */
@@ -441,10 +551,25 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + TEXTW(selmon->ltsymbol))
click = ClkLtSymbol;
- else if (ev->x > selmon->ww - (int)TEXTW(stext))
+ else if (ev->x > selmon->ww - statusw - getsystraywidth()) {
+ x = selmon->ww - statusw;
click = ClkStatusText;
- else
+ statussig = 0;
+ for (text = s = stext; *s && x <= ev->x; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ x += TEXTW(text) - lrpad;
+ *s = ch;
+ text = s + 1;
+ if (x >= ev->x)
+ break;
+ statussig = ch;
+ }
+ }
+ } else {
click = ClkWinTitle;
+ }
} else if ((c = wintoclient(ev->window))) {
focus(c);
restack(selmon);
@@ -484,6 +609,13 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root);
while (mons)
cleanupmon(mons);
+
+ if (showsystray) {
+ XUnmapWindow(dpy, systray->win);
+ XDestroyWindow(dpy, systray->win);
+ free(systray);
+ }
+
for (i = 0; i < CurLast; i++)
drw_cur_free(drw, cursor[i]);
for (i = 0; i < LENGTH(colors); i++)
@@ -515,9 +647,58 @@ cleanupmon(Monitor *mon)
void
clientmessage(XEvent *e)
{
+ XWindowAttributes wa;
+ XSetWindowAttributes swa;
XClientMessageEvent *cme = &e->xclient;
Client *c = wintoclient(cme->window);
+ if (showsystray && cme->window == systray->win && cme->message_type == netatom[NetSystemTrayOP]) {
+ /* add systray icons */
+ if (cme->data.l[1] == SYSTEM_TRAY_REQUEST_DOCK) {
+ if (!(c = (Client *)calloc(1, sizeof(Client))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Client));
+ if (!(c->win = cme->data.l[2])) {
+ free(c);
+ return;
+ }
+ c->mon = selmon;
+ c->next = systray->icons;
+ systray->icons = c;
+ if (!XGetWindowAttributes(dpy, c->win, &wa)) {
+ /* use sane defaults */
+ wa.width = bh;
+ wa.height = bh;
+ wa.border_width = 0;
+ }
+ c->x = c->oldx = c->y = c->oldy = 0;
+ c->w = c->oldw = wa.width;
+ c->h = c->oldh = wa.height;
+ c->oldbw = wa.border_width;
+ c->bw = 0;
+ c->isfloating = True;
+ /* reuse tags field as mapped status */
+ c->tags = 1;
+ updatesizehints(c);
+ updatesystrayicongeom(c, wa.width, wa.height);
+ XAddToSaveSet(dpy, c->win);
+ XSelectInput(dpy, c->win, StructureNotifyMask | PropertyChangeMask | ResizeRedirectMask);
+ XReparentWindow(dpy, c->win, systray->win, 0, 0);
+ /* use parents background color */
+ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, c->win, CWBackPixel, &swa);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ /* FIXME not sure if I have to send these events, too */
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_FOCUS_IN, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ sendevent(c->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_MODALITY_ON, 0 , systray->win, XEMBED_EMBEDDED_VERSION);
+ XSync(dpy, False);
+ resizebarwin(selmon);
+ updatesystray();
+ setclientstate(c, NormalState);
+ }
+ return;
+ }
+
if (!c)
return;
if (cme->message_type == netatom[NetWMState]) {
@@ -570,7 +751,7 @@ configurenotify(XEvent *e)
for (c = m->clients; c; c = c->next)
if (c->isfullscreen)
resizeclient(c, m->mx, m->my, m->mw, m->mh);
- XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, m->ww, bh);
+ resizebarwin(m);
}
focus(NULL);
arrange(NULL);
@@ -641,6 +822,10 @@ createmon(void)
m->nmaster = nmaster;
m->showbar = showbar;
m->topbar = topbar;
+ m->gappih = gappih;
+ m->gappiv = gappiv;
+ m->gappoh = gappoh;
+ m->gappov = gappov;
m->lt[0] = &layouts[0];
m->lt[1] = &layouts[1 % LENGTH(layouts)];
strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol);
@@ -655,6 +840,13 @@ destroynotify(XEvent *e)
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
+ else if ((c = swallowingclient(ev->window)))
+ unmanage(c->swallowing, 1);
+ else if ((c = wintosystrayicon(ev->window))) {
+ removesystrayicon(c);
+ resizebarwin(selmon);
+ updatesystray();
+ }
}
void
@@ -698,7 +890,7 @@ dirtomon(int dir)
void
drawbar(Monitor *m)
{
- int x, w, tw = 0;
+ int x, w, tw = 0, stw = 0;
int boxs = drw->fonts->h / 9;
int boxw = drw->fonts->h / 6 + 2;
unsigned int i, occ = 0, urg = 0;
@@ -707,13 +899,32 @@ drawbar(Monitor *m)
if (!m->showbar)
return;
+ if(showsystray && m == systraytomon(m) && !systrayonleft)
+ stw = getsystraywidth();
+
/* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */
+ char *text, *s, ch;
drw_setscheme(drw, scheme[SchemeNorm]);
- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
+
+ x = -stw; // TODO
+ for (text = s = stext; *s; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ tw = TEXTW(text) - lrpad;
+ drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
+ x += tw;
+ *s = ch;
+ text = s + 1;
+ }
+ }
+ tw = TEXTW(text) - lrpad + 2;
+ drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
+ tw = statusw;
}
+ resizebarwin(m);
for (c = m->clients; c; c = c->next) {
occ |= c->tags;
if (c->isurgent)
@@ -734,7 +945,7 @@ drawbar(Monitor *m)
drw_setscheme(drw, scheme[SchemeNorm]);
x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0);
- if ((w = m->ww - tw - x) > bh) {
+ if ((w = m->ww - tw - stw - x) > bh) {
if (m->sel) {
drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]);
drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0);
@@ -745,7 +956,7 @@ drawbar(Monitor *m)
drw_rect(drw, x, 0, w, bh, 1, 1);
}
}
- drw_map(drw, m->barwin, 0, 0, m->ww, bh);
+ drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh);
}
void
@@ -782,8 +993,11 @@ expose(XEvent *e)
Monitor *m;
XExposeEvent *ev = &e->xexpose;
- if (ev->count == 0 && (m = wintomon(ev->window)))
+ if (ev->count == 0 && (m = wintomon(ev->window))) {
drawbar(m);
+ if (m == selmon)
+ updatesystray();
+ }
}
void
@@ -869,14 +1083,56 @@ getatomprop(Client *c, Atom prop)
unsigned char *p = NULL;
Atom da, atom = None;
- if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM,
+ /* FIXME getatomprop should return the number of items and a pointer to
+ * the stored data instead of this workaround */
+ Atom req = XA_ATOM;
+ if (prop == xatom[XembedInfo])
+ req = xatom[XembedInfo];
+
+ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req,
&da, &di, &dl, &dl, &p) == Success && p) {
atom = *(Atom *)p;
+ if (da == xatom[XembedInfo] && dl == 2)
+ atom = ((Atom *)p)[1];
XFree(p);
}
return atom;
}
+pid_t
+getstatusbarpid()
+{
+ char buf[32], *str = buf, *c;
+ FILE *fp;
+
+ if (statuspid > 0) {
+ snprintf(buf, sizeof(buf), "/proc/%u/cmdline", statuspid);
+ if ((fp = fopen(buf, "r"))) {
+ fgets(buf, sizeof(buf), fp);
+ while ((c = strchr(str, '/')))
+ str = c + 1;
+ fclose(fp);
+ if (!strcmp(str, STATUSBAR))
+ return statuspid;
+ }
+ }
+ if (!(fp = popen("pidof -s "STATUSBAR, "r")))
+ return -1;
+ fgets(buf, sizeof(buf), fp);
+ pclose(fp);
+ return strtol(buf, NULL, 10);
+}
+
+unsigned int
+getsystraywidth()
+{
+ unsigned int w = 0;
+ Client *i;
+ if(showsystray)
+ for(i = systray->icons; i; w += i->w + systrayspacing, i = i->next) ;
+ return w ? w + systrayspacing : 1;
+}
+
int
getrootptr(int *x, int *y)
{
@@ -1017,7 +1273,8 @@ killclient(const Arg *arg)
{
if (!selmon->sel)
return;
- if (!sendevent(selmon->sel, wmatom[WMDelete])) {
+
+ if (!sendevent(selmon->sel->win, wmatom[WMDelete], NoEventMask, wmatom[WMDelete], CurrentTime, 0 , 0, 0)) {
XGrabServer(dpy);
XSetErrorHandler(xerrordummy);
XSetCloseDownMode(dpy, DestroyAll);
@@ -1031,12 +1288,13 @@ killclient(const Arg *arg)
void
manage(Window w, XWindowAttributes *wa)
{
- Client *c, *t = NULL;
+ Client *c, *t = NULL, *term = NULL;
Window trans = None;
XWindowChanges wc;
c = ecalloc(1, sizeof(Client));
c->win = w;
+ c->pid = winpid(w);
/* geometry */
c->x = c->oldx = wa->x;
c->y = c->oldy = wa->y;
@@ -1051,6 +1309,7 @@ manage(Window w, XWindowAttributes *wa)
} else {
c->mon = selmon;
applyrules(c);
+ term = termforwin(c);
}
if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
@@ -1068,6 +1327,26 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
+ {
+ int format;
+ unsigned long *data, n, extra;
+ Monitor *m;
+ Atom atom;
+ if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL,
+ &atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) {
+ c->tags = *data;
+ for (m = mons; m; m = m->next) {
+ if (m->num == *(data+1)) {
+ c->mon = m;
+ break;
+ }
+ }
+ }
+ if (n > 0)
+ XFree(data);
+ }
+ setclienttagprop(c);
+
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@@ -1085,6 +1364,8 @@ manage(Window w, XWindowAttributes *wa)
c->mon->sel = c;
arrange(c->mon);
XMapWindow(dpy, c->win);
+ if (term)
+ swallow(term, c);
focus(NULL);
}
@@ -1104,6 +1385,13 @@ maprequest(XEvent *e)
static XWindowAttributes wa;
XMapRequestEvent *ev = &e->xmaprequest;
+ Client *i;
+ if ((i = wintosystrayicon(ev->window))) {
+ sendevent(i->win, netatom[Xembed], StructureNotifyMask, CurrentTime, XEMBED_WINDOW_ACTIVATE, 0, systray->win, XEMBED_EMBEDDED_VERSION);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+
if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect)
return;
if (!wintoclient(ev->window))
@@ -1225,6 +1513,17 @@ propertynotify(XEvent *e)
Window trans;
XPropertyEvent *ev = &e->xproperty;
+ if ((c = wintosystrayicon(ev->window))) {
+ if (ev->atom == XA_WM_NORMAL_HINTS) {
+ updatesizehints(c);
+ updatesystrayicongeom(c, c->w, c->h);
+ }
+ else
+ updatesystrayiconstate(c, ev);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+
if ((ev->window == root) && (ev->atom == XA_WM_NAME))
updatestatus();
else if (ev->state == PropertyDelete)
@@ -1276,6 +1575,19 @@ recttomon(int x, int y, int w, int h)
}
void
+removesystrayicon(Client *i)
+{
+ Client **ii;
+
+ if (!showsystray || !i)
+ return;
+ for (ii = &systray->icons; *ii && *ii != i; ii = &(*ii)->next);
+ if (ii)
+ *ii = i->next;
+ free(i);
+}
+
+void
resize(Client *c, int x, int y, int w, int h, int interact)
{
if (applysizehints(c, &x, &y, &w, &h, interact))
@@ -1283,6 +1595,14 @@ resize(Client *c, int x, int y, int w, int h, int interact)
}
void
+resizebarwin(Monitor *m) {
+ unsigned int w = m->ww;
+ if (showsystray && m == systraytomon(m) && !systrayonleft)
+ w -= getsystraywidth();
+ XMoveResizeWindow(dpy, m->barwin, m->wx, m->by, w, bh);
+}
+
+void
resizeclient(Client *c, int x, int y, int w, int h)
{
XWindowChanges wc;
@@ -1298,6 +1618,19 @@ resizeclient(Client *c, int x, int y, int w, int h)
}
void
+resizerequest(XEvent *e)
+{
+ XResizeRequestEvent *ev = &e->xresizerequest;
+ Client *i;
+
+ if ((i = wintosystrayicon(ev->window))) {
+ updatesystrayicongeom(i, ev->width, ev->height);
+ resizebarwin(selmon);
+ updatesystray();
+ }
+}
+
+void
resizemouse(const Arg *arg)
{
int ocx, ocy, nw, nh;
@@ -1429,6 +1762,7 @@ sendmon(Client *c, Monitor *m)
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c);
attachstack(c);
+ setclienttagprop(c);
focus(NULL);
arrange(NULL);
}
@@ -1443,26 +1777,37 @@ setclientstate(Client *c, long state)
}
int
-sendevent(Client *c, Atom proto)
+sendevent(Window w, Atom proto, int mask, long d0, long d1, long d2, long d3, long d4)
{
int n;
- Atom *protocols;
+ Atom *protocols, mt;
int exists = 0;
XEvent ev;
- if (XGetWMProtocols(dpy, c->win, &protocols, &n)) {
- while (!exists && n--)
- exists = protocols[n] == proto;
- XFree(protocols);
+ if (proto == wmatom[WMTakeFocus] || proto == wmatom[WMDelete]) {
+ mt = wmatom[WMProtocols];
+ if (XGetWMProtocols(dpy, w, &protocols, &n)) {
+ while (!exists && n--)
+ exists = protocols[n] == proto;
+ XFree(protocols);
+ }
+ }
+ else {
+ exists = True;
+ mt = proto;
}
+
if (exists) {
ev.type = ClientMessage;
- ev.xclient.window = c->win;
- ev.xclient.message_type = wmatom[WMProtocols];
+ ev.xclient.window = w;
+ ev.xclient.message_type = mt;
ev.xclient.format = 32;
- ev.xclient.data.l[0] = proto;
- ev.xclient.data.l[1] = CurrentTime;
- XSendEvent(dpy, c->win, False, NoEventMask, &ev);
+ ev.xclient.data.l[0] = d0;
+ ev.xclient.data.l[1] = d1;
+ ev.xclient.data.l[2] = d2;
+ ev.xclient.data.l[3] = d3;
+ ev.xclient.data.l[4] = d4;
+ XSendEvent(dpy, w, False, mask, &ev);
}
return exists;
}
@@ -1476,7 +1821,7 @@ setfocus(Client *c)
XA_WINDOW, 32, PropModeReplace,
(unsigned char *) &(c->win), 1);
}
- sendevent(c, wmatom[WMTakeFocus]);
+ sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0);
}
void
@@ -1572,6 +1917,10 @@ setup(void)
wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False);
netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False);
netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False);
+ netatom[NetSystemTray] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_S0", False);
+ netatom[NetSystemTrayOP] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_OPCODE", False);
+ netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False);
+ netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", False);
netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False);
netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False);
netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False);
@@ -1579,6 +1928,10 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False);
+ xatom[Manager] = XInternAtom(dpy, "MANAGER", False);
+ xatom[Xembed] = XInternAtom(dpy, "_XEMBED", False);
+ xatom[XembedInfo] = XInternAtom(dpy, "_XEMBED_INFO", False);
/* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
@@ -1587,6 +1940,8 @@ setup(void)
scheme = ecalloc(LENGTH(colors), sizeof(Clr *));
for (i = 0; i < LENGTH(colors); i++)
scheme[i] = drw_scm_create(drw, colors[i], 3);
+ /* init system tray */
+ updatesystray();
/* init bars */
updatebars();
updatestatus();
@@ -1602,6 +1957,7 @@ setup(void)
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
+ XDeleteProperty(dpy, root, netatom[NetClientInfo]);
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
@@ -1645,6 +2001,20 @@ showhide(Client *c)
}
void
+sigstatusbar(const Arg *arg)
+{
+ union sigval sv;
+
+ if (!statussig)
+ return;
+ sv.sival_int = arg->i;
+ if ((statuspid = getstatusbarpid()) <= 0)
+ return;
+
+ sigqueue(statuspid, SIGRTMIN+statussig, sv);
+}
+
+void
spawn(const Arg *arg)
{
struct sigaction sa;
@@ -1667,10 +2037,21 @@ spawn(const Arg *arg)
}
void
+setclienttagprop(Client *c)
+{
+ long data[] = { (long) c->tags, (long) c->mon->num };
+ XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *) data, 2);
+}
+
+void
tag(const Arg *arg)
{
+ Client *c;
if (selmon->sel && arg->ui & TAGMASK) {
+ c = selmon->sel;
selmon->sel->tags = arg->ui & TAGMASK;
+ setclienttagprop(c);
focus(NULL);
arrange(selmon);
}
@@ -1685,39 +2066,22 @@ tagmon(const Arg *arg)
}
void
-tile(Monitor *m)
-{
- unsigned int i, n, h, mw, my, ty;
- Client *c;
-
- for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
- 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), 0);
- if (my + HEIGHT(c) < m->wh)
- 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), 0);
- if (ty + HEIGHT(c) < m->wh)
- ty += HEIGHT(c);
- }
-}
-
-void
togglebar(const Arg *arg)
{
selmon->showbar = !selmon->showbar;
updatebarpos(selmon);
- XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh);
+ resizebarwin(selmon);
+ if (showsystray) {
+ XWindowChanges wc;
+ if (!selmon->showbar)
+ wc.y = -bh;
+ else if (selmon->showbar) {
+ wc.y = 0;
+ if (!selmon->topbar)
+ wc.y = selmon->mh - bh;
+ }
+ XConfigureWindow(dpy, systray->win, CWY, &wc);
+ }
arrange(selmon);
}
@@ -1736,6 +2100,13 @@ togglefloating(const Arg *arg)
}
void
+togglefullscr(const Arg *arg)
+{
+ if(selmon->sel)
+ setfullscreen(selmon->sel, !selmon->sel->isfullscreen);
+}
+
+void
toggletag(const Arg *arg)
{
unsigned int newtags;
@@ -1745,6 +2116,7 @@ toggletag(const Arg *arg)
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
+ setclienttagprop(selmon->sel);
focus(NULL);
arrange(selmon);
}
@@ -1781,6 +2153,20 @@ unmanage(Client *c, int destroyed)
Monitor *m = c->mon;
XWindowChanges wc;
+ if (c->swallowing) {
+ unswallow(c);
+ return;
+ }
+
+ Client *s = swallowingclient(c->win);
+ if (s) {
+ free(s->swallowing);
+ s->swallowing = NULL;
+ arrange(m);
+ focus(NULL);
+ return;
+ }
+
detach(c);
detachstack(c);
if (!destroyed) {
@@ -1796,9 +2182,12 @@ unmanage(Client *c, int destroyed)
XUngrabServer(dpy);
}
free(c);
- focus(NULL);
- updateclientlist();
- arrange(m);
+
+ if (!s) {
+ arrange(m);
+ focus(NULL);
+ updateclientlist();
+ }
}
void
@@ -1813,11 +2202,18 @@ unmapnotify(XEvent *e)
else
unmanage(c, 0);
}
+ else if ((c = wintosystrayicon(ev->window))) {
+ /* KLUDGE! sometimes icons occasionally unmap their windows, but do
+ * _not_ destroy them. We map those windows back */
+ XMapRaised(dpy, c->win);
+ updatesystray();
+ }
}
void
updatebars(void)
{
+ unsigned int w;
Monitor *m;
XSetWindowAttributes wa = {
.override_redirect = True,
@@ -1828,10 +2224,15 @@ updatebars(void)
for (m = mons; m; m = m->next) {
if (m->barwin)
continue;
- m->barwin = XCreateWindow(dpy, root, m->wx, m->by, m->ww, bh, 0, DefaultDepth(dpy, screen),
+ w = m->ww;
+ if (showsystray && m == systraytomon(m))
+ w -= getsystraywidth();
+ m->barwin = XCreateWindow(dpy, root, m->wx, m->by, w, bh, 0, DefaultDepth(dpy, screen),
CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa);
XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor);
+ if (showsystray && m == systraytomon(m))
+ XMapRaised(dpy, systray->win);
XMapRaised(dpy, m->barwin);
XSetClassHint(dpy, m->barwin, &ch);
}
@@ -2005,9 +2406,145 @@ updatesizehints(Client *c)
void
updatestatus(void)
{
- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
strcpy(stext, "dwm-"VERSION);
+ statusw = TEXTW(stext) - lrpad + 2;
+ } else {
+ char *text, *s, ch;
+
+ statusw = 0;
+ for (text = s = stext; *s; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ statusw += TEXTW(text) - lrpad;
+ *s = ch;
+ text = s + 1;
+ }
+ }
+ statusw += TEXTW(text) - lrpad + 2;
+
+ }
drawbar(selmon);
+ updatesystray();
+}
+
+
+void
+updatesystrayicongeom(Client *i, int w, int h)
+{
+ if (i) {
+ i->h = bh;
+ if (w == h)
+ i->w = bh;
+ else if (h == bh)
+ i->w = w;
+ else
+ i->w = (int) ((float)bh * ((float)w / (float)h));
+ applysizehints(i, &(i->x), &(i->y), &(i->w), &(i->h), False);
+ /* force icons into the systray dimensions if they don't want to */
+ if (i->h > bh) {
+ if (i->w == i->h)
+ i->w = bh;
+ else
+ i->w = (int) ((float)bh * ((float)i->w / (float)i->h));
+ i->h = bh;
+ }
+ }
+}
+
+void
+updatesystrayiconstate(Client *i, XPropertyEvent *ev)
+{
+ long flags;
+ int code = 0;
+
+ if (!showsystray || !i || ev->atom != xatom[XembedInfo] ||
+ !(flags = getatomprop(i, xatom[XembedInfo])))
+ return;
+
+ if (flags & XEMBED_MAPPED && !i->tags) {
+ i->tags = 1;
+ code = XEMBED_WINDOW_ACTIVATE;
+ XMapRaised(dpy, i->win);
+ setclientstate(i, NormalState);
+ }
+ else if (!(flags & XEMBED_MAPPED) && i->tags) {
+ i->tags = 0;
+ code = XEMBED_WINDOW_DEACTIVATE;
+ XUnmapWindow(dpy, i->win);
+ setclientstate(i, WithdrawnState);
+ }
+ else
+ return;
+ sendevent(i->win, xatom[Xembed], StructureNotifyMask, CurrentTime, code, 0,
+ systray->win, XEMBED_EMBEDDED_VERSION);
+}
+
+void
+updatesystray(void)
+{
+ XSetWindowAttributes wa;
+ XWindowChanges wc;
+ Client *i;
+ Monitor *m = systraytomon(NULL);
+ unsigned int x = m->mx + m->mw;
+ unsigned int sw = TEXTW(stext) - lrpad + systrayspacing;
+ unsigned int w = 1;
+
+ if (!showsystray)
+ return;
+ if (systrayonleft)
+ x -= sw + lrpad / 2;
+ if (!systray) {
+ /* init systray */
+ if (!(systray = (Systray *)calloc(1, sizeof(Systray))))
+ die("fatal: could not malloc() %u bytes\n", sizeof(Systray));
+ systray->win = XCreateSimpleWindow(dpy, root, x, m->by, w, bh, 0, 0, scheme[SchemeSel][ColBg].pixel);
+ wa.event_mask = ButtonPressMask | ExposureMask;
+ wa.override_redirect = True;
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XSelectInput(dpy, systray->win, SubstructureNotifyMask);
+ XChangeProperty(dpy, systray->win, netatom[NetSystemTrayOrientation], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *)&netatom[NetSystemTrayOrientationHorz], 1);
+ XChangeWindowAttributes(dpy, systray->win, CWEventMask|CWOverrideRedirect|CWBackPixel, &wa);
+ XMapRaised(dpy, systray->win);
+ XSetSelectionOwner(dpy, netatom[NetSystemTray], systray->win, CurrentTime);
+ if (XGetSelectionOwner(dpy, netatom[NetSystemTray]) == systray->win) {
+ sendevent(root, xatom[Manager], StructureNotifyMask, CurrentTime, netatom[NetSystemTray], systray->win, 0, 0);
+ XSync(dpy, False);
+ }
+ else {
+ fprintf(stderr, "dwm: unable to obtain system tray.\n");
+ free(systray);
+ systray = NULL;
+ return;
+ }
+ }
+ for (w = 0, i = systray->icons; i; i = i->next) {
+ /* make sure the background color stays the same */
+ wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+ XChangeWindowAttributes(dpy, i->win, CWBackPixel, &wa);
+ XMapRaised(dpy, i->win);
+ w += systrayspacing;
+ i->x = w;
+ XMoveResizeWindow(dpy, i->win, i->x, 0, i->w, i->h);
+ w += i->w;
+ if (i->mon != m)
+ i->mon = m;
+ }
+ w = w ? w + systrayspacing : 1;
+ x -= w;
+ XMoveResizeWindow(dpy, systray->win, x, m->by, w, bh);
+ wc.x = x; wc.y = m->by; wc.width = w; wc.height = bh;
+ wc.stack_mode = Above; wc.sibling = m->barwin;
+ XConfigureWindow(dpy, systray->win, CWX|CWY|CWWidth|CWHeight|CWSibling|CWStackMode, &wc);
+ XMapWindow(dpy, systray->win);
+ XMapSubwindows(dpy, systray->win);
+ /* redraw background */
+ XSetForeground(dpy, drw->gc, scheme[SchemeNorm][ColBg].pixel);
+ XFillRectangle(dpy, systray->win, drw->gc, 0, 0, w, bh);
+ XSync(dpy, False);
}
void
@@ -2062,6 +2599,136 @@ view(const Arg *arg)
arrange(selmon);
}
+pid_t
+winpid(Window w)
+{
+
+ pid_t result = 0;
+
+#ifdef __linux__
+ xcb_res_client_id_spec_t spec = {0};
+ spec.client = w;
+ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
+
+ xcb_generic_error_t *e = NULL;
+ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
+ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
+
+ if (!r)
+ return (pid_t)0;
+
+ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
+ for (; i.rem; xcb_res_client_id_value_next(&i)) {
+ spec = i.data->spec;
+ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
+ uint32_t *t = xcb_res_client_id_value_value(i.data);
+ result = *t;
+ break;
+ }
+ }
+
+ free(r);
+
+ if (result == (pid_t)-1)
+ result = 0;
+
+#endif /* __linux__ */
+
+#ifdef __OpenBSD__
+ Atom type;
+ int format;
+ unsigned long len, bytes;
+ unsigned char *prop;
+ pid_t ret;
+
+ if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop)
+ return 0;
+
+ ret = *(pid_t*)prop;
+ XFree(prop);
+ result = ret;
+
+#endif /* __OpenBSD__ */
+ return result;
+}
+
+pid_t
+getparentprocess(pid_t p)
+{
+ unsigned int v = 0;
+
+#ifdef __linux__
+ FILE *f;
+ char buf[256];
+ snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p);
+
+ if (!(f = fopen(buf, "r")))
+ return 0;
+
+ fscanf(f, "%*u %*s %*c %u", &v);
+ fclose(f);
+#endif /* __linux__*/
+
+#ifdef __OpenBSD__
+ int n;
+ kvm_t *kd;
+ struct kinfo_proc *kp;
+
+ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
+ if (!kd)
+ return 0;
+
+ kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
+ v = kp->p_ppid;
+#endif /* __OpenBSD__ */
+
+ return (pid_t)v;
+}
+
+int
+isdescprocess(pid_t p, pid_t c)
+{
+ while (p != c && c != 0)
+ c = getparentprocess(c);
+
+ return (int)c;
+}
+
+Client *
+termforwin(const Client *w)
+{
+ Client *c;
+ Monitor *m;
+
+ if (!w->pid || w->isterminal)
+ return NULL;
+
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next) {
+ if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
+ return c;
+ }
+ }
+
+ return NULL;
+}
+
+Client *
+swallowingclient(Window w)
+{
+ Client *c;
+ Monitor *m;
+
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next) {
+ if (c->swallowing && c->swallowing->win == w)
+ return c;
+ }
+ }
+
+ return NULL;
+}
+
Client *
wintoclient(Window w)
{
@@ -2075,6 +2742,16 @@ wintoclient(Window w)
return NULL;
}
+Client *
+wintosystrayicon(Window w) {
+ Client *i = NULL;
+
+ if (!showsystray || !w)
+ return i;
+ for (i = systray->icons; i && i->win != w; i = i->next) ;
+ return i;
+}
+
Monitor *
wintomon(Window w)
{
@@ -2128,6 +2805,22 @@ xerrorstart(Display *dpy, XErrorEvent *ee)
return -1;
}
+Monitor *
+systraytomon(Monitor *m) {
+ Monitor *t;
+ int i, n;
+ if(!systraypinning) {
+ if(!m)
+ return selmon;
+ return m == selmon ? m : NULL;
+ }
+ for(n = 1, t = mons; t && t->next; n++, t = t->next) ;
+ for(i = 1, t = mons; t && t->next && i < systraypinning; i++, t = t->next) ;
+ if(systraypinningfailfirst && n < systraypinning)
+ return mons;
+ return t;
+}
+
void
zoom(const Arg *arg)
{
@@ -2151,10 +2844,12 @@ main(int argc, char *argv[])
fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL)))
die("dwm: cannot open display");
+ if (!(xcon = XGetXCBConnection(dpy)))
+ die("dwm: cannot get xcb connection\n");
checkotherwm();
setup();
#ifdef __OpenBSD__
- if (pledge("stdio rpath proc exec", NULL) == -1)
+ if (pledge("stdio rpath proc exec ps", NULL) == -1)
die("pledge");
#endif /* __OpenBSD__ */
scan();
diff --git a/vanitygaps.c b/vanitygaps.c
new file mode 100644
index 0000000..4111d1e
--- /dev/null
+++ b/vanitygaps.c
@@ -0,0 +1,809 @@
+/* Key binding functions */
+static void defaultgaps(const Arg *arg);
+static void incrgaps(const Arg *arg);
+static void incrigaps(const Arg *arg);
+static void incrogaps(const Arg *arg);
+static void incrohgaps(const Arg *arg);
+static void incrovgaps(const Arg *arg);
+static void incrihgaps(const Arg *arg);
+static void incrivgaps(const Arg *arg);
+static void togglegaps(const Arg *arg);
+/* Layouts (delete the ones you do not need) */
+static void bstack(Monitor *m);
+static void bstackhoriz(Monitor *m);
+static void centeredmaster(Monitor *m);
+static void centeredfloatingmaster(Monitor *m);
+static void deck(Monitor *m);
+static void dwindle(Monitor *m);
+static void fibonacci(Monitor *m, int s);
+static void grid(Monitor *m);
+static void nrowgrid(Monitor *m);
+static void spiral(Monitor *m);
+static void tile(Monitor *m);
+/* Internals */
+static void getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc);
+static void getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr);
+static void setgaps(int oh, int ov, int ih, int iv);
+
+/* Settings */
+#if !PERTAG_PATCH
+static int enablegaps = 1;
+#endif // PERTAG_PATCH
+
+void
+setgaps(int oh, int ov, int ih, int iv)
+{
+ if (oh < 0) oh = 0;
+ if (ov < 0) ov = 0;
+ if (ih < 0) ih = 0;
+ if (iv < 0) iv = 0;
+
+ selmon->gappoh = oh;
+ selmon->gappov = ov;
+ selmon->gappih = ih;
+ selmon->gappiv = iv;
+ arrange(selmon);
+}
+
+void
+togglegaps(const Arg *arg)
+{
+ #if PERTAG_PATCH
+ selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag];
+ #else
+ enablegaps = !enablegaps;
+ #endif // PERTAG_PATCH
+ arrange(NULL);
+}
+
+void
+defaultgaps(const Arg *arg)
+{
+ setgaps(gappoh, gappov, gappih, gappiv);
+}
+
+void
+incrgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incrigaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incrogaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incrohgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incrovgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incrihgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv
+ );
+}
+
+void
+incrivgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc)
+{
+ unsigned int n, oe, ie;
+ #if PERTAG_PATCH
+ oe = ie = selmon->pertag->enablegaps[selmon->pertag->curtag];
+ #else
+ oe = ie = enablegaps;
+ #endif // PERTAG_PATCH
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ if (smartgaps && n == 1) {
+ oe = 0; // outer gaps disabled when only one client
+ }
+
+ *oh = m->gappoh*oe; // outer horizontal gap
+ *ov = m->gappov*oe; // outer vertical gap
+ *ih = m->gappih*ie; // inner horizontal gap
+ *iv = m->gappiv*ie; // inner vertical gap
+ *nc = n; // number of clients
+}
+
+void
+getfacts(Monitor *m, int msize, int ssize, float *mf, float *sf, int *mr, int *sr)
+{
+ unsigned int n;
+ float mfacts, sfacts;
+ int mtotal = 0, stotal = 0;
+ Client *c;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
+ mfacts = MIN(n, m->nmaster);
+ sfacts = n - m->nmaster;
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++)
+ if (n < m->nmaster)
+ mtotal += msize / mfacts;
+ else
+ stotal += ssize / sfacts;
+
+ *mf = mfacts; // total factor of master area
+ *sf = sfacts; // total factor of stack area
+ *mr = msize - mtotal; // the remainder (rest) of pixels after an even master split
+ *sr = ssize - stotal; // the remainder (rest) of pixels after an even stack split
+}
+
+/***
+ * Layouts
+ */
+
+/*
+ * Bottomstack layout + gaps
+ * https://dwm.suckless.org/patches/bottomstack/
+ */
+static void
+bstack(Monitor *m)
+{
+ unsigned int i, n;
+ int oh, ov, ih, iv;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int sx = 0, sy = 0, sh = 0, sw = 0;
+ float mfacts, sfacts;
+ int mrest, srest;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ sx = mx = m->wx + ov;
+ sy = my = m->wy + oh;
+ sh = mh = m->wh - 2*oh;
+ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1);
+ sw = m->ww - 2*ov - iv * (n - m->nmaster - 1);
+
+ if (m->nmaster && n > m->nmaster) {
+ sh = (mh - ih) * (1 - m->mfact);
+ mh = mh - ih - sh;
+ sx = mx;
+ sy = my + mh + ih;
+ }
+
+ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest);
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
+ if (i < m->nmaster) {
+ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
+ mx += WIDTH(c) + iv;
+ } else {
+ resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0);
+ sx += WIDTH(c) + iv;
+ }
+ }
+}
+
+static void
+bstackhoriz(Monitor *m)
+{
+ unsigned int i, n;
+ int oh, ov, ih, iv;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int sx = 0, sy = 0, sh = 0, sw = 0;
+ float mfacts, sfacts;
+ int mrest, srest;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ sx = mx = m->wx + ov;
+ sy = my = m->wy + oh;
+ mh = m->wh - 2*oh;
+ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1);
+ mw = m->ww - 2*ov - iv * (MIN(n, m->nmaster) - 1);
+ sw = m->ww - 2*ov;
+
+ if (m->nmaster && n > m->nmaster) {
+ sh = (mh - ih) * (1 - m->mfact);
+ mh = mh - ih - sh;
+ sy = my + mh + ih;
+ sh = m->wh - mh - 2*oh - ih * (n - m->nmaster);
+ }
+
+ getfacts(m, mw, sh, &mfacts, &sfacts, &mrest, &srest);
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
+ if (i < m->nmaster) {
+ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
+ mx += WIDTH(c) + iv;
+ } else {
+ resize(c, sx, sy, sw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0);
+ sy += HEIGHT(c) + ih;
+ }
+ }
+}
+
+/*
+ * Centred master layout + gaps
+ * https://dwm.suckless.org/patches/centeredmaster/
+ */
+void
+centeredmaster(Monitor *m)
+{
+ unsigned int i, n;
+ int oh, ov, ih, iv;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int lx = 0, ly = 0, lw = 0, lh = 0;
+ int rx = 0, ry = 0, rw = 0, rh = 0;
+ float mfacts = 0, lfacts = 0, rfacts = 0;
+ int mtotal = 0, ltotal = 0, rtotal = 0;
+ int mrest = 0, lrest = 0, rrest = 0;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ /* initialize areas */
+ mx = m->wx + ov;
+ my = m->wy + oh;
+ mh = m->wh - 2*oh - ih * ((!m->nmaster ? n : MIN(n, m->nmaster)) - 1);
+ mw = m->ww - 2*ov;
+ lh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - 1);
+ rh = m->wh - 2*oh - ih * (((n - m->nmaster) / 2) - ((n - m->nmaster) % 2 ? 0 : 1));
+
+ if (m->nmaster && n > m->nmaster) {
+ /* go mfact box in the center if more than nmaster clients */
+ if (n - m->nmaster > 1) {
+ /* ||<-S->|<---M--->|<-S->|| */
+ mw = (m->ww - 2*ov - 2*iv) * m->mfact;
+ lw = (m->ww - mw - 2*ov - 2*iv) / 2;
+ rw = (m->ww - mw - 2*ov - 2*iv) - lw;
+ mx += lw + iv;
+ } else {
+ /* ||<---M--->|<-S->|| */
+ mw = (mw - iv) * m->mfact;
+ lw = 0;
+ rw = m->ww - mw - iv - 2*ov;
+ }
+ lx = m->wx + ov;
+ ly = m->wy + oh;
+ rx = mx + mw + iv;
+ ry = m->wy + oh;
+ }
+
+ /* calculate facts */
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) {
+ if (!m->nmaster || n < m->nmaster)
+ mfacts += 1;
+ else if ((n - m->nmaster) % 2)
+ lfacts += 1; // total factor of left hand stack area
+ else
+ rfacts += 1; // total factor of right hand stack area
+ }
+
+ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++)
+ if (!m->nmaster || n < m->nmaster)
+ mtotal += mh / mfacts;
+ else if ((n - m->nmaster) % 2)
+ ltotal += lh / lfacts;
+ else
+ rtotal += rh / rfacts;
+
+ mrest = mh - mtotal;
+ lrest = lh - ltotal;
+ rrest = rh - rtotal;
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
+ if (!m->nmaster || i < m->nmaster) {
+ /* nmaster clients are stacked vertically, in the center of the screen */
+ resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0);
+ my += HEIGHT(c) + ih;
+ } else {
+ /* stack clients are stacked vertically */
+ if ((i - m->nmaster) % 2 ) {
+ resize(c, lx, ly, lw - (2*c->bw), (lh / lfacts) + ((i - 2*m->nmaster) < 2*lrest ? 1 : 0) - (2*c->bw), 0);
+ ly += HEIGHT(c) + ih;
+ } else {
+ resize(c, rx, ry, rw - (2*c->bw), (rh / rfacts) + ((i - 2*m->nmaster) < 2*rrest ? 1 : 0) - (2*c->bw), 0);
+ ry += HEIGHT(c) + ih;
+ }
+ }
+ }
+}
+
+void
+centeredfloatingmaster(Monitor *m)
+{
+ unsigned int i, n;
+ float mfacts, sfacts;
+ float mivf = 1.0; // master inner vertical gap factor
+ int oh, ov, ih, iv, mrest, srest;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int sx = 0, sy = 0, sh = 0, sw = 0;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ sx = mx = m->wx + ov;
+ sy = my = m->wy + oh;
+ sh = mh = m->wh - 2*oh;
+ mw = m->ww - 2*ov - iv*(n - 1);
+ sw = m->ww - 2*ov - iv*(n - m->nmaster - 1);
+
+ if (m->nmaster && n > m->nmaster) {
+ mivf = 0.8;
+ /* go mfact box in the center if more than nmaster clients */
+ if (m->ww > m->wh) {
+ mw = m->ww * m->mfact - iv*mivf*(MIN(n, m->nmaster) - 1);
+ mh = m->wh * 0.9;
+ } else {
+ mw = m->ww * 0.9 - iv*mivf*(MIN(n, m->nmaster) - 1);
+ mh = m->wh * m->mfact;
+ }
+ mx = m->wx + (m->ww - mw) / 2;
+ my = m->wy + (m->wh - mh - 2*oh) / 2;
+
+ sx = m->wx + ov;
+ sy = m->wy + oh;
+ sh = m->wh - 2*oh;
+ }
+
+ getfacts(m, mw, sw, &mfacts, &sfacts, &mrest, &srest);
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ /* nmaster clients are stacked horizontally, in the center of the screen */
+ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
+ mx += WIDTH(c) + iv*mivf;
+ } else {
+ /* stack clients are stacked horizontally */
+ resize(c, sx, sy, (sw / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0);
+ sx += WIDTH(c) + iv;
+ }
+}
+
+/*
+ * Deck layout + gaps
+ * https://dwm.suckless.org/patches/deck/
+ */
+void
+deck(Monitor *m)
+{
+ unsigned int i, n;
+ int oh, ov, ih, iv;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int sx = 0, sy = 0, sh = 0, sw = 0;
+ float mfacts, sfacts;
+ int mrest, srest;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ sx = mx = m->wx + ov;
+ sy = my = m->wy + oh;
+ sh = mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1);
+ sw = mw = m->ww - 2*ov;
+
+ if (m->nmaster && n > m->nmaster) {
+ sw = (mw - iv) * (1 - m->mfact);
+ mw = mw - iv - sw;
+ sx = mx + mw + iv;
+ sh = m->wh - 2*oh;
+ }
+
+ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest);
+
+ if (n - m->nmaster > 0) /* override layout symbol */
+ snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster);
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ resize(c, mx, my, mw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0);
+ my += HEIGHT(c) + ih;
+ } else {
+ resize(c, sx, sy, sw - (2*c->bw), sh - (2*c->bw), 0);
+ }
+}
+
+/*
+ * Fibonacci layout + gaps
+ * https://dwm.suckless.org/patches/fibonacci/
+ */
+void
+fibonacci(Monitor *m, int s)
+{
+ unsigned int i, n;
+ int nx, ny, nw, nh;
+ int oh, ov, ih, iv;
+ int nv, hrest = 0, wrest = 0, r = 1;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ nx = m->wx + ov;
+ ny = m->wy + oh;
+ nw = m->ww - 2*ov;
+ nh = m->wh - 2*oh;
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) {
+ if (r) {
+ if ((i % 2 && (nh - ih) / 2 <= (bh + 2*c->bw))
+ || (!(i % 2) && (nw - iv) / 2 <= (bh + 2*c->bw))) {
+ r = 0;
+ }
+ if (r && i < n - 1) {
+ if (i % 2) {
+ nv = (nh - ih) / 2;
+ hrest = nh - 2*nv - ih;
+ nh = nv;
+ } else {
+ nv = (nw - iv) / 2;
+ wrest = nw - 2*nv - iv;
+ nw = nv;
+ }
+
+ if ((i % 4) == 2 && !s)
+ nx += nw + iv;
+ else if ((i % 4) == 3 && !s)
+ ny += nh + ih;
+ }
+
+ if ((i % 4) == 0) {
+ if (s) {
+ ny += nh + ih;
+ nh += hrest;
+ }
+ else {
+ nh -= hrest;
+ ny -= nh + ih;
+ }
+ }
+ else if ((i % 4) == 1) {
+ nx += nw + iv;
+ nw += wrest;
+ }
+ else if ((i % 4) == 2) {
+ ny += nh + ih;
+ nh += hrest;
+ if (i < n - 1)
+ nw += wrest;
+ }
+ else if ((i % 4) == 3) {
+ if (s) {
+ nx += nw + iv;
+ nw -= wrest;
+ } else {
+ nw -= wrest;
+ nx -= nw + iv;
+ nh += hrest;
+ }
+ }
+ if (i == 0) {
+ if (n != 1) {
+ nw = (m->ww - iv - 2*ov) - (m->ww - iv - 2*ov) * (1 - m->mfact);
+ wrest = 0;
+ }
+ ny = m->wy + oh;
+ }
+ else if (i == 1)
+ nw = m->ww - nw - iv - 2*ov;
+ i++;
+ }
+
+ resize(c, nx, ny, nw - (2*c->bw), nh - (2*c->bw), False);
+ }
+}
+
+void
+dwindle(Monitor *m)
+{
+ fibonacci(m, 1);
+}
+
+void
+spiral(Monitor *m)
+{
+ fibonacci(m, 0);
+}
+
+/*
+ * Gappless grid layout + gaps (ironically)
+ * https://dwm.suckless.org/patches/gaplessgrid/
+ */
+void
+gaplessgrid(Monitor *m)
+{
+ unsigned int i, n;
+ int x, y, cols, rows, ch, cw, cn, rn, rrest, crest; // counters
+ int oh, ov, ih, iv;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ /* grid dimensions */
+ for (cols = 0; cols <= n/2; cols++)
+ if (cols*cols >= n)
+ break;
+ if (n == 5) /* set layout against the general calculation: not 1:2:2, but 2:3 */
+ cols = 2;
+ rows = n/cols;
+ cn = rn = 0; // reset column no, row no, client count
+
+ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows;
+ cw = (m->ww - 2*ov - iv * (cols - 1)) / cols;
+ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows;
+ crest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols;
+ x = m->wx + ov;
+ y = m->wy + oh;
+
+ for (i = 0, c = nexttiled(m->clients); c; i++, c = nexttiled(c->next)) {
+ if (i/rows + 1 > cols - n%cols) {
+ rows = n/cols + 1;
+ ch = (m->wh - 2*oh - ih * (rows - 1)) / rows;
+ rrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows;
+ }
+ resize(c,
+ x,
+ y + rn*(ch + ih) + MIN(rn, rrest),
+ cw + (cn < crest ? 1 : 0) - 2*c->bw,
+ ch + (rn < rrest ? 1 : 0) - 2*c->bw,
+ 0);
+ rn++;
+ if (rn >= rows) {
+ rn = 0;
+ x += cw + ih + (cn < crest ? 1 : 0);
+ cn++;
+ }
+ }
+}
+
+/*
+ * Gridmode layout + gaps
+ * https://dwm.suckless.org/patches/gridmode/
+ */
+void
+grid(Monitor *m)
+{
+ unsigned int i, n;
+ int cx, cy, cw, ch, cc, cr, chrest, cwrest, cols, rows;
+ int oh, ov, ih, iv;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+
+ /* grid dimensions */
+ for (rows = 0; rows <= n/2; rows++)
+ if (rows*rows >= n)
+ break;
+ cols = (rows && (rows - 1) * rows >= n) ? rows - 1 : rows;
+
+ /* window geoms (cell height/width) */
+ ch = (m->wh - 2*oh - ih * (rows - 1)) / (rows ? rows : 1);
+ cw = (m->ww - 2*ov - iv * (cols - 1)) / (cols ? cols : 1);
+ chrest = (m->wh - 2*oh - ih * (rows - 1)) - ch * rows;
+ cwrest = (m->ww - 2*ov - iv * (cols - 1)) - cw * cols;
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) {
+ cc = i / rows;
+ cr = i % rows;
+ cx = m->wx + ov + cc * (cw + iv) + MIN(cc, cwrest);
+ cy = m->wy + oh + cr * (ch + ih) + MIN(cr, chrest);
+ resize(c, cx, cy, cw + (cc < cwrest ? 1 : 0) - 2*c->bw, ch + (cr < chrest ? 1 : 0) - 2*c->bw, False);
+ }
+}
+
+/*
+ * Horizontal grid layout + gaps
+ * https://dwm.suckless.org/patches/horizgrid/
+ */
+void
+horizgrid(Monitor *m) {
+ Client *c;
+ unsigned int n, i;
+ int oh, ov, ih, iv;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int sx = 0, sy = 0, sh = 0, sw = 0;
+ int ntop, nbottom = 1;
+ float mfacts, sfacts;
+ int mrest, srest;
+
+ /* Count windows */
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ if (n <= 2)
+ ntop = n;
+ else {
+ ntop = n / 2;
+ nbottom = n - ntop;
+ }
+ sx = mx = m->wx + ov;
+ sy = my = m->wy + oh;
+ sh = mh = m->wh - 2*oh;
+ sw = mw = m->ww - 2*ov;
+
+ if (n > ntop) {
+ sh = (mh - ih) / 2;
+ mh = mh - ih - sh;
+ sy = my + mh + ih;
+ mw = m->ww - 2*ov - iv * (ntop - 1);
+ sw = m->ww - 2*ov - iv * (nbottom - 1);
+ }
+
+ mfacts = ntop;
+ sfacts = nbottom;
+ mrest = mw - (mw / ntop) * ntop;
+ srest = sw - (sw / nbottom) * nbottom;
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < ntop) {
+ resize(c, mx, my, (mw / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), mh - (2*c->bw), 0);
+ mx += WIDTH(c) + iv;
+ } else {
+ resize(c, sx, sy, (sw / sfacts) + ((i - ntop) < srest ? 1 : 0) - (2*c->bw), sh - (2*c->bw), 0);
+ sx += WIDTH(c) + iv;
+ }
+}
+
+/*
+ * nrowgrid layout + gaps
+ * https://dwm.suckless.org/patches/nrowgrid/
+ */
+void
+nrowgrid(Monitor *m)
+{
+ unsigned int n;
+ int ri = 0, ci = 0; /* counters */
+ int oh, ov, ih, iv; /* vanitygap settings */
+ unsigned int cx, cy, cw, ch; /* client geometry */
+ unsigned int uw = 0, uh = 0, uc = 0; /* utilization trackers */
+ unsigned int cols, rows = m->nmaster + 1;
+ Client *c;
+
+ /* count clients */
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+
+ /* nothing to do here */
+ if (n == 0)
+ return;
+
+ /* force 2 clients to always split vertically */
+ if (FORCE_VSPLIT && n == 2)
+ rows = 1;
+
+ /* never allow empty rows */
+ if (n < rows)
+ rows = n;
+
+ /* define first row */
+ cols = n / rows;
+ uc = cols;
+ cy = m->wy + oh;
+ ch = (m->wh - 2*oh - ih*(rows - 1)) / rows;
+ uh = ch;
+
+ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), ci++) {
+ if (ci == cols) {
+ uw = 0;
+ ci = 0;
+ ri++;
+
+ /* next row */
+ cols = (n - uc) / (rows - ri);
+ uc += cols;
+ cy = m->wy + oh + uh + ih;
+ uh += ch + ih;
+ }
+
+ cx = m->wx + ov + uw;
+ cw = (m->ww - 2*ov - uw) / (cols - ci);
+ uw += cw + iv;
+
+ resize(c, cx, cy, cw - (2*c->bw), ch - (2*c->bw), 0);
+ }
+}
+
+/*
+ * Default tile layout + gaps
+ */
+static void
+tile(Monitor *m)
+{
+ unsigned int i, n;
+ int oh, ov, ih, iv;
+ int mx = 0, my = 0, mh = 0, mw = 0;
+ int sx = 0, sy = 0, sh = 0, sw = 0;
+ float mfacts, sfacts;
+ int mrest, srest;
+ Client *c;
+
+ getgaps(m, &oh, &ov, &ih, &iv, &n);
+ if (n == 0)
+ return;
+
+ sx = mx = m->wx + ov;
+ sy = my = m->wy + oh;
+ mh = m->wh - 2*oh - ih * (MIN(n, m->nmaster) - 1);
+ sh = m->wh - 2*oh - ih * (n - m->nmaster - 1);
+ sw = mw = m->ww - 2*ov;
+
+ if (m->nmaster && n > m->nmaster) {
+ sw = (mw - iv) * (1 - m->mfact);
+ mw = mw - iv - sw;
+ sx = mx + mw + iv;
+ }
+
+ getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest);
+
+ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
+ if (i < m->nmaster) {
+ resize(c, sx, sy, sw - (2*c->bw), (mh / mfacts) + (i < mrest ? 1 : 0) - (2*c->bw), 0);
+ sy += HEIGHT(c) + ih;
+ } else {
+ resize(c, mx, my, mw - (2*c->bw), (sh / sfacts) + ((i - m->nmaster) < srest ? 1 : 0) - (2*c->bw), 0);
+ my += HEIGHT(c) + ih;
+ }
+}
Software created with 💖