From: A-Larsen Date: Tue, 6 Oct 2026 14:21:54 +0000 (-0400) Subject: first commit X-Git-Url: https://nyquistdev.net/git/?a=commitdiff_plain;h=c8015672f49cd563fd840790ec1ef83b555c762f;p=dwm-config.git first commit --- c8015672f49cd563fd840790ec1ef83b555c762f 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 0000000..ee27507 Binary files /dev/null and b/drw.o differ diff --git a/dwm b/dwm new file mode 100755 index 0000000..cf10240 Binary files /dev/null and b/dwm differ 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 0000000..0182daa Binary files /dev/null and b/dwm.o differ diff --git a/dwm.png b/dwm.png new file mode 100644 index 0000000..b1f9ba7 Binary files /dev/null and b/dwm.png differ diff --git a/dwm.tar.gz b/dwm.tar.gz new file mode 100644 index 0000000..8f40069 Binary files /dev/null and b/dwm.tar.gz differ 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 0000000..1e56810 Binary files /dev/null and b/util.o differ