From c8015672f49cd563fd840790ec1ef83b555c762f Mon Sep 17 00:00:00 2001 From: A-Larsen Date: Tue, 6 Oct 2026 10:21:54 -0400 Subject: [PATCH 1/1] first commit --- LICENSE | 38 + Makefile | 45 + README | 48 + ai-dumps/window-below.md | 121 + calc-diff | 377 ++ calc-diff2 | 404 ++ calc-diff3 | 862 ++++ calc-diff4 | 115 + center_first_window.patch | 127 + centered_master.patch | 186 + config.def.h | 262 ++ config.h | 262 ++ config.mk | 39 + drw.c | 471 +++ drw.h | 60 + drw.o | Bin 0 -> 11904 bytes dwm | Bin 0 -> 81328 bytes dwm.1 | 176 + dwm.c | 4116 +++++++++++++++++++ dwm.o | Bin 0 -> 75728 bytes dwm.png | Bin 0 -> 373 bytes dwm.tar.gz | Bin 0 -> 17691 bytes gaps.patch | 169 + movestack.c | 48 + notes.adoc | 7 + patches/dwm-mmnametags-prepend-6.6.diff | 156 + patches/dwm-movestack-20211115-a786211.diff | 95 + patches/dwm-pertag-20200914-61bb8b2.diff | 177 + test.sh | 5 + todo.adoc | 0 transient.c | 42 + util.c | 37 + util.h | 9 + util.o | Bin 0 -> 2440 bytes 34 files changed, 8454 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 ai-dumps/window-below.md create mode 100644 calc-diff create mode 100644 calc-diff2 create mode 100644 calc-diff3 create mode 100644 calc-diff4 create mode 100644 center_first_window.patch create mode 100644 centered_master.patch create mode 100644 config.def.h create mode 100644 config.h create mode 100644 config.mk create mode 100644 drw.c create mode 100644 drw.h create mode 100644 drw.o create mode 100755 dwm create mode 100644 dwm.1 create mode 100644 dwm.c create mode 100644 dwm.o create mode 100644 dwm.png create mode 100644 dwm.tar.gz create mode 100644 gaps.patch create mode 100644 movestack.c create mode 100644 notes.adoc create mode 100644 patches/dwm-mmnametags-prepend-6.6.diff create mode 100644 patches/dwm-movestack-20211115-a786211.diff create mode 100644 patches/dwm-pertag-20200914-61bb8b2.diff create mode 100755 test.sh create mode 100644 todo.adoc create mode 100644 transient.c create mode 100644 util.c create mode 100644 util.h create mode 100644 util.o diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..995172f --- /dev/null +++ b/LICENSE @@ -0,0 +1,38 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2009 Jukka Salmi +© 2006-2007 Sander van Dijk +© 2007-2011 Peter Hartlich +© 2007-2009 Szabolcs Nagy +© 2007-2009 Christof Musik +© 2007-2009 Premysl Hruby +© 2007-2008 Enno Gottox Boland +© 2008 Martin Hurton +© 2008 Neale Pickett +© 2009 Mate Nagy +© 2010-2016 Hiltjo Posthuma +© 2010-2012 Connor Lane Smith +© 2011 Christoph Lohmann <20h@r-36.net> +© 2015-2016 Quentin Rameau +© 2015-2016 Eric Pruitt +© 2016-2017 Markus Teich +© 2020-2022 Chris Down + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ffa69b4 --- /dev/null +++ b/Makefile @@ -0,0 +1,45 @@ +# dwm - dynamic window manager +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dwm.c util.c +OBJ = ${SRC:.c=.o} + +all: dwm + +.c.o: + ${CC} -c ${CFLAGS} $< + +${OBJ}: config.h config.mk + +config.h: + cp config.def.h $@ + +dwm: ${OBJ} + ${CC} -o $@ ${OBJ} ${LDFLAGS} + +clean: + rm -f dwm ${OBJ} dwm-${VERSION}.tar.gz + +dist: clean + mkdir -p dwm-${VERSION} + cp -R LICENSE Makefile README config.def.h config.mk\ + dwm.1 drw.h util.h ${SRC} dwm.png transient.c dwm-${VERSION} + tar -cf dwm-${VERSION}.tar dwm-${VERSION} + gzip dwm-${VERSION}.tar + rm -rf dwm-${VERSION} + +install: all + mkdir -p ${DESTDIR}${PREFIX}/bin + cp -f dwm ${DESTDIR}${PREFIX}/bin + chmod 755 ${DESTDIR}${PREFIX}/bin/dwm + mkdir -p ${DESTDIR}${MANPREFIX}/man1 + sed "s/VERSION/${VERSION}/g" < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1 + chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/dwm\ + ${DESTDIR}${MANPREFIX}/man1/dwm.1 + +.PHONY: all clean dist install uninstall diff --git a/README b/README new file mode 100644 index 0000000..95d4fd0 --- /dev/null +++ b/README @@ -0,0 +1,48 @@ +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/ai-dumps/window-below.md b/ai-dumps/window-below.md new file mode 100644 index 0000000..ed04866 --- /dev/null +++ b/ai-dumps/window-below.md @@ -0,0 +1,121 @@ +To make `dwm` listen for `_NET_WM_STATE_BELOW`, we need to update the `setclientstate` function or the `propertynotify` handler. Since you want to be able to toggle this via a script using `xprop`, modifying `propertynotify` is the most effective way. + +Here is how to implement support for the "Below" state. + +### 1. Define the Atom + +First, we need to make sure `dwm` knows what that atom is. In `dwm.c`, look for the `enum` that defines `netatom`. + +```c +/* near the top of dwm.c */ +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetWMStateBelow, /* Add NetWMStateBelow here */ + NetLast }; + +``` + +Then, initialize it in the `setup` function: + +```c +/* inside setup(void) */ +netatom[NetWMStateBelow] = XInternAtom(dpy, "_NET_WM_STATE_BELOW", False); + +``` + +### 2. Update `propertynotify` + +Now, we tell `dwm` to react when that property changes. Find `propertynotify` and add a check for `NetWMState`. + +```c +/* inside propertynotify(XEvent *e) */ +if (ev->atom == netatom[NetWMState]) { + updateclientstate(c); +} + +``` + +*Note: If you don't have an `updateclientstate` function, you can handle it directly in the switch case for `XA_WM_HINTS` or as a standalone check.* + +### 3. Create a logic to check the state + +Add a small helper to check if the window has the "Below" property set. In `dwm.c`: + +```c +void +updateclientstate(Client *c) +{ + Atom actual_type; + int actual_format; + unsigned long nitems, bytes_after; + unsigned char *prop = NULL; + + if (XGetWindowProperty(dpy, c->win, netatom[NetWMState], 0L, sizeof(Atom), False, XA_ATOM, + &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success && prop) { + Atom s = *(Atom *)prop; + if (s == netatom[NetWMStateBelow]) { + c->isbelow = 1; // You need to add 'int isbelow' to the Client struct in dwm.h + } else { + c->isbelow = 0; + } + XFree(prop); + } + arrange(c->mon); +} + +``` + +### 4. Update the `restack` function + +This is where the magic happens. We tell `dwm` to physically lower the window if `isbelow` is true. + +```c +/* inside restack(Monitor *m) */ +if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + + /* PUSH BELOW: Any window marked isbelow goes to the very bottom */ + for (c = m->stack; c; c = c->snext) { + if (c->isbelow && ISVISIBLE(c)) { + XLowerWindow(dpy, c->win); + } + } + // ... rest of normal tiling restack ... +} + +``` + +--- + +### The Simpler "Suckless" Alternative + +If you don't want to add new atoms and complex property checking, you can repurpose the `_DWM_RELOAD` logic we already built. Since we know only the calculator/facecam uses that signal, we can just hardcode the "Lower" command into that handler. + +**Modify your `_DWM_RELOAD` block in `propertynotify`:** + +```c +if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { + c->hintsvalid = 0; + updatesizehints(c); + + if (c->minw > 0) { + c->w = c->minw; + c->h = c->minh; + c->isfloating = 0; + + /* THE TRICK: Lower the window right here */ + XLowerWindow(dpy, c->win); + } + + arrange(c->mon); + return; +} + +``` + +### Why this is better for your script: + +By putting `XLowerWindow` inside the `_DWM_RELOAD` handler, your existing script will work perfectly without needing the extra `_NET_WM_STATE_BELOW` line. When you send the reload signal, `dwm` will update the size **and** push it to the back in one atomic operation. + +**Would you like me to help you add `int isbelow;` to your `Client` struct in `dwm.h` so the first method works correctly?** diff --git a/calc-diff b/calc-diff new file mode 100644 index 0000000..e369cd7 --- /dev/null +++ b/calc-diff @@ -0,0 +1,377 @@ +diff --git a/config.def.h b/config.def.h +index ad06ccb..8812718 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -37,6 +37,7 @@ static const Rule rules[] = { + /* class instance title tags mask isfloating isCentered monitor resize hint*/ + // Keep ffplay floating a and in all tags for facecam script + { NULL, NULL, "facecam", (1<<9)-1, 0, 0, -1, 1, }, ++ { NULL, NULL, "calculator", (1<<9)-1, 0, 0, -1, 1, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1, 0, }, +diff --git a/config.h b/config.h +index ad06ccb..8812718 100644 +--- a/config.h ++++ b/config.h +@@ -37,6 +37,7 @@ static const Rule rules[] = { + /* class instance title tags mask isfloating isCentered monitor resize hint*/ + // Keep ffplay floating a and in all tags for facecam script + { NULL, NULL, "facecam", (1<<9)-1, 0, 0, -1, 1, }, ++ { NULL, NULL, "calculator", (1<<9)-1, 0, 0, -1, 1, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1, 0, }, +diff --git a/dwm b/dwm +index d34b334..d9c8c4d 100755 +Binary files a/dwm and b/dwm differ +diff --git a/dwm.c b/dwm.c +index d484c75..1ccb28f 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1223,11 +1223,19 @@ manage(Window w, XWindowAttributes *wa) + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); ++ ++ // just added this ++ c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); +- if (!c->isfloating) +- c->isfloating = c->oldstate = trans != None || c->isfixed; ++ // if (!c->isfloating) ++ // c->isfloating = c->oldstate = trans != None || c->isfixed; ++ ++ /* If it's the calculator or facecam, we WANT it tiled even if isfixed is true */ ++ if (!c->isfloating) c->isfloating = c->oldstate = trans != None || (c->isfixed && !c->resizehints); ++ + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); +@@ -1418,6 +1426,53 @@ pop(Client *c) + arrange(c->mon); + } + ++// void ++// propertynotify(XEvent *e) ++// { ++// Client *c; ++// Window trans; ++// XPropertyEvent *ev = &e->xproperty; ++ ++// if ((ev->window == root) && (ev->atom == XA_WM_NAME)) ++// updatestatus(); ++// else if (ev->state == PropertyDelete) ++// return; /* ignore */ ++// else if ((c = wintoclient(ev->window))) { ++// switch(ev->atom) { ++// default: break; ++// case XA_WM_TRANSIENT_FOR: ++// if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && ++// (c->isfloating = (wintoclient(trans)) != NULL)) ++// arrange(c->mon); ++// break; ++// case XA_WM_NORMAL_HINTS: ++// c->hintsvalid = 0; ++// updatesizehints(c); ++// /* Double check if it just became fixed */ ++// int wasfixed = c->resizehints; ++// c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++// if (c->resizehints) { ++// /* If it just became fixed, we might want to ensure it isn't ++// getting crushed by the tile resize logic anymore */ ++// arrange(c->mon); ++// } ++// break; ++// case XA_WM_HINTS: ++// updatewmhints(c); ++// drawbars(); ++// break; ++// } ++// 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); ++// } ++// } ++ + void + propertynotify(XEvent *e) + { +@@ -1430,6 +1485,31 @@ propertynotify(XEvent *e) + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { ++ ++ /* --- ADD THIS BLOCK HERE --- */ ++ /* This listens for the xprop command from your script */ ++ // if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ // c->hintsvalid = 0; // Invalidate old hints ++ // updatesizehints(c); // Force read of new 602px width ++ // arrange(c->mon); // Force re-tile immediately ++ // return; ++ // } ++ if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ c->hintsvalid = 0; ++ updatesizehints(c); ++ ++ /* Force the internal width to match the hint immediately ++ so the first tile pass doesn't use the old 760px value */ ++ if (c->minw > 0) { ++ c->w = c->minw; ++ c->h = c->minh; ++ } ++ ++ arrange(c->mon); ++ return; ++ } ++ /* --------------------------- */ ++ + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: +@@ -1439,16 +1519,14 @@ propertynotify(XEvent *e) + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; +- updatesizehints(c); +- /* Double check if it just became fixed */ +- int wasfixed = c->resizehints; +- c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); +- +- if (c->resizehints) { +- /* If it just became fixed, we might want to ensure it isn't +- getting crushed by the tile resize logic anymore */ +- arrange(c->mon); +- } ++ updatesizehints(c); ++ ++ /* Your logic to auto-detect fixed windows */ ++ int wasfixed = c->resizehints; ++ c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++ if (c->resizehints || wasfixed) ++ arrange(c->mon); + break; + case XA_WM_HINTS: + updatewmhints(c); +@@ -2584,17 +2662,132 @@ tagmon(const Arg *arg) + // } + // } + ++// void ++// tile(Monitor *m) ++// { ++// Client *c; ++// int g = gappx; ++// unsigned int n = 0, nh = 0, fixed_w = 0; ++ ++// /* 1. Find the facecam width and count windows */ ++// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// if (c->resizehints) { ++// nh++; ++// int w = (c->minw > 0) ? c->minw : c->w; ++// if (w + (2 * c->bw) > fixed_w) ++// fixed_w = w + (2 * c->bw); ++// } ++// } ++ ++// if (n == 0) return; ++ ++// /* 2. Setup the "Virtual Zone" (The area left of the facecam) */ ++// int reserved_space = (nh > 0) ? (fixed_w + g) : 0; ++// int vx = m->wx + g; ++// int vy = m->wy + g; ++// int vw = m->ww - 2 * g - reserved_space; ++// int vh = m->wh - 2 * g; ++ ++// unsigned int nt = n - nh; // Number of "Normal" windows ++// unsigned int nm = MIN(nt, m->nmaster); ++// unsigned int ns = nt - nm; ++ ++// /* 3. Calculate Virtual Master Width */ ++// unsigned int mw; ++// if (ns > 0) ++// mw = nm ? (vw - g) * m->mfact : 0; ++// else ++// mw = vw; ++ ++// /* 4. Independent Y-trackers for each column */ ++// unsigned int im = 0, is = 0, ifx = 0; ++// unsigned int h, r, my = 0, ty = 0, fy = 0; ++ ++// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++// if (!c->resizehints) { ++// /* TILING LOGIC (Left Side) */ ++// if (im < nm) { ++// /* Master Column */ ++// r = nm - im; ++// h = (vh - my - g * (r - 1)) / r; ++// resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); ++// my += h + g; ++// im++; ++// } else { ++// /* Stack Column */ ++// r = ns - is; ++// h = (vh - ty - g * (r - 1)) / r; ++// resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); ++// ty += h + g; ++// is++; ++// } ++// } else { ++// /* FACECAM LOGIC (Right Side Reserved Zone) */ ++// r = nh - ifx; ++// h = (vh - fy - g * (r - 1)) / r; ++// int fx = m->wx + m->ww - g - fixed_w; ++ ++// resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++// fy += h + g; ++// ifx++; ++// } ++// } ++// } ++ + void + tile(Monitor *m) + { + Client *c; + int g = gappx; + unsigned int n = 0, nh = 0, fixed_w = 0; ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ if (c->resizehints) ++ printf("DEBUG: Fixed Window Found! Width: %d, minw: %d\n", c->w, c->minw); ++ } ++ for (c = m->clients; c; c = c->next) { ++ if (strstr(c->name, "calculator")) { ++ printf("CALC DEBUG: tiled=%d, floating=%d, minw=%d\n", ++ ISVISIBLE(c) && !c->isfloating, c->isfloating, c->minw); ++ } ++} + +- /* 1. Find the facecam width and count windows */ ++ /* 1. MEASUREMENT PHASE */ ++ // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ // if (c->resizehints) { ++ // nh++; ++ ++ // /* --- THE FIX IS HERE --- */ ++ // /* Force dwm to read the latest size hints from X right now. */ ++ // updatesizehints(c); ++ // /* ----------------------- */ ++ ++ // /* Now c->minw is guaranteed to be correct (602) */ ++ // int w = (c->minw > 0) ? c->minw : c->w; ++ // if (w + (2 * c->bw) > fixed_w) ++ // fixed_w = w + (2 * c->bw); ++ // } ++ // } ++ // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ // if (c->resizehints) { ++ // nh++; ++ // updatesizehints(c); ++ ++ // /* Prioritize minw! If the script set a fixed size, use that. ++ // Otherwise, fall back to the current window width. */ ++ // int w = (c->minw > 0) ? c->minw : c->w; ++ ++ // if (w + (2 * c->bw) > fixed_w) ++ // fixed_w = w + (2 * c->bw); ++ // } ++ // } ++ fixed_w = 0; ++ nh = 0; + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (c->resizehints) { + nh++; ++ updatesizehints(c); ++ ++ /* Use the maximum of minw or current w, including borders */ + int w = (c->minw > 0) ? c->minw : c->w; + if (w + (2 * c->bw) > fixed_w) + fixed_w = w + (2 * c->bw); +@@ -2603,40 +2796,38 @@ tile(Monitor *m) + + if (n == 0) return; + +- /* 2. Setup the "Virtual Zone" (The area left of the facecam) */ ++ /* 2. SETUP VIRTUAL ZONE */ + int reserved_space = (nh > 0) ? (fixed_w + g) : 0; + int vx = m->wx + g; + int vy = m->wy + g; + int vw = m->ww - 2 * g - reserved_space; + int vh = m->wh - 2 * g; + +- unsigned int nt = n - nh; // Number of "Normal" windows ++ unsigned int nt = n - nh; + unsigned int nm = MIN(nt, m->nmaster); + unsigned int ns = nt - nm; + +- /* 3. Calculate Virtual Master Width */ ++ /* 3. CALCULATE VIRTUAL MASTER WIDTH */ + unsigned int mw; + if (ns > 0) + mw = nm ? (vw - g) * m->mfact : 0; + else + mw = vw; + +- /* 4. Independent Y-trackers for each column */ ++ /* 4. PLACEMENT PHASE */ + unsigned int im = 0, is = 0, ifx = 0; + unsigned int h, r, my = 0, ty = 0, fy = 0; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!c->resizehints) { +- /* TILING LOGIC (Left Side) */ ++ /* NORMAL TILING (Left Side) */ + if (im < nm) { +- /* Master Column */ + r = nm - im; + h = (vh - my - g * (r - 1)) / r; + resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); + my += h + g; + im++; + } else { +- /* Stack Column */ + r = ns - is; + h = (vh - ty - g * (r - 1)) / r; + resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); +@@ -2644,12 +2835,27 @@ tile(Monitor *m) + is++; + } + } else { +- /* FACECAM LOGIC (Right Side Reserved Zone) */ ++ /* RESERVED ZONE (Right Side) */ ++ // r = nh - ifx; ++ // h = (vh - fy - g * (r - 1)) / r; ++ ++ // int fx = m->wx + m->ww - g - fixed_w; ++ ++ // resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++ // fy += h + g; ++ // ifx++; + r = nh - ifx; + h = (vh - fy - g * (r - 1)) / r; ++ ++ /* Use the fixed_w we measured in Phase 1 for the X position */ + int fx = m->wx + m->ww - g - fixed_w; +- ++ ++ /* * THE CRITICAL CHANGE: ++ * Instead of passing 'c->w' (which might be 760), ++ * we pass 'fixed_w' (which we know is 602). ++ */ + resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++ + fy += h + g; + ifx++; + } +diff --git a/dwm.o b/dwm.o +index 7ca47bb..6374156 100644 +Binary files a/dwm.o and b/dwm.o differ diff --git a/calc-diff2 b/calc-diff2 new file mode 100644 index 0000000..d34c4d4 --- /dev/null +++ b/calc-diff2 @@ -0,0 +1,404 @@ +diff --git a/config.def.h b/config.def.h +index ad06ccb..8812718 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -37,6 +37,7 @@ static const Rule rules[] = { + /* class instance title tags mask isfloating isCentered monitor resize hint*/ + // Keep ffplay floating a and in all tags for facecam script + { NULL, NULL, "facecam", (1<<9)-1, 0, 0, -1, 1, }, ++ { NULL, NULL, "calculator", (1<<9)-1, 0, 0, -1, 1, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1, 0, }, +diff --git a/config.h b/config.h +index ad06ccb..8812718 100644 +--- a/config.h ++++ b/config.h +@@ -37,6 +37,7 @@ static const Rule rules[] = { + /* class instance title tags mask isfloating isCentered monitor resize hint*/ + // Keep ffplay floating a and in all tags for facecam script + { NULL, NULL, "facecam", (1<<9)-1, 0, 0, -1, 1, }, ++ { NULL, NULL, "calculator", (1<<9)-1, 0, 0, -1, 1, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1, 0, }, +diff --git a/dwm b/dwm +index d34b334..14e4ac9 100755 +Binary files a/dwm and b/dwm differ +diff --git a/dwm.c b/dwm.c +index d484c75..ba0621a 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1223,11 +1223,19 @@ manage(Window w, XWindowAttributes *wa) + configure(c); /* propagates border_width, if size doesn't change */ + updatewindowtype(c); + updatesizehints(c); ++ ++ // just added this ++ c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); +- if (!c->isfloating) +- c->isfloating = c->oldstate = trans != None || c->isfixed; ++ // if (!c->isfloating) ++ // c->isfloating = c->oldstate = trans != None || c->isfixed; ++ ++ /* If it's the calculator or facecam, we WANT it tiled even if isfixed is true */ ++ if (!c->isfloating) c->isfloating = c->oldstate = trans != None || (c->isfixed && !c->resizehints); ++ + if (c->isfloating) + XRaiseWindow(dpy, c->win); + attach(c); +@@ -1418,6 +1426,53 @@ pop(Client *c) + arrange(c->mon); + } + ++// void ++// propertynotify(XEvent *e) ++// { ++// Client *c; ++// Window trans; ++// XPropertyEvent *ev = &e->xproperty; ++ ++// if ((ev->window == root) && (ev->atom == XA_WM_NAME)) ++// updatestatus(); ++// else if (ev->state == PropertyDelete) ++// return; /* ignore */ ++// else if ((c = wintoclient(ev->window))) { ++// switch(ev->atom) { ++// default: break; ++// case XA_WM_TRANSIENT_FOR: ++// if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && ++// (c->isfloating = (wintoclient(trans)) != NULL)) ++// arrange(c->mon); ++// break; ++// case XA_WM_NORMAL_HINTS: ++// c->hintsvalid = 0; ++// updatesizehints(c); ++// /* Double check if it just became fixed */ ++// int wasfixed = c->resizehints; ++// c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++// if (c->resizehints) { ++// /* If it just became fixed, we might want to ensure it isn't ++// getting crushed by the tile resize logic anymore */ ++// arrange(c->mon); ++// } ++// break; ++// case XA_WM_HINTS: ++// updatewmhints(c); ++// drawbars(); ++// break; ++// } ++// 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); ++// } ++// } ++ + void + propertynotify(XEvent *e) + { +@@ -1430,6 +1485,46 @@ propertynotify(XEvent *e) + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { ++ ++ /* --- ADD THIS BLOCK HERE --- */ ++ /* This listens for the xprop command from your script */ ++ // if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ // c->hintsvalid = 0; // Invalidate old hints ++ // updatesizehints(c); // Force read of new 602px width ++ // arrange(c->mon); // Force re-tile immediately ++ // return; ++ // } ++ ++ // if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ // c->hintsvalid = 0; ++ // updatesizehints(c); ++ ++ // /* Force the internal width to match the hint immediately ++ // so the first tile pass doesn't use the old 760px value */ ++ // if (c->minw > 0) { ++ // c->w = c->minw; ++ // c->h = c->minh; ++ // } ++ ++ // arrange(c->mon); ++ // return; ++ // } ++ if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ c->hintsvalid = 0; ++ updatesizehints(c); ++ ++ if (c->minw > 0) { ++ c->w = c->minw; ++ c->h = c->minh; ++ /* FORCE tiling mode */ ++ c->isfloating = 0; ++ } ++ ++ arrange(c->mon); ++ return; ++ } ++ /* --------------------------- */ ++ + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: +@@ -1439,16 +1534,14 @@ propertynotify(XEvent *e) + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; +- updatesizehints(c); +- /* Double check if it just became fixed */ +- int wasfixed = c->resizehints; +- c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); +- +- if (c->resizehints) { +- /* If it just became fixed, we might want to ensure it isn't +- getting crushed by the tile resize logic anymore */ +- arrange(c->mon); +- } ++ updatesizehints(c); ++ ++ /* Your logic to auto-detect fixed windows */ ++ int wasfixed = c->resizehints; ++ c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++ if (c->resizehints || wasfixed) ++ arrange(c->mon); + break; + case XA_WM_HINTS: + updatewmhints(c); +@@ -2584,17 +2677,144 @@ tagmon(const Arg *arg) + // } + // } + ++// void ++// tile(Monitor *m) ++// { ++// Client *c; ++// int g = gappx; ++// unsigned int n = 0, nh = 0, fixed_w = 0; ++ ++// /* 1. Find the facecam width and count windows */ ++// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// if (c->resizehints) { ++// nh++; ++// int w = (c->minw > 0) ? c->minw : c->w; ++// if (w + (2 * c->bw) > fixed_w) ++// fixed_w = w + (2 * c->bw); ++// } ++// } ++ ++// if (n == 0) return; ++ ++// /* 2. Setup the "Virtual Zone" (The area left of the facecam) */ ++// int reserved_space = (nh > 0) ? (fixed_w + g) : 0; ++// int vx = m->wx + g; ++// int vy = m->wy + g; ++// int vw = m->ww - 2 * g - reserved_space; ++// int vh = m->wh - 2 * g; ++ ++// unsigned int nt = n - nh; // Number of "Normal" windows ++// unsigned int nm = MIN(nt, m->nmaster); ++// unsigned int ns = nt - nm; ++ ++// /* 3. Calculate Virtual Master Width */ ++// unsigned int mw; ++// if (ns > 0) ++// mw = nm ? (vw - g) * m->mfact : 0; ++// else ++// mw = vw; ++ ++// /* 4. Independent Y-trackers for each column */ ++// unsigned int im = 0, is = 0, ifx = 0; ++// unsigned int h, r, my = 0, ty = 0, fy = 0; ++ ++// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++// if (!c->resizehints) { ++// /* TILING LOGIC (Left Side) */ ++// if (im < nm) { ++// /* Master Column */ ++// r = nm - im; ++// h = (vh - my - g * (r - 1)) / r; ++// resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); ++// my += h + g; ++// im++; ++// } else { ++// /* Stack Column */ ++// r = ns - is; ++// h = (vh - ty - g * (r - 1)) / r; ++// resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); ++// ty += h + g; ++// is++; ++// } ++// } else { ++// /* FACECAM LOGIC (Right Side Reserved Zone) */ ++// r = nh - ifx; ++// h = (vh - fy - g * (r - 1)) / r; ++// int fx = m->wx + m->ww - g - fixed_w; ++ ++// resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++// fy += h + g; ++// ifx++; ++// } ++// } ++// } ++ + void + tile(Monitor *m) + { + Client *c; + int g = gappx; + unsigned int n = 0, nh = 0, fixed_w = 0; ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ if (c->resizehints) ++ printf("DEBUG: Fixed Window Found! Width: %d, minw: %d\n", c->w, c->minw); ++ } ++ for (c = m->clients; c; c = c->next) { ++ if (strstr(c->name, "calculator")) { ++ printf("CALC DEBUG: tiled=%d, floating=%d, minw=%d\n", ++ ISVISIBLE(c) && !c->isfloating, c->isfloating, c->minw); ++ } ++} + +- /* 1. Find the facecam width and count windows */ ++ /* 1. MEASUREMENT PHASE */ ++ // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ // if (c->resizehints) { ++ // nh++; ++ ++ // /* --- THE FIX IS HERE --- */ ++ // /* Force dwm to read the latest size hints from X right now. */ ++ // updatesizehints(c); ++ // /* ----------------------- */ ++ ++ // /* Now c->minw is guaranteed to be correct (602) */ ++ // int w = (c->minw > 0) ? c->minw : c->w; ++ // if (w + (2 * c->bw) > fixed_w) ++ // fixed_w = w + (2 * c->bw); ++ // } ++ // } ++ // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ // if (c->resizehints) { ++ // nh++; ++ // updatesizehints(c); ++ ++ // /* Prioritize minw! If the script set a fixed size, use that. ++ // Otherwise, fall back to the current window width. */ ++ // int w = (c->minw > 0) ? c->minw : c->w; ++ ++ // if (w + (2 * c->bw) > fixed_w) ++ // fixed_w = w + (2 * c->bw); ++ // } ++ // } ++ // fixed_w = 0; ++ // nh = 0; ++ // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ // if (c->resizehints) { ++ // nh++; ++ // updatesizehints(c); ++ ++ // /* Use the maximum of minw or current w, including borders */ ++ // int w = (c->minw > 0) ? c->minw : c->w; ++ // if (w + (2 * c->bw) > fixed_w) ++ // fixed_w = w + (2 * c->bw); ++ // } ++ // } ++ /* 1. MEASUREMENT PHASE */ ++ nh = 0; ++ fixed_w = 0; + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (c->resizehints) { + nh++; ++ updatesizehints(c); + int w = (c->minw > 0) ? c->minw : c->w; + if (w + (2 * c->bw) > fixed_w) + fixed_w = w + (2 * c->bw); +@@ -2603,40 +2823,38 @@ tile(Monitor *m) + + if (n == 0) return; + +- /* 2. Setup the "Virtual Zone" (The area left of the facecam) */ ++ /* 2. SETUP VIRTUAL ZONE */ + int reserved_space = (nh > 0) ? (fixed_w + g) : 0; + int vx = m->wx + g; + int vy = m->wy + g; + int vw = m->ww - 2 * g - reserved_space; + int vh = m->wh - 2 * g; + +- unsigned int nt = n - nh; // Number of "Normal" windows ++ unsigned int nt = n - nh; + unsigned int nm = MIN(nt, m->nmaster); + unsigned int ns = nt - nm; + +- /* 3. Calculate Virtual Master Width */ ++ /* 3. CALCULATE VIRTUAL MASTER WIDTH */ + unsigned int mw; + if (ns > 0) + mw = nm ? (vw - g) * m->mfact : 0; + else + mw = vw; + +- /* 4. Independent Y-trackers for each column */ ++ /* 4. PLACEMENT PHASE */ + unsigned int im = 0, is = 0, ifx = 0; + unsigned int h, r, my = 0, ty = 0, fy = 0; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!c->resizehints) { +- /* TILING LOGIC (Left Side) */ ++ /* NORMAL TILING (Left Side) */ + if (im < nm) { +- /* Master Column */ + r = nm - im; + h = (vh - my - g * (r - 1)) / r; + resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); + my += h + g; + im++; + } else { +- /* Stack Column */ + r = ns - is; + h = (vh - ty - g * (r - 1)) / r; + resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); +@@ -2644,12 +2862,27 @@ tile(Monitor *m) + is++; + } + } else { +- /* FACECAM LOGIC (Right Side Reserved Zone) */ ++ /* RESERVED ZONE (Right Side) */ ++ // r = nh - ifx; ++ // h = (vh - fy - g * (r - 1)) / r; ++ ++ // int fx = m->wx + m->ww - g - fixed_w; ++ ++ // resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++ // fy += h + g; ++ // ifx++; + r = nh - ifx; + h = (vh - fy - g * (r - 1)) / r; ++ ++ /* Use the fixed_w we measured in Phase 1 for the X position */ + int fx = m->wx + m->ww - g - fixed_w; +- ++ ++ /* * THE CRITICAL CHANGE: ++ * Instead of passing 'c->w' (which might be 760), ++ * we pass 'fixed_w' (which we know is 602). ++ */ + resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++ + fy += h + g; + ifx++; + } +diff --git a/dwm.o b/dwm.o +index 7ca47bb..7e8b5a3 100644 +Binary files a/dwm.o and b/dwm.o differ diff --git a/calc-diff3 b/calc-diff3 new file mode 100644 index 0000000..135c90c --- /dev/null +++ b/calc-diff3 @@ -0,0 +1,862 @@ +diff --git a/config.def.h b/config.def.h +index ad06ccb..8812718 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -37,6 +37,7 @@ static const Rule rules[] = { + /* class instance title tags mask isfloating isCentered monitor resize hint*/ + // Keep ffplay floating a and in all tags for facecam script + { NULL, NULL, "facecam", (1<<9)-1, 0, 0, -1, 1, }, ++ { NULL, NULL, "calculator", (1<<9)-1, 0, 0, -1, 1, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1, 0, }, +diff --git a/config.h b/config.h +index ad06ccb..8812718 100644 +--- a/config.h ++++ b/config.h +@@ -37,6 +37,7 @@ static const Rule rules[] = { + /* class instance title tags mask isfloating isCentered monitor resize hint*/ + // Keep ffplay floating a and in all tags for facecam script + { NULL, NULL, "facecam", (1<<9)-1, 0, 0, -1, 1, }, ++ { NULL, NULL, "calculator", (1<<9)-1, 0, 0, -1, 1, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1, 0, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1, 0, }, +diff --git a/dwm b/dwm +index d34b334..1a4f0e6 100755 +Binary files a/dwm and b/dwm differ +diff --git a/dwm.c b/dwm.c +index d484c75..28c46f9 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -416,21 +416,49 @@ applyrules(Client *c) + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + +- for (i = 0; i < LENGTH(rules); i++) { +- r = &rules[i]; +- if ((!r->title || strstr(c->name, r->title)) +- && (!r->class || strstr(class, r->class)) +- && (!r->instance || strstr(instance, r->instance))) +- { +- c->isfloating = r->isfloating; +- c->CenterThisWindow = r->CenterThisWindow; +- c->tags |= r->tags; ++ // for (i = 0; i < LENGTH(rules); i++) { ++ // r = &rules[i]; ++ // if ((!r->title || strstr(c->name, r->title)) ++ // && (!r->class || strstr(class, r->class)) ++ // && (!r->instance || strstr(instance, r->instance))) ++ // { ++ // c->isfloating = r->isfloating; ++ // c->CenterThisWindow = r->CenterThisWindow; ++ // c->tags |= r->tags; ++ // c->resizehints = r->resizehints; ++ // for (m = mons; m && m->num != r->monitor; m = m->next); ++ // if (m) ++ // c->mon = m; ++ // } ++ // } ++ ++ /* In dwm.c, inside applyrules(Client *c) */ ++ for (i = 0; i < LENGTH(rules); i++) { ++ r = &rules[i]; ++ if ((!r->title || strstr(c->name, r->title)) ++ && (!r->class || strstr(class, r->class)) ++ && (!r->instance || strstr(instance, r->instance))) ++ { ++ c->isfloating = r->isfloating; ++ c->tags |= r->tags; + c->resizehints = r->resizehints; +- for (m = mons; m && m->num != r->monitor; m = m->next); +- if (m) +- c->mon = m; +- } +- } ++ ++ /* --- THE PERMANENT FIX --- */ ++ /* If this is the calculator, force the dimensions NOW. ++ This happens before manage() or tile() ever run. */ ++ if (r->title && strcmp(r->title, "calculator") == 0) { ++ c->minw = c->maxw = 602; ++ c->minh = c->maxh = 450; ++ c->resizehints = 1; // Double-ensure this is set ++ printf("MATCHED: Hardcoding calculator dimensions now.\n"); ++ } ++ /* ------------------------- */ ++ ++ for (m = mons; m && m->num != r->monitor; m = m->next); ++ if (m) ++ c->mon = m; ++ } ++ } + + if (ch.res_class) + XFree(ch.res_class); +@@ -1183,75 +1211,259 @@ killclient(const Arg *arg) + } + } + ++// void ++// manage(Window w, XWindowAttributes *wa) ++// { ++// Client *c, *t = NULL; ++// Window trans = None; ++// XWindowChanges wc; ++ ++// c = ecalloc(1, sizeof(Client)); ++// c->win = w; ++// /* geometry */ ++// c->x = c->oldx = wa->x; ++// c->y = c->oldy = wa->y; ++// c->w = c->oldw = wa->width; ++// c->h = c->oldh = wa->height; ++// c->oldbw = wa->border_width; ++// c->resizehints = resizehints; ++ ++// updatetitle(c); ++// if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { ++// c->mon = t->mon; ++// c->tags = t->tags; ++// } else { ++// c->mon = selmon; ++// applyrules(c); ++// } ++ ++// if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) ++// c->x = c->mon->wx + c->mon->ww - WIDTH(c); ++// if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) ++// c->y = c->mon->wy + c->mon->wh - HEIGHT(c); ++// c->x = MAX(c->x, c->mon->wx); ++// c->y = MAX(c->y, c->mon->wy); ++// c->bw = borderpx; ++ ++// wc.border_width = c->bw; ++// XConfigureWindow(dpy, w, CWBorderWidth, &wc); ++// XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); ++// configure(c); /* propagates border_width, if size doesn't change */ ++// updatewindowtype(c); ++// updatesizehints(c); ++ ++// // just added this ++// c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++// updatewmhints(c); ++// XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); ++// grabbuttons(c, 0); ++// // if (!c->isfloating) ++// // c->isfloating = c->oldstate = trans != None || c->isfixed; ++ ++// /* If it's the calculator or facecam, we WANT it tiled even if isfixed is true */ ++// if (!c->isfloating) c->isfloating = c->oldstate = trans != None || (c->isfixed && !c->resizehints); ++ ++// if (c->isfloating) ++// XRaiseWindow(dpy, c->win); ++// attach(c); ++// attachstack(c); ++// XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, ++// (unsigned char *) &(c->win), 1); ++// XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ ++// setclientstate(c, NormalState); ++// if (c->mon == selmon) ++// unfocus(selmon->sel, 0); ++// c->mon->sel = c; ++ ++// // arrange(c->mon); ++// if (!c->isfloating) ++// arrange(c->mon); ++// else { ++// // If it is floating, we still want to show it and restack it, ++// // but we don't want to trigger the tile() logic for other windows. ++// showhide(c->mon->stack); ++// restack(c->mon); ++// } ++ ++// XMapWindow(dpy, c->win); ++// focus(NULL); ++// } ++ ++// void ++// manage(Window w, XWindowAttributes *wa) ++// { ++// Client *c, *t = NULL; ++// Window trans = None; ++// XWindowChanges wc; ++ ++// c = ecalloc(1, sizeof(Client)); ++// c->win = w; ++// /* geometry */ ++// c->x = c->oldx = wa->x; ++// c->y = c->oldy = wa->y; ++// c->w = c->oldw = wa->width; ++// c->h = c->oldh = wa->height; ++// c->oldbw = wa->border_width; ++ ++// updatetitle(c); ++// if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { ++// c->mon = t->mon; ++// c->tags = t->tags; ++// } else { ++// c->mon = selmon; ++// applyrules(c); // c->resizehints and c->minw/maxw set here ++// if (c->minw == 0) updatesizehints(c); // Only update if not hardcoded ++// } ++ ++// // ... (geometric constraints code) ... ++// c->bw = borderpx; ++ ++// wc.border_width = c->bw; ++// XConfigureWindow(dpy, w, CWBorderWidth, &wc); ++// XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); ++// configure(c); ++// updatewindowtype(c); ++ ++// /* 1. Only update hints if they aren't already set by applyrules. ++// This prevents the "Race Condition" where X11 returns 0/0. */ ++// if (c->minw == 0 && c->minh == 0) { ++// updatesizehints(c); ++// } ++ ++// /* 2. Determine if it is a fixed-size window */ ++// c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++// /* 3. If it's the calculator (has resizehints), FORCE isfixed and NOT floating */ ++// if (c->resizehints) { ++// c->isfixed = 1; ++// c->isfloating = 0; ++// } else if (!c->isfloating) { ++// /* Standard dwm floating logic for other windows */ ++// c->isfloating = c->oldstate = trans != None || c->isfixed; ++// } ++ ++// updatewmhints(c); ++// XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); ++// grabbuttons(c, 0); ++ ++// if (c->isfloating) ++// XRaiseWindow(dpy, c->win); ++ ++// attach(c); ++// attachstack(c); ++// XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, ++// (unsigned char *) &(c->win), 1); ++ ++// /* Move out of view before mapping to prevent flicker */ ++// XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); ++// setclientstate(c, NormalState); ++ ++// if (c->mon == selmon) ++// unfocus(selmon->sel, 0); ++// c->mon->sel = c; ++ ++// /* 4. Final Arrange */ ++// if (!c->isfloating) ++// arrange(c->mon); ++// else { ++// showhide(c->mon->stack); ++// restack(c->mon); ++// } ++ ++// XMapWindow(dpy, c->win); ++// focus(NULL); ++// } ++ + void + manage(Window w, XWindowAttributes *wa) + { +- Client *c, *t = NULL; +- Window trans = None; +- XWindowChanges wc; ++ Client *c, *t = NULL; ++ Window trans = None; ++ XWindowChanges wc; + +- c = ecalloc(1, sizeof(Client)); +- c->win = w; +- /* geometry */ +- c->x = c->oldx = wa->x; +- c->y = c->oldy = wa->y; +- c->w = c->oldw = wa->width; +- c->h = c->oldh = wa->height; +- c->oldbw = wa->border_width; +- c->resizehints = resizehints; +- +- updatetitle(c); +- if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +- c->mon = t->mon; +- c->tags = t->tags; +- } else { +- c->mon = selmon; +- applyrules(c); +- } ++ c = ecalloc(1, sizeof(Client)); ++ c->win = w; ++ ++ /* 1. Initial Geometry */ ++ c->x = c->oldx = wa->x; ++ c->y = c->oldy = wa->y; ++ c->w = c->oldw = wa->width; ++ c->h = c->oldh = wa->height; ++ c->oldbw = wa->border_width; ++ ++ updatetitle(c); ++ ++ /* 2. Apply Rules & Hardcoded Dimensions */ ++ if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { ++ c->mon = t->mon; ++ c->tags = t->tags; ++ } else { ++ c->mon = selmon; ++ applyrules(c); // This sets c->minw/maxw to 602 if it matches "calculator" ++ } + +- if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) +- c->x = c->mon->wx + c->mon->ww - WIDTH(c); +- if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) +- c->y = c->mon->wy + c->mon->wh - HEIGHT(c); +- c->x = MAX(c->x, c->mon->wx); +- c->y = MAX(c->y, c->mon->wy); +- c->bw = borderpx; ++ /* 3. Geometric Constraints (Don't let window spawn off-screen) */ ++ if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) ++ c->x = c->mon->wx + c->mon->ww - WIDTH(c); ++ if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) ++ c->y = c->mon->wy + c->mon->wh - HEIGHT(c); ++ c->x = MAX(c->x, c->mon->wx); ++ c->y = MAX(c->y, c->mon->wy); ++ c->bw = borderpx; ++ ++ wc.border_width = c->bw; ++ XConfigureWindow(dpy, w, CWBorderWidth, &wc); ++ XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); ++ configure(c); ++ updatewindowtype(c); ++ ++ /* 4. Update Hints ONLY if not hardcoded by applyrules */ ++ if (c->minw == 0 && c->minh == 0) { ++ updatesizehints(c); ++ } + +- wc.border_width = c->bw; +- XConfigureWindow(dpy, w, CWBorderWidth, &wc); +- XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); +- configure(c); /* propagates border_width, if size doesn't change */ +- updatewindowtype(c); +- updatesizehints(c); +- updatewmhints(c); +- XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); +- grabbuttons(c, 0); +- if (!c->isfloating) +- c->isfloating = c->oldstate = trans != None || c->isfixed; +- if (c->isfloating) +- XRaiseWindow(dpy, c->win); +- attach(c); +- attachstack(c); +- XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, +- (unsigned char *) &(c->win), 1); +- XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); /* some windows require this */ +- setclientstate(c, NormalState); +- if (c->mon == selmon) +- unfocus(selmon->sel, 0); +- c->mon->sel = c; ++ /* 5. Set Fixed State & Floating State */ ++ c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++ if (c->resizehints) { ++ c->isfixed = 1; // It's the calculator/facecam ++ c->isfloating = 0; // Force it to tile ++ } else { ++ c->isfloating = c->oldstate = trans != None || c->isfixed; ++ } ++ ++ /* 6. Standard X11 setup */ ++ updatewmhints(c); ++ XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); ++ grabbuttons(c, 0); + +- // arrange(c->mon); ++ if (c->isfloating) ++ XRaiseWindow(dpy, c->win); ++ ++ attach(c); ++ attachstack(c); ++ XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, ++ (unsigned char *) &(c->win), 1); ++ ++ /* Move out of view before mapping to prevent flicker */ ++ XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); ++ setclientstate(c, NormalState); ++ ++ if (c->mon == selmon) ++ unfocus(selmon->sel, 0); ++ c->mon->sel = c; ++ ++ /* 7. Final Arrange & Map */ + if (!c->isfloating) + arrange(c->mon); + else { +- // If it is floating, we still want to show it and restack it, +- // but we don't want to trigger the tile() logic for other windows. + showhide(c->mon->stack); + restack(c->mon); + } + +- XMapWindow(dpy, c->win); +- focus(NULL); ++ XMapWindow(dpy, c->win); ++ focus(NULL); + } + + void +@@ -1418,6 +1630,53 @@ pop(Client *c) + arrange(c->mon); + } + ++// void ++// propertynotify(XEvent *e) ++// { ++// Client *c; ++// Window trans; ++// XPropertyEvent *ev = &e->xproperty; ++ ++// if ((ev->window == root) && (ev->atom == XA_WM_NAME)) ++// updatestatus(); ++// else if (ev->state == PropertyDelete) ++// return; /* ignore */ ++// else if ((c = wintoclient(ev->window))) { ++// switch(ev->atom) { ++// default: break; ++// case XA_WM_TRANSIENT_FOR: ++// if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && ++// (c->isfloating = (wintoclient(trans)) != NULL)) ++// arrange(c->mon); ++// break; ++// case XA_WM_NORMAL_HINTS: ++// c->hintsvalid = 0; ++// updatesizehints(c); ++// /* Double check if it just became fixed */ ++// int wasfixed = c->resizehints; ++// c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++// if (c->resizehints) { ++// /* If it just became fixed, we might want to ensure it isn't ++// getting crushed by the tile resize logic anymore */ ++// arrange(c->mon); ++// } ++// break; ++// case XA_WM_HINTS: ++// updatewmhints(c); ++// drawbars(); ++// break; ++// } ++// 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); ++// } ++// } ++ + void + propertynotify(XEvent *e) + { +@@ -1430,6 +1689,46 @@ propertynotify(XEvent *e) + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { ++ ++ /* --- ADD THIS BLOCK HERE --- */ ++ /* This listens for the xprop command from your script */ ++ // if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ // c->hintsvalid = 0; // Invalidate old hints ++ // updatesizehints(c); // Force read of new 602px width ++ // arrange(c->mon); // Force re-tile immediately ++ // return; ++ // } ++ ++ // if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ // c->hintsvalid = 0; ++ // updatesizehints(c); ++ ++ // /* Force the internal width to match the hint immediately ++ // so the first tile pass doesn't use the old 760px value */ ++ // if (c->minw > 0) { ++ // c->w = c->minw; ++ // c->h = c->minh; ++ // } ++ ++ // arrange(c->mon); ++ // return; ++ // } ++ if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { ++ c->hintsvalid = 0; ++ updatesizehints(c); ++ ++ if (c->minw > 0) { ++ c->w = c->minw; ++ c->h = c->minh; ++ /* FORCE tiling mode */ ++ c->isfloating = 0; ++ } ++ ++ arrange(c->mon); ++ return; ++ } ++ /* --------------------------- */ ++ + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: +@@ -1439,16 +1738,14 @@ propertynotify(XEvent *e) + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; +- updatesizehints(c); +- /* Double check if it just became fixed */ +- int wasfixed = c->resizehints; +- c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); +- +- if (c->resizehints) { +- /* If it just became fixed, we might want to ensure it isn't +- getting crushed by the tile resize logic anymore */ +- arrange(c->mon); +- } ++ updatesizehints(c); ++ ++ /* Your logic to auto-detect fixed windows */ ++ int wasfixed = c->resizehints; ++ c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); ++ ++ if (c->resizehints || wasfixed) ++ arrange(c->mon); + break; + case XA_WM_HINTS: + updatewmhints(c); +@@ -2584,17 +2881,255 @@ tagmon(const Arg *arg) + // } + // } + ++// void ++// tile(Monitor *m) ++// { ++// Client *c; ++// int g = gappx; ++// unsigned int n = 0, nh = 0, fixed_w = 0; ++ ++// /* 1. Find the facecam width and count windows */ ++// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// if (c->resizehints) { ++// nh++; ++// int w = (c->minw > 0) ? c->minw : c->w; ++// if (w + (2 * c->bw) > fixed_w) ++// fixed_w = w + (2 * c->bw); ++// } ++// } ++ ++// if (n == 0) return; ++ ++// /* 2. Setup the "Virtual Zone" (The area left of the facecam) */ ++// int reserved_space = (nh > 0) ? (fixed_w + g) : 0; ++// int vx = m->wx + g; ++// int vy = m->wy + g; ++// int vw = m->ww - 2 * g - reserved_space; ++// int vh = m->wh - 2 * g; ++ ++// unsigned int nt = n - nh; // Number of "Normal" windows ++// unsigned int nm = MIN(nt, m->nmaster); ++// unsigned int ns = nt - nm; ++ ++// /* 3. Calculate Virtual Master Width */ ++// unsigned int mw; ++// if (ns > 0) ++// mw = nm ? (vw - g) * m->mfact : 0; ++// else ++// mw = vw; ++ ++// /* 4. Independent Y-trackers for each column */ ++// unsigned int im = 0, is = 0, ifx = 0; ++// unsigned int h, r, my = 0, ty = 0, fy = 0; ++ ++// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++// if (!c->resizehints) { ++// /* TILING LOGIC (Left Side) */ ++// if (im < nm) { ++// /* Master Column */ ++// r = nm - im; ++// h = (vh - my - g * (r - 1)) / r; ++// resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); ++// my += h + g; ++// im++; ++// } else { ++// /* Stack Column */ ++// r = ns - is; ++// h = (vh - ty - g * (r - 1)) / r; ++// resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); ++// ty += h + g; ++// is++; ++// } ++// } else { ++// /* FACECAM LOGIC (Right Side Reserved Zone) */ ++// r = nh - ifx; ++// h = (vh - fy - g * (r - 1)) / r; ++// int fx = m->wx + m->ww - g - fixed_w; ++ ++// resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++// fy += h + g; ++// ifx++; ++// } ++// } ++// } ++ ++// void ++// tile(Monitor *m) ++// { ++// Client *c; ++// int g = gappx; ++// unsigned int n = 0, nh = 0, fixed_w = 0; ++// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++// if (c->resizehints) ++// printf("DEBUG: Fixed Window Found! Width: %d, minw: %d\n", c->w, c->minw); ++// } ++// for (c = m->clients; c; c = c->next) { ++// if (strstr(c->name, "calculator")) { ++// printf("CALC DEBUG: tiled=%d, floating=%d, minw=%d\n", ++// ISVISIBLE(c) && !c->isfloating, c->isfloating, c->minw); ++// } ++// } ++ ++// /* 1. MEASUREMENT PHASE */ ++// // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// // if (c->resizehints) { ++// // nh++; ++ ++// // /* --- THE FIX IS HERE --- */ ++// // /* Force dwm to read the latest size hints from X right now. */ ++// // updatesizehints(c); ++// // /* ----------------------- */ ++ ++// // /* Now c->minw is guaranteed to be correct (602) */ ++// // int w = (c->minw > 0) ? c->minw : c->w; ++// // if (w + (2 * c->bw) > fixed_w) ++// // fixed_w = w + (2 * c->bw); ++// // } ++// // } ++// // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// // if (c->resizehints) { ++// // nh++; ++// // updatesizehints(c); ++ ++// // /* Prioritize minw! If the script set a fixed size, use that. ++// // Otherwise, fall back to the current window width. */ ++// // int w = (c->minw > 0) ? c->minw : c->w; ++ ++// // if (w + (2 * c->bw) > fixed_w) ++// // fixed_w = w + (2 * c->bw); ++// // } ++// // } ++// // fixed_w = 0; ++// // nh = 0; ++// // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// // if (c->resizehints) { ++// // nh++; ++// // updatesizehints(c); ++ ++// // /* Use the maximum of minw or current w, including borders */ ++// // int w = (c->minw > 0) ? c->minw : c->w; ++// // if (w + (2 * c->bw) > fixed_w) ++// // fixed_w = w + (2 * c->bw); ++// // } ++// // } ++// /* 1. MEASUREMENT PHASE */ ++// // nh = 0; ++// // fixed_w = 0; ++// // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// // if (c->resizehints) { ++// // nh++; ++// // updatesizehints(c); ++// // int w = (c->minw > 0) ? c->minw : c->w; ++// // if (w + (2 * c->bw) > fixed_w) ++// // fixed_w = w + (2 * c->bw); ++// // } ++// // } ++ ++// /* 1. MEASUREMENT PHASE */ ++// // for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// // if (c->resizehints) { ++// // nh++; ++// // /* Sync with X Server immediately to get the hints set by the script */ ++// // XSync(dpy, False); ++// // updatesizehints(c); ++ ++// // int w = (c->minw > 0) ? c->minw : c->w; ++// // if (w + (2 * c->bw) > fixed_w) ++// // fixed_w = w + (2 * c->bw); ++// // } ++// // } ++ ++// /* 1. Find count and the widest fixed window */ ++// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++// if (c->resizehints) { ++// nh++; ++// /* Use the hardcoded minw from applyrules */ ++// int w = (c->minw > 0) ? c->minw : c->w; ++// if (w + (2 * c->bw) > fixed_w) ++// fixed_w = w + (2 * c->bw); ++// } ++// } ++ ++// if (n == 0) return; ++ ++// /* 2. SETUP VIRTUAL ZONE */ ++// int reserved_space = (nh > 0) ? (fixed_w + g) : 0; ++// int vx = m->wx + g; ++// int vy = m->wy + g; ++// int vw = m->ww - 2 * g - reserved_space; ++// int vh = m->wh - 2 * g; ++ ++// unsigned int nt = n - nh; ++// unsigned int nm = MIN(nt, m->nmaster); ++// unsigned int ns = nt - nm; ++ ++// /* 3. CALCULATE VIRTUAL MASTER WIDTH */ ++// unsigned int mw; ++// if (ns > 0) ++// mw = nm ? (vw - g) * m->mfact : 0; ++// else ++// mw = vw; ++ ++// /* 4. PLACEMENT PHASE */ ++// unsigned int im = 0, is = 0, ifx = 0; ++// unsigned int h, r, my = 0, ty = 0, fy = 0; ++ ++// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++// if (!c->resizehints) { ++// /* NORMAL TILING (Left Side) */ ++// if (im < nm) { ++// r = nm - im; ++// h = (vh - my - g * (r - 1)) / r; ++// resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); ++// my += h + g; ++// im++; ++// } else { ++// r = ns - is; ++// h = (vh - ty - g * (r - 1)) / r; ++// resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); ++// ty += h + g; ++// is++; ++// } ++// } else { ++// /* RESERVED ZONE (Right Side) */ ++// // r = nh - ifx; ++// // h = (vh - fy - g * (r - 1)) / r; ++ ++// // int fx = m->wx + m->ww - g - fixed_w; ++ ++// // resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++// // fy += h + g; ++// // ifx++; ++// r = nh - ifx; ++// h = (vh - fy - g * (r - 1)) / r; ++ ++// /* Use the fixed_w we measured in Phase 1 for the X position */ ++// int fx = m->wx + m->ww - g - fixed_w; ++ ++// /* * THE CRITICAL CHANGE: ++// * Instead of passing 'c->w' (which might be 760), ++// * we pass 'fixed_w' (which we know is 602). ++// */ ++// resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++ ++// fy += h + g; ++// ifx++; ++// } ++// } ++// } ++ + void + tile(Monitor *m) + { + Client *c; + int g = gappx; + unsigned int n = 0, nh = 0, fixed_w = 0; +- +- /* 1. Find the facecam width and count windows */ ++ ++ /* 1. MEASUREMENT PHASE */ + for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { + if (c->resizehints) { + nh++; ++ /* Force the measurement to use the hardcoded 602px, not the 'ghost' 640/760px */ + int w = (c->minw > 0) ? c->minw : c->w; + if (w + (2 * c->bw) > fixed_w) + fixed_w = w + (2 * c->bw); +@@ -2603,53 +3138,45 @@ tile(Monitor *m) + + if (n == 0) return; + +- /* 2. Setup the "Virtual Zone" (The area left of the facecam) */ ++ /* 2. VIRTUAL ZONE CALCULATIONS */ + int reserved_space = (nh > 0) ? (fixed_w + g) : 0; + int vx = m->wx + g; + int vy = m->wy + g; + int vw = m->ww - 2 * g - reserved_space; + int vh = m->wh - 2 * g; + +- unsigned int nt = n - nh; // Number of "Normal" windows ++ unsigned int nt = n - nh; + unsigned int nm = MIN(nt, m->nmaster); + unsigned int ns = nt - nm; ++ unsigned int mw = (ns > 0) ? (nm ? (vw - g) * m->mfact : 0) : vw; + +- /* 3. Calculate Virtual Master Width */ +- unsigned int mw; +- if (ns > 0) +- mw = nm ? (vw - g) * m->mfact : 0; +- else +- mw = vw; +- +- /* 4. Independent Y-trackers for each column */ ++ /* 3. PLACEMENT PHASE */ + unsigned int im = 0, is = 0, ifx = 0; + unsigned int h, r, my = 0, ty = 0, fy = 0; + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!c->resizehints) { +- /* TILING LOGIC (Left Side) */ ++ /* ... (Standard Master/Stack logic) ... */ + if (im < nm) { +- /* Master Column */ + r = nm - im; + h = (vh - my - g * (r - 1)) / r; + resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); +- my += h + g; +- im++; ++ my += h + g; im++; + } else { +- /* Stack Column */ + r = ns - is; + h = (vh - ty - g * (r - 1)) / r; + resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); +- ty += h + g; +- is++; ++ ty += h + g; is++; + } + } else { +- /* FACECAM LOGIC (Right Side Reserved Zone) */ ++ /* 4. RESERVED ZONE (Calculator) */ + r = nh - ifx; + h = (vh - fy - g * (r - 1)) / r; + int fx = m->wx + m->ww - g - fixed_w; +- ++ ++ /* FORCE the resize to use fixed_w (602) instead of c->w (640) */ + resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); ++ + fy += h + g; + ifx++; + } +diff --git a/dwm.o b/dwm.o +index 7ca47bb..680caa4 100644 +Binary files a/dwm.o and b/dwm.o differ diff --git a/calc-diff4 b/calc-diff4 new file mode 100644 index 0000000..31726b1 --- /dev/null +++ b/calc-diff4 @@ -0,0 +1,115 @@ +diff --git a/dwm.c b/dwm.c +index d484c75..b41c7a9 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -416,6 +416,16 @@ applyrules(Client *c) + c->isfloating = r->isfloating; + c->tags |= r->tags; + c->resizehints = r->resizehints; ++ ++ /* hardcode calculator size early */ ++ if (r->title && !strcmp(r->title, "calculator")) { ++ c->minw = c->maxw = 602; ++ c->minh = c->maxh = 450; ++ c->resizehints = 1; ++ } ++ + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; +@@ -1183,6 +1193,17 @@ manage(Window w, XWindowAttributes *wa) + updatewindowtype(c); + updatesizehints(c); + ++ /* determine fixed-size windows */ ++ c->isfixed = (c->maxw && c->maxh && ++ c->maxw == c->minw && ++ c->maxh == c->minh); ++ ++ /* fixed-size windows with resizehints stay tiled */ ++ if (c->resizehints) ++ c->isfloating = 0; ++ else ++ c->isfloating = c->oldstate = trans != None || c->isfixed; ++ + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); +@@ -2578,6 +2599,75 @@ tile(Monitor *m) + { + Client *c; + int g = gappx; +- unsigned int n = 0; ++ unsigned int n = 0, nh = 0, fixedw = 0; ++ ++ /* measurement phase */ ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next), n++) { ++ if (c->resizehints) { ++ nh++; ++ int w = c->minw ? c->minw : c->w; ++ if (w + 2*c->bw > fixedw) ++ fixedw = w + 2*c->bw; ++ } ++ } ++ ++ if (n == 0) ++ return; ++ ++ /* virtual tiling area (exclude fixed column) */ ++ int reserved = nh ? fixedw + g : 0; ++ int vx = m->wx + g; ++ int vy = m->wy + g; ++ int vw = m->ww - 2*g - reserved; ++ int vh = m->wh - 2*g; ++ ++ unsigned int nt = n - nh; ++ unsigned int nm = MIN(nt, m->nmaster); ++ unsigned int ns = nt - nm; ++ unsigned int mw = ns ? (vw - g) * m->mfact : vw; ++ ++ unsigned int im = 0, is = 0, ifx = 0; ++ unsigned int my = 0, ty = 0, fy = 0; ++ ++ /* placement phase */ ++ for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { ++ unsigned int r, h; ++ ++ if (!c->resizehints) { ++ if (im < nm) { ++ r = nm - im; ++ h = (vh - my - g*(r-1)) / r; ++ resize(c, ++ vx, ++ vy + my, ++ mw - 2*c->bw, ++ h - 2*c->bw, ++ 0); ++ my += h + g; ++ im++; ++ } else { ++ r = ns - is; ++ h = (vh - ty - g*(r-1)) / r; ++ resize(c, ++ vx + mw + g, ++ vy + ty, ++ vw - mw - g - 2*c->bw, ++ h - 2*c->bw, ++ 0); ++ ty += h + g; ++ is++; ++ } ++ } else { ++ r = nh - ifx; ++ h = (vh - fy - g*(r-1)) / r; ++ int fx = m->wx + m->ww - g - fixedw; ++ resize(c, ++ fx, ++ vy + fy, ++ fixedw - 2*c->bw, ++ h - 2*c->bw, ++ 0); ++ fy += h + g; ++ ifx++; ++ } ++ } + } diff --git a/center_first_window.patch b/center_first_window.patch new file mode 100644 index 0000000..b1f20eb --- /dev/null +++ b/center_first_window.patch @@ -0,0 +1,127 @@ +diff --git a/config.def.h b/config.def.h +index 4bffe0e..c7efb0f 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -33,16 +33,18 @@ static const Rule rules[] = { + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ +- /* class instance title tags mask isfloating monitor */ ++ /* class instance title tags mask isfloating isCentered monitor */ + // Keep ffplay floating a and in all tags for facecam script +- { NULL, NULL, "facecam", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "pulsemixer", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "htop", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "youtube-upload", (1<<9)-1, 1, -1 }, +- { "SpeedCrunch", NULL, NULL, (1<<9)-1, 1, -1 }, +- { "Rainlendar2", NULL, NULL, (1<<9)-1, 1, -1 }, +- { NULL, NULL, "note", 0, 1, -1 }, +- { "Xephyr", "Xephyr", NULL, 0, 0, -1 }, ++ { NULL, NULL, "facecam", (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1 }, ++ { "SpeedCrunch", NULL, NULL, (1<<9)-1, 1, 0, -1 }, ++ { "Rainlendar2", NULL, NULL, (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "note", 0, 1, 0, -1 }, ++ { "Xephyr", "Xephyr", NULL, 0, 0, 0, -1 }, ++ { NULL, NULL, NULL, 0, 0, 1, -1 }, ++ + // there is a possibility to need a terminal in misc tag as well.. + // { NULL, NULL, "Alacritty", 1 << 0, 0, -1 }, + // { "Brave", NULL, NULL, 1 << 1, 0, -1 }, +diff --git a/config.h b/config.h +index 4bffe0e..c7efb0f 100644 +--- a/config.h ++++ b/config.h +@@ -33,16 +33,18 @@ static const Rule rules[] = { + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ +- /* class instance title tags mask isfloating monitor */ ++ /* class instance title tags mask isfloating isCentered monitor */ + // Keep ffplay floating a and in all tags for facecam script +- { NULL, NULL, "facecam", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "pulsemixer", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "htop", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "youtube-upload", (1<<9)-1, 1, -1 }, +- { "SpeedCrunch", NULL, NULL, (1<<9)-1, 1, -1 }, +- { "Rainlendar2", NULL, NULL, (1<<9)-1, 1, -1 }, +- { NULL, NULL, "note", 0, 1, -1 }, +- { "Xephyr", "Xephyr", NULL, 0, 0, -1 }, ++ { NULL, NULL, "facecam", (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "htop", (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, -1 }, ++ { "SpeedCrunch", NULL, NULL, (1<<9)-1, 1, 0, -1 }, ++ { "Rainlendar2", NULL, NULL, (1<<9)-1, 1, 0, -1 }, ++ { NULL, NULL, "note", 0, 1, 0, -1 }, ++ { "Xephyr", "Xephyr", NULL, 0, 0, 0, -1 }, ++ { NULL, NULL, NULL, 0, 0, 1, -1 }, ++ + // there is a possibility to need a terminal in misc tag as well.. + // { NULL, NULL, "Alacritty", 1 << 0, 0, -1 }, + // { "Brave", NULL, NULL, 1 << 1, 0, -1 }, +diff --git a/dwm b/dwm +index 4c46a61..ff1c1ec 100755 +Binary files a/dwm and b/dwm differ +diff --git a/dwm.c b/dwm.c +index 60915b9..b349f5e 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -92,7 +92,7 @@ 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, CenterThisWindow; + Client *next; + Client *snext; + Monitor *mon; +@@ -141,6 +141,7 @@ typedef struct { + const char *title; + unsigned int tags; + int isfloating; ++ int CenterThisWindow; + int monitor; + } Rule; + +@@ -405,6 +406,7 @@ applyrules(Client *c) + + /* rule matching */ + c->isfloating = 0; ++ c->CenterThisWindow = 0; + c->tags = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; +@@ -417,12 +419,14 @@ applyrules(Client *c) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; ++ c->CenterThisWindow = r->CenterThisWindow; + c->tags |= r->tags; + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } ++ + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) +@@ -1889,6 +1893,12 @@ tile(Monitor *m) + if (ty + HEIGHT(c) < m->wh) + ty += HEIGHT(c); + } ++ if (n == 1 && selmon->sel->CenterThisWindow) ++ resizeclient(selmon->sel, ++ (selmon->mw - selmon->mw * 0.5) / 2, ++ (selmon->mh - selmon->mh * 0.9) / 2, ++ selmon->mw * 0.5, ++ (selmon->mh * 0.9) + borderpx + 10); + } + + void +diff --git a/dwm.o b/dwm.o +index d8c430a..7047333 100644 +Binary files a/dwm.o and b/dwm.o differ diff --git a/centered_master.patch b/centered_master.patch new file mode 100644 index 0000000..6c4cb5c --- /dev/null +++ b/centered_master.patch @@ -0,0 +1,186 @@ +diff --git a/config.def.h b/config.def.h +index c1eb172..4bffe0e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -38,7 +38,6 @@ static const Rule rules[] = { + { NULL, NULL, "facecam", (1<<9)-1, 1, -1 }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, -1 }, + { NULL, NULL, "htop", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "Calendar", 0, 1, -1 }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, -1 }, + { "SpeedCrunch", NULL, NULL, (1<<9)-1, 1, -1 }, + { "Rainlendar2", NULL, NULL, (1<<9)-1, 1, -1 }, +@@ -65,6 +64,8 @@ static const Layout layouts[] = { + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "|M|", centeredmaster }, ++ { ">M>", centeredfloatingmaster }, + }; + + /* key definitions */ +@@ -128,6 +129,8 @@ static const Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, ++ { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, ++ { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, +diff --git a/config.h b/config.h +index c1eb172..4bffe0e 100644 +--- a/config.h ++++ b/config.h +@@ -38,7 +38,6 @@ static const Rule rules[] = { + { NULL, NULL, "facecam", (1<<9)-1, 1, -1 }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, -1 }, + { NULL, NULL, "htop", (1<<9)-1, 1, -1 }, +- { NULL, NULL, "Calendar", 0, 1, -1 }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, -1 }, + { "SpeedCrunch", NULL, NULL, (1<<9)-1, 1, -1 }, + { "Rainlendar2", NULL, NULL, (1<<9)-1, 1, -1 }, +@@ -65,6 +64,8 @@ static const Layout layouts[] = { + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, ++ { "|M|", centeredmaster }, ++ { ">M>", centeredfloatingmaster }, + }; + + /* key definitions */ +@@ -128,6 +129,8 @@ static const Key keys[] = { + { MODKEY, XK_Tab, view, {0} }, + { MODKEY|ShiftMask, XK_c, killclient, {0} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, ++ { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, ++ { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, +diff --git a/dwm b/dwm +index 33ec708..4c46a61 100755 +Binary files a/dwm and b/dwm differ +diff --git a/dwm.c b/dwm.c +index 29b661c..60915b9 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -237,9 +237,115 @@ 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 void centeredmaster(Monitor *m); ++static void centeredfloatingmaster(Monitor *m); + + /* variables */ + static const char broken[] = "broken"; ++ ++void ++centeredmaster(Monitor *m) ++{ ++ unsigned int i, n, h, mw, mx, my, oty, ety, tw; ++ Client *c; ++ ++ /* count number of clients in the selected monitor */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ /* initialize areas */ ++ mw = m->ww; ++ mx = 0; ++ my = 0; ++ tw = mw; ++ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ tw = m->ww - mw; ++ ++ if (n - m->nmaster > 1) { ++ /* only one client */ ++ mx = (m->ww - mw) / 2; ++ tw = (m->ww - mw) / 2; ++ } ++ } ++ ++ oty = 0; ++ ety = 0; ++ for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ if (i < m->nmaster) { ++ /* nmaster clients are stacked vertically, in the center ++ * of the screen */ ++ h = (m->wh - my) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx + mx, m->wy + my, mw - (2*c->bw), ++ h - (2*c->bw), 0); ++ my += HEIGHT(c); ++ } else { ++ /* stack clients are stacked vertically */ ++ if ((i - m->nmaster) % 2 ) { ++ h = (m->wh - ety) / ( (1 + n - i) / 2); ++ resize(c, m->wx, m->wy + ety, tw - (2*c->bw), ++ h - (2*c->bw), 0); ++ ety += HEIGHT(c); ++ } else { ++ h = (m->wh - oty) / ((1 + n - i) / 2); ++ resize(c, m->wx + mx + mw, m->wy + oty, ++ tw - (2*c->bw), h - (2*c->bw), 0); ++ oty += HEIGHT(c); ++ } ++ } ++} ++ ++void ++centeredfloatingmaster(Monitor *m) ++{ ++ unsigned int i, n, w, mh, mw, mx, mxo, my, myo, tx; ++ Client *c; ++ ++ /* count number of clients in the selected monitor */ ++ for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); ++ if (n == 0) ++ return; ++ ++ /* initialize nmaster area */ ++ if (n > m->nmaster) { ++ /* go mfact box in the center if more than nmaster clients */ ++ if (m->ww > m->wh) { ++ mw = m->nmaster ? m->ww * m->mfact : 0; ++ mh = m->nmaster ? m->wh * 0.9 : 0; ++ } else { ++ mh = m->nmaster ? m->wh * m->mfact : 0; ++ mw = m->nmaster ? m->ww * 0.9 : 0; ++ } ++ mx = mxo = (m->ww - mw) / 2; ++ my = myo = (m->wh - mh) / 2; ++ } else { ++ /* go fullscreen if all clients are in the master area */ ++ mh = m->wh; ++ mw = m->ww; ++ mx = mxo = 0; ++ my = myo = 0; ++ } ++ ++ for(i = tx = 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 */ ++ w = (mw + mxo - mx) / (MIN(n, m->nmaster) - i); ++ resize(c, m->wx + mx, m->wy + my, w - (2*c->bw), ++ mh - (2*c->bw), 0); ++ mx += WIDTH(c); ++ } else { ++ /* stack clients are stacked horizontally */ ++ w = (m->ww - tx) / (n - i); ++ resize(c, m->wx + tx, m->wy, w - (2*c->bw), ++ m->wh - (2*c->bw), 0); ++ tx += WIDTH(c); ++ } ++} ++ + static char stext[256]; + static int screen; + static int sw, sh; /* X display screen geometry width, height */ +diff --git a/dwm.o b/dwm.o +index ff49d4d..d8c430a 100644 +Binary files a/dwm.o and b/dwm.o differ diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..f1b68b3 --- /dev/null +++ b/config.def.h @@ -0,0 +1,262 @@ + +/* See LICENSE file for copyright and license details. */ +#include +// #include "movestack.c" + +/* appearance */ +static const unsigned int borderpx = 5; /* border pixel of windows */ +static const unsigned int snap = 0; /* snap pixel */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const unsigned int gappx = 15; /* gap pixel between windows */ +// static const char *fonts[] = { "monospace:size=14" }; +// static const char *fonts[] = { +// "JetBrainsMono Nerd Font:size=12", +// "monospace:size=12" +// }; +static const char *fonts[] = { + "JetBrainsMono Nerd Font:size=14:antialias=true:autohint=true", + "monospace:size=14" +}; +static const char dmenufont[] = "monospace:size=14"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#222222"; +static const char col_cyan[] = "#d8bb6d"; +// static const char *colors[][3] = { +// /* fg bg border */ +// [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +// [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +// }; +// static const char *colors[][3] = { +// /* fg bg border */ +// [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +// [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +// [SchemeTagNum] = { col_gray4, "#4b0082", "#000000" }, // Example: Indigo background +// }; +/* In config.h */ + +const unsigned int blender_width = 800; +const unsigned int blender_height = 500; +// const unsigned int calc_width = 400; +// const unsigned int calc_height = 670; +const unsigned int calc_width = 312; +const unsigned int calc_height = 570; +const int xephyr_width = 450; /* Set your desired pixel width */ + +const float centered_height = .95; +// const float centered_width = .65; +const float centered_width = .75; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + // [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + // [SchemeSel] = { col_gray3, col_gray1, "#ff0080" }, + [SchemeSel] = { col_gray3, col_gray1, col_cyan}, + // [SchemeTagNum] = { col_gray4, "#eaaeae", "#000000" }, // Unselected Number + // [SchemeTagNumSel] = { col_gray4, "#cf4a4a", "#000000" }, // Selected Number + // [SchemeTagNum] = { col_gray4, "#eaaeae", "#000000" }, // Unselected Number + [SchemeTagNum] = { col_gray3, col_gray2, "#000000" }, // Unselected Number + // [SchemeTagNumSel] = { col_gray4, "#cf4a4a", "#000000" }, // Selected Number + [SchemeTagNumSel] = { col_gray4, col_cyan, "#000000" }, // Selected Number +}; + +/* tagging */ +#define MAX_TAGNAME_LEN 14 /* excludes TAG_PREPEND */ +#define TAG_PREPEND "%1i:" /* formatted as 2 chars */ +// #define MAX_TAGLEN 5 /* altogether */ +// static char tags[MAX_TAGLEN][MAX_TAGNAME_LEN] = { "1:main", "2:web", "3:tools", "4:discord", "5:misc"}; +static char tags[][MAX_TAGNAME_LEN] = { "1\0", "2\0", "3\0", "4\0", "5\0"}; + + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + // Keep ffplay floating a and in all tags for facecam script + // last tag is for calendar + /* class instance title tags mask isfloating isCentered isSidePanel monitor resize hint*/ + // + { NULL, NULL, NULL, 0, 1, 1, 0, -1, 0 }, + { NULL, NULL, "facecam", (1<<4)-1, 0, 0, 1, -1, 1, }, + // { "SpeedCrunch", NULL, "calculator", 0, 1, 0, 0, -1, 0, }, + { "SpeedCrunch", NULL, "calculator", 0, 0, 0, 1, -1, 0, }, + //{ "SpeedCrunch", NULL, "SpeedCrunch", 0, 0, 0, 0, -1, 1, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, 0, -1, 0, }, + { NULL, NULL, "Calendar", 0, 0, 0, 0, -1, 0, }, + { "Xephyr", NULL, NULL, 0, 0, 0, 1, -1, 0, }, + { "Brave-browser", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "Alacritty", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "Zathura", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "steam", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "steam", NULL, "Friends List", 0, 0, 0, 1, -1, 1, }, + { "steam", NULL, "Steam Settings", 0, 1, 0, 0, -1, 0, }, + { "Gimp", NULL, "Open Image", 0, 1, 1, 0, -1, 0, }, + { "Gimp", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "discord", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "Blender", NULL,"Blender File View", 0, 1, 1, 0, -1, 0 }, + { "Blender", NULL,"Blender Render", 0, 0, 0, 1, -1, 0 }, + { "Blender", NULL, NULL, 0, 0, 1, 0, -1, 0 }, + { NULL, NULL, "note", 0, 1, 0, 0, -1, 0, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, 0, -1, 0, }, + // { NULL, NULL, NULL, 0, 0, 1, 0, -1 }, + + // there is a possibility to need a terminal in misc tag as well.. + // { NULL, NULL, "Alacritty", 1 << 0, 0, -1 }, + // { "Brave", NULL, NULL, 1 << 1, 0, -1 }, + // { "Gimp", NULL, NULL, 1 << 2, 0, -1 }, + // { "Discord", NULL, NULL, 1 << 3, 0, -1 }, + // { "OBS", NULL, NULL, 1 << 4, 0, -1 }, + // { "KeePassXC", NULL, NULL, 1 << 4, 0, -1 }, +}; + +/* 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 int refreshrate = 120; /* refresh rate (per second) for client move/resize */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "|M|", centeredmaster }, + // { ">M>", centeredfloatingmaster }, +}; + +const char * +geticon(char *class) +{ + if (strstr(class, "Brave-browser")) return ""; + if (strstr(class, "Alacritty")) return ""; + if (strstr(class, "Gimp")) return ""; + if (strstr(class, "dolphin")) return ""; + if (strstr(class, "Blender")) return ""; + if (strstr(class, "steam")) return ""; + if (strstr(class, "discord")) return ""; + if (strstr(class, "calendar")) return ""; + if (strstr(class, "Zathura")) return ""; + + return ""; /* Default icon for unknown apps */ +} + +/* 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", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "alacritty", "--title", "Alacritty", NULL }; +static const char *flameshotcmd[] = { "flameshot", "gui", NULL }; +static const char *suscmd[] = { "systemctl", "suspend", NULL }; +static const char *volupcmd[] = { "/usr/local/bin/dwm_volume_up", NULL }; +static const char *voldowncmd[] = { "/usr/local/bin/dwm_volume_down", NULL }; +static const char *volmutecmd[] = { "/usr/local/bin/dwm_volume_mute", NULL }; +static const char *mixercmd[] = { "/usr/local/bin/dwm-term-toggle", "pulsemixer", NULL }; +static const char *calccmd[] = { "/usr/local/bin/dwm-speedcrunch-toggle", NULL }; +static const char *calc_mcw_cmd[] = { "/usr/local/bin/dwm-speedcrunch-move", "cw", NULL }; +static const char *calc_mccw_cmd[] = { "/usr/local/bin/dwm-speedcrunch-move", "ccw", NULL }; +static const char *facecamcmd[] = { "/usr/local/bin/facecam", NULL }; +static const char *cpucmd[] = { "/usr/local/bin/dwm-term-toggle", "htop", NULL }; +static const char *brightnesscmd[] = { "/usr/local/bin/brightness-launch", NULL }; +static const char *ffsrecstartcmd[] = { "/usr/local/bin/ffsrec", NULL }; +static const char *ffsrecstopcmd[] = { "/usr/local/bin/ffsrec-stop-recording", NULL }; +static const char *wakeupcmd[] = { "/usr/local/bin/wake-up", "toggle", NULL }; +static const char *calendarcmd[] = { "/usr/local/bin/dwm-calendar", NULL }; +static const char *windowcontainercmd[] = { "/usr/local/bin/dwm-window-container-toggle", NULL }; + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_r, spawn, {.v = ffsrecstartcmd } }, + { MODKEY|ControlMask, XK_r, spawn, {.v = ffsrecstopcmd } }, + { MODKEY|ShiftMask, XK_s, spawn, {.v = flameshotcmd } }, + { MODKEY, XK_F11, spawn, {.v = wakeupcmd} }, + { MODKEY, XK_F12, spawn, {.v = suscmd } }, + { MODKEY, XK_F1, spawn, {.v = mixercmd } }, + { MODKEY, XK_F2, spawn, {.v = voldowncmd } }, + { MODKEY, XK_F3, spawn, {.v = volupcmd } }, + { MODKEY, XK_F4, spawn, {.v = volmutecmd } }, + { MODKEY|ShiftMask, XK_m, spawn, {.v = cpucmd } }, + { MODKEY|ShiftMask, XK_e, spawn, {.v = calccmd } }, + { MODKEY, XK_e, spawn, {.v = calc_mccw_cmd } }, + { MODKEY|ControlMask, XK_e, spawn, {.v = calc_mcw_cmd } }, + { MODKEY|ShiftMask, XK_f, spawn, {.v = facecamcmd } }, + { MODKEY|ShiftMask, XK_b, spawn, {.v = brightnesscmd} }, + { MODKEY|ShiftMask, XK_p, spawn, {.v = calendarcmd} }, + { MODKEY|ShiftMask, XK_w, spawn, {.v = windowcontainercmd} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ControlMask, XK_j, sethfact, {.f = -0.05} }, + { MODKEY|ControlMask, XK_k, sethfact, {.f = +0.05} }, + { MODKEY|Mod1Mask, XK_h, setcwfact, {.f = -0.05} }, + { MODKEY|Mod1Mask, XK_l, setcwfact, {.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} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, + // { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.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 } }, + // { MODKEY, XK_n, nametag, {0} }, + 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} }, +}; + +/* 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 } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + // TODO: + // fix right clicking tag you're on causes dwm to freeze + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/config.h b/config.h new file mode 100644 index 0000000..f1b68b3 --- /dev/null +++ b/config.h @@ -0,0 +1,262 @@ + +/* See LICENSE file for copyright and license details. */ +#include +// #include "movestack.c" + +/* appearance */ +static const unsigned int borderpx = 5; /* border pixel of windows */ +static const unsigned int snap = 0; /* snap pixel */ +static const int showbar = 1; /* 0 means no bar */ +static const int topbar = 1; /* 0 means bottom bar */ +static const unsigned int gappx = 15; /* gap pixel between windows */ +// static const char *fonts[] = { "monospace:size=14" }; +// static const char *fonts[] = { +// "JetBrainsMono Nerd Font:size=12", +// "monospace:size=12" +// }; +static const char *fonts[] = { + "JetBrainsMono Nerd Font:size=14:antialias=true:autohint=true", + "monospace:size=14" +}; +static const char dmenufont[] = "monospace:size=14"; +static const char col_gray1[] = "#222222"; +static const char col_gray2[] = "#444444"; +static const char col_gray3[] = "#bbbbbb"; +static const char col_gray4[] = "#222222"; +static const char col_cyan[] = "#d8bb6d"; +// static const char *colors[][3] = { +// /* fg bg border */ +// [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +// [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +// }; +// static const char *colors[][3] = { +// /* fg bg border */ +// [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, +// [SchemeSel] = { col_gray4, col_cyan, col_cyan }, +// [SchemeTagNum] = { col_gray4, "#4b0082", "#000000" }, // Example: Indigo background +// }; +/* In config.h */ + +const unsigned int blender_width = 800; +const unsigned int blender_height = 500; +// const unsigned int calc_width = 400; +// const unsigned int calc_height = 670; +const unsigned int calc_width = 312; +const unsigned int calc_height = 570; +const int xephyr_width = 450; /* Set your desired pixel width */ + +const float centered_height = .95; +// const float centered_width = .65; +const float centered_width = .75; +static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + // [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + // [SchemeSel] = { col_gray3, col_gray1, "#ff0080" }, + [SchemeSel] = { col_gray3, col_gray1, col_cyan}, + // [SchemeTagNum] = { col_gray4, "#eaaeae", "#000000" }, // Unselected Number + // [SchemeTagNumSel] = { col_gray4, "#cf4a4a", "#000000" }, // Selected Number + // [SchemeTagNum] = { col_gray4, "#eaaeae", "#000000" }, // Unselected Number + [SchemeTagNum] = { col_gray3, col_gray2, "#000000" }, // Unselected Number + // [SchemeTagNumSel] = { col_gray4, "#cf4a4a", "#000000" }, // Selected Number + [SchemeTagNumSel] = { col_gray4, col_cyan, "#000000" }, // Selected Number +}; + +/* tagging */ +#define MAX_TAGNAME_LEN 14 /* excludes TAG_PREPEND */ +#define TAG_PREPEND "%1i:" /* formatted as 2 chars */ +// #define MAX_TAGLEN 5 /* altogether */ +// static char tags[MAX_TAGLEN][MAX_TAGNAME_LEN] = { "1:main", "2:web", "3:tools", "4:discord", "5:misc"}; +static char tags[][MAX_TAGNAME_LEN] = { "1\0", "2\0", "3\0", "4\0", "5\0"}; + + +static const Rule rules[] = { + /* xprop(1): + * WM_CLASS(STRING) = instance, class + * WM_NAME(STRING) = title + */ + // Keep ffplay floating a and in all tags for facecam script + // last tag is for calendar + /* class instance title tags mask isfloating isCentered isSidePanel monitor resize hint*/ + // + { NULL, NULL, NULL, 0, 1, 1, 0, -1, 0 }, + { NULL, NULL, "facecam", (1<<4)-1, 0, 0, 1, -1, 1, }, + // { "SpeedCrunch", NULL, "calculator", 0, 1, 0, 0, -1, 0, }, + { "SpeedCrunch", NULL, "calculator", 0, 0, 0, 1, -1, 0, }, + //{ "SpeedCrunch", NULL, "SpeedCrunch", 0, 0, 0, 0, -1, 1, }, + { NULL, NULL, "youtube-upload", (1<<9)-1, 1, 0, 0, -1, 0, }, + { NULL, NULL, "Calendar", 0, 0, 0, 0, -1, 0, }, + { "Xephyr", NULL, NULL, 0, 0, 0, 1, -1, 0, }, + { "Brave-browser", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "Alacritty", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "Zathura", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "steam", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "steam", NULL, "Friends List", 0, 0, 0, 1, -1, 1, }, + { "steam", NULL, "Steam Settings", 0, 1, 0, 0, -1, 0, }, + { "Gimp", NULL, "Open Image", 0, 1, 1, 0, -1, 0, }, + { "Gimp", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "discord", NULL, NULL, 0, 0, 1, 0, -1, 0, }, + { "Blender", NULL,"Blender File View", 0, 1, 1, 0, -1, 0 }, + { "Blender", NULL,"Blender Render", 0, 0, 0, 1, -1, 0 }, + { "Blender", NULL, NULL, 0, 0, 1, 0, -1, 0 }, + { NULL, NULL, "note", 0, 1, 0, 0, -1, 0, }, + { NULL, NULL, "pulsemixer", (1<<9)-1, 1, 0, 0, -1, 0, }, + { NULL, NULL, "htop", (1<<9)-1, 1, 0, 0, -1, 0, }, + // { NULL, NULL, NULL, 0, 0, 1, 0, -1 }, + + // there is a possibility to need a terminal in misc tag as well.. + // { NULL, NULL, "Alacritty", 1 << 0, 0, -1 }, + // { "Brave", NULL, NULL, 1 << 1, 0, -1 }, + // { "Gimp", NULL, NULL, 1 << 2, 0, -1 }, + // { "Discord", NULL, NULL, 1 << 3, 0, -1 }, + // { "OBS", NULL, NULL, 1 << 4, 0, -1 }, + // { "KeePassXC", NULL, NULL, 1 << 4, 0, -1 }, +}; + +/* 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 int refreshrate = 120; /* refresh rate (per second) for client move/resize */ + +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, /* first entry is default */ + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, + { "|M|", centeredmaster }, + // { ">M>", centeredfloatingmaster }, +}; + +const char * +geticon(char *class) +{ + if (strstr(class, "Brave-browser")) return ""; + if (strstr(class, "Alacritty")) return ""; + if (strstr(class, "Gimp")) return ""; + if (strstr(class, "dolphin")) return ""; + if (strstr(class, "Blender")) return ""; + if (strstr(class, "steam")) return ""; + if (strstr(class, "discord")) return ""; + if (strstr(class, "calendar")) return ""; + if (strstr(class, "Zathura")) return ""; + + return ""; /* Default icon for unknown apps */ +} + +/* 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", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL }; +static const char *termcmd[] = { "alacritty", "--title", "Alacritty", NULL }; +static const char *flameshotcmd[] = { "flameshot", "gui", NULL }; +static const char *suscmd[] = { "systemctl", "suspend", NULL }; +static const char *volupcmd[] = { "/usr/local/bin/dwm_volume_up", NULL }; +static const char *voldowncmd[] = { "/usr/local/bin/dwm_volume_down", NULL }; +static const char *volmutecmd[] = { "/usr/local/bin/dwm_volume_mute", NULL }; +static const char *mixercmd[] = { "/usr/local/bin/dwm-term-toggle", "pulsemixer", NULL }; +static const char *calccmd[] = { "/usr/local/bin/dwm-speedcrunch-toggle", NULL }; +static const char *calc_mcw_cmd[] = { "/usr/local/bin/dwm-speedcrunch-move", "cw", NULL }; +static const char *calc_mccw_cmd[] = { "/usr/local/bin/dwm-speedcrunch-move", "ccw", NULL }; +static const char *facecamcmd[] = { "/usr/local/bin/facecam", NULL }; +static const char *cpucmd[] = { "/usr/local/bin/dwm-term-toggle", "htop", NULL }; +static const char *brightnesscmd[] = { "/usr/local/bin/brightness-launch", NULL }; +static const char *ffsrecstartcmd[] = { "/usr/local/bin/ffsrec", NULL }; +static const char *ffsrecstopcmd[] = { "/usr/local/bin/ffsrec-stop-recording", NULL }; +static const char *wakeupcmd[] = { "/usr/local/bin/wake-up", "toggle", NULL }; +static const char *calendarcmd[] = { "/usr/local/bin/dwm-calendar", NULL }; +static const char *windowcontainercmd[] = { "/usr/local/bin/dwm-window-container-toggle", NULL }; + +static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY|ShiftMask, XK_r, spawn, {.v = ffsrecstartcmd } }, + { MODKEY|ControlMask, XK_r, spawn, {.v = ffsrecstopcmd } }, + { MODKEY|ShiftMask, XK_s, spawn, {.v = flameshotcmd } }, + { MODKEY, XK_F11, spawn, {.v = wakeupcmd} }, + { MODKEY, XK_F12, spawn, {.v = suscmd } }, + { MODKEY, XK_F1, spawn, {.v = mixercmd } }, + { MODKEY, XK_F2, spawn, {.v = voldowncmd } }, + { MODKEY, XK_F3, spawn, {.v = volupcmd } }, + { MODKEY, XK_F4, spawn, {.v = volmutecmd } }, + { MODKEY|ShiftMask, XK_m, spawn, {.v = cpucmd } }, + { MODKEY|ShiftMask, XK_e, spawn, {.v = calccmd } }, + { MODKEY, XK_e, spawn, {.v = calc_mccw_cmd } }, + { MODKEY|ControlMask, XK_e, spawn, {.v = calc_mcw_cmd } }, + { MODKEY|ShiftMask, XK_f, spawn, {.v = facecamcmd } }, + { MODKEY|ShiftMask, XK_b, spawn, {.v = brightnesscmd} }, + { MODKEY|ShiftMask, XK_p, spawn, {.v = calendarcmd} }, + { MODKEY|ShiftMask, XK_w, spawn, {.v = windowcontainercmd} }, + { MODKEY, XK_b, togglebar, {0} }, + { MODKEY, XK_j, focusstack, {.i = +1 } }, + { MODKEY, XK_k, focusstack, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, + { MODKEY, XK_l, setmfact, {.f = +0.05} }, + { MODKEY|ControlMask, XK_j, sethfact, {.f = -0.05} }, + { MODKEY|ControlMask, XK_k, sethfact, {.f = +0.05} }, + { MODKEY|Mod1Mask, XK_h, setcwfact, {.f = -0.05} }, + { MODKEY|Mod1Mask, XK_l, setcwfact, {.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} }, + { MODKEY, XK_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XK_u, setlayout, {.v = &layouts[3]} }, + // { MODKEY, XK_o, setlayout, {.v = &layouts[4]} }, + { MODKEY, XK_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XK_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_space, setlayout, {0} }, + { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, + { MODKEY, XK_0, view, {.ui = ~0 } }, + { MODKEY|ShiftMask, XK_0, tag, {.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 } }, + // { MODKEY, XK_n, nametag, {0} }, + 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} }, +}; + +/* 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 } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, + { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, + { ClkClientWin, MODKEY, Button3, resizemouse, {0} }, + { ClkTagBar, 0, Button1, view, {0} }, + // TODO: + // fix right clicking tag you're on causes dwm to freeze + { ClkTagBar, 0, Button3, toggleview, {0} }, + { ClkTagBar, MODKEY, Button1, tag, {0} }, + { ClkTagBar, MODKEY, Button3, toggletag, {0} }, +}; + diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..b469a2b --- /dev/null +++ b/config.mk @@ -0,0 +1,39 @@ +# dwm version +VERSION = 6.6 + +# Customize below to fit your system + +# paths +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = ${X11INC}/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I${X11INC} -I${FREETYPEINC} +LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS} +CFLAGS = -std=c99 -pedantic -Wall -Wno-deprecated-declarations -Os ${INCS} ${CPPFLAGS} +LDFLAGS = ${LIBS} + +# Solaris +#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" +#LDFLAGS = ${LIBS} + +# compiler and linker +CC = cc diff --git a/drw.c b/drw.c new file mode 100644 index 0000000..9fdd1a4 --- /dev/null +++ b/drw.c @@ -0,0 +1,471 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD + +static int +utf8decode(const char *s_in, long *u, int *err) +{ + static const unsigned char lens[] = { + /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */ + /* 110XX */ 2, 2, 2, 2, + /* 1110X */ 3, 3, + /* 11110 */ 4, + /* 11111 */ 0, /* invalid */ + }; + static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 }; + static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 }; + + const unsigned char *s = (const unsigned char *)s_in; + int len = lens[*s >> 3]; + *u = UTF_INVALID; + *err = 1; + if (len == 0) + return 1; + + long cp = s[0] & leading_mask[len - 1]; + for (int i = 1; i < len; ++i) { + if (s[i] == '\0' || (s[i] & 0xC0) != 0x80) + return i; + cp = (cp << 6) | (s[i] & 0x3F); + } + /* out of range, surrogate, overlong encoding */ + if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1]) + return len; + + *err = 0; + *u = cp; + return len; +} + +Drw * +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) +{ + Drw *drw = ecalloc(1, sizeof(Drw)); + + drw->dpy = dpy; + drw->screen = screen; + drw->root = root; + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + drw->w = w; + drw->h = h; + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +} + +void +drw_free(Drw *drw) +{ + XFreePixmap(drw->dpy, drw->drawable); + XFreeGC(drw->dpy, drw->gc); + drw_fontset_free(drw->fonts); + free(drw); +} + +/* This function is an implementation detail. Library users should use + * drw_fontset_create instead. + */ +static Fnt * +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern) +{ + Fnt *font; + XftFont *xfont = NULL; + FcPattern *pattern = NULL; + + if (fontname) { + /* Using the pattern found at font->xfont->pattern does not yield the + * same substitution results as using the pattern returned by + * FcNameParse; using the latter results in the desired fallback + * behaviour whereas the former just results in missing-character + * rectangles being drawn, at least with some fonts. */ + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) { + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname); + return NULL; + } + if (!(pattern = FcNameParse((FcChar8 *) fontname))) { + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname); + XftFontClose(drw->dpy, xfont); + return NULL; + } + } else if (fontpattern) { + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) { + fprintf(stderr, "error, cannot load font from pattern.\n"); + return NULL; + } + } else { + die("no font specified."); + } + + font = ecalloc(1, sizeof(Fnt)); + font->xfont = xfont; + font->pattern = pattern; + font->h = xfont->ascent + xfont->descent; + font->dpy = drw->dpy; + + return font; +} + +static void +xfont_free(Fnt *font) +{ + if (!font) + return; + if (font->pattern) + FcPatternDestroy(font->pattern); + XftFontClose(font->dpy, font->xfont); + free(font); +} + +Fnt* +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) +{ + Fnt *cur, *ret = NULL; + size_t i; + + if (!drw || !fonts) + return NULL; + + for (i = 1; i <= fontcount; i++) { + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) { + cur->next = ret; + ret = cur; + } + } + return (drw->fonts = ret); +} + +void +drw_fontset_free(Fnt *font) +{ + if (font) { + drw_fontset_free(font->next); + xfont_free(font); + } +} + +void +drw_clr_create(Drw *drw, Clr *dest, const char *clrname) +{ + if (!drw || !dest || !clrname) + return; + + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), + clrname, dest)) + die("error, cannot allocate color '%s'", clrname); +} + +/* Create color schemes. */ +Clr * +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) +{ + size_t i; + Clr *ret; + + /* need at least two colors for a scheme */ + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(Clr)))) + return NULL; + + for (i = 0; i < clrcount; i++) + drw_clr_create(drw, &ret[i], clrnames[i]); + return ret; +} + +void +drw_clr_free(Drw *drw, Clr *c) +{ + if (!drw || !c) + return; + + /* c is typedef XftColor Clr */ + XftColorFree(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), c); +} + +void +drw_scm_free(Drw *drw, Clr *scm, size_t clrcount) +{ + size_t i; + + if (!drw || !scm) + return; + + for (i = 0; i < clrcount; i++) + drw_clr_free(drw, &scm[i]); + free(scm); +} + +void +drw_setfontset(Drw *drw, Fnt *set) +{ + if (drw) + drw->fonts = set; +} + +void +drw_setscheme(Drw *drw, Clr *scm) +{ + if (drw) + drw->scheme = scm; +} + +void +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert) +{ + if (!drw || !drw->scheme) + return; + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel); + if (filled) + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + else + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1); +} + +int +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) +{ + int ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + int utf8strlen, utf8charlen, utf8err, render = x || y || w || h; + long utf8codepoint = 0; + const char *utf8str; + FcCharSet *fccharset; + FcPattern *fcpattern; + FcPattern *match; + XftResult result; + int charexists = 0, overflow = 0; + /* keep track of a couple codepoints for which we have no match. */ + static unsigned int nomatches[128], ellipsis_width, invalid_width; + static const char invalid[] = "�"; + + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) + return 0; + + if (!render) { + w = invert ? invert : ~invert; + } else { + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); + if (w < lpad) + return x + w; + d = XftDrawCreate(drw->dpy, drw->drawable, + DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen)); + x += lpad; + w -= lpad; + } + + usedfont = drw->fonts; + if (!ellipsis_width && render) + ellipsis_width = drw_fontset_getwidth(drw, "..."); + if (!invalid_width && render) + invalid_width = drw_fontset_getwidth(drw, invalid); + while (1) { + ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, &utf8err); + for (curfont = drw->fonts; curfont; curfont = curfont->next) { + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); + if (charexists) { + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); + if (ew + ellipsis_width <= w) { + /* keep track where the ellipsis still fits */ + ellipsis_x = x + ew; + ellipsis_w = w - ew; + ellipsis_len = utf8strlen; + } + + if (ew + tmpw > w) { + overflow = 1; + /* called from drw_fontset_getwidth_clamp(): + * it wants the width AFTER the overflow + */ + if (!render) + x += tmpw; + else + utf8strlen = ellipsis_len; + } else if (curfont == usedfont) { + text += utf8charlen; + utf8strlen += utf8err ? 0 : utf8charlen; + ew += utf8err ? 0 : tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont || utf8err) + break; + else + charexists = 0; + } + + if (utf8strlen) { + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } + x += ew; + w -= ew; + } + if (utf8err && (!render || invalid_width < w)) { + if (render) + drw_text(drw, x, y, w, h, 0, invalid, invert); + x += invalid_width; + w -= invalid_width; + } + if (render && overflow) + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); + + if (!*text || overflow) { + break; + } else if (nextfont) { + charexists = 0; + usedfont = nextfont; + } else { + /* Regardless of whether or not a fallback font is found, the + * character must be drawn. */ + charexists = 1; + + hash = (unsigned int)utf8codepoint; + hash = ((hash >> 16) ^ hash) * 0x21F0AAAD; + hash = ((hash >> 15) ^ hash) * 0xD35A2D97; + h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches); + h1 = (hash >> 17) % LENGTH(nomatches); + /* avoid expensive XftFontMatch call when we know we won't find a match */ + if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) + goto no_match; + + fccharset = FcCharSetCreate(); + FcCharSetAddChar(fccharset, utf8codepoint); + + if (!drw->fonts->pattern) { + /* Refer to the comment in xfont_create for more information. */ + die("the first font in the cache must be loaded from a font string."); + } + + fcpattern = FcPatternDuplicate(drw->fonts->pattern); + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset); + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue); + + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern); + FcDefaultSubstitute(fcpattern); + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result); + + FcCharSetDestroy(fccharset); + FcPatternDestroy(fcpattern); + + if (match) { + usedfont = xfont_create(drw, NULL, match); + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) { + for (curfont = drw->fonts; curfont->next; curfont = curfont->next) + ; /* NOP */ + curfont->next = usedfont; + } else { + xfont_free(usedfont); + nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint; +no_match: + usedfont = drw->fonts; + } + } + } + } + if (d) + XftDrawDestroy(d); + + return x + (render ? w : 0); +} + +void +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); + XSync(drw->dpy, False); +} + +unsigned int +drw_fontset_getwidth(Drw *drw, const char *text) +{ + if (!drw || !drw->fonts || !text) + return 0; + return drw_text(drw, 0, 0, 0, 0, 0, text, 0); +} + +unsigned int +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) +{ + unsigned int tmp = 0; + if (drw && drw->fonts && text && n) + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); + return MIN(n, tmp); +} + +void +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) +{ + XGlyphInfo ext; + + if (!font || !text) + return; + + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext); + if (w) + *w = ext.xOff; + if (h) + *h = font->h; +} + +Cur * +drw_cur_create(Drw *drw, int shape) +{ + Cur *cur; + + if (!drw || !(cur = ecalloc(1, sizeof(Cur)))) + return NULL; + + cur->cursor = XCreateFontCursor(drw->dpy, shape); + + return cur; +} + +void +drw_cur_free(Drw *drw, Cur *cursor) +{ + if (!cursor) + return; + + XFreeCursor(drw->dpy, cursor->cursor); + free(cursor); +} diff --git a/drw.h b/drw.h new file mode 100644 index 0000000..bda06f9 --- /dev/null +++ b/drw.h @@ -0,0 +1,60 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg, ColBorder }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +void drw_clr_free(Drw *drw, Clr *c); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); +void drw_scm_free(Drw *drw, Clr *scm, size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/drw.o b/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..ee27507cb26e4c4d4d75b5cb4c49b3129b80bcee GIT binary patch literal 11904 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 literal 0 HcmV?d00001 diff --git a/dwm b/dwm new file mode 100755 index 0000000000000000000000000000000000000000..cf10240c77303e17a64880dd83e8a2ced91bb34f GIT binary patch literal 81328 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 literal 0 HcmV?d00001 diff --git a/dwm.1 b/dwm.1 new file mode 100644 index 0000000..ddc8321 --- /dev/null +++ b/dwm.1 @@ -0,0 +1,176 @@ +.TH DWM 1 dwm\-VERSION +.SH NAME +dwm \- dynamic window manager +.SH SYNOPSIS +.B dwm +.RB [ \-v ] +.SH DESCRIPTION +dwm is a dynamic window manager for X. It manages windows in tiled, monocle +and floating layouts. Either layout can be applied dynamically, optimising the +environment for the application in use and the task performed. +.P +In tiled layouts windows are managed in a master and stacking area. The master +area on the left contains one window by default, and the stacking area on the +right contains all other windows. The number of master area windows can be +adjusted from zero to an arbitrary number. In monocle layout all windows are +maximised to the screen size. In floating layout windows can be resized and +moved freely. Dialog windows are always managed floating, regardless of the +layout applied. +.P +Windows are grouped by tags. Each window can be tagged with one or multiple +tags. Selecting certain tags displays all windows with these tags. +.P +Each screen contains a small status bar which displays all available tags, the +layout, the title of the focused window, and the text read from the root window +name property, if the screen is focused. A floating window is indicated with an +empty square and a maximised floating window is indicated with a filled square +before the windows title. The selected tags are indicated with a different +color. The tags of the focused window are indicated with a filled square in the +top left corner. The tags which are applied to one or more windows are +indicated with an empty square in the top left corner. +.P +dwm draws a small border around windows to indicate the focus state. +.SH OPTIONS +.TP +.B \-v +prints version information to stderr, then exits. +.SH USAGE +.SS Status bar +.TP +.B X root window name +is read and displayed in the status text area. It can be set with the +.BR xsetroot (1) +command. +.TP +.B Button1 +click on a tag label to display all windows with that tag, click on the layout +label toggles between tiled and floating layout. +.TP +.B Button3 +click on a tag label adds/removes all windows with that tag to/from the view. +.TP +.B Mod1\-Button1 +click on a tag label applies that tag to the focused window. +.TP +.B Mod1\-Button3 +click on a tag label adds/removes that tag to/from the focused window. +.SS Keyboard commands +.TP +.B Mod1\-Shift\-Return +Start +.BR st(1). +.TP +.B Mod1\-p +Spawn +.BR dmenu(1) +for launching other programs. +.TP +.B Mod1\-, +Focus previous screen, if any. +.TP +.B Mod1\-. +Focus next screen, if any. +.TP +.B Mod1\-Shift\-, +Send focused window to previous screen, if any. +.TP +.B Mod1\-Shift\-. +Send focused window to next screen, if any. +.TP +.B Mod1\-b +Toggles bar on and off. +.TP +.B Mod1\-t +Sets tiled layout. +.TP +.B Mod1\-f +Sets floating layout. +.TP +.B Mod1\-m +Sets monocle layout. +.TP +.B Mod1\-space +Toggles between current and previous layout. +.TP +.B Mod1\-j +Focus next window. +.TP +.B Mod1\-k +Focus previous window. +.TP +.B Mod1\-i +Increase number of windows in master area. +.TP +.B Mod1\-d +Decrease number of windows in master area. +.TP +.B Mod1\-l +Increase master area size. +.TP +.B Mod1\-h +Decrease master area size. +.TP +.B Mod1\-Return +Zooms/cycles focused window to/from master area (tiled layouts only). +.TP +.B Mod1\-Shift\-c +Close focused window. +.TP +.B Mod1\-Shift\-space +Toggle focused window between tiled and floating state. +.TP +.B Mod1\-Tab +Toggles to the previously selected tags. +.TP +.B Mod1\-Shift\-[1..n] +Apply nth tag to focused window. +.TP +.B Mod1\-Shift\-0 +Apply all tags to focused window. +.TP +.B Mod1\-Control\-Shift\-[1..n] +Add/remove nth tag to/from focused window. +.TP +.B Mod1\-[1..n] +View all windows with nth tag. +.TP +.B Mod1\-0 +View all windows with any tag. +.TP +.B Mod1\-Control\-[1..n] +Add/remove all windows with nth tag to/from the view. +.TP +.B Mod1\-Shift\-q +Quit dwm. +.SS Mouse commands +.TP +.B Mod1\-Button1 +Move focused window while dragging. Tiled windows will be toggled to the floating state. +.TP +.B Mod1\-Button2 +Toggles focused window between floating and tiled state. +.TP +.B Mod1\-Button3 +Resize focused window while dragging. Tiled windows will be toggled to the floating state. +.SH CUSTOMIZATION +dwm is customized by creating a custom config.h and (re)compiling the source +code. This keeps it fast, secure and simple. +.SH SEE ALSO +.BR dmenu (1), +.BR st (1) +.SH ISSUES +Java applications which use the XToolkit/XAWT backend may draw grey windows +only. The XToolkit/XAWT backend breaks ICCCM-compliance in recent JDK 1.5 and early +JDK 1.6 versions, because it assumes a reparenting window manager. Possible workarounds +are using JDK 1.4 (which doesn't contain the XToolkit/XAWT backend) or setting the +environment variable +.BR AWT_TOOLKIT=MToolkit +(to use the older Motif backend instead) or running +.B xprop -root -f _NET_WM_NAME 32a -set _NET_WM_NAME LG3D +or +.B wmname LG3D +(to pretend that a non-reparenting window manager is running that the +XToolkit/XAWT backend can recognize) or when using OpenJDK setting the environment variable +.BR _JAVA_AWT_WM_NONREPARENTING=1 . +.SH BUGS +Send all bug reports with a patch to hackers@suckless.org. diff --git a/dwm.c b/dwm.c new file mode 100644 index 0000000..6148137 --- /dev/null +++ b/dwm.c @@ -0,0 +1,4116 @@ +/* See LICENSE file for copyright and license details. + * + * dynamic window manager is designed like any other X client as well. It is + * driven through handling X events. In contrast to other X clients, a window + * manager selects for SubstructureRedirectMask on the root window, to receive + * events about window (dis-)appearance. Only one X connection at a time is + * allowed to select for this event mask. + * + * The event handlers of dwm are organized in an array which is accessed + * whenever a new event has been fetched. This allows event dispatching + * in O(1) time. + * + * Each child of the root window is called a client, except windows which have + * set the override_redirect flag. Clients are organized in a linked client + * list on each monitor, the focus history is remembered through a stack list + * on each monitor. Each client contains a bit array to indicate the tags of a + * client. + * + * Keys and tagging rules are organized as arrays and defined in config.h. + * + * To understand everything else, start reading main(). + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef XINERAMA +#include +#endif /* XINERAMA */ +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) +#define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) +#define SETDMENUMON(m) m[0] = '0' + selmon->num +#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 MOUSEMASK (BUTTONMASK|PointerMotionMask) +#define WIDTH(X) ((X)->w + 2 * (X)->bw) +#define HEIGHT(X) ((X)->h + 2 * (X)->bw) +#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) +// #define IS_FACECAM(X) (X && X->resizehints && strcmp(X->name, "calculator") != 0) +#define IS_FACECAM(X) (X && X->resizehints && strcmp(X->name, "facecam") == 0) + +/* enums */ +enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +// enum { SchemeNorm, SchemeSel, SchemeTagNum }; /* color schemes */ +enum { SchemeNorm, SchemeSel, SchemeTagNum, SchemeTagNumSel }; +enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetWMOpacity, NetLast }; /* EWMH atoms */ +enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ + +typedef union { + int i; + unsigned int ui; + float f; + const void *v; +} Arg; + +typedef struct { + unsigned int click; + unsigned int mask; + unsigned int button; + void (*func)(const Arg *arg); + const Arg arg; +} Button; + +typedef struct Monitor Monitor; +typedef struct Client Client; +struct Client { + char name[256]; + char class[256]; + float mina, maxa; + int x, y, w, h; + int oldx, oldy, oldw, oldh; + int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid; + int bw, oldbw; + int isPopup; + unsigned int tags; + int isfixed, iscentered, isfloating, isSidePanel, isurgent, neverfocus, oldstate, isfullscreen, resizehints; + Client *next; + Client *snext; + Monitor *mon; + Window win; +}; + +typedef struct { + unsigned int mod; + KeySym keysym; + void (*func)(const Arg *); + const Arg arg; +} Key; + +typedef struct { + const char *symbol; + void (*arrange)(Monitor *); +} Layout; + +typedef struct Pertag Pertag; +struct Monitor { + char ltsymbol[16]; + float mfact; + // float center_mfact; + float hfact; + float cwfact; + int nmaster; + int num; + int by; /* bar geometry */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; + unsigned int sellt; + unsigned int tagset[2]; + int showbar; + int topbar; + Client *clients; + Client *sel; + Client *stack; + Monitor *next; + Window barwin; + const Layout *lt[2]; + Pertag *pertag; + char **tags; +}; + +typedef struct { + const char *class; + const char *instance; + const char *title; + unsigned int tags; + int isfloating; + int iscentered; + int isSidePanel; + int monitor; + int resizehints; +} Rule; + +unsigned char one_window = 0; +/* function declarations */ +static void setopacity(Client *c, double opacity); +static void applyrules(Client *c); +static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact); +static void arrange(Monitor *m); +static void arrangemon(Monitor *m); +static void attach(Client *c); +static void attachstack(Client *c); +static void buttonpress(XEvent *e); +static void checkotherwm(void); +static void cleanup(void); +static void cleanupmon(Monitor *mon); +static void clientmessage(XEvent *e); +static void configure(Client *c); +static void configurenotify(XEvent *e); +static void configurerequest(XEvent *e); +static Monitor *createmon(void); +static void destroynotify(XEvent *e); +static void detach(Client *c); +static void detachstack(Client *c); +static Monitor *dirtomon(int dir); +static void drawbar(Monitor *m); +static void drawbars(void); +static void enternotify(XEvent *e); +static void expose(XEvent *e); +static void focus(Client *c); +static void focusin(XEvent *e); +static void focusmon(const Arg *arg); +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 void gettagtext(Monitor *m, int tag, char *text, unsigned int maxlen); +static int gettextprop(Window w, Atom atom, char *text, unsigned int size); +static void grabbuttons(Client *c, int focused); +static void grabkeys(void); +static void incnmaster(const Arg *arg); +static void keypress(XEvent *e); +static void killclient(const Arg *arg); +static void manage(Window w, XWindowAttributes *wa); +static void mappingnotify(XEvent *e); +static void maprequest(XEvent *e); +static void monocle(Monitor *m); +static void motionnotify(XEvent *e); +static void movemouse(const Arg *arg); +static void movestack(const Arg *arg); +static void nametag(const Arg *arg); +static Client *nexttiled(Client *c); +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 resize(Client *c, int x, int y, int w, int h, int interact); +static void resizeclient(Client *c, int x, int y, int w, int h); +static void resizemouse(const Arg *arg); +static void restack(Monitor *m); +static void run(void); +static void scan(void); +static int sendevent(Client *c, Atom proto); +static void sendmon(Client *c, Monitor *m); +static void setclientstate(Client *c, long state); +static void setfocus(Client *c); +static void setfullscreen(Client *c, int fullscreen); +static void setcwfact(const Arg *arg); +static void sethfact(const Arg *arg); +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 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 toggletag(const Arg *arg); +static void toggleview(const Arg *arg); +static void unfocus(Client *c, int setfocus); +static void unmanage(Client *c, int destroyed); +static void unmapnotify(XEvent *e); +static void updatebarpos(Monitor *m); +static void updatebars(void); +static void updateclientlist(void); +static int updategeom(void); +static void updatenumlockmask(void); +static void updatesizehints(Client *c); +static void updatestatus(void); +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 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 void centeredmaster(Monitor *m); +static void centeredfloatingmaster(Monitor *m); + +/* variables */ +static const char broken[] = "broken"; +#include "config.h" + + +void +getcenteredbox(Monitor *m, unsigned int n, int *bx, int *by, int *bw, int *bh) +{ + int avail_w = m->ww - (2 * gappx); + int avail_h = m->wh - (2 * gappx); + int base_x = m->wx + gappx; + int base_y = m->wy + gappx; + + if (n == 1) { + *bw = avail_w * m->cwfact; + *bh = avail_h * m->hfact; + *bx = base_x + (avail_w - *bw) / 2; + *by = base_y + (avail_h - *bh) / 2; + } else { + *bw = avail_w; + *bh = avail_h; + *bx = base_x; + *by = base_y; + } +} + +void +update_alacritty_opacity(Monitor *m) +{ + static double last_opacity = -1.0; + if (!m || !m->lt[m->sellt]) + return; + + double target_opacity = (m->lt[m->sellt]->arrange == monocle) ? 1.0 : 0.93; + + /* Only send IPC message if opacity state changed to prevent unnecessary commands */ + if (target_opacity != last_opacity) { + char cmd[128]; + snprintf(cmd, sizeof(cmd), + "alacritty msg config \"window.opacity=%.2f\" >/dev/null 2>&1 &", + target_opacity); + system(cmd); + last_opacity = target_opacity; + } +} + +static void +get_layout_box(Monitor *m, unsigned int force_centered, unsigned int *out_nt, int *bx, int *by, int *bw, int *bh) +{ + Client *c, *tmp; + int g = gappx; + unsigned int n = 0, nh = 0, fixed_w = 0; + + /* 1. Count visible windows & determine max side-panel width */ + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!(c->tags & m->tagset[m->seltags])) + continue; + + n++; + if (c->isSidePanel) { + nh++; + int w; + if (c->name && strstr(c->class, "Xephyr")) + w = xephyr_width; + else if (c->name && strstr(c->name, "Blender Render")) + w = blender_width; + else if (c->name && strstr(c->name, "calculator")) + w = calc_width; + else + w = (c->minw > 0) ? c->minw : c->w; + + if (w + (2 * c->bw) > fixed_w) + fixed_w = w + (2 * c->bw); + } + } + + unsigned int nt = n - nh; + if (out_nt) + *out_nt = nt; + + if (n == 0) + return; + + /* 2. Setup Virtual Zone */ + int reserved_space = (nh > 0) ? (fixed_w + g) : 0; + int vx = m->wx + g; + int vy = m->wy + g; + int vw = m->ww - (2 * g) - reserved_space; + int vh = m->wh - (2 * g); + + /* 3. Render Side Panels */ + if (nh > 0) { + unsigned int ifx = 0; + int fy = 0; + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!(c->tags & m->tagset[m->seltags])) + continue; + + if (c->isSidePanel) { + unsigned int r = nh - ifx; + int reserved_for_later = 0; + + for (tmp = c->next; tmp; tmp = nexttiled(tmp->next)) { + if (tmp->isSidePanel && (tmp->tags & m->tagset[m->seltags])) { + if (tmp->name && strstr(tmp->name, "Blender Render")) + reserved_for_later += blender_height + g; + else if (tmp->name && strstr(tmp->name, "calculator")) + reserved_for_later += calc_height + g; + } + } + + int h; + if (c->name && strstr(c->name, "Blender Render")) { + h = blender_height; + } else if (c->name && strstr(c->name, "calculator")) { + h = calc_height; + } else { + int available = vh - fy - reserved_for_later; + int flex_left = 0; + for (tmp = c; tmp; tmp = nexttiled(tmp->next)) { + if (tmp->isSidePanel && (tmp->tags & m->tagset[m->seltags])) { + int is_forced = (tmp->name && (strstr(tmp->name, "Blender Render") || strstr(tmp->name, "calculator"))); + if (!is_forced) flex_left++; + } + } + h = (flex_left > 0) ? (available / flex_left) : available; + if (r > 1) h -= g; + } + + if (h < 50) h = 50; + if (fy + h > vh) h = vh - fy; + + int fx = m->wx + m->ww - g - fixed_w; + resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); + + fy += h + g; + ifx++; + } + } + } + + /* 4. Main Layout Bounding Box Calculation */ + if (force_centered || nt == 1) { + *bw = vw * m->cwfact; + *bh = vh * m->hfact; + *bx = vx + (vw - *bw) / 2; + *by = vy + (vh - *bh) / 2; + } else { + *bw = vw; + *bh = vh; + *bx = vx; + *by = vy; + } +} + +// void +// centeredmaster(Monitor *m) +// { +// unsigned int i, n, h, mw, mx, my, oty, ety, tw; +// Client *c; + +// /* count number of clients in the selected monitor */ +// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +// if (n == 0) +// return; + +// /* initialize areas */ +// mw = m->ww; +// mx = 0; +// my = 0; +// tw = mw; + +// if (n > m->nmaster) { +// /* go mfact box in the center if more than nmaster clients */ +// mw = m->nmaster ? m->ww * m->mfact : 0; +// tw = m->ww - mw; + +// if (n - m->nmaster > 1) { +// /* only one client */ +// mx = (m->ww - mw) / 2; +// tw = (m->ww - mw) / 2; +// } +// } + +// oty = 0; +// ety = 0; +// for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) +// if (i < m->nmaster) { +// /* nmaster clients are stacked vertically, in the center +// * of the screen */ +// h = (m->wh - my) / (MIN(n, m->nmaster) - i); +// resize(c, m->wx + mx, m->wy + my, mw - (2*c->bw), +// h - (2*c->bw), 0); +// my += HEIGHT(c); +// } else { +// /* stack clients are stacked vertically */ +// if ((i - m->nmaster) % 2 ) { +// h = (m->wh - ety) / ( (1 + n - i) / 2); +// resize(c, m->wx, m->wy + ety, tw - (2*c->bw), +// h - (2*c->bw), 0); +// ety += HEIGHT(c); +// } else { +// h = (m->wh - oty) / ((1 + n - i) / 2); +// resize(c, m->wx + mx + mw, m->wy + oty, +// tw - (2*c->bw), h - (2*c->bw), 0); +// oty += HEIGHT(c); +// } +// } +// } + +// void +// centeredmaster(Monitor *m) +// { +// unsigned int i, n, h, mw, mx, my, oty, ety, tw; +// Client *c; +// int bx, by, bw, bh; + +// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +// if (n == 0) +// return; + +// getcenteredbox(m, n, &bx, &by, &bw, &bh); + +// mw = bw; +// mx = bx; +// tw = bw; + +// if (n > m->nmaster) { +// mw = m->nmaster ? (bw - 2 * gappx) * m->mfact : 0; +// tw = (bw - mw - 2 * gappx) / 2; +// if (n - m->nmaster > 1) +// mx = bx + tw + gappx; +// else +// tw = bw - mw - gappx; +// } + +// oty = 0; +// ety = 0; +// my = 0; +// for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { +// if (i < m->nmaster) { +// unsigned int nmat = MIN(n, m->nmaster); +// h = (bh - (nmat - 1) * gappx) / nmat; +// resize(c, mx, by + my, mw - (2*c->bw), h - (2*c->bw), 0); +// my += h + gappx; +// } else { +// unsigned int nstk = n - m->nmaster; +// unsigned int nside = (nstk + 1) / 2; +// if ((i - m->nmaster) % 2) { +// h = (bh - (nside - 1) * gappx) / nside; +// resize(c, bx, by + ety, tw - (2*c->bw), h - (2*c->bw), 0); +// ety += h + gappx; +// } else { +// h = (bh - (nside - 1) * gappx) / nside; +// resize(c, mx + mw + gappx, by + oty, tw - (2*c->bw), h - (2*c->bw), 0); +// oty += h + gappx; +// } +// } +// } +// } + +void +centeredmaster(Monitor *m) +{ + unsigned int i, h, mw, mx, my, oty, ety, tw; + Client *c; + int bx, by, bw, bh; + unsigned int nt; + + get_layout_box(m, 0, &nt, &bx, &by, &bw, &bh); + if (nt == 0) + return; + + int g = gappx; + mw = bw; + mx = bx; + tw = bw; + + if (nt > m->nmaster) { + mw = m->nmaster ? (bw - 2 * g) * m->mfact : 0; + tw = (bw - mw - 2 * g) / 2; + if (nt - m->nmaster > 1) + mx = bx + tw + g; + else + tw = bw - mw - g; + } + + oty = 0; + ety = 0; + my = 0; + for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!(c->tags & m->tagset[m->seltags]) || c->isSidePanel) + continue; + + if (i < m->nmaster) { + unsigned int nmat = MIN(nt, m->nmaster); + h = (bh - (nmat - 1) * g) / nmat; + resize(c, mx, by + my, mw - (2 * c->bw), h - (2 * c->bw), 0); + my += h + g; + } else { + unsigned int nstk = nt - m->nmaster; + unsigned int nside = (nstk + 1) / 2; + if ((i - m->nmaster) % 2) { + h = (bh - (nside - 1) * g) / nside; + resize(c, bx, by + ety, tw - (2 * c->bw), h - (2 * c->bw), 0); + ety += h + g; + } else { + h = (bh - (nside - 1) * g) / nside; + resize(c, mx + mw + g, by + oty, tw - (2 * c->bw), h - (2 * c->bw), 0); + oty += h + g; + } + } + i++; + } +} + +// void +// centeredfloatingmaster(Monitor *m) +// { +// unsigned int i, n, w, mh, mw, mx, mxo, my, myo, tx; +// Client *c; + +// /* count number of clients in the selected monitor */ +// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +// if (n == 0) +// return; + +// /* initialize nmaster area */ +// if (n > m->nmaster) { +// /* go mfact box in the center if more than nmaster clients */ +// if (m->ww > m->wh) { +// mw = m->nmaster ? m->ww * m->mfact : 0; +// mh = m->nmaster ? m->wh * 0.9 : 0; +// } else { +// mh = m->nmaster ? m->wh * m->mfact : 0; +// mw = m->nmaster ? m->ww * 0.9 : 0; +// } +// mx = mxo = (m->ww - mw) / 2; +// my = myo = (m->wh - mh) / 2; +// } else { +// /* go fullscreen if all clients are in the master area */ +// mh = m->wh; +// mw = m->ww; +// mx = mxo = 0; +// my = myo = 0; +// } + +// for(i = tx = 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 */ +// w = (mw + mxo - mx) / (MIN(n, m->nmaster) - i); +// resize(c, m->wx + mx, m->wy + my, w - (2*c->bw), +// mh - (2*c->bw), 0); +// mx += WIDTH(c); +// } else { +// /* stack clients are stacked horizontally */ +// w = (m->ww - tx) / (n - i); +// resize(c, m->wx + tx, m->wy, w - (2*c->bw), +// m->wh - (2*c->bw), 0); +// tx += WIDTH(c); +// } +// } + +// void +// centeredfloatingmaster(Monitor *m) +// { +// unsigned int i, n, w, tx; +// Client *c; +// int bx, by, bw, bh; + +// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +// if (n == 0) +// return; + +// getcenteredbox(m, 1, &bx, &by, &bw, &bh); + +// for (i = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { +// if (i < m->nmaster) { +// unsigned int nmat = MIN(n, m->nmaster); +// w = (bw - (nmat - 1) * gappx) / nmat; +// resize(c, bx + i * (w + gappx), by, w - (2*c->bw), bh - (2*c->bw), 0); +// } else { +// unsigned int nstk = n - m->nmaster; +// w = (bw - tx) / nstk; +// resize(c, bx + tx, by, w - (2*c->bw), bh - (2*c->bw), 0); +// tx += w + gappx; +// } +// } +// } + +void +centeredfloatingmaster(Monitor *m) +{ + unsigned int i, w, tx; + Client *c; + int bx, by, bw, bh; + unsigned int nt; + + get_layout_box(m, 1, &nt, &bx, &by, &bw, &bh); + if (nt == 0) + return; + + int g = gappx; + + for (i = tx = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!(c->tags & m->tagset[m->seltags]) || c->isSidePanel) + continue; + + if (i < m->nmaster) { + unsigned int nmat = MIN(nt, m->nmaster); + w = (bw - (nmat - 1) * g) / nmat; + resize(c, bx + i * (w + g), by, w - (2 * c->bw), bh - (2 * c->bw), 0); + } else { + unsigned int nstk = nt - m->nmaster; + w = (bw - tx) / nstk; + resize(c, bx + tx, by, w - (2 * c->bw), bh - (2 * c->bw), 0); + tx += w + g; + } + i++; + } +} + +static char stext[256]; +static int screen; +static int sw, sh; /* X display screen geometry width, height */ +static int bh; /* bar height */ +static int lrpad; /* sum of left and right padding for text */ +static int (*xerrorxlib)(Display *, XErrorEvent *); +static unsigned int numlockmask = 0; +static void (*handler[LASTEvent]) (XEvent *) = { + [ButtonPress] = buttonpress, + [ClientMessage] = clientmessage, + [ConfigureRequest] = configurerequest, + [ConfigureNotify] = configurenotify, + [DestroyNotify] = destroynotify, + [EnterNotify] = enternotify, + [Expose] = expose, + [FocusIn] = focusin, + [KeyPress] = keypress, + [MappingNotify] = mappingnotify, + [MapRequest] = maprequest, + [MotionNotify] = motionnotify, + [PropertyNotify] = propertynotify, + [UnmapNotify] = unmapnotify +}; +static Atom wmatom[WMLast], netatom[NetLast]; +static int running = 1; +static Cur *cursor[CurLast]; +static Clr **scheme; +static Display *dpy; +static Drw *drw; +static Monitor *mons, *selmon; +static Window root, wmcheckwin; + +/* 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 */ + Client *selclients[LENGTH(tags) + 1]; /* save selected window */ +}; + +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + +/* function implementations */ +void +applyrules(Client *c) +{ + const char *class, *instance; + unsigned int i; + const Rule *r; + Monitor *m; + XClassHint ch = { NULL, NULL }; + + /* rule matching */ + c->isfloating = 0; + c->iscentered = 0; + c->tags = 0; + c->resizehints = 0; + c->isSidePanel = 0; + XGetClassHint(dpy, c->win, &ch); + class = ch.res_class ? ch.res_class : broken; + instance = ch.res_name ? ch.res_name : broken; + + + + // for (i = 0; i < LENGTH(rules); i++) { + // r = &rules[i]; + // if ((!r->title || strstr(c->name, r->title)) + // && (!r->class || strstr(class, r->class)) + // && (!r->instance || strstr(instance, r->instance))) + // { + // c->isfloating = r->isfloating; + // c->CenterThisWindow = r->CenterThisWindow; + // c->tags |= r->tags; + // c->resizehints = r->resizehints; + // for (m = mons; m && m->num != r->monitor; m = m->next); + // if (m) + // c->mon = m; + // } + // } + + /* In dwm.c, inside applyrules(Client *c) */ + for (i = 0; i < LENGTH(rules); i++) { + r = &rules[i]; + if ((!r->title || strstr(c->name, r->title)) + && (!r->class || strstr(class, r->class)) + && (!r->instance || strstr(instance, r->instance))) + { + c->isfloating = r->isfloating; + c->tags |= r->tags; + c->resizehints = r->resizehints; + c->iscentered = r->iscentered; + c->isSidePanel = r->isSidePanel; + + /* --- THE PERMANENT FIX --- */ + /* If this is the calculator, force the dimensions NOW. + This happens before manage() or tile() ever run. */ + /* + if (r->title && strcmp(r->title, "calculator") == 0) { + c->minw = c->maxw = 602; + c->minh = c->maxh = 450; + c->resizehints = 1; // Double-ensure this is set + // printf("MATCHED: Hardcoding calculator dimensions now.\n"); + } + */ + /* ------------------------- */ + + for (m = mons; m && m->num != r->monitor; m = m->next); + if (m) + c->mon = m; + } + } + + if (ch.res_class) + XFree(ch.res_class); + if (ch.res_name) + XFree(ch.res_name); + c->tags = c->tags & TAGMASK ? c->tags & TAGMASK : c->mon->tagset[c->mon->seltags]; +} + +int +applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact) +{ + int baseismin; + Monitor *m = c->mon; + + /* set minimum possible */ + *w = MAX(1, *w); + *h = MAX(1, *h); + if (interact) { + if (*x > sw) + *x = sw - WIDTH(c); + if (*y > sh) + *y = sh - HEIGHT(c); + if (*x + *w + 2 * c->bw < 0) + *x = 0; + if (*y + *h + 2 * c->bw < 0) + *y = 0; + } else { + if (*x >= m->wx + m->ww) + *x = m->wx + m->ww - WIDTH(c); + if (*y >= m->wy + m->wh) + *y = m->wy + m->wh - HEIGHT(c); + if (*x + *w + 2 * c->bw <= m->wx) + *x = m->wx; + if (*y + *h + 2 * c->bw <= m->wy) + *y = m->wy; + } + if (*h < bh) + *h = bh; + if (*w < bh) + *w = bh; + if (c->resizehints || c->isfloating || !c->mon->lt[c->mon->sellt]->arrange) { + if (!c->hintsvalid) + updatesizehints(c); + /* see last two sentences in ICCCM 4.1.2.3 */ + baseismin = c->basew == c->minw && c->baseh == c->minh; + if (!baseismin) { /* temporarily remove base dimensions */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for aspect limits */ + if (c->mina > 0 && c->maxa > 0) { + if (c->maxa < (float)*w / *h) + *w = *h * c->maxa + 0.5; + else if (c->mina < (float)*h / *w) + *h = *w * c->mina + 0.5; + } + if (baseismin) { /* increment calculation requires this */ + *w -= c->basew; + *h -= c->baseh; + } + /* adjust for increment value */ + if (c->incw) + *w -= *w % c->incw; + if (c->inch) + *h -= *h % c->inch; + /* restore base dimensions */ + *w = MAX(*w + c->basew, c->minw); + *h = MAX(*h + c->baseh, c->minh); + if (c->maxw) + *w = MIN(*w, c->maxw); + if (c->maxh) + *h = MIN(*h, c->maxh); + } + return *x != c->x || *y != c->y || *w != c->w || *h != c->h; +} + +// void +// arrange(Monitor *m) +// { +// if (m) +// showhide(m->stack); +// else for (m = mons; m; m = m->next) +// showhide(m->stack); +// if (m) { +// arrangemon(m); +// restack(m); +// } else for (m = mons; m; m = m->next) +// arrangemon(m); +// } + +void +arrange(Monitor *m) +{ + if (m) + showhide(m->stack); + else + for (m = mons; m; m = m->next) + showhide(m->stack); + + /* Update global Alacritty opacity based on active layout */ + update_alacritty_opacity(m ? m : selmon); + + if (m) { + arrangemon(m); + restack(m); + } else { + for (m = mons; m; m = m->next) + arrangemon(m); + } +} + +void +arrangemon(Monitor *m) +{ + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof m->ltsymbol); + if (m->lt[m->sellt]->arrange) + m->lt[m->sellt]->arrange(m); +} + +void +attach(Client *c) +{ + c->next = c->mon->clients; + c->mon->clients = c; +} + +void +attachstack(Client *c) +{ + c->snext = c->mon->stack; + c->mon->stack = c; +} + +void +buttonpress(XEvent *e) +{ + unsigned int i, x, click; + Arg arg = {0}; + Client *c; + Monitor *m; + XButtonPressedEvent *ev = &e->xbutton; + + click = ClkRootWin; + /* focus monitor if necessary */ + if ((m = wintomon(ev->window)) && m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + if (ev->window == m->barwin) { + i = x = 0; + char dynamic_tag[256]; + + /* Loop through tags and calculate their REAL dynamic width */ + do { + gettagtext(m, i, dynamic_tag, sizeof(dynamic_tag)); + /* Use the same logic as drawbar: num_w + text_w */ + unsigned int num_w = TEXTW(tags[i]); + unsigned int text_w = (dynamic_tag[0] != '\0') ? TEXTW(dynamic_tag) : 0; + x += num_w + text_w; + } while (ev->x >= x && ++i < LENGTH(tags)); + + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(m->ltsymbol)) + click = ClkLtSymbol; + else if (ev->x > selmon->ww - (int)TEXTW(stext)) + click = ClkStatusText; + else + click = ClkWinTitle; + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); + XAllowEvents(dpy, ReplayPointer, CurrentTime); + click = ClkClientWin; + } + for (i = 0; i < LENGTH(buttons); i++) + if (click == buttons[i].click && buttons[i].func && buttons[i].button == ev->button + && CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(click == ClkTagBar && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); +} + +void +checkotherwm(void) +{ + xerrorxlib = XSetErrorHandler(xerrorstart); + /* this causes an error if some other window manager is running */ + XSelectInput(dpy, DefaultRootWindow(dpy), SubstructureRedirectMask); + XSync(dpy, False); + XSetErrorHandler(xerror); + XSync(dpy, False); +} + +void +cleanup(void) +{ + Arg a = {.ui = ~0}; + Layout foo = { "", NULL }; + Monitor *m; + size_t i; + + view(&a); + selmon->lt[selmon->sellt] = &foo; + for (m = mons; m; m = m->next) + while (m->stack) + unmanage(m->stack, 0); + XUngrabKey(dpy, AnyKey, AnyModifier, root); + while (mons) + cleanupmon(mons); + for (i = 0; i < CurLast; i++) + drw_cur_free(drw, cursor[i]); + for (i = 0; i < LENGTH(colors); i++) + drw_scm_free(drw, scheme[i], 3); + free(scheme); + XDestroyWindow(dpy, wmcheckwin); + drw_free(drw); + XSync(dpy, False); + XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +} + +void +cleanupmon(Monitor *mon) +{ + Monitor *m; + unsigned int i; + + if (mon == mons) + mons = mons->next; + else { + for (m = mons; m && m->next != mon; m = m->next); + m->next = mon->next; + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); + for (i = 0; i <= LENGTH(tags); i++) { + free(mon->tags[i]); + } + free(mon->tags); + free(mon); +} + +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *cme = &e->xclient; + Client *c = wintoclient(cme->window); + + if (!c) + return; + if (cme->message_type == netatom[NetWMState]) { + if (cme->data.l[1] == netatom[NetWMFullscreen] + || 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))); + } else if (cme->message_type == netatom[NetActiveWindow]) { + if (c != selmon->sel && !c->isurgent) + seturgent(c, 1); + } +} + +void +configure(Client *c) +{ + XConfigureEvent ce; + + ce.type = ConfigureNotify; + ce.display = dpy; + ce.event = c->win; + ce.window = c->win; + ce.x = c->x; + ce.y = c->y; + ce.width = c->w; + ce.height = c->h; + ce.border_width = c->bw; + ce.above = None; + ce.override_redirect = False; + XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce); +} + +void +configurenotify(XEvent *e) +{ + Monitor *m; + Client *c; + XConfigureEvent *ev = &e->xconfigure; + int dirty; + + /* TODO: updategeom handling sucks, needs to be simplified */ + if (ev->window == root) { + dirty = (sw != ev->width || sh != ev->height); + sw = ev->width; + sh = ev->height; + if (updategeom() || dirty) { + drw_resize(drw, sw, bh); + updatebars(); + for (m = mons; m; m = m->next) { + 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); + } + focus(NULL); + arrange(NULL); + } + } +} + +void +configurerequest(XEvent *e) +{ + Client *c; + Monitor *m; + XConfigureRequestEvent *ev = &e->xconfigurerequest; + XWindowChanges wc; + + if ((c = wintoclient(ev->window))) { + if (ev->value_mask & CWBorderWidth) + c->bw = ev->border_width; + else if (c->isfloating || !selmon->lt[selmon->sellt]->arrange) { + m = c->mon; + if (ev->value_mask & CWX) { + c->oldx = c->x; + c->x = m->mx + ev->x; + } + if (ev->value_mask & CWY) { + c->oldy = c->y; + c->y = m->my + ev->y; + } + if (ev->value_mask & CWWidth) { + c->oldw = c->w; + c->w = ev->width; + } + if (ev->value_mask & CWHeight) { + c->oldh = c->h; + c->h = ev->height; + } + if ((c->x + c->w) > m->mx + m->mw && c->isfloating) + c->x = m->mx + (m->mw / 2 - WIDTH(c) / 2); /* center in x direction */ + if ((c->y + c->h) > m->my + m->mh && c->isfloating) + c->y = m->my + (m->mh / 2 - HEIGHT(c) / 2); /* center in y direction */ + if ((ev->value_mask & (CWX|CWY)) && !(ev->value_mask & (CWWidth|CWHeight))) + configure(c); + if (ISVISIBLE(c)) + XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h); + } else + configure(c); + } else { + wc.x = ev->x; + wc.y = ev->y; + wc.width = ev->width; + wc.height = ev->height; + wc.border_width = ev->border_width; + wc.sibling = ev->above; + wc.stack_mode = ev->detail; + XConfigureWindow(dpy, ev->window, ev->value_mask, &wc); + } + XSync(dpy, False); +} + +// Monitor * +// createmon(void) +// { +// Monitor *m; +// unsigned int i; + +// m = ecalloc(1, sizeof(Monitor)); +// m->tagset[0] = m->tagset[1] = 1; +// m->mfact = mfact; +// m->center_mfact = center_mfact; +// m->nmaster = nmaster; +// m->showbar = showbar; +// m->topbar = topbar; +// 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; + +// m->tags = ecalloc(LENGTH(tags), sizeof(char) * (MAX_TAGNAME_LEN + 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; +// m->tags[i] = ecalloc(1, sizeof(char) * (MAX_TAGNAME_LEN + 1)); +// m->pertag->selclients[i] = NULL; +// memset(m->tags[i], 0, MAX_TAGNAME_LEN); +// memcpy(m->tags[i], tags[i], sizeof(char) * strlen(tags[i])); +// } + + +// return m; +// } + +Monitor * +createmon(void) +{ + Monitor *m; + unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; + m->mfact = mfact; + m->hfact = centered_height; + m->cwfact = centered_width; + // m->center_mfact = center_mfact; + m->nmaster = nmaster; + m->showbar = showbar; + m->topbar = topbar; + 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; + + /* Ensure this is large enough to hold all tags */ + m->tags = ecalloc(LENGTH(tags), sizeof(char *)); + + 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; + + /* INITIALIZE FOCUS HISTORY */ + m->pertag->selclients[i] = NULL; + + /* SAFETY: Only allocate/copy tags for valid tag indices (0 to LENGTH-1) */ + if (i < LENGTH(tags)) { + m->tags[i] = ecalloc(1, sizeof(char) * (MAX_TAGNAME_LEN + 1)); + strncpy(m->tags[i], tags[i], MAX_TAGNAME_LEN); + } + } + + return m; +} + +void +destroynotify(XEvent *e) +{ + Client *c; + XDestroyWindowEvent *ev = &e->xdestroywindow; + + if ((c = wintoclient(ev->window))) + unmanage(c, 1); +} + +void +detach(Client *c) +{ + Client **tc; + + for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next); + *tc = c->next; +} + +void +detachstack(Client *c) +{ + Client **tc, *t; + + for (tc = &c->mon->stack; *tc && *tc != c; tc = &(*tc)->snext); + *tc = c->snext; + + if (c == c->mon->sel) { + for (t = c->mon->stack; t && !ISVISIBLE(t); t = t->snext); + c->mon->sel = t; + } +} + +Monitor * +dirtomon(int dir) +{ + Monitor *m = NULL; + + if (dir > 0) { + if (!(m = selmon->next)) + m = mons; + } else if (selmon == mons) + for (m = mons; m->next; m = m->next); + else + for (m = mons; m->next != selmon; m = m->next); + return m; +} + +void +gettagtext(Monitor *m, int tag, char *text, unsigned int maxlen) +{ + Client *c; + int found = 0; + text[0] = '\0'; + + for (c = m->clients; c; c = c->next) { + if (c->tags & (1 << tag)) { + + /* Exclusion Filter */ + if ( + strcmp(c->name, "calculator") == 0 || + strcmp(c->name, "facecam") == 0 || + strcmp(c->name, "pulsemixer") == 0 || + strcmp(c->name, "htop") == 0 || + ((strcmp(c->class, "steam") == 0) && strcmp(c->name, "Friends List") == 0) || + (strcmp(c->class, "Xephyr") == 0) || + ((strcmp(c->class, "Blender") == 0) && strcmp(c->name, "Blender Render") == 0) + ) + continue; + + if (c->isSidePanel || c->isPopup) + continue; + + const char *icon = geticon(c->class); + + if (strstr(text, icon) != NULL) + continue; + + const char *sep = found ? " │ " : ""; + + /* Append Icon + Class */ + if (strcmp(icon, "") == 0) { + strcat(text, sep); + strcat(text, icon); + strcat(text, " "); /* Space between icon and name */ + strcat(text, c->class); + found = 1; + }else if (strlen(text) + strlen(sep) + strlen(icon) + strlen(c->class) + 1 < maxlen - 1) { + strcat(text, sep); + strcat(text, icon); + // strcat(text, " "); /* Space between icon and name */ + // strcat(text, c->class); + found = 1; + } + } + } +} + +void +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; + + if (!m->showbar) + return; + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + 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); + } + + for (c = m->clients; c; c = c->next) { + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + char dynamic_tag[256]; + char num_buf[16]; + + for (i = 0; i < LENGTH(tags); i++) { + gettagtext(m, i, dynamic_tag, sizeof(dynamic_tag)); + snprintf(num_buf, sizeof(num_buf), "%s", tags[i]); + + unsigned int num_w = TEXTW(num_buf); + unsigned int text_w = (dynamic_tag[0] != '\0') ? TEXTW(dynamic_tag) : 0; + w = num_w + text_w; + + /* 1. Draw the number with your custom schemes */ + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeTagNumSel : SchemeTagNum]); + drw_text(drw, x, 0, num_w, bh, lrpad / 2, num_buf, 0); + + /* 2. Draw the Class Names */ + if (text_w > 0) { + drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + + /* FIX: Pass 0 instead of 'urg & (1 << i)' to prevent background sticking */ + drw_text(drw, x + num_w, 0, text_w, bh, lrpad / 2, dynamic_tag, 0); + } + + /* Keep the rest of your loop (occupied squares, etc.) here */ + x += w; + } + + w = TEXTW(m->ltsymbol); + 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 (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); +} + +void +drawbars(void) +{ + Monitor *m; + + for (m = mons; m; m = m->next) + drawbar(m); +} + +// void +// enternotify(XEvent *e) +// { +// Client *c; +// Monitor *m; +// XCrossingEvent *ev = &e->xcrossing; + +// if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) +// return; +// c = wintoclient(ev->window); +// m = c ? c->mon : wintomon(ev->window); +// if (m != selmon) { +// unfocus(selmon->sel, 1); +// selmon = m; +// } else if (!c || c == selmon->sel) +// return; +// focus(c); +// } +// void +// enternotify(XEvent *e) +// { +// Client *c; +// Monitor *m; +// XCrossingEvent *ev = &e->xcrossing; + +// if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) +// return; + +// c = wintoclient(ev->window); +// m = c ? c->mon : wintomon(ev->window); + +// if (m != selmon) { +// unfocus(selmon->sel, 1); +// selmon = m; +// } else if (!c || c == selmon->sel) { +// return; +// } + +// /* FACECAM BYPASS: If the mouse enters the Facecam, do not focus it. */ +// if (c && IS_FACECAM(c)) +// return; + +// focus(c); +// } +void +enternotify(XEvent *e) +{ + Client *c; + Monitor *m; + XCrossingEvent *ev = &e->xcrossing; + + if ((ev->mode != NotifyNormal || ev->detail == NotifyInferior) && ev->window != root) + return; + c = wintoclient(ev->window); + m = c ? c->mon : wintomon(ev->window); + if (m != selmon) { + unfocus(selmon->sel, 1); + selmon = m; + } else if (!c || c == selmon->sel) + return; + + /* ALLOW mouse focus on Facecam */ + /* We call focus(c) directly here. Since focus(c) contains our redirection logic, + we need to make sure focus() knows that this specific call is intentional. */ + focus(c); +} + +void +expose(XEvent *e) +{ + Monitor *m; + XExposeEvent *ev = &e->xexpose; + + if (ev->count == 0 && (m = wintomon(ev->window))) + drawbar(m); +} + +// void +// focus(Client *c) +// { +// Client *i; + +// if (!c || !ISVISIBLE(c)) +// for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +// if (selmon->sel && selmon->sel != c) +// unfocus(selmon->sel, 0); +// if (c) { +// if (c->mon != selmon) +// selmon = c->mon; +// if (c->isurgent) +// seturgent(c, 0); +// detachstack(c); +// attachstack(c); +// grabbuttons(c, 1); +// XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); +// setfocus(c); +// } else { +// XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); +// XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +// } +// /* ADD THIS: Clean up ghost borders on Facecam/Fixed windows */ +// for (i = selmon->clients; i; i = i->next) { +// if (ISVISIBLE(i) && i != c && i->resizehints) { +// XSetWindowBorder(dpy, i->win, scheme[SchemeNorm][ColBorder].pixel); +// } +// } +// selmon->sel = c; +// drawbars(); +// } +// void +// focus(Client *c) +// { +// /* 1. If we are trying to focus a Facecam, and there's a better option, +// redirect focus to the next available window instead. */ +// if (c && IS_FACECAM(c)) { +// Client *n; +// for (n = selmon->stack; n && (n == c || !ISVISIBLE(n) || IS_FACECAM(n)); n = n->snext); +// if (n) c = n; +// /* If 'n' is NULL, it means only the Facecam exists, so we let it through */ +// } + +// if (!c || !ISVISIBLE(c)) +// for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + +// if (selmon->sel && selmon->sel != c) +// unfocus(selmon->sel, 0); + +// if (c) { +// if (c->mon != selmon) +// selmon = c->mon; +// detachstack(c); +// attachstack(c); +// grabbuttons(c, 1); +// XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); +// setfocus(c); +// } else { +// XSetInputFocus(dpy, root, RevertToParent, CurrentTime); +// XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +// } +// selmon->sel = c; +// drawbars(); +// } +// void +// focus(Client *c) +// { +// /* If we are trying to focus a Facecam, redirect to the next best thing */ +// if (c && IS_FACECAM(c)) { +// Client *n; +// /* Look in the stack for the first visible window that isn't a Facecam */ +// for (n = selmon->stack; n && (!ISVISIBLE(n) || IS_FACECAM(n)); n = n->snext); +// if (n) { +// c = n; // Found a better candidate (like your Terminal) +// } else { +// /* If ONLY Facecams exist, we have to let it focus or dwm hangs */ +// if (c == selmon->sel) return; +// } +// } + +// if (!c || !ISVISIBLE(c)) +// for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + +// if (selmon->sel && selmon->sel != c) +// unfocus(selmon->sel, 0); + +// if (c) { +// if (c->mon != selmon) +// selmon = c->mon; +// detachstack(c); +// attachstack(c); +// grabbuttons(c, 1); +// XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); +// setfocus(c); +// } else { +// XSetInputFocus(dpy, root, RevertToParent, CurrentTime); +// XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +// } +// selmon->sel = c; +// drawbars(); +// } +// void +// focus(Client *c) +// { +// /* 1. Redirect logic: If we're targeting a Facecam... */ +// if (c && IS_FACECAM(c)) { +// Client *n; +// /* ...look for any visible window that is NOT a Facecam */ +// for (n = selmon->stack; n && (!ISVISIBLE(n) || IS_FACECAM(n)); n = n->snext); + +// if (n) { +// c = n; /* Found a terminal/calculator! Focus that instead. */ +// } else { +// /* If we are here, Facecam is literally the only visible window. +// * We allow it to be focused so DWM doesn't break, but we stop +// * unnecessary recursion if it's already selected. */ +// if (c == selmon->sel) +// return; +// } +// } + +// /* 2. Standard DWM Fallback: If c is null/invisible, find the top of the stack */ +// if (!c || !ISVISIBLE(c)) { +// for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +// } + +// if (selmon->sel && selmon->sel != c) +// unfocus(selmon->sel, 0); + +// if (c) { +// if (c->mon != selmon) +// selmon = c->mon; +// detachstack(c); +// attachstack(c); +// grabbuttons(c, 1); +// XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); +// setfocus(c); +// } else { +// XSetInputFocus(dpy, root, RevertToParent, CurrentTime); +// XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +// } +// selmon->sel = c; +// drawbars(); +// } +// void +// focus(Client *c) +// { +// /* If we are trying to focus a Facecam... */ +// if (c && IS_FACECAM(c)) { +// Client *n; +// /* Find a non-facecam window in the stack */ +// for (n = selmon->stack; n && (n == c || !ISVISIBLE(n) || IS_FACECAM(n)); n = n->snext); + +// if (n) { +// c = n; /* Redirect to the terminal/calculator */ +// } else if (c == selmon->sel && ISVISIBLE(c)) { +// /* If it's already focused and the only window, leave it alone */ +// return; +// } +// } + +// /* Standard dwm visibility check */ +// if (!c || !ISVISIBLE(c)) +// for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + +// if (selmon->sel && selmon->sel != c) +// unfocus(selmon->sel, 0); + +// if (c) { +// if (c->mon != selmon) +// selmon = c->mon; +// detachstack(c); +// attachstack(c); +// grabbuttons(c, 1); +// XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); +// setfocus(c); +// } else { +// XSetInputFocus(dpy, root, RevertToParent, CurrentTime); +// XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +// } +// selmon->sel = c; +// drawbars(); +// } +// void +// focus(Client *c) +// { +// /* 1. If targeting a Facecam, try to find a better candidate */ +// if (c && IS_FACECAM(c)) { +// Client *n; +// for (n = selmon->stack; n && (n == c || !ISVISIBLE(n) || IS_FACECAM(n)); n = n->snext); +// if (n) { +// c = n; +// } else { +// /* If Facecam is the ONLY window and we aren't already focusing it, +// return early to prevent it from grabbing focus by default */ +// if (selmon->sel != c) { +// /* Optional: unfocus current if you want the screen to have NO focus */ +// return; +// } +// } +// } + +// /* 2. Standard fallback: if c is NULL, find the first visible window */ +// if (!c || !ISVISIBLE(c)) { +// for (c = selmon->stack; c && (!ISVISIBLE(c) || IS_FACECAM(c)); c = c->next); +// /* If only Facecams exist, c will be a Facecam here. +// To prevent auto-focusing it, we null it out again. */ +// if (c && IS_FACECAM(c)) +// c = NULL; +// } + +// if (selmon->sel && selmon->sel != c) +// unfocus(selmon->sel, 0); + +// if (c) { +// if (c->mon != selmon) +// selmon = c->mon; +// detachstack(c); +// attachstack(c); +// grabbuttons(c, 1); +// XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); +// setfocus(c); +// selmon->sel = c; +// } else { +// XSetInputFocus(dpy, root, RevertToParent, CurrentTime); +// XDeleteProperty(dpy, root, netatom[NetActiveWindow]); +// selmon->sel = NULL; +// } +// drawbars(); +// } +void +focus(Client *c) +{ + /* 1. Redirect logic: ONLY redirect if c is NULL (auto-focusing) + or if we are jumping tags. If c is explicitly provided + (like from a mouse click or enter event), we allow it. */ + + if (!c || !ISVISIBLE(c)) { + /* Find the top of the stack that isn't a Facecam */ + for (c = selmon->stack; c && (!ISVISIBLE(c) || IS_FACECAM(c)); c = c->snext); + + /* If literally only Facecams are left, just stay NULL to avoid auto-focus */ + if (c && IS_FACECAM(c)) + c = NULL; + } + + /* ... rest of your focus function as previously written ... */ + if (selmon->sel && selmon->sel != c) + unfocus(selmon->sel, 0); + + if (c) { + if (c->mon != selmon) + selmon = c->mon; + detachstack(c); + attachstack(c); + grabbuttons(c, 1); + XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel); + c->isurgent = 0; + setfocus(c); + } else { + XSetInputFocus(dpy, root, RevertToParent, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } + selmon->sel = c; + drawbars(); +} + +/* there are some broken focus acquiring clients needing extra handling */ +void +focusin(XEvent *e) +{ + XFocusChangeEvent *ev = &e->xfocus; + + if (selmon->sel && ev->window != selmon->sel->win) + setfocus(selmon->sel); +} + +void +focusmon(const Arg *arg) +{ + Monitor *m; + + if (!mons->next) + return; + if ((m = dirtomon(arg->i)) == selmon) + return; + unfocus(selmon->sel, 0); + selmon = m; + focus(NULL); +} + +// void +// focusstack(const Arg *arg) +// { +// Client *c = NULL, *i; + +// if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) +// return; +// if (arg->i > 0) { +// for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); +// if (!c) +// for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); +// } else { +// for (i = selmon->clients; i != selmon->sel; i = i->next) +// if (ISVISIBLE(i)) +// c = i; +// if (!c) +// for (; i; i = i->next) +// if (ISVISIBLE(i)) +// c = i; +// } +// if (c) { +// focus(c); +// restack(selmon); +// } +// } +void +focusstack(const Arg *arg) +{ + Client *c = NULL, *i; + /* Helper to check if a window should be skipped in navigation */ + #define ISSKIPPABLE(X) (X->resizehints && (!X->name || strcmp(X->name, "calculator") != 0)) + + if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) + return; + + if (arg->i > 0) { + /* Focus Next */ + for (c = selmon->sel->next; c && (!ISVISIBLE(c) || ISSKIPPABLE(c)); c = c->next); + if (!c) + for (c = selmon->clients; c && (!ISVISIBLE(c) || ISSKIPPABLE(c)); c = c->next); + } else { + /* Focus Previous */ + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i) && !ISSKIPPABLE(i)) + c = i; + if (!c) + for (; i; i = i->next) + if (ISVISIBLE(i) && !ISSKIPPABLE(i)) + c = i; + } + + if (c) { + focus(c); + restack(selmon); + } +} + +Atom +getatomprop(Client *c, Atom prop) +{ + int di; + unsigned long dl; + unsigned char *p = NULL; + Atom da, atom = None; + + if (XGetWindowProperty(dpy, c->win, prop, 0L, sizeof atom, False, XA_ATOM, + &da, &di, &dl, &dl, &p) == Success && p) { + atom = *(Atom *)p; + XFree(p); + } + return atom; +} + +int +getrootptr(int *x, int *y) +{ + int di; + unsigned int dui; + Window dummy; + + return XQueryPointer(dpy, root, &dummy, &dummy, x, y, &di, &di, &dui); +} + +long +getstate(Window w) +{ + int format; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + if (XGetWindowProperty(dpy, w, wmatom[WMState], 0L, 2L, False, wmatom[WMState], + &real, &format, &n, &extra, (unsigned char **)&p) != Success) + return -1; + if (n != 0) + result = *p; + XFree(p); + return result; +} + +int +gettextprop(Window w, Atom atom, char *text, unsigned int size) +{ + char **list = NULL; + int n; + XTextProperty name; + + if (!text || size == 0) + return 0; + text[0] = '\0'; + if (!XGetTextProperty(dpy, w, &name, atom) || !name.nitems) + return 0; + if (name.encoding == XA_STRING) { + strncpy(text, (char *)name.value, size - 1); + } else if (XmbTextPropertyToTextList(dpy, &name, &list, &n) >= Success && n > 0 && *list) { + strncpy(text, *list, size - 1); + XFreeStringList(list); + } + text[size - 1] = '\0'; + XFree(name.value); + return 1; +} + +void +grabbuttons(Client *c, int focused) +{ + updatenumlockmask(); + { + unsigned int i, j; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + if (!focused) + XGrabButton(dpy, AnyButton, AnyModifier, c->win, False, + BUTTONMASK, GrabModeSync, GrabModeSync, None, None); + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].click == ClkClientWin) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabButton(dpy, buttons[i].button, + buttons[i].mask | modifiers[j], + c->win, False, BUTTONMASK, + GrabModeAsync, GrabModeSync, None, None); + } +} + +void +grabkeys(void) +{ + updatenumlockmask(); + { + unsigned int i, j, k; + unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + int start, end, skip; + KeySym *syms; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + XDisplayKeycodes(dpy, &start, &end); + syms = XGetKeyboardMapping(dpy, start, end - start + 1, &skip); + if (!syms) + return; + for (k = start; k <= end; k++) + for (i = 0; i < LENGTH(keys); i++) + /* skip modifier codes, we do that ourselves */ + if (keys[i].keysym == syms[(k - start) * skip]) + for (j = 0; j < LENGTH(modifiers); j++) + XGrabKey(dpy, k, + keys[i].mod | modifiers[j], + root, True, + GrabModeAsync, GrabModeAsync); + XFree(syms); + } +} + +void +incnmaster(const Arg *arg) +{ + selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); +} + +#ifdef XINERAMA +static int +isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) +{ + while (n--) + if (unique[n].x_org == info->x_org && unique[n].y_org == info->y_org + && unique[n].width == info->width && unique[n].height == info->height) + return 0; + return 1; +} +#endif /* XINERAMA */ + +void +keypress(XEvent *e) +{ + unsigned int i; + KeySym keysym; + XKeyEvent *ev; + + ev = &e->xkey; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); + for (i = 0; i < LENGTH(keys); i++) + if (keysym == keys[i].keysym + && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) + && keys[i].func) + keys[i].func(&(keys[i].arg)); +} + +void +killclient(const Arg *arg) +{ + if (!selmon->sel) + return; + if (!sendevent(selmon->sel, wmatom[WMDelete])) { + XGrabServer(dpy); + XSetErrorHandler(xerrordummy); + XSetCloseDownMode(dpy, DestroyAll); + XKillClient(dpy, selmon->sel->win); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } +} + +// void +// manage(Window w, XWindowAttributes *wa) +// { +// Client *c, *t = NULL; +// Window trans = None; +// XWindowChanges wc; + +// c = ecalloc(1, sizeof(Client)); +// c->win = w; + +// /* 1. Initial Geometry */ +// c->x = c->oldx = wa->x; +// c->y = c->oldy = wa->y; +// c->w = c->oldw = wa->width; +// c->h = c->oldh = wa->height; +// c->oldbw = wa->border_width; + +// updatetitle(c); + +// /* 2. Apply Rules & Hardcoded Dimensions */ +// if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +// c->mon = t->mon; +// c->tags = t->tags; +// } else { +// c->mon = selmon; +// applyrules(c); // This sets c->minw/maxw to 602 if it matches "calculator" +// } + +// /* 3. Geometric Constraints (Don't let window spawn off-screen) */ +// if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) +// c->x = c->mon->wx + c->mon->ww - WIDTH(c); +// if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) +// c->y = c->mon->wy + c->mon->wh - HEIGHT(c); +// c->x = MAX(c->x, c->mon->wx); +// c->y = MAX(c->y, c->mon->wy); +// c->bw = borderpx; + +// wc.border_width = c->bw; +// XConfigureWindow(dpy, w, CWBorderWidth, &wc); +// XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); +// configure(c); +// updatewindowtype(c); + +// /* 4. Update Hints ONLY if not hardcoded by applyrules */ +// if (c->minw == 0 && c->minh == 0) { +// updatesizehints(c); +// } + +// /* 5. Set Fixed State & Floating State */ +// c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + +// if (c->resizehints) { +// c->isfixed = 1; // It's the calculator/facecam +// c->isfloating = 0; // Force it to tile +// } else { +// c->isfloating = c->oldstate = trans != None || c->isfixed; +// } + +// /* 6. Standard X11 setup */ +// updatewmhints(c); +// XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); +// grabbuttons(c, 0); + +// if (c->isfloating) +// XRaiseWindow(dpy, c->win); + +// attach(c); +// attachstack(c); +// XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, +// (unsigned char *) &(c->win), 1); + +// if (IS_FACECAM(c)) { +// /* If the new window is Facecam, don't focus it. +// Instead, keep focus on whatever was already focused. */ +// focus(NULL); +// } else { +// /* Normal behavior for calculators and other apps */ +// unfocus(selmon->sel, 0); +// selmon->sel = c; +// focus(c); +// } + +// /* Move out of view before mapping to prevent flicker */ +// XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); +// setclientstate(c, NormalState); + +// if (c->mon == selmon) +// unfocus(selmon->sel, 0); +// c->mon->sel = c; + +// /* 7. Final Arrange & Map */ +// if (!c->isfloating) +// arrange(c->mon); +// else { +// showhide(c->mon->stack); +// restack(c->mon); +// } + +// XMapWindow(dpy, c->win); +// focus(NULL); +// } + +// void +// manage(Window w, XWindowAttributes *wa) +// { +// Client *c, *t = NULL; +// Window trans = None; +// XWindowChanges wc; + +// c = ecalloc(1, sizeof(Client)); +// c->win = w; + +// /* 1. Initial Geometry */ +// c->x = c->oldx = wa->x; +// c->y = c->oldy = wa->y; +// c->w = c->oldw = wa->width; +// c->h = c->oldh = wa->height; +// c->oldbw = wa->border_width; + +// updatetitle(c); + +// /* 2. Apply Rules & Hardcoded Dimensions */ +// if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { +// c->mon = t->mon; +// c->tags = t->tags; +// } else { +// c->mon = selmon; +// applyrules(c); +// } + +// /* 3. Geometric Constraints */ +// if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) +// c->x = c->mon->wx + c->mon->ww - WIDTH(c); +// if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) +// c->y = c->mon->wy + c->mon->wh - HEIGHT(c); +// c->x = MAX(c->x, c->mon->wx); +// c->y = MAX(c->y, c->mon->wy); +// c->bw = borderpx; + +// wc.border_width = c->bw; +// XConfigureWindow(dpy, w, CWBorderWidth, &wc); +// XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); +// configure(c); +// updatewindowtype(c); + +// /* 4. Update Hints ONLY if not hardcoded */ +// if (c->minw == 0 && c->minh == 0) { +// updatesizehints(c); +// } + +// /* 5. Set Fixed State & Floating State */ +// c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + +// if (c->resizehints) { +// c->isfixed = 1; +// c->isfloating = 0; +// } else { +// c->isfloating = c->oldstate = trans != None || c->isfixed; +// } + +// /* 6. Standard X11 setup */ +// updatewmhints(c); +// XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); +// grabbuttons(c, 0); + +// if (c->isfloating) +// XRaiseWindow(dpy, c->win); + +// attach(c); +// attachstack(c); +// XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, +// (unsigned char *) &(c->win), 1); + +// /* Move out of view before mapping to prevent flicker */ +// XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); +// setclientstate(c, NormalState); + +// /* 7. REFINED FOCUS LOGIC */ +// if (IS_FACECAM(c)) { +// /* Facecam logic: DO NOT update selmon->sel. +// Just arrange and map. The focus(NULL) below will +// re-assert focus on the existing selmon->sel. */ +// // unfocus(selmon->sel, 0); +// // selmon->sel = c; +// } else { +// /* Normal apps & Calculator logic: They take focus */ +// unfocus(selmon->sel, 0); +// selmon->sel = c; +// arrange(c->mon); +// } + +// XMapWindow(dpy, c->win); + +// /* Re-assert focus on selmon->sel (which we didn't change if it was a Facecam) */ +// focus(NULL); +// } +void +manage(Window w, XWindowAttributes *wa) +{ + Client *c, *t = NULL; + Window trans = None; + + c = ecalloc(1, sizeof(Client)); + XClassHint ch = { NULL, NULL }; + if (XGetClassHint(dpy, w, &ch)) { + snprintf(c->class, sizeof(c->class), "%s", ch.res_class ? ch.res_class : "broken"); + if (ch.res_class) XFree(ch.res_class); + if (ch.res_name) XFree(ch.res_name); + } + c->win = w; + + /* 1. Initial Geometry */ + c->x = c->oldx = wa->x; + c->y = c->oldy = wa->y; + c->w = c->oldw = wa->width; + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + + updatetitle(c); + + /* 2. Apply Rules & Hardcoded Dimensions */ + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; + c->tags = t->tags; + } else { + c->mon = selmon; + applyrules(c); + } + + /* 3. Geometric Constraints */ + if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww) + c->x = c->mon->wx + c->mon->ww - WIDTH(c); + if (c->y + HEIGHT(c) > c->mon->wy + c->mon->wh) + c->y = c->mon->wy + c->mon->wh - HEIGHT(c); + c->x = MAX(c->x, c->mon->wx); + c->y = MAX(c->y, c->mon->wy); + c->bw = borderpx; + + /* Removed XWindowChanges wc usage to fix compiler warning */ + XConfigureWindow(dpy, w, CWBorderWidth, &(XWindowChanges){.border_width = c->bw}); + XSetWindowBorder(dpy, w, scheme[SchemeNorm][ColBorder].pixel); + configure(c); + updatewindowtype(c); + + /* 4. Update Hints ONLY if not hardcoded */ + if (c->minw == 0 && c->minh == 0) { + updatesizehints(c); + } + + /* 5. Set Fixed State & Floating State */ + // c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + + /* 5. Set Fixed State & Floating State */ + // c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + + // /* Check if it's one of your 'Sidebar' items */ + // int is_sidebar = (c->name && (strcmp(c->name, "calculator") == 0 || strcmp(c->name, "facecam") == 0)); + + // if (is_sidebar) { + // c->isfixed = 1; + // c->isfloating = 0; // Force sidebar items to tile + // } else { + // /* For everything else, respect rules (transient or isfloating set by applyrules) */ + // c->isfloating = c->oldstate = trans != None || c->isfixed || c->isfloating; + // } + /* 5. Set Fixed State & Floating State */ + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + + /* Check if it's one of your 'Sidebar' items by name */ + if (strcmp(c->name, "facecam") == 0) { + c->isfixed = 1; + c->isfloating = 0; // Force sidebar items to tile + } else { + /* Respect rules (transient or isfloating set by applyrules) */ + c->isfloating = c->oldstate = trans != None || c->isfixed || c->isfloating; + } + + if ((c->isfloating && c->iscentered) || c->isPopup) { + c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2; + c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2; + } + + c->isPopup = trans != None; + + // if (c->resizehints) { + // c->isfixed = 1; + // c->isfloating = 0; + // } else { + // c->isfloating = c->oldstate = trans != None || c->isfixed; + // } + + /* 6. Standard X11 setup */ + updatewmhints(c); + XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); + grabbuttons(c, 0); + + if (c->isfloating) + XRaiseWindow(dpy, c->win); + + attach(c); + attachstack(c); + XChangeProperty(dpy, root, netatom[NetClientList], XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); + + /* Move out of view before mapping to prevent flicker */ + XMoveResizeWindow(dpy, c->win, c->x + 2 * sw, c->y, c->w, c->h); + setclientstate(c, NormalState); + + /* 7. REFINED FOCUS LOGIC (FIXED) */ + if (IS_FACECAM(c)) { + /* If it's the Facecam, WE DO NOTHING to selmon->sel. + Your previous focused window stays as selmon->sel. */ + } else { + /* Normal apps & Calculator logic: They take focus */ + unfocus(selmon->sel, 0); + selmon->sel = c; + } + + XMapWindow(dpy, c->win); + + /* Re-assert focus on selmon->sel. + If a Facecam just spawned, selmon->sel is still your terminal. */ + focus(NULL); + arrange(c->mon); +} + +void +mappingnotify(XEvent *e) +{ + XMappingEvent *ev = &e->xmapping; + + XRefreshKeyboardMapping(ev); + if (ev->request == MappingKeyboard) + grabkeys(); +} + +void +maprequest(XEvent *e) +{ + static XWindowAttributes wa; + XMapRequestEvent *ev = &e->xmaprequest; + + if (!XGetWindowAttributes(dpy, ev->window, &wa) || wa.override_redirect) + return; + if (!wintoclient(ev->window)) + manage(ev->window, &wa); +} + +// void +// monocle(Monitor *m) +// { +// unsigned int n = 0; +// Client *c; + +// for (c = m->clients; c; c = c->next) +// if (ISVISIBLE(c)) +// n++; +// if (n > 0) /* override layout symbol */ +// snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); +// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) +// resize(c, m->wx, m->wy, m->ww - 2 * c->bw, m->wh - 2 * c->bw, 0); +// } + +// void +// monocle(Monitor *m) +// { +// unsigned int n = 0; +// Client *c; +// int bx, by, bw, bh; + +// for (c = m->clients; c; c = c->next) +// if (ISVISIBLE(c)) +// n++; +// if (n > 0) +// snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", n); + +// getcenteredbox(m, 1, &bx, &by, &bw, &bh); + +// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) +// resize(c, bx, by, bw - (2 * c->bw), bh - (2 * c->bw), 0); +// } + +void +monocle(Monitor *m) +{ + unsigned int nt; + Client *c; + int bx, by, bw, bh; + + get_layout_box(m, 1, &nt, &bx, &by, &bw, &bh); + if (nt == 0) + return; + + snprintf(m->ltsymbol, sizeof m->ltsymbol, "[%d]", nt); + + for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!(c->tags & m->tagset[m->seltags]) || c->isSidePanel) + continue; + resize(c, bx, by, bw - (2 * c->bw), bh - (2 * c->bw), 0); + } +} + +void +motionnotify(XEvent *e) +{ + static Monitor *mon = NULL; + Monitor *m; + XMotionEvent *ev = &e->xmotion; + + if (ev->window != root) + return; + if ((m = recttomon(ev->x_root, ev->y_root, 1, 1)) != mon && mon) { + unfocus(selmon->sel, 1); + selmon = m; + focus(NULL); + } + mon = m; +} + +void +movemouse(const Arg *arg) +{ + int x, y, ocx, ocy, nx, ny; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support moving fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurMove]->cursor, CurrentTime) != GrabSuccess) + return; + if (!getrootptr(&x, &y)) + return; + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) + continue; + lasttime = ev.xmotion.time; + + nx = ocx + (ev.xmotion.x - x); + ny = ocy + (ev.xmotion.y - y); + if (abs(selmon->wx - nx) < snap) + nx = selmon->wx; + else if (abs((selmon->wx + selmon->ww) - (nx + WIDTH(c))) < snap) + nx = selmon->wx + selmon->ww - WIDTH(c); + if (abs(selmon->wy - ny) < snap) + ny = selmon->wy; + else if (abs((selmon->wy + selmon->wh) - (ny + HEIGHT(c))) < snap) + ny = selmon->wy + selmon->wh - HEIGHT(c); + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nx - c->x) > snap || abs(ny - c->y) > snap)) + togglefloating(NULL); + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, nx, ny, c->w, c->h, 1); + break; + } + } while (ev.type != ButtonRelease); + XUngrabPointer(dpy, CurrentTime); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +nametag(const Arg *arg) { + char *p, name[MAX_TAGNAME_LEN]; + FILE *f; + int i; + char dmenumon[2]; + char cmd[25]; + + SETDMENUMON(dmenumon); + memset(cmd, 0, 25); + sprintf(cmd, "dmenu -m %s < /dev/null", dmenumon); + errno = 0; // popen(3p) says on failure it "may" set errno + + if(!(f = popen(cmd, "r"))) { + fprintf(stderr, "dwm: popen 'dmenu < /dev/null' failed%s%s\n", errno ? ": " : "", errno ? strerror(errno) : ""); + return; + } + if (!(p = fgets(name, MAX_TAGNAME_LEN, f)) && (i = errno) && ferror(f)) + fprintf(stderr, "dwm: fgets failed: %s\n", strerror(i)); + if (pclose(f) < 0) + fprintf(stderr, "dwm: pclose failed: %s\n", strerror(errno)); + if(!p) + return; + if((p = strchr(name, '\n'))) + *p = '\0'; + + for(i = 0; i < LENGTH(tags); i++) + if(selmon->tagset[selmon->seltags] & (1 << i)) { + sprintf(selmon->tags[i], TAG_PREPEND, i+1); + strcat(selmon->tags[i], name); + } + drawbars(); +} + +Client * +nexttiled(Client *c) +{ + for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + return c; +} + +void +pop(Client *c) +{ + detach(c); + attach(c); + focus(c); + arrange(c->mon); +} + +void +propertynotify(XEvent *e) +{ + Client *c; + Window trans; + XPropertyEvent *ev = &e->xproperty; + + if ((ev->window == root) && (ev->atom == XA_WM_NAME)) + updatestatus(); + else if (ev->state == PropertyDelete) + return; /* ignore */ + else if ((c = wintoclient(ev->window))) { + + if (ev->atom == XInternAtom(dpy, "_DWM_RELOAD", False)) { + c->hintsvalid = 0; + updatesizehints(c); + + if (c->minw > 0) { + c->w = c->minw; + c->h = c->minh; + /* FORCE tiling mode */ + c->isfloating = 0; + } + + arrange(c->mon); + return; + } + + switch(ev->atom) { + default: break; + case XA_WM_TRANSIENT_FOR: + if (!c->isfloating && (XGetTransientForHint(dpy, c->win, &trans)) && + (c->isfloating = (wintoclient(trans)) != NULL)) + arrange(c->mon); + break; + case XA_WM_NORMAL_HINTS: + c->hintsvalid = 0; + updatesizehints(c); + + /* Your logic to auto-detect fixed windows */ + int wasfixed = c->resizehints; + c->resizehints = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + + if (c->resizehints || wasfixed) + arrange(c->mon); + break; + case XA_WM_HINTS: + updatewmhints(c); + drawbars(); + break; + } + 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); + } +} + +void +quit(const Arg *arg) +{ + running = 0; +} + +Monitor * +recttomon(int x, int y, int w, int h) +{ + Monitor *m, *r = selmon; + int a, area = 0; + + for (m = mons; m; m = m->next) + if ((a = INTERSECT(x, y, w, h, m)) > area) { + area = a; + r = m; + } + return r; +} + +void +resize(Client *c, int x, int y, int w, int h, int interact) +{ + if (applysizehints(c, &x, &y, &w, &h, interact)) + resizeclient(c, x, y, w, h); +} + +void +resizeclient(Client *c, int x, int y, int w, int h) +{ + XWindowChanges wc; + + c->oldx = c->x; c->x = wc.x = x; + c->oldy = c->y; c->y = wc.y = y; + c->oldw = c->w; c->w = wc.width = w; + c->oldh = c->h; c->h = wc.height = h; + wc.border_width = c->bw; + XConfigureWindow(dpy, c->win, CWX|CWY|CWWidth|CWHeight|CWBorderWidth, &wc); + configure(c); + XSync(dpy, False); +} + +void +resizemouse(const Arg *arg) +{ + int ocx, ocy, nw, nh; + Client *c; + Monitor *m; + XEvent ev; + Time lasttime = 0; + + if (!(c = selmon->sel)) + return; + if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */ + return; + restack(selmon); + ocx = c->x; + ocy = c->y; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, + None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) + return; + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { + case ConfigureRequest: + case Expose: + case MapRequest: + handler[ev.type](&ev); + break; + case MotionNotify: + if ((ev.xmotion.time - lasttime) <= (1000 / refreshrate)) + continue; + lasttime = ev.xmotion.time; + + nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); + nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { + if (!c->isfloating && selmon->lt[selmon->sellt]->arrange + && (abs(nw - c->w) > snap || abs(nh - c->h) > snap)) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) + resize(c, c->x, c->y, nw, nh, 1); + break; + } + } while (ev.type != ButtonRelease); + XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { + sendmon(c, m); + selmon = m; + focus(NULL); + } +} + +void +restack(Monitor *m) +{ + Client *c; + XEvent ev; + XWindowChanges wc; + + drawbar(m); + if (!m->sel) + return; + if (m->sel->isfloating || !m->lt[m->sellt]->arrange) + XRaiseWindow(dpy, m->sel->win); + if (m->lt[m->sellt]->arrange) { + wc.stack_mode = Below; + wc.sibling = m->barwin; + for (c = m->stack; c; c = c->snext) + if (!c->isfloating && ISVISIBLE(c)) { + XConfigureWindow(dpy, c->win, CWSibling|CWStackMode, &wc); + wc.sibling = c->win; + } + } + XSync(dpy, False); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); +} + +void +run(void) +{ + XEvent ev; + /* main event loop */ + XSync(dpy, False); + while (running && !XNextEvent(dpy, &ev)) + if (handler[ev.type]) + handler[ev.type](&ev); /* call handler */ +} + +void +scan(void) +{ + unsigned int i, num; + Window d1, d2, *wins = NULL; + XWindowAttributes wa; + + if (XQueryTree(dpy, root, &d1, &d2, &wins, &num)) { + for (i = 0; i < num; i++) { + if (!XGetWindowAttributes(dpy, wins[i], &wa) + || wa.override_redirect || XGetTransientForHint(dpy, wins[i], &d1)) + continue; + if (wa.map_state == IsViewable || getstate(wins[i]) == IconicState) + manage(wins[i], &wa); + } + for (i = 0; i < num; i++) { /* now the transients */ + if (!XGetWindowAttributes(dpy, wins[i], &wa)) + continue; + if (XGetTransientForHint(dpy, wins[i], &d1) + && (wa.map_state == IsViewable || getstate(wins[i]) == IconicState)) + manage(wins[i], &wa); + } + if (wins) + XFree(wins); + } +} + +void +sendmon(Client *c, Monitor *m) +{ + if (c->mon == m) + return; + unfocus(c, 1); + detach(c); + detachstack(c); + c->mon = m; + c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */ + attach(c); + attachstack(c); + focus(NULL); + arrange(NULL); +} + +void +setclientstate(Client *c, long state) +{ + long data[] = { state, None }; + + XChangeProperty(dpy, c->win, wmatom[WMState], wmatom[WMState], 32, + PropModeReplace, (unsigned char *)data, 2); +} + +int +sendevent(Client *c, Atom proto) +{ + int n; + Atom *protocols; + int exists = 0; + XEvent ev; + + if (XGetWMProtocols(dpy, c->win, &protocols, &n)) { + while (!exists && n--) + exists = protocols[n] == proto; + XFree(protocols); + } + if (exists) { + ev.type = ClientMessage; + ev.xclient.window = c->win; + ev.xclient.message_type = wmatom[WMProtocols]; + ev.xclient.format = 32; + ev.xclient.data.l[0] = proto; + ev.xclient.data.l[1] = CurrentTime; + XSendEvent(dpy, c->win, False, NoEventMask, &ev); + } + return exists; +} + +void +setfocus(Client *c) +{ + if (!c->neverfocus) { + XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XChangeProperty(dpy, root, netatom[NetActiveWindow], + XA_WINDOW, 32, PropModeReplace, + (unsigned char *) &(c->win), 1); + } + sendevent(c, wmatom[WMTakeFocus]); +} + +void +setfullscreen(Client *c, int fullscreen) +{ + if (fullscreen && !c->isfullscreen) { + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)&netatom[NetWMFullscreen], 1); + c->isfullscreen = 1; + c->oldstate = c->isfloating; + c->oldbw = c->bw; + c->bw = 0; + c->isfloating = 1; + resizeclient(c, c->mon->mx, c->mon->my, c->mon->mw, c->mon->mh); + XRaiseWindow(dpy, c->win); + } else if (!fullscreen && c->isfullscreen){ + XChangeProperty(dpy, c->win, netatom[NetWMState], XA_ATOM, 32, + PropModeReplace, (unsigned char*)0, 0); + c->isfullscreen = 0; + c->isfloating = c->oldstate; + c->bw = c->oldbw; + c->x = c->oldx; + c->y = c->oldy; + c->w = c->oldw; + c->h = c->oldh; + resizeclient(c, c->x, c->y, c->w, c->h); + arrange(c->mon); + } +} + +void +setlayout(const Arg *arg) +{ + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) + selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && 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); + else + drawbar(selmon); +} + +void +setcwfact(const Arg *arg) +{ + float f; + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->cwfact : arg->f - 1.0; + if (f < 0.1 || f > 1.0) + return; + selmon->cwfact = f; + arrange(selmon); +} + + void + sethfact(const Arg *arg) + { + float f; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + selmon->hfact : arg->f - 1.0; + if (f < 0.1 || f > 1.0) + return; + selmon->hfact = f; + arrange(selmon); + } + +/* arg > 1.0 will set mfact absolutely */ +void +setmfact(const Arg *arg) +{ + float *mfact; + float f; + // if (one_window) mfact = &selmon->center_mfact; + // else mfact = &selmon->mfact; + mfact = &selmon->mfact; + + if (!arg || !selmon->lt[selmon->sellt]->arrange) + return; + f = arg->f < 1.0 ? arg->f + (*mfact) : arg->f - 1.0; + + float size = f*(*mfact); + + // if (size < 0.05 || size > 1) + if (size < 0.05 || size > 1) + return; + (*mfact) = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); +} + +void +setup(void) +{ + int i; + XSetWindowAttributes wa; + Atom utf8string; + struct sigaction sa; + + /* do not transform children into zombies when they terminate */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_NOCLDSTOP | SA_NOCLDWAIT | SA_RESTART; + sa.sa_handler = SIG_IGN; + sigaction(SIGCHLD, &sa, NULL); + + /* clean up any zombies (inherited from .xinitrc etc) immediately */ + while (waitpid(-1, NULL, WNOHANG) > 0); + + /* init screen */ + screen = DefaultScreen(dpy); + sw = DisplayWidth(dpy, screen); + sh = DisplayHeight(dpy, screen); + root = RootWindow(dpy, screen); + drw = drw_create(dpy, screen, root, sw, sh); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + bh = drw->fonts->h + 2; + updategeom(); + /* init atoms */ + utf8string = XInternAtom(dpy, "UTF8_STRING", False); + wmatom[WMProtocols] = XInternAtom(dpy, "WM_PROTOCOLS", False); + wmatom[WMDelete] = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + wmatom[WMState] = XInternAtom(dpy, "WM_STATE", False); + wmatom[WMTakeFocus] = XInternAtom(dpy, "WM_TAKE_FOCUS", False); + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", 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); + netatom[NetWMFullscreen] = XInternAtom(dpy, "_NET_WM_STATE_FULLSCREEN", 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); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); + cursor[CurResize] = drw_cur_create(drw, XC_sizing); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); + for (i = 0; i < LENGTH(colors); i++) + scheme[i] = drw_scm_create(drw, colors[i], 3); + /* init bars */ + updatebars(); + updatestatus(); + /* supporting window for NetWMCheck */ + wmcheckwin = XCreateSimpleWindow(dpy, root, 0, 0, 1, 1, 0, 0, 0); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + XChangeProperty(dpy, wmcheckwin, netatom[NetWMName], utf8string, 8, + PropModeReplace, (unsigned char *) "dwm", 3); + XChangeProperty(dpy, root, netatom[NetWMCheck], XA_WINDOW, 32, + PropModeReplace, (unsigned char *) &wmcheckwin, 1); + /* EWMH support per view */ + XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32, + PropModeReplace, (unsigned char *) netatom, NetLast); + XDeleteProperty(dpy, root, netatom[NetClientList]); + /* select events */ + wa.cursor = cursor[CurNormal]->cursor; + wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask + |ButtonPressMask|PointerMotionMask|EnterWindowMask + |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; + XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); + XSelectInput(dpy, root, wa.event_mask); + grabkeys(); + focus(NULL); +} + +void +seturgent(Client *c, int urg) +{ + XWMHints *wmh; + + c->isurgent = urg; + if (!(wmh = XGetWMHints(dpy, c->win))) + return; + wmh->flags = urg ? (wmh->flags | XUrgencyHint) : (wmh->flags & ~XUrgencyHint); + XSetWMHints(dpy, c->win, wmh); + XFree(wmh); +} + +void +showhide(Client *c) +{ + if (!c) + 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); + showhide(c->snext); + } else { + /* hide clients bottom up */ + showhide(c->snext); + XMoveWindow(dpy, c->win, WIDTH(c) * -2, c->y); + } +} + +void +spawn(const Arg *arg) +{ + struct sigaction sa; + + if (arg->v == dmenucmd) + dmenumon[0] = '0' + selmon->num; + if (fork() == 0) { + if (dpy) + close(ConnectionNumber(dpy)); + setsid(); + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &sa, NULL); + + execvp(((char **)arg->v)[0], (char **)arg->v); + die("dwm: execvp '%s' failed:", ((char **)arg->v)[0]); + } +} + +void +tag(const Arg *arg) +{ + if (selmon->sel && arg->ui & TAGMASK) { + selmon->sel->tags = arg->ui & TAGMASK; + focus(NULL); + arrange(selmon); + } +} + +void +tagmon(const Arg *arg) +{ + if (!selmon->sel || !mons->next) + return; + sendmon(selmon->sel, dirtomon(arg->i)); +} + +// void +// tile(Monitor *m) +// { +// Client *c, *tmp; +// int g = gappx; +// unsigned int n = 0, nh = 0, fixed_w = 0; + +// /* 1. Find count and max width for visible windows only */ +// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { +// if (!(c->tags & m->tagset[m->seltags])) +// continue; + +// n++; +// if (c->isSidePanel) { +// nh++; +// int w; +// if (c->name && strstr(c->class, "Xephyr")) +// w = xephyr_width; +// else if (c->name && strstr(c->name, "Blender Render")) +// w = blender_width; +// else if (c->name && strstr(c->name, "calculator")) +// w = calc_width; +// else +// w = (c->minw > 0) ? c->minw : c->w; + +// if (w + (2 * c->bw) > fixed_w) +// fixed_w = w + (2 * c->bw); +// } +// } + +// if (n == 0) return; + +// /* 2. Setup Virtual Zone */ +// int reserved_space = (nh > 0) ? (fixed_w + g) : 0; +// int vx = m->wx + g; +// int vy = m->wy + g; +// int vw = m->ww - 2 * g - reserved_space; +// int vh = m->wh - 2 * g; + +// unsigned int nt = n - nh; +// unsigned int nm = MIN(nt, m->nmaster); +// unsigned int ns = nt - nm; + +// /* 3. Logic Counters */ +// unsigned int im = 0, is = 0, ifx = 0; +// unsigned int h, r, my = 0, ty = 0, fy = 0; +// unsigned int mw; + +// for (c = nexttiled(m->clients); c; c = nexttiled(c->next)) { +// /* SKIP windows not on the current tag */ +// if (!(c->tags & m->tagset[m->seltags])) +// continue; + +// if (!c->isSidePanel) { +// /* MASTER / STACK AREA */ +// if (im < nm) { +// r = nm - im; +// if (nt == 1 && c->iscentered) { +// // h = vh * m->hfact; +// // int cy = vy + (vh - h) / 2; +// // mw = m->ww * m->mfact; +// // if (mw > vw) mw = vw; +// // int cx = m->wx + (m->ww - mw) / 2; +// // int facecam_start = m->wx + m->ww - g - reserved_space; +// // if (cx + mw > facecam_start) cx = facecam_start - mw; +// // resize(c, cx, cy, mw - (2*c->bw), h - (2*c->bw), 0); +// h = vh * m->hfact; +// int cy = vy + (vh - h) / 2; + +// /* WIDTH: Now uses cwfact instead of mfact */ +// mw = m->ww * m->cwfact; + +// /* Clamping logic stays the same */ +// if (mw > vw) mw = vw; + +// int cx = m->wx + (m->ww - mw) / 2; +// int facecam_start = m->wx + m->ww - g - reserved_space; + +// if (cx + mw > facecam_start) +// cx = facecam_start - mw; + +// resize(c, cx, cy, mw - (2*c->bw), h - (2*c->bw), 0); +// } else { +// mw = (ns > 0) ? (nm ? (vw - g) * m->mfact : 0) : vw; +// h = (vh - my - g * (r - 1)) / r; +// resize(c, vx, vy + my, mw - (2 * c->bw), h - (2 * c->bw), 0); +// my += h + g; +// } +// im++; +// } else { +// mw = (vw - g) * m->mfact; +// r = ns - is; +// h = (vh - ty - g * (r - 1)) / r; +// resize(c, vx + mw + g, vy + ty, vw - mw - g - (2 * c->bw), h - (2 * c->bw), 0); +// ty += h + g; +// is++; +// } +// } else { +// /* 4. SIDE PANEL (Strict Tag Isolation & Overflow Protection) */ +// r = nh - ifx; +// int reserved_for_later = 0; + +// /* Lookahead: Only reserve space for forced windows VISIBLE on this tag */ +// for (tmp = c->next; tmp; tmp = nexttiled(tmp->next)) { +// if (tmp->isSidePanel && (tmp->tags & m->tagset[m->seltags])) { +// if (tmp->name && strstr(tmp->name, "Blender Render")) +// reserved_for_later += blender_height + g; +// else if (tmp->name && strstr(tmp->name, "calculator")) +// reserved_for_later += calc_height + g; +// } +// } + +// if (c->name && strstr(c->name, "Blender Render")) { +// h = blender_height; +// } else if (c->name && strstr(c->name, "calculator")) { +// h = calc_height; +// } else { +// /* Flexible height calculation */ +// int available = vh - fy - reserved_for_later; +// int flex_left = 0; +// for (tmp = c; tmp; tmp = nexttiled(tmp->next)) { +// if (tmp->isSidePanel && (tmp->tags & m->tagset[m->seltags])) { +// int is_forced = (tmp->name && (strstr(tmp->name, "Blender Render") || strstr(tmp->name, "calculator"))); +// if (!is_forced) flex_left++; +// } +// } +// h = (flex_left > 0) ? (available / flex_left) : available; +// if (r > 1) h -= g; +// } + +// /* Safety: Floor and Ceiling checks */ +// if (h < 50) h = 50; +// if (fy + h > vh) h = vh - fy; + +// int fx = m->wx + m->ww - g - fixed_w; +// resize(c, fx, vy + fy, fixed_w - (2 * c->bw), h - (2 * c->bw), 0); + +// fy += h + g; +// ifx++; +// } +// } +// } + +// void +// tile(Monitor *m) +// { +// unsigned int i, n, h, mw, my, ty; +// Client *c; +// int bx, by, bw, bh; + +// for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++); +// if (n == 0) +// return; + +// getcenteredbox(m, n, &bx, &by, &bw, &bh); + +// if (n > m->nmaster) +// mw = m->nmaster ? (bw - gappx) * m->mfact : 0; +// else +// mw = bw; + +// for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) { +// if (i < m->nmaster) { +// unsigned int nmat = MIN(n, m->nmaster); +// h = (bh - (nmat - 1) * gappx) / nmat; +// resize(c, bx, by + my, mw - (2*c->bw), h - (2*c->bw), 0); +// my += h + gappx; +// } else { +// unsigned int nstk = n - m->nmaster; +// h = (bh - (nstk - 1) * gappx) / nstk; +// resize(c, bx + mw + gappx, by + ty, (bw - mw - gappx) - (2*c->bw), h - (2*c->bw), 0); +// ty += h + gappx; +// } +// } +// } + +void +tile(Monitor *m) +{ + unsigned int i, h, mw, my, ty; + Client *c; + int bx, by, bw, bh; + unsigned int nt; + + get_layout_box(m, 0, &nt, &bx, &by, &bw, &bh); + if (nt == 0) + return; + + int g = gappx; + + if (nt > m->nmaster) + mw = m->nmaster ? (bw - g) * m->mfact : 0; + else + mw = bw; + + for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next)) { + if (!(c->tags & m->tagset[m->seltags]) || c->isSidePanel) + continue; + + if (i < m->nmaster) { + unsigned int nmat = MIN(nt, m->nmaster); + h = (bh - my - g * (nmat - 1 - i)) / (nmat - i); + resize(c, bx, by + my, mw - (2 * c->bw), h - (2 * c->bw), 0); + my += h + g; + } else { + unsigned int nstk = nt - m->nmaster; + unsigned int is = i - m->nmaster; + h = (bh - ty - g * (nstk - 1 - is)) / (nstk - is); + resize(c, bx + mw + g, by + ty, (bw - mw - g) - (2 * c->bw), h - (2 * c->bw), 0); + ty += h + g; + } + i++; + } +} + +void +togglebar(const Arg *arg) +{ + 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); +} + +void +togglefloating(const Arg *arg) +{ + if (!selmon->sel) + return; + if (selmon->sel->isfullscreen) /* no support for fullscreen windows */ + return; + selmon->sel->isfloating = !selmon->sel->isfloating || selmon->sel->isfixed; + if (selmon->sel->isfloating) + resize(selmon->sel, selmon->sel->x, selmon->sel->y, + selmon->sel->w, selmon->sel->h, 0); + arrange(selmon); +} + +void +toggletag(const Arg *arg) +{ + unsigned int newtags; + + if (!selmon->sel) + return; + newtags = selmon->sel->tags ^ (arg->ui & TAGMASK); + if (newtags) { + selmon->sel->tags = newtags; + focus(NULL); + arrange(selmon); + } +} + +void +toggleview(const Arg *arg) +{ + int i; + unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); + + 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); + } +} + +// Probably part of an old patch +// void +// toggleview(const Arg *arg) +// { +// unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); +// int i; + +// 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); + + +// if (newtagset) { +// selmon->tagset[selmon->seltags] = newtagset; +// focus(NULL); +// arrange(selmon); +// } +// } + +void +unfocus(Client *c, int setfocus) +{ + if (!c) + return; + grabbuttons(c, 0); + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + if (setfocus) { + XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XDeleteProperty(dpy, root, netatom[NetActiveWindow]); + } +} + +// void +// unmanage(Client *c, int destroyed) +// { +// Monitor *m = c->mon; +// XWindowChanges wc; + +// detach(c); +// detachstack(c); +// if (!destroyed) { +// wc.border_width = c->oldbw; +// XGrabServer(dpy); /* avoid race conditions */ +// XSetErrorHandler(xerrordummy); +// XSelectInput(dpy, c->win, NoEventMask); +// XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ +// XUngrabButton(dpy, AnyButton, AnyModifier, c->win); +// setclientstate(c, WithdrawnState); +// XSync(dpy, False); +// XSetErrorHandler(xerror); +// XUngrabServer(dpy); +// } +// free(c); +// focus(NULL); +// updateclientlist(); +// arrange(m); +// } +// void +// unmanage(Client *c, int destroyed) +// { +// Monitor *m = c->mon; +// XWindowChanges wc; +// int i; + +// /* 1. PERTAG FOCUS CLEANUP: +// Remove this client from the focus history of all tags */ +// if (m->pertag) { +// for (i = 0; i <= LENGTH(tags); i++) { +// if (m->pertag->selclients[i] == c) +// m->pertag->selclients[i] = NULL; +// } +// } + +// detach(c); +// detachstack(c); + +// if (!destroyed) { +// wc.border_width = c->oldbw; +// XGrabServer(dpy); /* avoid race conditions */ +// XSetErrorHandler(xerrordummy); +// XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ +// XUngrabButton(dpy, AnyButton, AnyModifier, c->win); +// setclientstate(c, WithdrawnState); +// XSync(dpy, False); +// XSetErrorHandler(xerror); +// XUngrabServer(dpy); +// } + +// free(c); +// focus(NULL); +// updateclientlist(); +// arrange(m); +// } + +void +unmanage(Client *c, int destroyed) +{ + Monitor *m = c->mon; + XWindowChanges wc; + int i; + + /* 1. PERTAG FOCUS CLEANUP */ + if (m->pertag) { + for (i = 0; i <= LENGTH(tags); i++) { + if (m->pertag->selclients[i] == c) + m->pertag->selclients[i] = NULL; + } + } + + detach(c); + detachstack(c); + + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ + XSetErrorHandler(xerrordummy); + XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ + XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + setclientstate(c, WithdrawnState); + XSync(dpy, False); + XSetErrorHandler(xerror); + XUngrabServer(dpy); + } + + free(c); + + /* 2. SMART FOCUS RECOVERY */ + /* If we just closed the window that pertag was remembering for the current tag, + * we need to find a new candidate (ideally not a Facecam/Calculator) */ + if (m->pertag->selclients[m->pertag->curtag] == NULL) { + Client *nextsel; + for (nextsel = m->clients; nextsel && (!ISVISIBLE(nextsel) || nextsel->resizehints); nextsel = nextsel->next); + if (!nextsel) // Fallback to any visible window if no "normal" ones exist + for (nextsel = m->clients; nextsel && !ISVISIBLE(nextsel); nextsel = nextsel->next); + + m->pertag->selclients[m->pertag->curtag] = nextsel; + m->sel = nextsel; + } + + focus(NULL); + updateclientlist(); + arrange(m); +} + +void +unmapnotify(XEvent *e) +{ + Client *c; + XUnmapEvent *ev = &e->xunmap; + + if ((c = wintoclient(ev->window))) { + if (ev->send_event) + setclientstate(c, WithdrawnState); + else + unmanage(c, 0); + } +} + +void +updatebars(void) +{ + Monitor *m; + XSetWindowAttributes wa = { + .override_redirect = True, + .background_pixmap = ParentRelative, + .event_mask = ButtonPressMask|ExposureMask + }; + XClassHint ch = {"dwm", "dwm"}; + 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), + CopyFromParent, DefaultVisual(dpy, screen), + CWOverrideRedirect|CWBackPixmap|CWEventMask, &wa); + XDefineCursor(dpy, m->barwin, cursor[CurNormal]->cursor); + XMapRaised(dpy, m->barwin); + XSetClassHint(dpy, m->barwin, &ch); + } +} + +void +updatebarpos(Monitor *m) +{ + m->wy = m->my; + m->wh = m->mh; + if (m->showbar) { + m->wh -= bh; + m->by = m->topbar ? m->wy : m->wy + m->wh; + m->wy = m->topbar ? m->wy + bh : m->wy; + } else + m->by = -bh; +} + +void +updateclientlist(void) +{ + Client *c; + Monitor *m; + + XDeleteProperty(dpy, root, netatom[NetClientList]); + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + XChangeProperty(dpy, root, netatom[NetClientList], + XA_WINDOW, 32, PropModeAppend, + (unsigned char *) &(c->win), 1); +} + +int +updategeom(void) +{ + int dirty = 0; + +#ifdef XINERAMA + if (XineramaIsActive(dpy)) { + int i, j, n, nn; + Client *c; + Monitor *m; + XineramaScreenInfo *info = XineramaQueryScreens(dpy, &nn); + XineramaScreenInfo *unique = NULL; + + for (n = 0, m = mons; m; m = m->next, n++); + /* only consider unique geometries as separate screens */ + unique = ecalloc(nn, sizeof(XineramaScreenInfo)); + for (i = 0, j = 0; i < nn; i++) + if (isuniquegeom(unique, j, &info[i])) + memcpy(&unique[j++], &info[i], sizeof(XineramaScreenInfo)); + XFree(info); + nn = j; + + /* new monitors if nn > n */ + for (i = n; i < nn; i++) { + for (m = mons; m && m->next; m = m->next); + if (m) + m->next = createmon(); + else + mons = createmon(); + } + for (i = 0, m = mons; i < nn && m; m = m->next, i++) + if (i >= n + || unique[i].x_org != m->mx || unique[i].y_org != m->my + || unique[i].width != m->mw || unique[i].height != m->mh) + { + dirty = 1; + m->num = i; + m->mx = m->wx = unique[i].x_org; + m->my = m->wy = unique[i].y_org; + m->mw = m->ww = unique[i].width; + m->mh = m->wh = unique[i].height; + updatebarpos(m); + } + /* removed monitors if n > nn */ + for (i = nn; i < n; i++) { + for (m = mons; m && m->next; m = m->next); + while ((c = m->clients)) { + dirty = 1; + m->clients = c->next; + detachstack(c); + c->mon = mons; + attach(c); + attachstack(c); + } + if (m == selmon) + selmon = mons; + cleanupmon(m); + } + free(unique); + } else +#endif /* XINERAMA */ + { /* default monitor setup */ + if (!mons) + mons = createmon(); + if (mons->mw != sw || mons->mh != sh) { + dirty = 1; + mons->mw = mons->ww = sw; + mons->mh = mons->wh = sh; + updatebarpos(mons); + } + } + if (dirty) { + selmon = mons; + selmon = wintomon(root); + } + return dirty; +} + +void +updatenumlockmask(void) +{ + unsigned int i, j; + XModifierKeymap *modmap; + + numlockmask = 0; + modmap = XGetModifierMapping(dpy); + for (i = 0; i < 8; i++) + for (j = 0; j < modmap->max_keypermod; j++) + if (modmap->modifiermap[i * modmap->max_keypermod + j] + == XKeysymToKeycode(dpy, XK_Num_Lock)) + numlockmask = (1 << i); + XFreeModifiermap(modmap); +} + +void +updatesizehints(Client *c) +{ + long msize; + XSizeHints size; + + if (!XGetWMNormalHints(dpy, c->win, &size, &msize)) + /* size is uninitialized, ensure that size.flags aren't used */ + size.flags = PSize; + if (size.flags & PBaseSize) { + c->basew = size.base_width; + c->baseh = size.base_height; + } else if (size.flags & PMinSize) { + c->basew = size.min_width; + c->baseh = size.min_height; + } else + c->basew = c->baseh = 0; + if (size.flags & PResizeInc) { + c->incw = size.width_inc; + c->inch = size.height_inc; + } else + c->incw = c->inch = 0; + if (size.flags & PMaxSize) { + c->maxw = size.max_width; + c->maxh = size.max_height; + } else + c->maxw = c->maxh = 0; + if (size.flags & PMinSize) { + c->minw = size.min_width; + c->minh = size.min_height; + } else if (size.flags & PBaseSize) { + c->minw = size.base_width; + c->minh = size.base_height; + } else + c->minw = c->minh = 0; + if (size.flags & PAspect) { + c->mina = (float)size.min_aspect.y / size.min_aspect.x; + c->maxa = (float)size.max_aspect.x / size.max_aspect.y; + } else + c->maxa = c->mina = 0.0; + c->isfixed = (c->maxw && c->maxh && c->maxw == c->minw && c->maxh == c->minh); + c->hintsvalid = 1; +} + +void +updatestatus(void) +{ + if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) + strcpy(stext, "dwm-"VERSION); + drawbar(selmon); +} + +// void +// updatetitle(Client *c) +// { +// if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) +// gettextprop(c->win, XA_WM_NAME, c->name, sizeof c->name); +// if (c->name[0] == '\0') /* hack to mark broken clients */ +// strcpy(c->name, broken); +// } + +void +updatetitle(Client *c) +{ + /* Let dwm do its existing title-getting logic first */ + if (!gettextprop(c->win, netatom[NetWMName], c->name, sizeof c->name)) + gettextprop(c->win, wmatom[WMState], c->name, sizeof c->name); + /* Note: Most people just use the existing lines already in their dwm.c here */ + + /* --- ADD THIS BLOCK --- */ + // if (c->name && strstr(c->name, "Blender Render")) { + // if (!c->isSidePanel) { + // c->isSidePanel = 1; + // arrange(c->mon); + // } + // } + if (c->name && strstr(c->name, "Blender Render")) { + if (!c->isSidePanel) { + c->isSidePanel = 1; + c->isfloating = 0; /* Force it to tile so it goes to the side panel */ + arrange(c->mon); + } + } +} + +void +updatewindowtype(Client *c) +{ + Atom state = getatomprop(c, netatom[NetWMState]); + Atom wtype = getatomprop(c, netatom[NetWMWindowType]); + + if (state == netatom[NetWMFullscreen]) + setfullscreen(c, 1); + if (wtype == netatom[NetWMWindowTypeDialog]) + c->isfloating = 1; +} + +void +updatewmhints(Client *c) +{ + XWMHints *wmh; + + if ((wmh = XGetWMHints(dpy, c->win))) { + if (c == selmon->sel && wmh->flags & XUrgencyHint) { + wmh->flags &= ~XUrgencyHint; + XSetWMHints(dpy, c->win, wmh); + } else + c->isurgent = (wmh->flags & XUrgencyHint) ? 1 : 0; + if (wmh->flags & InputHint) + c->neverfocus = !wmh->input; + else + c->neverfocus = 0; + XFree(wmh); + } +} + +// void +// view(const Arg *arg) +// { +// int i; +// unsigned int tmptag; +// Client *c; + +// if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) +// return; + +// /* --- STEP 1: SAVE current focus --- */ +// /* We only 'remember' the window if it's a normal app or the calculator. +// * We explicitly ignore the Facecam so it doesn't hijack the tag on return. */ +// if (selmon->sel && (!selmon->sel->resizehints || strcmp(selmon->sel->name, "calculator") == 0)) { +// selmon->pertag->selclients[selmon->pertag->curtag] = selmon->sel; +// } + +// selmon->seltags ^= 1; /* toggle sel tagset */ +// 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; i < LENGTH(tags); i++) +// if (arg->ui & (1 << i)) { +// selmon->pertag->curtag = i + 1; +// break; +// } +// } +// } else { +// /* Toggle tag logic */ +// tmptag = selmon->pertag->prevtag; +// selmon->pertag->prevtag = selmon->pertag->curtag; +// selmon->pertag->curtag = tmptag; +// } + +// /* --- STEP 2: LOAD pertag layout settings --- */ +// 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); + +// /* --- STEP 3: RESTORE focus --- */ +// selmon->sel = selmon->pertag->selclients[selmon->pertag->curtag]; + +// // /* If the saved window is gone, invisible, or is the Facecam, find a new candidate */ +// // if (!selmon->sel || !ISVISIBLE(selmon->sel) || IS_FACECAM(selmon->sel)) { +// // /* First: Try to find a normal window (Terminal, Browser, Calculator) */ +// // for (c = selmon->clients; c && (!ISVISIBLE(c) || IS_FACECAM(c)); c = c->next); + +// // /* Second: If ONLY the Facecam exists on this tag, we have no choice */ +// // if (!c) +// // for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + +// // selmon->sel = c; +// // } + +// // focus(selmon->sel); +// // arrange(selmon); +// if (!selmon->sel || !ISVISIBLE(selmon->sel) || IS_FACECAM(selmon->sel)) { +// for (c = selmon->clients; c && (!ISVISIBLE(c) || IS_FACECAM(c)); c = c->next); +// /* If the only visible window is Facecam, set sel to NULL +// so focus() doesn't jump to it automatically. */ +// selmon->sel = c; +// } + +// focus(selmon->sel); +// arrange(selmon); +// } +// void +// view(const Arg *arg) +// { +// int i; +// unsigned int tmptag; +// Client *c; + +// /* Only act on the selected monitor's tagset */ +// if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) +// return; + +// /* --- STEP 1: SAVE current focus for THIS monitor --- */ +// /* We save the focused window into the pertag array specific to selmon. +// * This prevents one monitor's focus history from affecting the other. */ +// if (selmon->sel && (!selmon->sel->resizehints || strcmp(selmon->sel->name, "calculator") == 0)) { +// selmon->pertag->selclients[selmon->pertag->curtag] = selmon->sel; +// } + +// selmon->seltags ^= 1; /* toggle sel tagset */ +// 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; i < LENGTH(tags); i++) +// if (arg->ui & (1 << i)) { +// selmon->pertag->curtag = i + 1; +// break; +// } +// } +// } else { +// /* Toggle tag logic using this monitor's history */ +// tmptag = selmon->pertag->prevtag; +// selmon->pertag->prevtag = selmon->pertag->curtag; +// selmon->pertag->curtag = tmptag; +// } + +// /* --- STEP 2: LOAD pertag layout settings for the NEW tag --- */ +// 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); + +// /* --- STEP 3: RESTORE focus from this monitor's history --- */ +// selmon->sel = selmon->pertag->selclients[selmon->pertag->curtag]; + +// /* Validation: If the saved window is gone, invisible, or is the Facecam, find a new candidate */ +// if (!selmon->sel || !ISVISIBLE(selmon->sel) || IS_FACECAM(selmon->sel)) { +// /* Look for a valid client on THIS monitor only */ +// for (c = selmon->clients; c && (!ISVISIBLE(c) || IS_FACECAM(c)); c = c->next); + +// /* If the only visible window is Facecam, set sel to NULL +// * so focus() doesn't jump to the Facecam automatically. */ +// selmon->sel = c; +// } + +// focus(selmon->sel); +// arrange(selmon); +// } + +void +view(const Arg *arg) +{ + int i; + unsigned int tmptag; + Client *c; + + if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + + /* --- FIX: Unfocus current window before leaving the tag --- */ + if (selmon->sel) + unfocus(selmon->sel, 0); + + /* --- STEP 1: SAVE current focus --- */ + if (selmon->sel && (!selmon->sel->resizehints || strcmp(selmon->sel->name, "calculator") == 0)) { + selmon->pertag->selclients[selmon->pertag->curtag] = selmon->sel; + } + + selmon->seltags ^= 1; /* toggle sel tagset */ + 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; i < LENGTH(tags); i++) + if (arg->ui & (1 << i)) { + selmon->pertag->curtag = i + 1; + break; + } + } + } else { + tmptag = selmon->pertag->prevtag; + selmon->pertag->prevtag = selmon->pertag->curtag; + selmon->pertag->curtag = tmptag; + } + + /* --- STEP 2: LOAD pertag layout settings --- */ + 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); + + /* --- STEP 3: RESTORE focus --- */ + selmon->sel = selmon->pertag->selclients[selmon->pertag->curtag]; + + if (!selmon->sel || !ISVISIBLE(selmon->sel) || IS_FACECAM(selmon->sel)) { + for (c = selmon->clients; c && (!ISVISIBLE(c) || IS_FACECAM(c)); c = c->next); + selmon->sel = c; + } + + /* --- FIX: Ensure all other windows on this monitor are set to "Normal" border --- */ + for (c = selmon->clients; c; c = c->next) { + if (c != selmon->sel) + XSetWindowBorder(dpy, c->win, scheme[SchemeNorm][ColBorder].pixel); + } + + focus(selmon->sel); + arrange(selmon); +} + +Client * +wintoclient(Window w) +{ + Client *c; + Monitor *m; + + for (m = mons; m; m = m->next) + for (c = m->clients; c; c = c->next) + if (c->win == w) + return c; + return NULL; +} + +Monitor * +wintomon(Window w) +{ + int x, y; + Client *c; + Monitor *m; + + if (w == root && getrootptr(&x, &y)) + return recttomon(x, y, 1, 1); + for (m = mons; m; m = m->next) + if (w == m->barwin) + return m; + if ((c = wintoclient(w))) + return c->mon; + return selmon; +} + +/* There's no way to check accesses to destroyed windows, thus those cases are + * ignored (especially on UnmapNotify's). Other types of errors call Xlibs + * default error handler, which may call exit. */ +int +xerror(Display *dpy, XErrorEvent *ee) +{ + if (ee->error_code == BadWindow + || (ee->request_code == X_SetInputFocus && ee->error_code == BadMatch) + || (ee->request_code == X_PolyText8 && ee->error_code == BadDrawable) + || (ee->request_code == X_PolyFillRectangle && ee->error_code == BadDrawable) + || (ee->request_code == X_PolySegment && ee->error_code == BadDrawable) + || (ee->request_code == X_ConfigureWindow && ee->error_code == BadMatch) + || (ee->request_code == X_GrabButton && ee->error_code == BadAccess) + || (ee->request_code == X_GrabKey && ee->error_code == BadAccess) + || (ee->request_code == X_CopyArea && ee->error_code == BadDrawable)) + return 0; + fprintf(stderr, "dwm: fatal error: request code=%d, error code=%d\n", + ee->request_code, ee->error_code); + return xerrorxlib(dpy, ee); /* may call exit */ +} + +int +xerrordummy(Display *dpy, XErrorEvent *ee) +{ + return 0; +} + +/* Startup Error handler to check if another window manager + * is already running. */ +int +xerrorstart(Display *dpy, XErrorEvent *ee) +{ + die("dwm: another window manager is already running"); + return -1; +} + +void +zoom(const Arg *arg) +{ + Client *c = selmon->sel; + + if (!selmon->lt[selmon->sellt]->arrange || !c || c->isfloating) + return; + if (c == nexttiled(selmon->clients) && !(c = nexttiled(c->next))) + return; + pop(c); +} + +int +main(int argc, char *argv[]) +{ + if (argc == 2 && !strcmp("-v", argv[1])) + die("dwm-"VERSION); + else if (argc != 1) + die("usage: dwm [-v]"); + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("dwm: cannot open display"); + checkotherwm(); + setup(); +#ifdef __OpenBSD__ + if (pledge("stdio rpath proc exec", NULL) == -1) + die("pledge"); +#endif /* __OpenBSD__ */ + scan(); + run(); + cleanup(); + XCloseDisplay(dpy); + return EXIT_SUCCESS; +} +#include "movestack.c" +void +setopacity(Client *c, double opacity) +{ + if (!c) + return; + unsigned long opa = (unsigned long)(opacity * 0xffffffff); + if (opacity >= 1.0) + XDeleteProperty(dpy, c->win, netatom[NetWMOpacity]); + else + XChangeProperty(dpy, c->win, netatom[NetWMOpacity], XA_CARDINAL, 32, + PropModeReplace, (unsigned char *)&opa, 1); +} diff --git a/dwm.o b/dwm.o new file mode 100644 index 0000000000000000000000000000000000000000..0182daad0c01d382b96145c3af08ab3183c03ac1 GIT binary patch literal 75728 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ɅXb4nAl^Ks&p@FMeSi5K#P!brZACDw09s_tJ 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 bdfFY^_VMZ~f6jh9E~MA0uRSl3_H6$bHRr!n literal 0 HcmV?d00001 diff --git a/dwm.png b/dwm.png new file mode 100644 index 0000000000000000000000000000000000000000..b1f9ba7e5f4cc7350ee2392ebcea5fcbe00fb49b GIT binary patch literal 373 zcmeAS@N?(olHy`uVBq!ia0vp^2Y@($g9*gC@m3f}u_bxCyDx`7I;J! zGca%iWx0hJ8D`Cq01C2~c>21sUt<^MF=V?Ztt9{yk}YwKC~?lu%}vcKVQ?-=O)N=G zQ7F$W$xsN%NL6t6^bL5QqM8R(c+=CxF{I+w+q;fj4F)_6j>`Z3pZ>_($QEQ&92OXP z%lpEKGwG8$G-U1H{@Y%;mx-mNK|p|siBVAj$Z~Mt-~h6K0!}~{PyozQ07(f5fTdVi zm=-zT`NweeJ#%S&{fequZGmkDDC*%x$$Sa*fAP=$`nJkhx1Y~k<8b2;Hq)FOdV=P$ q&oWzoxz_&nv&n0)xBzV8k*jsxheTIy&cCY600f?{elF{r5}E*x)opSB literal 0 HcmV?d00001 diff --git a/dwm.tar.gz b/dwm.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..8f40069896c44143d4294152fa781a1ae965f263 GIT binary patch literal 17691 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

