Applied patches (in listed order): dwm-warp-6.4.diff dwm-swallow-6.3.diff dwm-sticky-6.4.diff dwm-pertag-20200914-61bb8b2.diff dwm-notitle-20210715-138b405.diff dwm-movestack-20211115-a786211.diff dwm-fullgaps-6.4.diff dwm-attachbottom-6.3.diff dwm-alwayscenter-20200625-f04cac6.diff dwm-windowmap-20221026.diff dwm-noborderselflickerfix-2022042627-d93ff48803f0.diff --- diff '--color=auto' -Nu a/config.def.h b/config.def.h --- a/config.def.h 2024-12-18 18:49:39.004372993 -0600 +++ b/config.def.h 2024-12-18 18:43:59.041041712 -0600 @@ -3,6 +3,7 @@ /* 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 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 +27,11 @@ * 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) */ @@ -60,6 +63,7 @@ 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 "movestack.c" static const Key keys[] = { /* modifier key function argument */ { MODKEY, XK_p, spawn, {.v = dmenucmd } }, @@ -71,6 +75,8 @@ { MODKEY, XK_d, incnmaster, {.i = -1 } }, { MODKEY, XK_h, setmfact, {.f = -0.05} }, { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, { MODKEY, XK_Return, zoom, {0} }, { MODKEY, XK_Tab, view, {0} }, { MODKEY|ShiftMask, XK_c, killclient, {0} }, @@ -95,6 +101,7 @@ TAGKEYS( XK_8, 7) TAGKEYS( XK_9, 8) { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY, XK_s, togglesticky, {0} }, }; /* button definitions */ @@ -103,7 +110,6 @@ /* 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 } }, { ClkClientWin, MODKEY, Button1, movemouse, {0} }, { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, diff '--color=auto' -Nu a/config.h b/config.h --- a/config.h 1969-12-31 18:00:00.000000000 -0600 +++ b/config.h 2024-12-18 18:48:22.571040090 -0600 @@ -0,0 +1,123 @@ +/* See LICENSE file for copyright and license details. */ + +/* appearance */ +static const unsigned int borderpx = 1; /* border pixel of windows */ +static const unsigned int snap = 1; /* snap pixel */ +static const int swallowfloating = 0; /* 1 means swallow floating windows by default */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const char *fonts[] = { "JetBrainsMono:bold:size=10" }; +static const char dmenufont[] = "JetBrainsMono:size=10"; +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[] = "#005577"; +/*Custom colors*/ +static const char col_norm_fg[] = "#374145"; +static const char col_norm_bg[] = "#1e2326"; +static const char col_sel_fg[] = "#7fbbb3"; +static const char col_sel_bg[] = "#1e2326"; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_norm_fg, col_norm_bg, col_norm_bg }, + [SchemeSel] = { col_sel_fg, col_sel_bg, col_sel_fg }, +}; + +/* tagging */ +static const char *tags[] = { " 1 ", " 2 ", " 3 ", " 4 ", " 5 ", " 6 ", " 7 ", " 8 ", " 9 " }; + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + /* class instance title tags mask isfloating isterminal noswallow monitor */ + { "st", NULL, NULL, 0, 0, 1, 0, -1 }, + { "Display-im6.q16", NULL, NULL, 0, 1, 1, 0, -1 }, + { "zbar", NULL, NULL, 0, 1, 1, 0, -1 }, + { "qemu-system-x86_64", NULL, NULL, 0, 1, 1, 0, -1 }, + { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */ +}; + +/* layout(s) */ +static const float mfact = 0.50; /* factor of master area size [0.05..0.95] */ +static const int nmaster = 1; /* number of clients in master area */ +static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "", tile }, /* first entry is default */ + { "[M]", monocle }, +}; + +/* key definitions */ +#define MODKEY Mod4Mask +#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 } } + +/* commands */ +static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn() */ +static const char *dmenucmd[] = { "dmenu_run", NULL }; +static const char *termcmd[] = { "st", NULL }; + +#include "movestack.c" +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_r, spawn, {.v = dmenucmd } }, + { MODKEY, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_i, incnmaster, {.i = +1 } }, + { MODKEY|ShiftMask, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ShiftMask, XK_j, movestack, {.i = +1 } }, + { MODKEY|ShiftMask, XK_k, movestack, {.i = -1 } }, + { MODKEY|ShiftMask, XK_Return, zoom, {0} }, + { MODKEY, XK_c, killclient, {0} }, + { MODKEY|ShiftMask, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY|ShiftMask, XK_m, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_t, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { 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 } }, + 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) + { MODKEY|ShiftMask, XK_q, quit, {0} }, + { MODKEY, XK_s, togglesticky, {0} }, +}; + +/* 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]} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { 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 '--color=auto' -Nu a/config.mk b/config.mk --- a/config.mk 2024-12-18 18:49:39.004372993 -0600 +++ b/config.mk 2024-12-18 18:43:59.044375045 -0600 @@ -20,10 +20,11 @@ # 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 '--color=auto' -Nu a/dwm.c b/dwm.c --- a/dwm.c 2024-12-18 18:49:39.004372993 -0600 +++ b/dwm.c 2024-12-18 18:43:59.044375045 -0600 @@ -40,6 +40,12 @@ #include #endif /* XINERAMA */ #include +#include +#include +#ifdef __OpenBSD__ +#include +#include +#endif /* __OpenBSD */ #include "drw.h" #include "util.h" @@ -49,7 +55,7 @@ #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) #define INTERSECT(x,y,w,h,m) (MAX(0, MIN((x)+(w),(m)->wx+(m)->ww) - MAX((x),(m)->wx)) \ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) -#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags])) +#define ISVISIBLE(C) ((C->tags & C->mon->tagset[C->mon->seltags]) || C->issticky) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define WIDTH(X) ((X)->w + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw) @@ -60,11 +66,11 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ enum { SchemeNorm, SchemeSel }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, - NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMFullscreen, NetWMSticky, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, - ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, + ClkRootWin, ClkLast }; /* clicks */ typedef union { int i; @@ -91,9 +97,11 @@ 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, issticky; + pid_t pid; Client *next; Client *snext; + Client *swallowing; Monitor *mon; Window win; }; @@ -110,6 +118,7 @@ void (*arrange)(Monitor *); } Layout; +typedef struct Pertag Pertag; struct Monitor { char ltsymbol[16]; float mfact; @@ -129,6 +138,7 @@ Monitor *next; Window barwin; const Layout *lt[2]; + Pertag *pertag; }; typedef struct { @@ -137,6 +147,8 @@ const char *title; unsigned int tags; int isfloating; + int isterminal; + int noswallow; int monitor; } Rule; @@ -146,6 +158,7 @@ static void arrange(Monitor *m); static void arrangemon(Monitor *m); static void attach(Client *c); +static void attachbottom(Client *c); static void attachstack(Client *c); static void buttonpress(XEvent *e); static void checkotherwm(void); @@ -199,17 +212,20 @@ static void setclientstate(Client *c, long state); static void setfocus(Client *c); static void setfullscreen(Client *c, int fullscreen); +static void setsticky(Client *c, int sticky); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setup(void); static void seturgent(Client *c, int urg); static void showhide(Client *c); +static int solitary(Client *c); static void spawn(const Arg *arg); 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 togglesticky(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); @@ -226,6 +242,10 @@ static void updatewindowtype(Client *c); static void updatewmhints(Client *c); static void view(const Arg *arg); +static void window_set_state(Display *dpy, Window win, long state); +static void window_map(Display *dpy, Client *c, int deiconify); +static void window_unmap(Display *dpy, Window win, Window root, int iconify); +static void warp(const Client *c); static Client *wintoclient(Window w); static Monitor *wintomon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); @@ -233,6 +253,12 @@ 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 const char broken[] = "broken"; static char stext[256]; @@ -267,9 +293,20 @@ static Monitor *mons, *selmon; static Window root, wmcheckwin; +static xcb_connection_t *xcon; + /* configuration, allows nested code to access above variables */ #include "config.h" +struct Pertag { + unsigned int curtag, prevtag; /* current and previous tag */ + int nmasters[LENGTH(tags) + 1]; /* number of windows in master area */ + float mfacts[LENGTH(tags) + 1]; /* mfacts per tag */ + unsigned int sellts[LENGTH(tags) + 1]; /* selected layouts */ + const Layout *ltidxs[LENGTH(tags) + 1][2]; /* matrix of tags and layouts indexes */ + int showbars[LENGTH(tags) + 1]; /* display bar for the current tag */ +}; + /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; @@ -296,6 +333,8 @@ && (!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); @@ -408,6 +447,15 @@ } void +attachbottom(Client *c) +{ + Client **tc; + c->next = NULL; + for (tc = &c->mon->clients; *tc; tc = &(*tc)->next); + *tc = c; +} + +void attachstack(Client *c) { c->snext = c->mon->stack; @@ -415,6 +463,53 @@ } 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; @@ -440,10 +535,8 @@ arg.ui = 1 << i; } else if (ev->x < x + TEXTW(selmon->ltsymbol)) click = ClkLtSymbol; - else if (ev->x > selmon->ww - (int)TEXTW(stext)) - click = ClkStatusText; else - click = ClkWinTitle; + click = ClkStatusText; } else if ((c = wintoclient(ev->window))) { focus(c); restack(selmon); @@ -524,6 +617,10 @@ || cme->data.l[2] == netatom[NetWMFullscreen]) setfullscreen(c, (cme->data.l[0] == 1 /* _NET_WM_STATE_ADD */ || (cme->data.l[0] == 2 /* _NET_WM_STATE_TOGGLE */ && !c->isfullscreen))); + + if (cme->data.l[1] == netatom[NetWMSticky] + || cme->data.l[2] == netatom[NetWMSticky]) + setsticky(c, (cme->data.l[0] == 1 || (cme->data.l[0] == 2 && !c->issticky))); } else if (cme->message_type == netatom[NetActiveWindow]) { if (c != selmon->sel && !c->isurgent) seturgent(c, 1); @@ -633,6 +730,7 @@ createmon(void) { Monitor *m; + unsigned int i; m = ecalloc(1, sizeof(Monitor)); m->tagset[0] = m->tagset[1] = 1; @@ -643,6 +741,20 @@ m->lt[0] = &layouts[0]; m->lt[1] = &layouts[1 % LENGTH(layouts)]; strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); + m->pertag = ecalloc(1, sizeof(Pertag)); + m->pertag->curtag = m->pertag->prevtag = 1; + + for (i = 0; i <= LENGTH(tags); i++) { + m->pertag->nmasters[i] = m->nmaster; + m->pertag->mfacts[i] = m->mfact; + + m->pertag->ltidxs[i][0] = m->lt[0]; + m->pertag->ltidxs[i][1] = m->lt[1]; + m->pertag->sellts[i] = m->sellt; + + m->pertag->showbars[i] = m->showbar; + } + return m; } @@ -654,6 +766,9 @@ if ((c = wintoclient(ev->window))) unmanage(c, 1); + + else if ((c = swallowingclient(ev->window))) + unmanage(c->swallowing, 1); } void @@ -698,7 +813,6 @@ drawbar(Monitor *m) { int x, w, tw = 0; - int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; unsigned int i, occ = 0, urg = 0; Client *c; @@ -724,7 +838,7 @@ drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); if (occ & 1 << i) - drw_rect(drw, x + boxs, boxs, boxw, boxw, + drw_rect(drw, x + 2 * boxw, 3.8 * boxw, w - 4 * boxw, 1.2 * boxw, m == selmon && selmon->sel && selmon->sel->tags & 1 << i, urg & 1 << i); x += w; @@ -734,15 +848,8 @@ x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); if ((w = m->ww - tw - 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); - if (m->sel->isfloating) - drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); - } else { drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, x, 0, w, bh, 1, 1); - } } drw_map(drw, m->barwin, 0, 0, m->ww, bh); } @@ -800,7 +907,11 @@ detachstack(c); attachstack(c); grabbuttons(c, 1); - XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + /* Avoid flickering when another client appears and the border + * is restored */ + if (!solitary(c)) { + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + } setfocus(c); } else { XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); @@ -832,6 +943,7 @@ unfocus(selmon->sel, 0); selmon = m; focus(NULL); + warp(selmon->sel); } void @@ -979,7 +1091,7 @@ void incnmaster(const Arg *arg) { - selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); arrange(selmon); } @@ -1030,12 +1142,13 @@ 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; @@ -1050,6 +1163,7 @@ } else { c->mon = selmon; applyrules(c); + term = termforwin(c); } if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) @@ -1067,13 +1181,15 @@ updatewindowtype(c); updatesizehints(c); updatewmhints(c); + c->x = c->mon->mx + (c->mon->mw - WIDTH(c)) / 2; + c->y = c->mon->my + (c->mon->mh - HEIGHT(c)) / 2; XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); grabbuttons(c, 0); if (!c->isfloating) c->isfloating = c->oldstate = trans != None || c->isfixed; if (c->isfloating) XRaiseWindow(dpy, c->win); - attach(c); + attachbottom(c); attachstack(c); XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, (unsigned char *) &(c->win), 1); @@ -1084,6 +1200,8 @@ c->mon->sel = c; arrange(c->mon); XMapWindow(dpy, c->win); + if (term) + swallow(term, c); focus(NULL); } @@ -1244,11 +1362,8 @@ drawbars(); break; } - if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) updatetitle(c); - if (c == c->mon->sel) - drawbar(c->mon); - } if (ev->atom == netatom[NetWMWindowType]) updatewindowtype(c); } @@ -1291,6 +1406,11 @@ c->oldw = c->w; c->w = wc.width = w; c->oldh = c->h; c->h = wc.height = h; wc.border_width = c->bw; + if (solitary(c)) { + c->w = wc.width += c->bw * 2; + c->h = wc.height += c->bw * 2; + wc.border_width = 0; + } XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); configure(c); XSync(dpy, False); @@ -1374,6 +1494,8 @@ wc.sibling = c->win; } } + if (m == selmon && (m->tagset[m->seltags] & m->sel->tags) && m->lt[m->sellt]->arrange != &monocle) + warp(m->sel); XSync(dpy, False); while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); } @@ -1426,7 +1548,7 @@ detachstack(c); c->mon = m; c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ - attach(c); + attachbottom(c); attachstack(c); focus(NULL); arrange(NULL); @@ -1507,12 +1629,29 @@ } void + setsticky(Client *c, int sticky) + { + + if(sticky && !c->issticky) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char *) &netatom[NetWMSticky], 1); + c->issticky = 1; + } else if(!sticky && c->issticky){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char *)0, 0); + c->issticky = 0; + arrange(c->mon); + } + } + + +void setlayout(const Arg *arg) { if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) - selmon->sellt ^= 1; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; if (arg && arg->v) - selmon->lt[selmon->sellt] = (Layout *)arg->v; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof selmon->ltsymbol); if (selmon->sel) arrange(selmon); @@ -1531,7 +1670,7 @@ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0; if (f < 0.05 || f > 0.95) return; - selmon->mfact = f; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; arrange(selmon); } @@ -1575,6 +1714,7 @@ netatom[NetWMState] = XInternAtom(dpy, "_NET_WM_STATE", False); netatom[NetWMCheck] = XInternAtom(dpy, "_NET_SUPPORTING_WM_CHECK", False); netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", False); + netatom[NetWMSticky] = XInternAtom(dpy, "_NET_WM_STATE_STICKY", False); 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); @@ -1632,17 +1772,24 @@ return; if (ISVISIBLE(c)) { /* show clients top down */ - XMoveWindow(dpy, c->win, c->x, c->y); - if ((!c->mon->lt[c->mon->sellt]->arrange || c->isfloating) && !c->isfullscreen) - resize(c, c->x, c->y, c->w, c->h, 0); + window_map(dpy, c, 1); showhide(c->snext); } else { /* hide clients bottom up */ showhide(c->snext); - XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + window_unmap(dpy, c->win, root, 1); } } +int +solitary(Client *c) +{ + return ((nexttiled(c->mon->clients) == c && !nexttiled(c->next)) + || &monocle == c->mon->lt[c->mon->sellt]->arrange) + && !c->isfullscreen && !c->isfloating + && NULL != c->mon->lt[c->mon->sellt]->arrange; +} + void spawn(const Arg *arg) { @@ -1714,7 +1861,7 @@ void togglebar(const Arg *arg) { - selmon->showbar = !selmon->showbar; + selmon->showbar = selmon->pertag->showbars[selmon->pertag->curtag] = !selmon->showbar; updatebarpos(selmon); XMoveResizeWindow(dpy, selmon->barwin, selmon->wx, selmon->by, selmon->ww, bh); arrange(selmon); @@ -1735,6 +1882,15 @@ } void +togglesticky(const Arg *arg) +{ + if (!selmon->sel) + return; + setsticky(selmon->sel, !selmon->sel->issticky); + arrange(selmon); +} + +void toggletag(const Arg *arg) { unsigned int newtags; @@ -1753,9 +1909,33 @@ toggleview(const Arg *arg) { unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + int i; if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; + + if (newtagset == ~0) { + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = 0; + } + + /* test if the user did not select the same tag */ + if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { + selmon->pertag->prevtag = selmon->pertag->curtag; + for (i = 0; !(newtagset & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + + /* apply settings for this view */ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + focus(NULL); arrange(selmon); } @@ -1780,6 +1960,20 @@ 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) { @@ -1795,9 +1989,12 @@ XUngrabServer(dpy); } free(c); - focus(NULL); - updateclientlist(); - arrange(m); + + if (!s) { + arrange(m); + focus(NULL); + updateclientlist(); + } } void @@ -1914,7 +2111,7 @@ m->clients = c->next; detachstack(c); c->mon = mons; - attach(c); + attachbottom(c); attachstack(c); } if (m == selmon) @@ -2026,6 +2223,9 @@ if (state == netatom[NetWMFullscreen]) setfullscreen(c, 1); + if (state == netatom[NetWMSticky]) { + setsticky(c, 1); + } if (wtype == netatom[NetWMWindowTypeDialog]) c->isfloating = 1; } @@ -2050,17 +2250,240 @@ } void +window_set_state(Display *dpy, Window win, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char*)data, 2); +} + +void +window_map(Display *dpy, Client *c, int deiconify) +{ + Window win = c->win; + + if (deiconify) + window_set_state(dpy, win, NormalState); + + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + XSetInputFocus(dpy, win, RevertToPointerRoot, CurrentTime); + XMapWindow(dpy, win); +} + +void +window_unmap(Display *dpy, Window win, Window root, int iconify) +{ + static XWindowAttributes ca, ra; + + XGrabServer(dpy); + XGetWindowAttributes(dpy, root, &ra); + XGetWindowAttributes(dpy, win, &ca); + + /* Prevent UnmapNotify events */ + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, win, ca.your_event_mask & ~StructureNotifyMask); + + XUnmapWindow(dpy, win); + + if (iconify) + window_set_state(dpy, win, IconicState); + + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, win, ca.your_event_mask); + XUngrabServer(dpy); +} + +void view(const Arg *arg) { + int i; + unsigned int tmptag; + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ - if (arg->ui & TAGMASK) + if (arg->ui & TAGMASK) { selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; + selmon->pertag->prevtag = selmon->pertag->curtag; + + if (arg->ui == ~0) + selmon->pertag->curtag = 0; + else { + for (i = 0; !(arg->ui & 1 << i); i++) ; + selmon->pertag->curtag = i + 1; + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; + selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; + selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; + selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; + + if (selmon->showbar != selmon->pertag->showbars[selmon->pertag->curtag]) + togglebar(NULL); + focus(NULL); arrange(selmon); } +void +warp(const Client *c) +{ + int x, y; + + if (!c) { + XWarpPointer(dpy, None, root, 0, 0, 0, 0, selmon->wx + selmon->ww / 2, selmon->wy + selmon->wh / 2); + return; + } + + if (!getrootptr(&x, &y) || + (x > c->x - c->bw && + y > c->y - c->bw && + x < c->x + c->w + c->bw*2 && + y < c->y + c->h + c->bw*2) || + (y > c->mon->by && y < c->mon->by + bh) || + (c->mon->topbar && !y)) + return; + + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w / 2, c->h / 2); +} + +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) { @@ -2150,10 +2573,12 @@ 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(); @@ -2162,3 +2587,4 @@ XCloseDisplay(dpy); return EXIT_SUCCESS; } + Common subdirectories: a/.git and b/.git diff '--color=auto' -Nu a/movestack.c b/movestack.c --- a/movestack.c 1969-12-31 18:00:00.000000000 -0600 +++ b/movestack.c 2024-12-18 18:43:59.044375045 -0600 @@ -0,0 +1,48 @@ +void +movestack(const Arg *arg) { + Client *c = NULL, *p = NULL, *pc = NULL, *i; + + if(arg->i > 0) { + /* find the client after selmon->sel */ + for(c = selmon->sel->next; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + if(!c) + for(c = selmon->clients; c && (!ISVISIBLE(c) || c->isfloating); c = c->next); + + } + else { + /* find the client before selmon->sel */ + for(i = selmon->clients; i != selmon->sel; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + if(!c) + for(; i; i = i->next) + if(ISVISIBLE(i) && !i->isfloating) + c = i; + } + /* find the client before selmon->sel and c */ + for(i = selmon->clients; i && (!p || !pc); i = i->next) { + if(i->next == selmon->sel) + p = i; + if(i->next == c) + pc = i; + } + + /* swap c and selmon->sel selmon->clients in the selmon->clients list */ + if(c && c != selmon->sel) { + Client *temp = selmon->sel->next==c?selmon->sel:selmon->sel->next; + selmon->sel->next = c->next==selmon->sel?c:c->next; + c->next = temp; + + if(p && p != c) + p->next = c; + if(pc && pc != selmon->sel) + pc->next = selmon->sel; + + if(selmon->sel == selmon->clients) + selmon->clients = c; + else if(c == selmon->clients) + selmon->clients = selmon->sel; + + arrange(selmon); + } +} \ No newline at end of file