diff --git a/README.md b/README.md index 44b6de3..a349526 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6 ### Changelog: +2022-06-17 - Ported the seamless restart feature from dusk into dwm-flexipatch + 2022-02-11 - Added the isfreesize version of the sizehints patch and the [tagsync](https://github.com/bakkeby/dwm-flexipatch/pull/219) patch (contributed by [Bagelli](https://github.com/Bagellll)) 2021-11-23 - Added the taglabels and underlinetags patches @@ -602,6 +604,9 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6 - this alternative patch enables a scratchpad feature in dwm similar to the scratchpad feature in i3wm + - seamless_restart + - allows for selected layout, assigned tags, etc. to be persisted across restarts + - [selfrestart](https://dwm.suckless.org/patches/selfrestart/) - restart dwm without the unnecessary dependency of an external script diff --git a/dwm.c b/dwm.c index 0fffa10..02dea6b 100644 --- a/dwm.c +++ b/dwm.c @@ -212,6 +212,14 @@ enum { WMLast }; /* default atoms */ +#if SEAMLESS_RESTART_PATCH +enum { + ClientFields, + ClientTags, + ClientLast +}; /* dwm client atoms */ +#endif // SEAMLESS_RESTART_PATCH + enum { #if BAR_STATUSBUTTON_PATCH ClkButton, @@ -335,6 +343,9 @@ struct Client { #if SAVEFLOATS_PATCH || EXRESIZE_PATCH int sfx, sfy, sfw, sfh; /* stored float geometry, used on mode revert */ #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH + #if SEAMLESS_RESTART_PATCH + unsigned int idx; + #endif // SEAMLESS_RESTART_PATCH int oldx, oldy, oldw, oldh; int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; int bw, oldbw; @@ -620,7 +631,7 @@ static void focusmon(const Arg *arg); #if !STACKER_PATCH static void focusstack(const Arg *arg); #endif // STACKER_PATCH -static Atom getatomprop(Client *c, Atom prop); +static Atom getatomprop(Client *c, Atom prop, Atom req); static int getrootptr(int *x, int *y); static long getstate(Window w); static int gettextprop(Window w, Atom atom, char *text, unsigned int size); @@ -782,11 +793,13 @@ static void (*handler[LASTEvent]) (XEvent *) = { #endif // BAR_SYSTRAY_PATCH [UnmapNotify] = unmapnotify }; -#if BAR_SYSTRAY_PATCH -static Atom wmatom[WMLast], netatom[NetLast], xatom[XLast]; -#else static Atom wmatom[WMLast], netatom[NetLast]; +#if BAR_SYSTRAY_PATCH +static Atom xatom[XLast]; #endif // BAR_SYSTRAY_PATCH +#if SEAMLESS_RESTART_PATCH +static Atom clientatom[ClientLast]; +#endif // SEAMLESS_RESTART_PATCH #if ON_EMPTY_KEYS_PATCH static int isempty = 0; #endif // ON_EMPTY_KEYS_PATCH @@ -839,7 +852,7 @@ applyrules(Client *c) XGetClassHint(dpy, c->win, &ch); class = ch.res_class ? ch.res_class : broken; instance = ch.res_name ? ch.res_name : broken; - wintype = getatomprop(c, netatom[NetWMWindowType]); + wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM); #if WINDOWROLERULE_PATCH gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); #endif // WINDOWROLERULE_PATCH @@ -1199,11 +1212,15 @@ checkotherwm(void) void cleanup(void) { + #if !SEAMLESS_RESTART_PATCH Arg a = {.ui = ~0}; + #endif // SEAMLESS_RESTART_PATCH Layout foo = { "", NULL }; Monitor *m; size_t i; + #if !SEAMLESS_RESTART_PATCH view(&a); + #endif // SEAMLESS_RESTART_PATCH selmon->lt[selmon->sellt] = &foo; for (m = mons; m; m = m->next) while (m->stack) @@ -1690,6 +1707,11 @@ createmon(void) #endif // PERTAG_VANITYGAPS_PATCH | VANITYGAPS_PATCH } #endif // PERTAG_PATCH + + #if SEAMLESS_RESTART_PATCH + restoremonitorstate(m); + #endif // SEAMLESS_RESTART_PATCH + #if INSETS_PATCH m->inset = default_inset; #endif // INSETS_PATCH @@ -1735,9 +1757,13 @@ void detach(Client *c) { Client **tc; + #if SEAMLESS_RESTART_PATCH + c->idx = 0; + #endif // SEAMLESS_RESTART_PATCH for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); *tc = c->next; + c->next = NULL; } void @@ -1752,6 +1778,7 @@ detachstack(Client *c) for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); c->mon->sel = t; } + c->snext = NULL; } Monitor * @@ -2080,7 +2107,7 @@ focusstack(const Arg *arg) #endif // STACKER_PATCH Atom -getatomprop(Client *c, Atom prop) +getatomprop(Client *c, Atom prop, Atom req) { int di; unsigned long dl; @@ -2088,26 +2115,21 @@ getatomprop(Client *c, Atom prop) Atom da, atom = None; #if BAR_SYSTRAY_PATCH - /* 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]; + #endif // BAR_SYSTRAY_PATCH + /* FIXME getatomprop should return the number of items and a pointer to + * the stored data instead of this workaround */ if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, req, &da, &di, &dl, &dl, &p) == Success && p) { atom = *(Atom *)p; + #if BAR_SYSTRAY_PATCH if (da == xatom[XembedInfo] && dl == 2) atom = ((Atom *)p)[1]; + #endif // BAR_SYSTRAY_PATCH XFree(p); } - #else - if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, - &da, &di, &dl, &dl, &p) == Success && p) { - atom = *(Atom *)p; - XFree(p); - } - #endif // BAR_SYSTRAY_PATCH return atom; } @@ -2310,6 +2332,9 @@ manage(Window w, XWindowAttributes *wa) #if SWALLOW_PATCH Client *term = NULL; #endif // SWALLOW_PATCH + #if SEAMLESS_RESTART_PATCH + int settings_restored; + #endif // SEAMLESS_RESTART_PATCH Window trans = None; XWindowChanges wc; @@ -2319,6 +2344,9 @@ manage(Window w, XWindowAttributes *wa) c->pid = winpid(w); #endif // SWALLOW_PATCH /* geometry */ + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + c->sfx = c->sfy = c->sfw = c->sfh = -9999; + #endif // SAVEFLOATS_PATCH | EXRESIZE_PATCH c->x = c->oldx = wa->x; c->y = c->oldy = wa->y; c->w = c->oldw = wa->width; @@ -2327,6 +2355,9 @@ manage(Window w, XWindowAttributes *wa) #if CFACTS_PATCH c->cfact = 1.0; #endif // CFACTS_PATCH + #if SEAMLESS_RESTART_PATCH + settings_restored = restoreclientstate(c); + #endif // SEAMLESS_RESTART_PATCH #if BAR_WINICON_PATCH updateicon(c); #endif // BAR_WINICON_PATCH @@ -2359,7 +2390,12 @@ manage(Window w, XWindowAttributes *wa) c->iscentered = 1; #endif // CENTER_TRANSIENT_WINDOWS_PATCH | CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH | CENTER_PATCH } else { + #if SEAMLESS_RESTART_PATCH + if (!settings_restored) + c->mon = selmon; + #else c->mon = selmon; + #endif // SEAMLESS_RESTART_PATCH #if CENTER_PATCH if (c->x == c->mon->wx && c->y == c->mon->wy) c->iscentered = 1; @@ -2369,7 +2405,12 @@ manage(Window w, XWindowAttributes *wa) #else c->bw = borderpx; #endif // SETBORDERPX_PATCH + #if SEAMLESS_RESTART_PATCH + if (!settings_restored) + applyrules(c); + #else applyrules(c); + #endif // SEAMLESS_RESTART_PATCH #if SWALLOW_PATCH term = termforwin(c); if (term) @@ -2396,7 +2437,7 @@ manage(Window w, XWindowAttributes *wa) #endif // BAR_FLEXWINTITLE_PATCH configure(c); /* propagates border_width, if size doesn't change */ updatesizehints(c); - if (getatomprop(c, netatom[NetWMState]) == netatom[NetWMFullscreen]) + if (getatomprop(c, netatom[NetWMState], XA_ATOM) == netatom[NetWMFullscreen]) setfullscreen(c, 1); updatewmhints(c); #if DECORATION_HINTS_PATCH @@ -2404,8 +2445,6 @@ manage(Window w, XWindowAttributes *wa) #endif // DECORATION_HINTS_PATCH #if CENTER_PATCH && SAVEFLOATS_PATCH || CENTER_PATCH && EXRESIZE_PATCH - c->sfx = -9999; - c->sfy = -9999; if (c->iscentered) { c->sfx = c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; c->sfy = c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; @@ -2421,13 +2460,12 @@ manage(Window w, XWindowAttributes *wa) #elif ALWAYSCENTER_PATCH c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; - #elif SAVEFLOATS_PATCH || EXRESIZE_PATCH - c->sfx = -9999; - c->sfy = -9999; #endif // CENTER_PATCH / ALWAYSCENTER_PATCH / SAVEFLOATS_PATCH #if SAVEFLOATS_PATCH || EXRESIZE_PATCH - c->sfw = c->w; - c->sfh = c->h; + if (c->sfw == -9999) { + c->sfw = c->w; + c->sfh = c->h; + } #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); @@ -2445,7 +2483,7 @@ manage(Window w, XWindowAttributes *wa) XRaiseWindow(dpy, c->win); XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColFloat].pixel); } - #if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH + #if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH || SEAMLESS_RESTART_PATCH attachx(c); #else attach(c); @@ -2600,8 +2638,8 @@ movemouse(const Arg *arg) #endif // FAKEFULLSCREEN_CLIENT_PATCH #endif // FAKEFULLSCREEN_PATCH restack(selmon); - ocx = c->x; - ocy = c->y; + nx = ocx = c->x; + ny = ocy = c->y; if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) return; @@ -2639,14 +2677,7 @@ movemouse(const Arg *arg) togglefloating(NULL); } if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { - #if SAVEFLOATS_PATCH || EXRESIZE_PATCH resize(c, nx, ny, c->w, c->h, 1); - /* save last known float coordinates */ - c->sfx = nx; - c->sfy = ny; - #else - resize(c, nx, ny, c->w, c->h, 1); - #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH } #if ROUNDED_CORNERS_PATCH drawroundedcorners(c); @@ -2654,6 +2685,7 @@ movemouse(const Arg *arg) break; } } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { #if SCRATCHPADS_PATCH @@ -2666,6 +2698,13 @@ movemouse(const Arg *arg) selmon = m; focus(NULL); } + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + /* save last known float coordinates */ + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + c->sfx = nx; + c->sfy = ny; + } + #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH #if ROUNDED_CORNERS_PATCH drawroundedcorners(c); #endif // ROUNDED_CORNERS_PATCH @@ -2773,8 +2812,13 @@ quit(const Arg *arg) #if RESTARTSIG_PATCH restart = arg->i; #endif // RESTARTSIG_PATCH + #if SEAMLESS_RESTART_PATCH + Monitor *m; + #endif // SEAMLESS_RESTART_PATCH #if ONLYQUITONEMPTY_PATCH + #if !SEAMLESS_RESTART_PATCH Monitor *m; + #endif // SEAMLESS_RESTART_PATCH Client *c; unsigned int n = 0; @@ -2794,6 +2838,11 @@ quit(const Arg *arg) running = 0; #endif // ONLYQUITONEMPTY_PATCH + #if SEAMLESS_RESTART_PATCH + for (m = mons; m && !running; m = m->next) + persistmonitorstate(m); + #endif // SEAMLESS_RESTART_PATCH + #if COOL_AUTOSTART_PATCH /* kill child processes */ for (i = 0; i < autostart_len && !running; i++) { @@ -2876,9 +2925,9 @@ resizeclient(Client *c, int x, int y, int w, int h) void resizemouse(const Arg *arg) { - int ocx, ocy, nw, nh; + int ocx, ocy, nw, nh, nx, ny; #if RESIZEPOINT_PATCH || RESIZECORNERS_PATCH - int opx, opy, och, ocw, nx, ny; + int opx, opy, och, ocw; int horizcorner, vertcorner; unsigned int dui; Window dummy; @@ -2900,8 +2949,10 @@ resizemouse(const Arg *arg) #endif // FAKEFULLSCREEN_CLIENT_PATCH #endif // !FAKEFULLSCREEN_PATCH restack(selmon); - ocx = c->x; - ocy = c->y; + nx = ocx = c->x; + ny = ocy = c->y; + nh = c->h; + nw = c->w; #if RESIZEPOINT_PATCH och = c->h; ocw = c->w; @@ -2968,24 +3019,7 @@ resizemouse(const Arg *arg) } } if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { - #if RESIZECORNERS_PATCH || RESIZEPOINT_PATCH - resizeclient(c, nx, ny, nw, nh); - #if SAVEFLOATS_PATCH || EXRESIZE_PATCH - /* save last known float dimensions */ - c->sfx = nx; - c->sfy = ny; - c->sfw = nw; - c->sfh = nh; - #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH - #else - resize(c, c->x, c->y, nw, nh, 1); - #if SAVEFLOATS_PATCH || EXRESIZE_PATCH - c->sfx = c->x; - c->sfy = c->y; - c->sfw = nw; - c->sfh = nh; - #endif // SAVEFLOATS_PATCH / EXRESIZE_PATCH - #endif // RESIZECORNERS_PATCH + resize(c, nx, ny, nw, nh, 1); #if ROUNDED_CORNERS_PATCH drawroundedcorners(c); #endif // ROUNDED_CORNERS_PATCH @@ -2993,6 +3027,7 @@ resizemouse(const Arg *arg) break; } } while (ev.type != ButtonRelease); + #if !RESIZEPOINT_PATCH #if RESIZECORNERS_PATCH XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, @@ -3015,6 +3050,15 @@ resizemouse(const Arg *arg) selmon = m; focus(NULL); } + #if SAVEFLOATS_PATCH || EXRESIZE_PATCH + /* save last known float dimensions */ + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) { + c->sfx = nx; + c->sfy = ny; + c->sfw = nw; + c->sfh = nh; + } + #endif // SAVEFLOATS_PATCH | EXRESIZE_PATCH ignoreconfigurerequests = 0; } @@ -3568,6 +3612,10 @@ setup(void) #if WINDOWROLERULE_PATCH wmatom[WMWindowRole] = XInternAtom(dpy, "WM_WINDOW_ROLE", False); #endif // WINDOWROLERULE_PATCH + #if SEAMLESS_RESTART_PATCH + clientatom[ClientFields] = XInternAtom(dpy, "_DWM_CLIENT_FIELDS", False); + clientatom[ClientTags] = XInternAtom(dpy, "_DWM_CLIENT_TAGS", False); + #endif // SEAMLESS_RESTART_PATCH netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); #if BAR_SYSTRAY_PATCH @@ -3757,7 +3805,7 @@ showhide(Client *c) /* show clients top down */ #if SAVEFLOATS_PATCH || EXRESIZE_PATCH if (!c->mon->lt[c->mon->sellt]->arrange && c->sfx != -9999 && !c->isfullscreen) { - XMoveWindow(dpy, c->win, c->sfx, c->sfy); + XMoveResizeWindow(dpy, c->win, c->sfx, c->sfy, c->sfw, c->sfh); resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 0); showhide(c->snext); return; @@ -3935,13 +3983,26 @@ tag(const Arg *arg) void tagmon(const Arg *arg) { - #if TAGMONFIXFS_PATCH Client *c = selmon->sel; + Monitor *dest; + #if SEAMLESS_RESTART_PATCH && SAVEFLOATS_PATCH + int restored; + #endif // SEAMLESS_RESTART_PATCH | SAVEFLOATS_PATCH if (!c || !mons->next) return; + dest = dirtomon(arg->i); + #if SEAMLESS_RESTART_PATCH && SAVEFLOATS_PATCH + savewindowfloatposition(c, c->mon); + restored = restorewindowfloatposition(c, dest); + if (restored && (!dest->lt[dest->sellt]->arrange || c->isfloating)) { + XMoveResizeWindow(dpy, c->win, c->sfx, c->sfy, c->sfw, c->sfh); + resize(c, c->sfx, c->sfy, c->sfw, c->sfh, 1); + } + #endif // SEAMLESS_RESTART_PATCH | SAVEFLOATS_PATCH + #if TAGMONFIXFS_PATCH if (c->isfullscreen) { c->isfullscreen = 0; - sendmon(c, dirtomon(arg->i)); + sendmon(c, dest); c->isfullscreen = 1; #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH if (c->fakefullscreen != 1) { @@ -3953,11 +4014,9 @@ tagmon(const Arg *arg) XRaiseWindow(dpy, c->win); #endif // FAKEFULLSCREEN_CLIENT_PATCH } else - sendmon(c, dirtomon(arg->i)); + sendmon(c, dest); #else - if (!selmon->sel || !mons->next) - return; - sendmon(selmon->sel, dirtomon(arg->i)); + sendmon(c, dest); #endif // TAGMONFIXFS_PATCH } @@ -4991,12 +5050,12 @@ main(int argc, char *argv[]) runautostart(); #endif run(); + cleanup(); + XCloseDisplay(dpy); #if RESTARTSIG_PATCH if (restart) execvp(argv[0], argv); #endif // RESTARTSIG_PATCH - cleanup(); - XCloseDisplay(dpy); return EXIT_SUCCESS; } diff --git a/patch/attachx.c b/patch/attachx.c index 9e06b94..0ce908a 100644 --- a/patch/attachx.c +++ b/patch/attachx.c @@ -1,8 +1,27 @@ void attachx(Client *c) { - #if ATTACHABOVE_PATCH + #if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBOTTOM_PATCH || SEAMLESS_RESTART_PATCH Client *at; + #endif // ATTACHABOVE_PATCH | ATTACHASIDE_PATCH | ATTACHBOTTOM_PATCH | SEAMLESS_RESTART_PATCH + + #if SEAMLESS_RESTART_PATCH + if (c->idx > 0) { /* then the client has a designated position in the client list */ + for (at = c->mon->clients; at; at = at->next) { + if (c->idx < at->idx) { + c->next = at; + c->mon->clients = c; + return; + } else if (at->idx <= c->idx && (!at->next || c->idx <= at->next->idx)) { + c->next = at->next; + at->next = c; + return; + } + } + } + #endif // SEAMLESS_RESTART_PATCH + + #if ATTACHABOVE_PATCH if (!(c->mon->sel == NULL || c->mon->sel == c->mon->clients || c->mon->sel->isfloating)) { for (at = c->mon->clients; at->next != c->mon->sel; at = at->next); c->next = at->next; @@ -10,9 +29,7 @@ attachx(Client *c) return; } #elif ATTACHASIDE_PATCH - Client *at; unsigned int n; - for (at = c->mon->clients, n = 0; at; at = at->next) if (!at->isfloating && ISVISIBLEONTAG(at, c->tags)) if (++n >= c->mon->nmaster) @@ -30,7 +47,6 @@ attachx(Client *c) return; } #elif ATTACHBOTTOM_PATCH - Client *at; for (at = c->mon->clients; at && at->next; at = at->next); if (at) { at->next = c; diff --git a/patch/bar_systray.c b/patch/bar_systray.c index 67d1d2d..36d569a 100644 --- a/patch/bar_systray.c +++ b/patch/bar_systray.c @@ -163,7 +163,7 @@ updatesystrayiconstate(Client *i, XPropertyEvent *ev) int code = 0; if (!showsystray || !systray || !i || ev->atom != xatom[XembedInfo] || - !(flags = getatomprop(i, xatom[XembedInfo]))) + !(flags = getatomprop(i, xatom[XembedInfo], xatom[XembedInfo]))) return; if (flags & XEMBED_MAPPED && !i->tags) { diff --git a/patch/include.c b/patch/include.c index 676e2ae..cf24638 100644 --- a/patch/include.c +++ b/patch/include.c @@ -97,7 +97,7 @@ #if ASPECTRESIZE_PATCH #include "aspectresize.c" #endif -#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH +#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH || SEAMLESS_RESTART_PATCH #include "attachx.c" #endif #if AUTOSTART_PATCH @@ -313,6 +313,9 @@ #if DRAGMFACT_PATCH #include "dragmfact.c" #endif +#if SEAMLESS_RESTART_PATCH +#include "seamless_restart.c" +#endif /* Layouts */ #if BSTACK_LAYOUT || BSTACKHORIZ_LAYOUT || CENTEREDMASTER_LAYOUT || CENTEREDFLOATINGMASTER_LAYOUT || COLUMNS_LAYOUT || DECK_LAYOUT || TILE_LAYOUT #include "layout_facts.c" diff --git a/patch/include.h b/patch/include.h index f26f36e..52da603 100644 --- a/patch/include.h +++ b/patch/include.h @@ -97,7 +97,7 @@ #if ASPECTRESIZE_PATCH #include "aspectresize.h" #endif -#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH +#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH || SEAMLESS_RESTART_PATCH #include "attachx.h" #endif #if AUTOSTART_PATCH @@ -223,6 +223,9 @@ #if SCRATCHPAD_ALT_1_PATCH #include "scratchpad_alt_1.h" #endif +#if SEAMLESS_RESTART_PATCH +#include "seamless_restart.h" +#endif #if SELFRESTART_PATCH #include "selfrestart.h" #endif diff --git a/patch/seamless_restart.c b/patch/seamless_restart.c new file mode 100644 index 0000000..7241eea --- /dev/null +++ b/patch/seamless_restart.c @@ -0,0 +1,471 @@ +void +persistmonitorstate(Monitor *m) +{ + Client *c; + unsigned int i; + + setmonitortags(m); + setmonitorfields(m); + + /* Set client atoms */ + for (i = 1, c = m->clients; c; c = c->next, ++i) { + c->idx = i; + persistclientstate(c); + #if SWALLOW_PATCH + if (c->swallowing) { + c->swallowing->idx = i; + persistclientstate(c->swallowing); + } + #endif // SWALLOW_PATCH + } +} + +int +restoremonitorstate(Monitor *m) +{ + return getmonitortags(m) | getmonitorfields(m); +} + +void +persistclientstate(Client *c) +{ + setclienttags(c); + setclientfields(c); + #if SAVEFLOATS_PATCH + savewindowfloatposition(c, c->mon); + #endif // SAVEFLOATS_PATCH +} + +int +restoreclientstate(Client *c) +{ + return getclienttags(c) + | getclientfields(c) + #if SAVEFLOATS_PATCH + | restorewindowfloatposition(c, c->mon ? c->mon : selmon) + #endif // SAVEFLOATS_PATCH + ; +} + +void setmonitorfields(Monitor *m) +{ + #if PERTAG_PATCH + unsigned int i; + #endif // PERTAG_PATCH + char atom[22] = {0}; + Atom monitor_fields; + #if FLEXTILE_DELUXE_LAYOUT + unsigned int flextile_deluxe_bitmask; + #endif // FLEXTILE_DELUXE_LAYOUT + + sprintf(atom, "_DWM_MONITOR_FIELDS_%u", m->num); + monitor_fields = XInternAtom(dpy, atom, False); + + /* Perists workspace information in 32 bits laid out like this: + * + * |0|0000|0|0000|0000|0000|0000|0000|000|000 + * | | | | | | | | | |-- nmaster + * | | | | | | | | |-- nstack + * | | | | | | | |-- layout + * | | | | | | |-- flextile LAYOUT (split) + * | | | | | |-- flextile MASTER + * | | | | |-- flextile STACK1 + * | | | |-- flextile STACK2 + * | | |-- flextile mirror layout (indicated by negative layout) + * | | + * | |-- reserved + * |-- showbar + */ + #if PERTAG_PATCH + for (i = 0; i <= NUMTAGS; i++) { + #if FLEXTILE_DELUXE_LAYOUT + flextile_deluxe_bitmask = (m->pertag->nstacks[i] & 0x7) << 3; + if (m->pertag->ltidxs[i][m->pertag->sellts[i]]->arrange == flextile) { + flextile_deluxe_bitmask |= + (abs(m->pertag->ltaxis[i][LAYOUT]) & 0xF) << 10 | + (m->pertag->ltaxis[i][MASTER] & 0xF) << 14 | + (m->pertag->ltaxis[i][STACK] & 0xF) << 18 | + (m->pertag->ltaxis[i][STACK2] & 0xF) << 22 | + (m->pertag->ltaxis[i][LAYOUT] < 0 ? 1 : 0) << 24; + } + #endif // FLEXTILE_DELUXE_L1AYOUT + uint32_t data[] = { + #if FLEXTILE_DELUXE_LAYOUT + flextile_deluxe_bitmask | + #endif // FLEXTILE_DELUXE_LAYOUT + (m->pertag->nmasters[i] & 0x7) | + (getlayoutindex(m->pertag->ltidxs[i][m->pertag->sellts[i]]) & 0xF) << 6 | + #if PERTAGBAR_PATCH + m->pertag->showbars[i] << 31 + #else + m->showbar << 31 + #endif // PERTAGBAR_PATCH + }; + + XChangeProperty(dpy, root, monitor_fields, XA_CARDINAL, 32, + i ? PropModeAppend : PropModeReplace, (unsigned char *)data, 1); + } + #else // !PERTAG_PATCH + #if FLEXTILE_DELUXE_LAYOUT + flextile_deluxe_bitmask = (m->nstack & 0x7) << 3; + if (m->lt[m->sellt]->arrange == flextile) { + flextile_deluxe_bitmask |= + (abs(m->ltaxis[LAYOUT]) & 0xF) << 10 | + (m->ltaxis[MASTER] & 0xF) << 14 | + (m->ltaxis[STACK] & 0xF) << 18 | + (m->ltaxis[STACK2] & 0xF) << 22 | + (m->ltaxis[LAYOUT] < 0 ? 1 : 0) << 24; + } + #endif // FLEXTILE_DELUXE_L1AYOUT + uint32_t data[] = { + #if FLEXTILE_DELUXE_LAYOUT + flextile_deluxe_bitmask | + #endif // FLEXTILE_DELUXE_LAYOUT + (m->nmaster & 0x7) | + (getlayoutindex(m->lt[m->sellt]) & 0xF) << 6 | + m->showbar << 31 + }; + + XChangeProperty(dpy, root, monitor_fields, XA_CARDINAL, 32, PropModeReplace, + (unsigned char *)data, 1); + #endif // PERTAG_PATCH +} + +int +getlayoutindex(const Layout *layout) +{ + int i; + + for (i = 0; i < LENGTH(layouts) && &layouts[i] != layout; i++); + if (i == LENGTH(layouts)) + i = 0; + return i; +} + +int +getmonitorfields(Monitor *m) +{ + int di, layout_index; + #if PERTAG_PATCH + unsigned int i, restored = 0; + unsigned int tags = m->tagset[m->seltags] << 1; + #endif // PERTAG_PATCH + unsigned long dl, nitems; + unsigned char *p = NULL; + char atom[22] = {0}; + Atom da, state = None; + + sprintf(atom, "_DWM_MONITOR_FIELDS_%u", m->num); + Atom dwm_monitor = XInternAtom(dpy, atom, False); + if (!dwm_monitor) + return 0; + + #if PERTAG_PATCH + for (i = 0; i <= NUMTAGS; i++) { + if (!(XGetWindowProperty(dpy, root, dwm_monitor, i, (NUMTAGS + 1) * sizeof dl, + False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) { + break; + } + + if (!nitems) { + XFree(p); + break; + } + + /* See bit layout in the persistmonitorstate function */ + state = *(Atom *)p; + + m->pertag->nmasters[i] = state & 0x7; + layout_index = (state >> 6) & 0xF; + if (layout_index < LENGTH(layouts)) + m->pertag->ltidxs[i][m->pertag->sellts[i]] = &layouts[layout_index]; + #if FLEXTILE_DELUXE_LAYOUT + m->pertag->nstacks[i] = (state >> 3) & 0x7; + if (m->pertag->ltidxs[i][m->pertag->sellts[i]]->arrange == flextile) { + m->pertag->ltaxis[i][LAYOUT] = (state >> 10) & 0xF; + m->pertag->ltaxis[i][MASTER] = (state >> 14) & 0xF; + m->pertag->ltaxis[i][STACK] = (state >> 18) & 0xF; + m->pertag->ltaxis[i][STACK2] = (state >> 22) & 0xF; + if (state >> 24 & 0x1) { + m->pertag->ltaxis[i][LAYOUT] *= -1; + } + } + #endif // FLEXTILE_DELUXE_LAYOUT + #if PERTAGBAR_PATCH + m->pertag->showbars[i] = (state >> 31) & 0x1; + #endif // PERTAGBAR_PATCH + + if (!restored && i && (tags & (1 << i))) { + m->nmaster = m->pertag->nmasters[i]; + m->sellt = m->pertag->sellts[i]; + m->lt[m->sellt] = m->pertag->ltidxs[i][m->sellt]; + #if FLEXTILE_DELUXE_LAYOUT + m->nstack = m->pertag->nstacks[i]; + if (m->lt[m->sellt]->arrange == flextile) { + m->ltaxis[LAYOUT] = m->pertag->ltaxis[i][LAYOUT]; + m->ltaxis[MASTER] = m->pertag->ltaxis[i][MASTER]; + m->ltaxis[STACK] = m->pertag->ltaxis[i][STACK]; + m->ltaxis[STACK2] = m->pertag->ltaxis[i][STACK2]; + } + #endif // FLEXTILE_DELUXE_LAYOUT + #if PERTAGBAR_PATCH + m->showbar = m->pertag->showbars[i]; + #else + m->showbar = (state >> 31) & 0x1; + #endif // PERTAGBAR_PATCH + restored = 1; + } + + XFree(p); + } + + return restored; + #else // !PERTAG_PATCH + if (!(XGetWindowProperty(dpy, root, dwm_monitor, 0L, sizeof dl, + False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) { + return 0; + } + + if (nitems) { + state = *(Atom *)p; + + /* See bit layout in the persistmonitorstate function */ + m->nmaster = state & 0x7; + #if FLEXTILE_DELUXE_LAYOUT + m->nstack = (state >> 3) & 0x7; + #endif // FLEXTILE_DELUXE_LAYOUT + layout_index = (state >> 6) & 0xF; + if (layout_index < LENGTH(layouts)) + m->lt[m->sellt] = &layouts[layout_index]; + #if FLEXTILE_DELUXE_LAYOUT + if (m->lt[m->sellt]->arrange == flextile) { + m->ltaxis[LAYOUT] = (state >> 10) & 0xF; + m->ltaxis[MASTER] = (state >> 14) & 0xF; + m->ltaxis[STACK] = (state >> 18) & 0xF; + m->ltaxis[STACK2] = (state >> 22) & 0xF; + } + #endif // FLEXTILE_DELUXE_LAYOUT + m->showbar = (state >> 31) & 0x1; + } + + XFree(p); + return 1; + #endif // PERTAG_PATCH +} + +void +setmonitortags(Monitor *m) +{ + char atom[22] = {0}; + Atom monitor_tags; + + sprintf(atom, "_DWM_MONITOR_TAGS_%u", m->num); + monitor_tags = XInternAtom(dpy, atom, False); + + uint32_t data[] = { m->tagset[m->seltags] }; + XChangeProperty(dpy, root, monitor_tags, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +int +getmonitortags(Monitor *m) +{ + int di; + unsigned long dl, nitems; + unsigned char *p = NULL; + char atom[22] = {0}; + Atom da, monitor_tags = None, tags; + + sprintf(atom, "_DWM_MONITOR_TAGS_%u", m->num); + monitor_tags = XInternAtom(dpy, atom, False); + + if (!(XGetWindowProperty(dpy, root, monitor_tags, 0L, sizeof dl, + False, AnyPropertyType, &da, &di, &nitems, &dl, &p) == Success && p)) { + return 0; + } + + if (nitems) { + tags = *(Atom *)p; + m->tagset[m->seltags] = tags & TAGMASK; + } + + XFree(p); + return 1; +} + +void +setclientfields(Client *c) +{ + /* Perists client information in 32 bits laid out like this: + * + * |00000000|00000|0|0|0|0|0|0|0|0|00000000|000 + * | | | | | | | | | | | |-- monitor index + * | | | | | | | | | | |-- client index + * | | | | | | | | | |-- isfloating + * | | | | | | | | |-- ispermanent + * | | | | | | | |-- isterminal + * | | | | | | |-- noswallow + * | | | | | |-- issteam + * | | | | |-- issticky + * | | | |-- fakefullscreen + * | | |-- isfreesize + * | | + * | |-- reserved + * |-- scratchkey (for scratchpads) + */ + uint32_t data[] = { + (c->mon->num & 0x7) + | (c->idx & 0xFF) << 3 + | (c->isfloating & 0x1) << 11 + #if ISPERMANENT_PATCH + | (c->ispermanent & 0x1) << 12 + #endif // ISPERMANENT_PATCH + #if SWALLOW_PATCH + | (c->isterminal & 0x1) << 13 + | (c->noswallow & 0x1) << 14 + #endif // SWALLOW_PATCH + #if STEAM_PATCH + | (c->issteam & 0x1) << 15 + #endif // STEAM_PATCH + #if STICKY_PATCH + | (c->issticky & 0x1) << 16 + #endif // STICKY_PATCH + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + | (c->fakefullscreen & 0x1) << 17 + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #if SIZEHINTS_ISFREESIZE_PATCH + | (c->isfreesize & 0x1) << 18 + #endif // SIZEHINTS_ISFREESIZE_PATCH + #if RENAMED_SCRATCHPADS_PATCH + | (c->scratchkey & 0xFF) << 24 + #endif // RENAMED_SCRATCHPADS_PATCH + }; + XChangeProperty(dpy, c->win, clientatom[ClientFields], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +int +getclientfields(Client *c) +{ + Monitor *m; + Atom fields = getatomprop(c, clientatom[ClientFields], AnyPropertyType); + if (fields == None) + return 0; + + /* See bit layout in the setclientfields function */ + for (m = mons; m; m = m->next) + if (m->num == (fields & 0x7)) { + c->mon = m; + break; + } + c->idx = (fields >> 3) & 0xFF; + c->isfloating = (fields >> 11) & 0x1; + #if ISPERMANENT_PATCH + c->ispermanent = (fields >> 12) & 0x1; + #endif // ISPERMANENT_PATCH + #if SWALLOW_PATCH + c->isterminal = (fields >> 13) & 0x1; + c->noswallow = (fields >> 14) & 0x1; + #endif // SWALLOW_PATCH + #if STEAM_PATCH + c->issteam = (fields >> 15) & 0x1; + #endif // STEAM_PATCH + #if STICKY_PATCH + c->issticky = (fields >> 16) & 0x1; + #endif // STICKY_PATCH + #if !FAKEFULLSCREEN_PATCH && FAKEFULLSCREEN_CLIENT_PATCH + c->fakefullscreen = (fields >> 17) & 0x1; + #endif // FAKEFULLSCREEN_CLIENT_PATCH + #if SIZEHINTS_ISFREESIZE_PATCH + c->isfreesize = (fields >> 18) & 0x1; + #endif // SIZEHINTS_ISFREESIZE_PATCH + #if RENAMED_SCRATCHPADS_PATCH + c->scratchkey = (fields >> 24) & 0xFF; + #endif // RENAMED_SCRATCHPADS_PATCH + return 1; +} + +void +setclienttags(Client *c) +{ + uint32_t data[] = { c->tags }; + XChangeProperty(dpy, c->win, clientatom[ClientTags], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); +} + +int +getclienttags(Client *c) +{ + Atom tags = getatomprop(c, clientatom[ClientTags], AnyPropertyType); + if (tags == None) + return 0; + + c->tags = tags & TAGMASK; + return 1; +} + +#if SAVEFLOATS_PATCH +void +savewindowfloatposition(Client *c, Monitor *m) +{ + char atom[22] = {0}; + if (c->sfx == -9999) + return; + + sprintf(atom, "_DWM_FLOATPOS_%u", m->num); + uint32_t pos[] = { (MAX(c->sfx - m->mx, 0) & 0xffff) | ((MAX(c->sfy - m->my, 0) & 0xffff) << 16) }; + XChangeProperty(dpy, c->win, XInternAtom(dpy, atom, False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)pos, 1); + + sprintf(atom, "_DWM_FLOATSIZE_%u", m->num); + uint32_t size[] = { (c->sfw & 0xffff) | ((c->sfh & 0xffff) << 16) }; + XChangeProperty(dpy, c->win, XInternAtom(dpy, atom, False), XA_CARDINAL, 32, PropModeReplace, (unsigned char *)size, 1); + + XSync(dpy, False); +} + +int +restorewindowfloatposition(Client *c, Monitor *m) +{ + char atom[22] = {0}; + Atom key, value; + int x, y, w, h; + + if (m == NULL) + return 0; + + sprintf(atom, "_DWM_FLOATPOS_%u", m->num); + + key = XInternAtom(dpy, atom, False); + if (!key) + return 0; + + value = getatomprop(c, key, AnyPropertyType); + if (!value) + return 0; + + x = value & 0xffff; + y = value >> 16; + + sprintf(atom, "_DWM_FLOATSIZE_%u", m->num); + + key = XInternAtom(dpy, atom, False); + if (!key) + return 0; + + value = getatomprop(c, key, AnyPropertyType); + if (!value) + return 0; + + w = value & 0xffff; + h = value >> 16; + + if (w <= 0 || h <= 0) { + fprintf(stderr, "restorewindowfloatposition: bad float values x = %d, y = %d, w = %d, h = %d for client = %s\n", x, y, w, h, c->name); + return 0; + } + + c->sfx = m->mx + x; + c->sfy = m->my + y; + c->sfw = w; + c->sfh = h; + + return 1; +} +#endif // SAVEFLOATS_PATCH diff --git a/patch/seamless_restart.h b/patch/seamless_restart.h new file mode 100644 index 0000000..ef29fa7 --- /dev/null +++ b/patch/seamless_restart.h @@ -0,0 +1,19 @@ +#include + +static void persistmonitorstate(Monitor *m); +static int restoremonitorstate(Monitor *m); +static void persistclientstate(Client *c); +static int restoreclientstate(Client *c); +static void setmonitorfields(Monitor *m); +static int getmonitorfields(Monitor *m); +static void setmonitortags(Monitor *m); +static int getmonitortags(Monitor *m); +static void setclientfields(Client *c); +static int getclientfields(Client *c); +static void setclienttags(Client *c); +static int getclienttags(Client *c); +static int getlayoutindex(const Layout *layout); +#if SAVEFLOATS_PATCH +static void savewindowfloatposition(Client *c, Monitor *m); +static int restorewindowfloatposition(Client *c, Monitor *m); +#endif // SAVEFLOATS_PATCH diff --git a/patch/sizehints_ruled.c b/patch/sizehints_ruled.c index ce2eeb9..6e97f9e 100644 --- a/patch/sizehints_ruled.c +++ b/patch/sizehints_ruled.c @@ -13,7 +13,7 @@ checkfloatingrules(Client *c) XGetClassHint(dpy, c->win, &ch); class = ch.res_class ? ch.res_class : broken; instance = ch.res_name ? ch.res_name : broken; - wintype = getatomprop(c, netatom[NetWMWindowType]); + wintype = getatomprop(c, netatom[NetWMWindowType], XA_ATOM); #if WINDOWROLERULE_PATCH gettextprop(c->win, wmatom[WMWindowRole], role, sizeof(role)); #endif // WINDOWROLERULE_PATCH diff --git a/patches.def.h b/patches.def.h index 697f6e6..759e97b 100644 --- a/patches.def.h +++ b/patches.def.h @@ -922,6 +922,18 @@ */ #define SCRATCHPAD_ALT_1_PATCH 0 +/* This patch persists some settings across window manager restarts. These include but are not + * limited to: + * - client's assigned tag(s) on which monitor + * - the order of clients + * - nmaster + * - selected layout + * - plus various additions depending on what other patches are used + * + * The above is not persisted across reboots, however. + */ +#define SEAMLESS_RESTART_PATCH 0 + /* As opposed to the original patch this only adds a rule option allowing fake fullscreen * to be enabled for applications when they start. This is intended to be used in combination * with the fakefullscreenclient patch and offers no practical functionality without it.