G0Y5wY)Kl;<% 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 literal 0 HcmV?d00001 diff --git a/gaps.patch b/gaps.patch new file mode 100644 index 0000000..33c0f1b --- /dev/null +++ b/gaps.patch @@ -0,0 +1,169 @@ +diff --git a/config.def.h b/config.def.h +index c7efb0f..848396e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,9 +5,10 @@ + + /* appearance */ + static const unsigned int borderpx = 5; /* border pixel of windows */ +-static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int snap = 5; /* snap pixel */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ ++static const unsigned int gappx = 20; /* gap pixel between windows */ + static const char *fonts[] = { "monospace:size=14" }; + static const char dmenufont[] = "monospace:size=14"; + static const char col_gray1[] = "#222222"; +diff --git a/config.h b/config.h +index c7efb0f..848396e 100644 +--- a/config.h ++++ b/config.h +@@ -5,9 +5,10 @@ + + /* appearance */ + static const unsigned int borderpx = 5; /* border pixel of windows */ +-static const unsigned int snap = 32; /* snap pixel */ ++static const unsigned int snap = 5; /* snap pixel */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ ++static const unsigned int gappx = 20; /* gap pixel between windows */ + static const char *fonts[] = { "monospace:size=14" }; + static const char dmenufont[] = "monospace:size=14"; + static const char col_gray1[] = "#222222"; +diff --git a/dwm b/dwm +index ff1c1ec..22f7431 100755 +Binary files a/dwm and b/dwm differ +diff --git a/dwm.c b/dwm.c +index b349f5e..590d2c6 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -1867,40 +1867,108 @@ tagmon(const Arg *arg) + sendmon(selmon->sel, dirtomon(arg->i)); + } + ++// void ++// tile(Monitor *m) ++// { ++// unsigned int i, n, h, r, g = 0, 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 - (g = gappx)) * 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) { ++// r = MIN(n, m->nmaster) - i; ++// h = (m->wh - my - gappx * (r - 1)) / r; ++// resize(c, ++// m->wx, ++// m->wy + my, ++// mw - (2 * c->bw), ++// h - (2 * c->bw), ++// 0); ++// my += HEIGHT(c) + gappx; ++// } else { ++// r = n - i; ++// h = (m->wh - ty - gappx * (r - 1)) / r; ++// resize(c, ++// m->wx + mw + g, ++// m->wy + ty, ++// m->ww - mw - g - (2 * c->bw), ++// h - (2 * c->bw), ++// 0); ++// ty += HEIGHT(c) + gappx; ++// } ++// } ++ ++// /* your custom single-window centering rule */ ++// if (n == 1 && selmon->sel && selmon->sel->CenterThisWindow) ++// resizeclient(selmon->sel, ++// (selmon->mw - selmon->mw * 0.5) / 2, ++// (selmon->mh - selmon->mh * 0.9) / 2, ++// selmon->mw * 0.5, ++// (selmon->mh * 0.9) + borderpx + 10); ++// } ++ + void + tile(Monitor *m) + { +- unsigned int i, n, h, mw, my, ty; ++ unsigned int i, n, h, r, g = gappx, mw, my, ty; + Client *c; + ++ int ox = m->wx + gappx; ++ int oy = m->wy + gappx; ++ int ow = m->ww - 2 * gappx; ++ int oh = m->wh - 2 * gappx; ++ + 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; ++ mw = m->nmaster ? (ow - g) * m->mfact : 0; + else +- mw = m->ww; +- for (i = my = ty = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++) ++ mw = ow; ++ ++ 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); ++ r = MIN(n, m->nmaster) - i; ++ h = (oh - my - gappx * (r - 1)) / r; ++ resize(c, ++ ox, ++ oy + my, ++ mw - (2 * c->bw), ++ h - (2 * c->bw), ++ 0); ++ my += HEIGHT(c) + gappx; + } 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); ++ r = n - i; ++ h = (oh - ty - gappx * (r - 1)) / r; ++ resize(c, ++ ox + mw + g, ++ oy + ty, ++ ow - mw - g - (2 * c->bw), ++ h - (2 * c->bw), ++ 0); ++ ty += HEIGHT(c) + gappx; + } +- if (n == 1 && selmon->sel->CenterThisWindow) +- resizeclient(selmon->sel, +- (selmon->mw - selmon->mw * 0.5) / 2, +- (selmon->mh - selmon->mh * 0.9) / 2, +- selmon->mw * 0.5, +- (selmon->mh * 0.9) + borderpx + 10); ++ } ++ ++ /* keep your single-window centering */ ++ if (n == 1 && selmon->sel && selmon->sel->CenterThisWindow) ++ resizeclient(selmon->sel, ++ (selmon->mw - selmon->mw * 0.5) / 2, ++ (selmon->mh - selmon->mh * 0.9) / 2, ++ selmon->mw * 0.5, ++ (selmon->mh * 0.9) + borderpx + 10); + } + ++ + void + togglebar(const Arg *arg) + { +diff --git a/dwm.o b/dwm.o +index 7047333..1552ee7 100644 +Binary files a/dwm.o and b/dwm.o differ diff --git a/movestack.c b/movestack.c new file mode 100644 index 0000000..8d22a6b --- /dev/null +++ b/movestack.c @@ -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); + } +} diff --git a/notes.adoc b/notes.adoc new file mode 100644 index 0000000..7501c40 --- /dev/null +++ b/notes.adoc @@ -0,0 +1,7 @@ +== Finding the window class name of a window + +---- +xprop | grep "WM_NAME" +---- + +and then click on the widow you want to find the class name of diff --git a/patches/dwm-mmnametags-prepend-6.6.diff b/patches/dwm-mmnametags-prepend-6.6.diff new file mode 100644 index 0000000..4813c85 --- /dev/null +++ b/patches/dwm-mmnametags-prepend-6.6.diff @@ -0,0 +1,156 @@ +diff --git a/config.def.h b/config.def.h +index 81c3fc0..262084a 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -19,7 +19,10 @@ static const char *colors[][3] = { + }; + + /* tagging */ +-static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; ++#define MAX_TAGNAME_LEN 14 /* excludes TAG_PREPEND */ ++#define TAG_PREPEND "%1i:" /* formatted as 2 chars */ ++#define MAX_TAGLEN 16 /* altogether */ ++static char tags[][MAX_TAGLEN] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + + static const Rule rules[] = { + /* xprop(1): +@@ -86,6 +89,7 @@ static const Key keys[] = { + { MODKEY, XK_period, focusmon, {.i = +1 } }, + { MODKEY|ShiftMask, XK_comma, tagmon, {.i = -1 } }, + { MODKEY|ShiftMask, XK_period, tagmon, {.i = +1 } }, ++ { MODKEY, XK_n, nametag, {0} }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +diff --git a/dwm.c b/dwm.c +index 4f345ee..fea44dc 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -47,6 +47,7 @@ + /* macros */ + #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) + #define CLEANMASK(mask) (mask & ~(numlockmask|LockMask) & (ShiftMask|ControlMask|Mod1Mask|Mod2Mask|Mod3Mask|Mod4Mask|Mod5Mask)) ++#define SETDMENUMON(m) m[0] = '0' + selmon->num + #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])) +@@ -129,6 +130,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ char **tags; + }; + + typedef struct { +@@ -183,6 +185,7 @@ static void maprequest(XEvent *e); + static void monocle(Monitor *m); + static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); ++static void nametag(const Arg *arg); + static Client *nexttiled(Client *c); + static void pop(Client *c); + static void propertynotify(XEvent *e); +@@ -433,7 +436,7 @@ buttonpress(XEvent *e) + if (ev->window == selmon->barwin) { + i = x = 0; + do +- x += TEXTW(tags[i]); ++ x += TEXTW(m->tags[i]); + while (ev->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; +@@ -499,6 +502,7 @@ void + cleanupmon(Monitor *mon) + { + Monitor *m; ++ unsigned int i; + + if (mon == mons) + mons = mons->next; +@@ -508,6 +512,10 @@ cleanupmon(Monitor *mon) + } + XUnmapWindow(dpy, mon->barwin); + XDestroyWindow(dpy, mon->barwin); ++ for (i = 0; i <= LENGTH(tags); i++) { ++ free(mon->tags[i]); ++ } ++ free(mon->tags); + free(mon); + } + +@@ -633,6 +641,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -643,6 +652,14 @@ createmon(void) + m->lt[0] = &layouts[0]; + m->lt[1] = &layouts[1 % LENGTH(layouts)]; + strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); ++ ++ m->tags = malloc(MAX_TAGLEN * LENGTH(tags)); ++ ++ for (i = 0; i <= LENGTH(tags); i++) { ++ m->tags[i] = malloc(MAX_TAGLEN); ++ memset(m->tags[i], 0, MAX_TAGLEN); ++ memcpy(m->tags[i], tags[i], MAX_TAGLEN); ++ } + return m; + } + +@@ -720,9 +737,9 @@ drawbar(Monitor *m) + } + x = 0; + for (i = 0; i < LENGTH(tags); i++) { +- w = TEXTW(tags[i]); ++ w = TEXTW(m->tags[i]); + 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); ++ drw_text(drw, x, 0, w, bh, lrpad / 2, m->tags[i], urg & 1 << i); + if (occ & 1 << i) + drw_rect(drw, x + boxs, boxs, boxw, boxw, + m == selmon && selmon->sel && selmon->sel->tags & 1 << i, +@@ -1201,6 +1218,40 @@ movemouse(const Arg *arg) + } + } + ++void ++nametag(const Arg *arg) { ++ char *p, name[MAX_TAGNAME_LEN]; ++ FILE *f; ++ int i; ++ char dmenumon[2]; ++ char cmd[25]; ++ ++ SETDMENUMON(dmenumon); ++ memset(cmd, 0, 25); ++ sprintf(cmd, "dmenu -m %s < /dev/null", dmenumon); ++ errno = 0; // popen(3p) says on failure it "may" set errno ++ ++ if(!(f = popen(cmd, "r"))) { ++ fprintf(stderr, "dwm: popen 'dmenu < /dev/null' failed%s%s\n", errno ? ": " : "", errno ? strerror(errno) : ""); ++ return; ++ } ++ if (!(p = fgets(name, MAX_TAGNAME_LEN, f)) && (i = errno) && ferror(f)) ++ fprintf(stderr, "dwm: fgets failed: %s\n", strerror(i)); ++ if (pclose(f) < 0) ++ fprintf(stderr, "dwm: pclose failed: %s\n", strerror(errno)); ++ if(!p) ++ return; ++ if((p = strchr(name, '\n'))) ++ *p = '\0'; ++ ++ for(i = 0; i < LENGTH(tags); i++) ++ if(selmon->tagset[selmon->seltags] & (1 << i)) { ++ sprintf(selmon->tags[i], TAG_PREPEND, i+1); ++ strcat(selmon->tags[i], name); ++ } ++ drawbars(); ++} ++ + Client * + nexttiled(Client *c) + { diff --git a/patches/dwm-movestack-20211115-a786211.diff b/patches/dwm-movestack-20211115-a786211.diff new file mode 100644 index 0000000..134abb8 --- /dev/null +++ b/patches/dwm-movestack-20211115-a786211.diff @@ -0,0 +1,95 @@ +From 9a4037dc0ef56f91c009317e78e9e3790dafbb58 Mon Sep 17 00:00:00 2001 +From: BrunoCooper17 +Date: Mon, 15 Nov 2021 14:04:53 -0600 +Subject: [PATCH] MoveStack patch + +This plugin allows you to move clients around in the stack and swap them +with the master. It emulates the behavior off mod+shift+j and mod+shift+k +in Xmonad. movestack(+1) will swap the client with the current focus with +the next client. movestack(-1) will swap the client with the current focus +with the previous client. +--- + config.def.h | 3 +++ + movestack.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + create mode 100644 movestack.c + +diff --git a/config.def.h b/config.def.h +index a2ac963..33efa5b 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -60,6 +60,7 @@ 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 "movestack.c" + static Key keys[] = { + /* modifier key function argument */ + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, +@@ -71,6 +72,8 @@ static Key keys[] = { + { 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} }, +diff --git a/movestack.c b/movestack.c +new file mode 100644 +index 0000000..520f4ae +--- /dev/null ++++ b/movestack.c +@@ -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 +-- +2.33.1 + diff --git a/patches/dwm-pertag-20200914-61bb8b2.diff b/patches/dwm-pertag-20200914-61bb8b2.diff new file mode 100644 index 0000000..c8d7fbc --- /dev/null +++ b/patches/dwm-pertag-20200914-61bb8b2.diff @@ -0,0 +1,177 @@ +diff --git a/dwm.c b/dwm.c +index 664c527..ac8e4ec 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -111,6 +111,7 @@ typedef struct { + void (*arrange)(Monitor *); + } Layout; + ++typedef struct Pertag Pertag; + struct Monitor { + char ltsymbol[16]; + float mfact; +@@ -130,6 +131,7 @@ struct Monitor { + Monitor *next; + Window barwin; + const Layout *lt[2]; ++ Pertag *pertag; + }; + + typedef struct { +@@ -272,6 +274,15 @@ static Window root, wmcheckwin; + /* 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]; }; + +@@ -632,6 +643,7 @@ Monitor * + createmon(void) + { + Monitor *m; ++ unsigned int i; + + m = ecalloc(1, sizeof(Monitor)); + m->tagset[0] = m->tagset[1] = 1; +@@ -642,6 +654,20 @@ createmon(void) + 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; + } + +@@ -967,7 +993,7 @@ grabkeys(void) + 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); + } + +@@ -1502,9 +1528,9 @@ 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); +@@ -1523,7 +1549,7 @@ setmfact(const Arg *arg) + 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); + } + +@@ -1702,7 +1728,7 @@ tile(Monitor *m) + 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); +@@ -1741,9 +1767,33 @@ void + 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); + } +@@ -2038,11 +2088,37 @@ updatewmhints(Client *c) + 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); + } diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..2f75a82 --- /dev/null +++ b/test.sh @@ -0,0 +1,5 @@ +#!/bin/sh +Xephyr :1 -screen 1600x900 -ac -reset -terminate & +sleep 0.5 +export DISPLAY=:1 +exec ./dwm diff --git a/todo.adoc b/todo.adoc new file mode 100644 index 0000000..e69de29 diff --git a/transient.c b/transient.c new file mode 100644 index 0000000..040adb5 --- /dev/null +++ b/transient.c @@ -0,0 +1,42 @@ +/* cc transient.c -o transient -lX11 */ + +#include +#include +#include +#include + +int main(void) { + Display *d; + Window r, f, t = None; + XSizeHints h; + XEvent e; + + d = XOpenDisplay(NULL); + if (!d) + exit(1); + r = DefaultRootWindow(d); + + f = XCreateSimpleWindow(d, r, 100, 100, 400, 400, 0, 0, 0); + h.min_width = h.max_width = h.min_height = h.max_height = 400; + h.flags = PMinSize | PMaxSize; + XSetWMNormalHints(d, f, &h); + XStoreName(d, f, "floating"); + XMapWindow(d, f); + + XSelectInput(d, f, ExposureMask); + while (1) { + XNextEvent(d, &e); + + if (t == None) { + sleep(5); + t = XCreateSimpleWindow(d, r, 50, 50, 100, 100, 0, 0, 0); + XSetTransientForHint(d, t, f); + XStoreName(d, t, "transient"); + XMapWindow(d, t); + XSelectInput(d, t, ExposureMask); + } + } + + XCloseDisplay(d); + exit(0); +} diff --git a/util.c b/util.c new file mode 100644 index 0000000..8e26a51 --- /dev/null +++ b/util.c @@ -0,0 +1,37 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + int saved_errno; + + saved_errno = errno; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') + fprintf(stderr, " %s", strerror(saved_errno)); + fputc('\n', stderr); + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..c0a50d4 --- /dev/null +++ b/util.h @@ -0,0 +1,9 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) +#define LENGTH(X) (sizeof (X) / sizeof (X)[0]) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/util.o b/util.o new file mode 100644 index 0000000000000000000000000000000000000000..1e56810df8d7ff7ed4c0719577e8944029dfda5c GIT binary patch literal 2440 zcmbuAO>7%Q6vv+vmzXxS(~6=X8Engg9{Ez@bM(U2!NkZU}Kf<;0;9MZLA4@cy$i>2zyf=#!q`o8SAG z*_qk(hsSF#jHOa!Bt_5BospqLnFsFp73EeaOS|c2?$KL|C*A0_`_c7ZnY)n`1vfe+ zN^VpWM{7}4RBG(G7Y6d_T(K$^a>bf>J6Aj)ClwJOpj zO{P@&`ZFYF-RSpt@c#JLTqvTy;?8k1)!N)K{YeDpGi92D{cgt=y+@YS!j_l>Z zUaOBb zYV#%hlxex;Q@!p7L8n{iYvjpRw?loo%!K*7&0f&z$Y#B`9NDrGB5Bpo;<0%TAe` zMt619?{G?|D}9GQ5r_NIYn;{NGo8@s1>K$>$a7qZhW<*u8F;I{S=s&BJeOlDtiK7> zu%dp63VNUP(0apTvY!q1wGt1ve=~qA3%VX>4%d94rV(U$oE6-B55XWSiFsoh=NcdO z5G#8A-}*AwVyYQdTg?2pd$yQm8C&5X{F0r-zSIf$p4npRjD3-V#<%mn8U}~to@R#k z+rF#l^Np_0V_=Ede}mp(2dT;WpLG3#Nz^u3-)27S{NK=j9cEu}{`jt#dUO86=EhXh sdO_=Y9XJQHOxB}k^6VMW=Nfh}utc}pCVSW$R{xn^|M~6A*h$v^2l;G4TmS$7 literal 0 HcmV?d00001 -- 2.43.0