diff --git a/config.def.h b/config.def.h index 6fff8ea..96e3ebc 100644 --- a/config.def.h +++ b/config.def.h @@ -26,6 +26,7 @@ static const char *colors[][3] = { /* fg bg border */ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + [SchemeHid] = { col_cyan, col_gray1, col_cyan }, }; typedef struct { diff --git a/config.def.h.rej b/config.def.h.rej index d265e11..ee73f24 100644 --- a/config.def.h.rej +++ b/config.def.h.rej @@ -1,10 +1,33 @@ --- config.def.h +++ config.def.h -@@ -103,6 +103,7 @@ static const Button buttons[] = { +@@ -67,8 +68,10 @@ static const Key keys[] = { + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, +- { MODKEY, XK_j, focusstack, {.i = +1 } }, +- { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY, XK_j, focusstackvis, {.i = +1 } }, ++ { MODKEY, XK_k, focusstackvis, {.i = -1 } }, ++ { MODKEY|ShiftMask, XK_j, focusstackhid, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_k, focusstackhid, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, +@@ -87,6 +90,9 @@ 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_s, show, {0} }, ++ { MODKEY|ShiftMask, XK_s, showall, {0} }, ++ { MODKEY, XK_h, hide, {0} }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -105,6 +111,7 @@ 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} }, ++ { ClkWinTitle, 0, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, { ClkClientWin, MODKEY, Button1, movemouse, {0} }, - { ClkClientWin, MODKEY, Button2, togglefloating, {0} }, diff --git a/config.h b/config.h index a76742d..99f12f7 100644 --- a/config.h +++ b/config.h @@ -27,6 +27,7 @@ static const char *colors[][3] = { /* fg bg border */ [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, [SchemeSel] = { col_gray4, col_cyan, col_cyan }, + [SchemeHid] = { col_cyan, col_gray1, col_cyan }, }; typedef struct { @@ -69,6 +70,7 @@ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] static const int nmaster = 1; /* number of clients in master area */ static const int resizehints = 1; /* 1 means respect size hints in tiled resizals */ static const int refreshrate = 144; /* refresh rate (per second) for client move/resize */ +static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ #define FORCE_VSPLIT 1 /* nrowgrid layout: force two clients to always split vertically */ #include "vanitygaps.c" @@ -190,6 +192,10 @@ static const Key keys[] = { { MODKEY, XK_v, setlayout, {.v = &layouts[1]} }, { MODKEY, XK_c, setlayout, {.v = &layouts[2]} }, + { MODKEY, XK_s, show, {0} }, + { MODKEY|ShiftMask, XK_s, showall, {0} }, + { MODKEY|ShiftMask, XK_h, hide, {0} }, + { MODKEY, XK_space, setlayout, {0} }, { MODKEY|ShiftMask, XK_space, togglefloating, {0} }, { MODKEY, XK_f, togglefullscreen, {0} }, @@ -227,5 +233,6 @@ static const Button buttons[] = { { ClkTagBar, 0, Button3, toggleview, {0} }, { ClkTagBar, MODKEY, Button1, tag, {0} }, { ClkTagBar, MODKEY, Button3, toggletag, {0} }, + { ClkWinTitle, 0, Button1, togglewin, {0} }, }; diff --git a/dwm b/dwm index 399ed88..ebab94c 100755 Binary files a/dwm and b/dwm differ diff --git a/dwm.c b/dwm.c index 9b3a0fe..ec0c5c3 100644 --- a/dwm.c +++ b/dwm.c @@ -51,6 +51,7 @@ * MAX(0, MIN((y)+(h),(m)->wy+(m)->wh) - MAX((y),(m)->wy))) #define ISVISIBLEONTAG(C, T) ((C->tags & T)) #define ISVISIBLE(C) ISVISIBLEONTAG(C, C->mon->tagset[C->mon->seltags]) +#define HIDDEN(C) ((getstate(C->win) == IconicState)) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define WIDTH(X) ((X)->w + 2 * (X)->bw) #define HEIGHT(X) ((X)->h + 2 * (X)->bw) @@ -76,7 +77,7 @@ /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ -enum { SchemeNorm, SchemeSel }; /* color schemes */ +enum { SchemeNorm, SchemeSel, SchemeHid }; /* color schemes */ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, NetWMFullscreen, NetActiveWindow, NetWMWindowType, @@ -139,6 +140,8 @@ struct Monitor { int nmaster; int num; int by; /* bar geometry */ + int btw; /* width of tasks portion of bar */ + int bt; /* number of tasks */ int mx, my, mw, mh; /* screen size */ int wx, wy, ww, wh; /* window area */ int gappih; /* horizontal gap between windows */ @@ -150,6 +153,7 @@ struct Monitor { unsigned int tagset[2]; int showbar; int topbar; + int hidsel; Client *clients; Client *sel; Client *stack; @@ -203,7 +207,9 @@ static void focus(Client *c); static void focusdir(const Arg *arg); static void focusin(XEvent *e); static void focusmon(const Arg *arg); -static void focusstack(const Arg *arg); +static void focusstackvis(const Arg *arg); +static void focusstackhid(const Arg *arg); +static void focusstack(int inc, int vis); static Atom getatomprop(Client *c, Atom prop); static int getrootptr(int *x, int *y); static long getstate(Window w); @@ -211,6 +217,8 @@ static unsigned int getsystraywidth(); 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 hide(const Arg *arg); +static void hidewin(Client *c); static void incnmaster(const Arg *arg); static void keypress(XEvent *e); static void killclient(const Arg *arg); @@ -253,6 +261,9 @@ static void setnumdesktops(void); static void setup(void); static void setviewport(void); static void seturgent(Client *c, int urg); +static void show(const Arg *arg); +static void showall(const Arg *arg); +static void showwin(Client *c); static void showhide(Client *c); static void sighup(int unused); static void sigterm(int unused); @@ -267,6 +278,7 @@ static void togglefullscreen(const Arg *arg); static void togglescratch(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); +static void togglewin(const Arg *arg); static void unfocus(Client *c, int setfocus); static void unmanage(Client *c, int destroyed); static void unmapnotify(XEvent *e); @@ -565,10 +577,24 @@ buttonpress(XEvent *e) arg.ui = 1 << i; } else if (ev->x < x + TEXTW(selmon->ltsymbol)) click = ClkLtSymbol; - else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) + else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth() + lrpad - 2) click = ClkStatusText; - else - click = ClkWinTitle; + else { + x += TEXTW(selmon->ltsymbol); + c = m->clients; + + if (c) { + do { + if (!ISVISIBLE(c)) + continue; + else + x +=(1.0 / (double)m->bt) * m->btw; + } while (ev->x > x && (c = c->next)); + + click = ClkWinTitle; + arg.v = c; + } + } } else if ((c = wintoclient(ev->window))) { focus(c); restack(selmon); @@ -578,7 +604,7 @@ buttonpress(XEvent *e) 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); + buttons[i].func((click == ClkTagBar || click == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); } void @@ -909,7 +935,7 @@ dirtomon(int dir) void drawbar(Monitor *m) { - int x, w, tw = 0, stw = 0; + int x, w, tw = 0, stw = 0, n = 0, scm; int boxs = drw->fonts->h / 9; int boxw = drw->fonts->h / 6 + 2; unsigned int i, occ = 0, urg = 0; @@ -930,6 +956,8 @@ drawbar(Monitor *m) resizebarwin(m); for (c = m->clients; c; c = c->next) { + if (ISVISIBLE(c)) + n++; occ |= c->tags; if (c->isurgent) urg |= c->tags; @@ -950,16 +978,36 @@ drawbar(Monitor *m) x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); if ((w = m->ww - tw - stw - x) > bh) { - if (m->sel) { - drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); - drw_text(drw, x, 0, w, bh, lrpad / 2, m->sel->name, 0); - if (m->sel->isfloating) - drw_rect(drw, x + boxs, boxs, boxw, boxw, m->sel->isfixed, 0); + if (n > 0) { + int remainder = w % n; + int tabw = (1.0 / (double)n) * w + 1; + for (c = m->clients; c; c = c->next) { + if (!ISVISIBLE(c)) + continue; + if (m->sel == c) + scm = SchemeSel; + else if (HIDDEN(c)) + scm = SchemeHid; + else + scm = SchemeNorm; + drw_setscheme(drw, scheme[scm]); + + if (remainder >= 0) { + if (remainder == 0) { + tabw--; + } + remainder--; + } + drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0); + x += tabw; + } } else { drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, x, 0, w, bh, 1, 1); } } + m->bt = n; + m->btw = w; drw_map(drw, m->barwin, 0, 0, m->ww - stw, bh); } @@ -1008,10 +1056,16 @@ void focus(Client *c) { if (!c || !ISVISIBLE(c)) - for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); + for (c = selmon->stack; c && (!ISVISIBLE(c) || HIDDEN(c)); c = c->snext); if (selmon->sel && selmon->sel != c) { losefullscreen(c); unfocus(selmon->sel, 0); + if (selmon->hidsel) { + hidewin(selmon->sel); + if (c) + arrange(c->mon); + selmon->hidsel = 0; + } } if (c) { if (c->mon != selmon) @@ -1122,28 +1176,53 @@ focusmon(const Arg *arg) } void -focusstack(const Arg *arg) +focusstackvis(const Arg *arg) { + focusstack(arg->i, 0); +} + +void +focusstackhid(const Arg *arg) { + focusstack(arg->i, 1); +} + +void +focusstack(int inc, int hid) { Client *c = NULL, *i; - if (!selmon->sel || (selmon->sel->isfullscreen && selmon->sel->fakefullscreen != 1)) + // if no client selected AND exclude hidden client; if client selected but fullscreened + if ((!selmon->sel && !hid) || (selmon->sel && selmon->sel->isfullscreen && lockfullscreen)) return; - if (arg->i > 0) { - for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); + if (!selmon->clients) + return; + if (inc > 0) { + if (selmon->sel) + for (c = selmon->sel->next; + c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); + c = c->next); if (!c) - for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + for (c = selmon->clients; + c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); + c = c->next); } else { - for (i = selmon->clients; i != selmon->sel; i = i->next) - if (ISVISIBLE(i)) - c = i; + if (selmon->sel) { + for (i = selmon->clients; i != selmon->sel; i = i->next) + if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) + c = i; + } else + c = selmon->clients; if (!c) for (; i; i = i->next) - if (ISVISIBLE(i)) + if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) c = i; } if (c) { focus(c); restack(selmon); + if (HIDDEN(c)) { + showwin(c); + c->mon->hidsel = 1; + } } } @@ -1281,6 +1360,36 @@ grabkeys(void) } } +void +hide(const Arg *arg) +{ + hidewin(selmon->sel); + focus(NULL); + arrange(selmon); +} + +void +hidewin(Client *c) { + if (!c || HIDDEN(c)) + return; + + Window w = c->win; + static XWindowAttributes ra, ca; + + // more or less taken directly from blackbox's hide() function + XGrabServer(dpy); + XGetWindowAttributes(dpy, root, &ra); + XGetWindowAttributes(dpy, w, &ca); + // prevent UnmapNotify events + XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); + XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); + XUnmapWindow(dpy, w); + setclientstate(c, IconicState); + XSelectInput(dpy, root, ra.your_event_mask); + XSelectInput(dpy, w, ca.your_event_mask); + XUngrabServer(dpy); +} + void incnmaster(const Arg *arg) { @@ -1415,14 +1524,16 @@ manage(Window w, XWindowAttributes *wa) 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 (!HIDDEN(c)) + setclientstate(c, NormalState); if (c->mon == selmon) { losefullscreen(c); unfocus(selmon->sel, 0); } c->mon->sel = c; arrange(c->mon); - XMapWindow(dpy, c->win); + if (!HIDDEN(c)) + XMapWindow(dpy, c->win); focus(NULL); } @@ -1560,7 +1671,7 @@ nexttagged(Client *c) { Client * nexttiled(Client *c) { - for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); + for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); return c; } @@ -1721,6 +1832,16 @@ propertynotify(XEvent *e) void quit(const Arg *arg) { + // fix: reloading dwm keeps all the hidden clients hidden + Monitor *m; + Client *c; + for (m = mons; m; m = m->next) { + if (m) { + for (c = m->stack; c; c = c->next) + if (c && HIDDEN(c)) showwin(c); + } + } + if(arg->i) restart = 1; running = 0; } @@ -2261,6 +2382,42 @@ seturgent(Client *c, int urg) XFree(wmh); } +void +show(const Arg *arg) +{ + if (selmon->hidsel) + selmon->hidsel = 0; + showwin(selmon->sel); +} + +void +showall(const Arg *arg) +{ + Client *c = NULL; + selmon->hidsel = 0; + for (c = selmon->clients; c; c = c->next) { + if (ISVISIBLE(c)) + showwin(c); + } + if (!selmon->sel) { + for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); + if (c) + focus(c); + } + restack(selmon); +} + +void +showwin(Client *c) +{ + if (!c || !HIDDEN(c)) + return; + + XMapWindow(dpy, c->win); + setclientstate(c, NormalState); + arrange(c->mon); +} + void showhide(Client *c) { @@ -2481,6 +2638,25 @@ toggleview(const Arg *arg) } } +void +togglewin(const Arg *arg) +{ + Client *c = (Client*)arg->v; + + if (!c) + return; + if (c == selmon->sel) { + hidewin(c); + focus(NULL); + arrange(c->mon); + } else { + if (HIDDEN(c)) + showwin(c); + focus(c); + restack(selmon); + } +} + void unfocus(Client *c, int setfocus) { diff --git a/dwm.c.orig b/dwm.c.orig index e8fe533..9b3a0fe 100644 --- a/dwm.c.orig +++ b/dwm.c.orig @@ -83,8 +83,8 @@ enum { NetSupported, NetWMName, NetWMState, NetWMCheck, NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, NetLast }; /* EWMH atoms */ enum { Manager, Xembed, XembedInfo, XLast }; /* Xembed atoms */ enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ -enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkClientWin, - ClkRootWin, ClkLast }; /* clicks */ +enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle, + ClkClientWin, ClkRootWin, ClkLast }; /* clicks */ typedef union { int i; @@ -568,7 +568,7 @@ buttonpress(XEvent *e) else if (ev->x > selmon->ww - (int)TEXTW(stext) - getsystraywidth()) click = ClkStatusText; else - click = ClkStatusText; + click = ClkWinTitle; } else if ((c = wintoclient(ev->window))) { focus(c); restack(selmon); @@ -950,8 +950,15 @@ drawbar(Monitor *m) x = drw_text(drw, x, 0, w, bh, lrpad / 2, m->ltsymbol, 0); if ((w = m->ww - tw - stw - x) > bh) { - drw_setscheme(drw, scheme[SchemeNorm]); - drw_rect(drw, x, 0, w, bh, 1, 1); + 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 - stw, bh); } @@ -1701,8 +1708,11 @@ propertynotify(XEvent *e) drawbars(); break; } - if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) + if (ev->atom == XA_WM_NAME || ev->atom == netatom[NetWMName]) { updatetitle(c); + if (c == c->mon->sel) + drawbar(c->mon); + } if (ev->atom == netatom[NetWMWindowType]) updatewindowtype(c); } diff --git a/dwm.c.rej b/dwm.c.rej index 73d32f8..fc2a28a 100644 --- a/dwm.c.rej +++ b/dwm.c.rej @@ -1,18 +1,226 @@ --- dwm.c +++ dwm.c -@@ -734,8 +734,15 @@ drawbar(Monitor *m) +@@ -50,6 +50,7 @@ + #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 HIDDEN(C) ((getstate(C->win) == IconicState)) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +@@ -170,13 +174,17 @@ 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 void focusstackvis(const Arg *arg); ++static void focusstackhid(const Arg *arg); ++static void focusstack(int inc, int vis); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + 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 hide(const Arg *arg); ++static void hidewin(Client *c); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); +@@ -447,10 +459,25 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ /* 2px right padding */ ++ else if (ev->x > selmon->ww - TEXTW(stext) + lrpad - 2) + click = ClkStatusText; +- else +- click = ClkWinTitle; ++ else { ++ x += TEXTW(selmon->ltsymbol); ++ c = m->clients; ++ ++ if (c) { ++ do { ++ if (!ISVISIBLE(c)) ++ continue; ++ else ++ x +=(1.0 / (double)m->bt) * m->btw; ++ } while (ev->x > x && (c = c->next)); ++ ++ click = ClkWinTitle; ++ arg.v = c; ++ } ++ } + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); +@@ -704,7 +731,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, n = 0, scm; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; +@@ -743,16 +772,36 @@ drawbar(Monitor *m) 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 { +- 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); ++ if (n > 0) { ++ int remainder = w % n; ++ int tabw = (1.0 / (double)n) * w + 1; ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (m->sel == c) ++ scm = SchemeSel; ++ else if (HIDDEN(c)) ++ scm = SchemeHid; ++ else ++ scm = SchemeNorm; ++ drw_setscheme(drw, scheme[scm]); ++ ++ if (remainder >= 0) { ++ if (remainder == 0) { ++ tabw--; ++ } ++ remainder--; ++ } ++ drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0); ++ x += tabw; ++ } + } else { drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, x, 0, w, bh, 1, 1); -+ } + } } ++ m->bt = n; ++ m->btw = w; drw_map(drw, m->barwin, 0, 0, m->ww, bh); } + +@@ -798,9 +847,17 @@ void + focus(Client *c) + { + if (!c || !ISVISIBLE(c)) +- for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ for (c = selmon->stack; c && (!ISVISIBLE(c) || HIDDEN(c)); c = c->snext); ++ if (selmon->sel && selmon->sel != c) { + unfocus(selmon->sel, 0); ++ ++ if (selmon->hidsel) { ++ hidewin(selmon->sel); ++ if (c) ++ arrange(c->mon); ++ selmon->hidsel = 0; ++ } ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -844,28 +901,52 @@ focusmon(const Arg *arg) + } + + void +-focusstack(const Arg *arg) ++focusstackvis(const Arg *arg) { ++ focusstack(arg->i, 0); ++} ++ ++void ++focusstackhid(const Arg *arg) { ++ focusstack(arg->i, 1); ++} ++ ++void ++focusstack(int inc, int hid) + { + Client *c = NULL, *i; +- +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ // if no client selected AND exclude hidden client; if client selected but fullscreened ++ if ((!selmon->sel && !hid) || (selmon->sel && selmon->sel->isfullscreen && lockfullscreen)) + return; +- if (arg->i > 0) { +- for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); ++ if (!selmon->clients) ++ return; ++ if (inc > 0) { ++ if (selmon->sel) ++ for (c = selmon->sel->next; ++ c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); ++ c = c->next); + if (!c) +- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->clients; ++ c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); ++ c = c->next); + } else { +- for (i = selmon->clients; i != selmon->sel; i = i->next) +- if (ISVISIBLE(i)) +- c = i; ++ if (selmon->sel) { ++ for (i = selmon->clients; i != selmon->sel; i = i->next) ++ if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) ++ c = i; ++ } else ++ c = selmon->clients; + if (!c) + for (; i; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) + c = i; + } + if (c) { + focus(c); + restack(selmon); ++ if (HIDDEN(c)) { ++ showwin(c); ++ c->mon->hidsel = 1; ++ } + } + } + +@@ -1117,12 +1228,14 @@ manage(Window w, XWindowAttributes *wa) + 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 (!HIDDEN(c)) ++ setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); +- XMapWindow(dpy, c->win); ++ if (!HIDDEN(c)) ++ XMapWindow(dpy, c->win); + focus(NULL); + } + +@@ -1296,6 +1409,16 @@ propertynotify(XEvent *e) + void + quit(const Arg *arg) + { ++ // fix: reloading dwm keeps all the hidden clients hidden ++ Monitor *m; ++ Client *c; ++ for (m = mons; m; m = m->next) { ++ if (m) { ++ for (c = m->stack; c; c = c->next) ++ if (c && HIDDEN(c)) showwin(c); ++ } ++ } ++ + running = 0; + } + diff --git a/dwm.o b/dwm.o index c84586b..b07d6d5 100644 Binary files a/dwm.o and b/dwm.o differ diff --git a/patches/dwm-awesomebar-20250923-6.6.diff b/patches/dwm-awesomebar-20250923-6.6.diff new file mode 100644 index 0000000..3caa9bc --- /dev/null +++ b/patches/dwm-awesomebar-20250923-6.6.diff @@ -0,0 +1,448 @@ +diff --git a/config.def.h b/config.def.h +index 81c3fc0..6497687 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -16,6 +16,7 @@ static const char *colors[][3] = { + /* fg bg border */ + [SchemeNorm] = { col_gray3, col_gray1, col_gray2 }, + [SchemeSel] = { col_gray4, col_cyan, col_cyan }, ++ [SchemeHid] = { col_cyan, col_gray1, col_cyan }, + }; + + /* tagging */ +@@ -66,8 +67,10 @@ static const Key keys[] = { + { MODKEY, XK_p, spawn, {.v = dmenucmd } }, + { MODKEY|ShiftMask, XK_Return, spawn, {.v = termcmd } }, + { MODKEY, XK_b, togglebar, {0} }, +- { MODKEY, XK_j, focusstack, {.i = +1 } }, +- { MODKEY, XK_k, focusstack, {.i = -1 } }, ++ { MODKEY, XK_j, focusstackvis, {.i = +1 } }, ++ { MODKEY, XK_k, focusstackvis, {.i = -1 } }, ++ { MODKEY|ShiftMask, XK_j, focusstackhid, {.i = +1 } }, ++ { MODKEY|ShiftMask, XK_k, focusstackhid, {.i = -1 } }, + { MODKEY, XK_i, incnmaster, {.i = +1 } }, + { MODKEY, XK_d, incnmaster, {.i = -1 } }, + { MODKEY, XK_h, setmfact, {.f = -0.05} }, +@@ -86,6 +89,9 @@ 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_s, show, {0} }, ++ { MODKEY|ShiftMask, XK_s, showall, {0} }, ++ { MODKEY, XK_h, hide, {0} }, + TAGKEYS( XK_1, 0) + TAGKEYS( XK_2, 1) + TAGKEYS( XK_3, 2) +@@ -104,6 +110,7 @@ static const Button buttons[] = { + /* click event mask button function argument */ + { ClkLtSymbol, 0, Button1, setlayout, {0} }, + { ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} }, ++ { ClkWinTitle, 0, Button1, togglewin, {0} }, + { ClkWinTitle, 0, Button2, zoom, {0} }, + { ClkStatusText, 0, Button2, spawn, {.v = termcmd } }, + { ClkClientWin, MODKEY, Button1, movemouse, {0} }, +diff --git a/dwm.c b/dwm.c +index 4cf07eb..a270d44 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -50,6 +50,7 @@ + #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 HIDDEN(C) ((getstate(C->win) == IconicState)) + #define MOUSEMASK (BUTTONMASK|PointerMotionMask) + #define WIDTH(X) ((X)->w + 2 * (X)->bw) + #define HEIGHT(X) ((X)->h + 2 * (X)->bw) +@@ -58,7 +59,7 @@ + + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ +-enum { SchemeNorm, SchemeSel }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeHid }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ +@@ -116,6 +117,8 @@ struct Monitor { + int nmaster; + int num; + int by; /* bar geometry */ ++ int btw; /* width of tasks portion of bar */ ++ int bt; /* number of tasks */ + int mx, my, mw, mh; /* screen size */ + int wx, wy, ww, wh; /* window area */ + unsigned int seltags; +@@ -123,6 +126,7 @@ struct Monitor { + unsigned int tagset[2]; + int showbar; + int topbar; ++ int hidsel; + Client *clients; + Client *sel; + Client *stack; +@@ -167,13 +171,17 @@ 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 void focusstackvis(const Arg *arg); ++static void focusstackhid(const Arg *arg); ++static void focusstack(int inc, int vis); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); + 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 hide(const Arg *arg); ++static void hidewin(Client *c); + static void incnmaster(const Arg *arg); + static void keypress(XEvent *e); + static void killclient(const Arg *arg); +@@ -203,6 +211,9 @@ 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 show(const Arg *arg); ++static void showall(const Arg *arg); ++static void showwin(Client *c); + static void showhide(Client *c); + static void spawn(const Arg *arg); + static void tag(const Arg *arg); +@@ -212,6 +223,7 @@ 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 togglewin(const Arg *arg); + static void unfocus(Client *c, int setfocus); + static void unmanage(Client *c, int destroyed); + static void unmapnotify(XEvent *e); +@@ -440,10 +452,25 @@ buttonpress(XEvent *e) + arg.ui = 1 << i; + } else if (ev->x < x + TEXTW(selmon->ltsymbol)) + click = ClkLtSymbol; +- else if (ev->x > selmon->ww - (int)TEXTW(stext)) ++ /* 2px right padding */ ++ else if (ev->x > selmon->ww - TEXTW(stext) + lrpad - 2) + click = ClkStatusText; +- else +- click = ClkWinTitle; ++ else { ++ x += TEXTW(selmon->ltsymbol); ++ c = m->clients; ++ ++ if (c) { ++ do { ++ if (!ISVISIBLE(c)) ++ continue; ++ else ++ x +=(1.0 / (double)m->bt) * m->btw; ++ } while (ev->x > x && (c = c->next)); ++ ++ click = ClkWinTitle; ++ arg.v = c; ++ } ++ } + } else if ((c = wintoclient(ev->window))) { + focus(c); + restack(selmon); +@@ -453,7 +480,7 @@ buttonpress(XEvent *e) + 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); ++ buttons[i].func((click == ClkTagBar || click == ClkWinTitle) && buttons[i].arg.i == 0 ? &arg : &buttons[i].arg); + } + + void +@@ -697,7 +724,7 @@ dirtomon(int dir) + void + drawbar(Monitor *m) + { +- int x, w, tw = 0; ++ int x, w, tw = 0, n = 0, scm; + int boxs = drw->fonts->h / 9; + int boxw = drw->fonts->h / 6 + 2; + unsigned int i, occ = 0, urg = 0; +@@ -714,6 +741,8 @@ drawbar(Monitor *m) + } + + for (c = m->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) ++ n++; + occ |= c->tags; + if (c->isurgent) + urg |= c->tags; +@@ -734,16 +763,36 @@ drawbar(Monitor *m) + 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); ++ if (n > 0) { ++ int remainder = w % n; ++ int tabw = (1.0 / (double)n) * w + 1; ++ for (c = m->clients; c; c = c->next) { ++ if (!ISVISIBLE(c)) ++ continue; ++ if (m->sel == c) ++ scm = SchemeSel; ++ else if (HIDDEN(c)) ++ scm = SchemeHid; ++ else ++ scm = SchemeNorm; ++ drw_setscheme(drw, scheme[scm]); ++ ++ if (remainder >= 0) { ++ if (remainder == 0) { ++ tabw--; ++ } ++ remainder--; ++ } ++ drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0); ++ x += tabw; ++ } + } else { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x, 0, w, bh, 1, 1); + } + } ++ m->bt = n; ++ m->btw = w; + drw_map(drw, m->barwin, 0, 0, m->ww, bh); + } + +@@ -789,9 +838,17 @@ void + focus(Client *c) + { + if (!c || !ISVISIBLE(c)) +- for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext); +- if (selmon->sel && selmon->sel != c) ++ for (c = selmon->stack; c && (!ISVISIBLE(c) || HIDDEN(c)); c = c->snext); ++ if (selmon->sel && selmon->sel != c) { + unfocus(selmon->sel, 0); ++ ++ if (selmon->hidsel) { ++ hidewin(selmon->sel); ++ if (c) ++ arrange(c->mon); ++ selmon->hidsel = 0; ++ } ++ } + if (c) { + if (c->mon != selmon) + selmon = c->mon; +@@ -835,28 +892,52 @@ focusmon(const Arg *arg) + } + + void +-focusstack(const Arg *arg) ++focusstackvis(const Arg *arg) { ++ focusstack(arg->i, 0); ++} ++ ++void ++focusstackhid(const Arg *arg) { ++ focusstack(arg->i, 1); ++} ++ ++void ++focusstack(int inc, int hid) + { + Client *c = NULL, *i; +- +- if (!selmon->sel || (selmon->sel->isfullscreen && lockfullscreen)) ++ // if no client selected AND exclude hidden client; if client selected but fullscreened ++ if ((!selmon->sel && !hid) || (selmon->sel && selmon->sel->isfullscreen && lockfullscreen)) + return; +- if (arg->i > 0) { +- for (c = selmon->sel->next; c && !ISVISIBLE(c); c = c->next); ++ if (!selmon->clients) ++ return; ++ if (inc > 0) { ++ if (selmon->sel) ++ for (c = selmon->sel->next; ++ c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); ++ c = c->next); + if (!c) +- for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ for (c = selmon->clients; ++ c && (!ISVISIBLE(c) || (!hid && HIDDEN(c))); ++ c = c->next); + } else { +- for (i = selmon->clients; i != selmon->sel; i = i->next) +- if (ISVISIBLE(i)) +- c = i; ++ if (selmon->sel) { ++ for (i = selmon->clients; i != selmon->sel; i = i->next) ++ if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) ++ c = i; ++ } else ++ c = selmon->clients; + if (!c) + for (; i; i = i->next) +- if (ISVISIBLE(i)) ++ if (ISVISIBLE(i) && !(!hid && HIDDEN(i))) + c = i; + } + if (c) { + focus(c); + restack(selmon); ++ if (HIDDEN(c)) { ++ showwin(c); ++ c->mon->hidsel = 1; ++ } + } + } + +@@ -976,6 +1057,36 @@ grabkeys(void) + } + } + ++void ++hide(const Arg *arg) ++{ ++ hidewin(selmon->sel); ++ focus(NULL); ++ arrange(selmon); ++} ++ ++void ++hidewin(Client *c) { ++ if (!c || HIDDEN(c)) ++ return; ++ ++ Window w = c->win; ++ static XWindowAttributes ra, ca; ++ ++ // more or less taken directly from blackbox's hide() function ++ XGrabServer(dpy); ++ XGetWindowAttributes(dpy, root, &ra); ++ XGetWindowAttributes(dpy, w, &ca); ++ // prevent UnmapNotify events ++ XSelectInput(dpy, root, ra.your_event_mask & ~SubstructureNotifyMask); ++ XSelectInput(dpy, w, ca.your_event_mask & ~StructureNotifyMask); ++ XUnmapWindow(dpy, w); ++ setclientstate(c, IconicState); ++ XSelectInput(dpy, root, ra.your_event_mask); ++ XSelectInput(dpy, w, ca.your_event_mask); ++ XUngrabServer(dpy); ++} ++ + void + incnmaster(const Arg *arg) + { +@@ -1078,12 +1189,14 @@ manage(Window w, XWindowAttributes *wa) + 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 (!HIDDEN(c)) ++ setclientstate(c, NormalState); + if (c->mon == selmon) + unfocus(selmon->sel, 0); + c->mon->sel = c; + arrange(c->mon); +- XMapWindow(dpy, c->win); ++ if (!HIDDEN(c)) ++ XMapWindow(dpy, c->win); + focus(NULL); + } + +@@ -1204,7 +1317,7 @@ movemouse(const Arg *arg) + Client * + nexttiled(Client *c) + { +- for (; c && (c->isfloating || !ISVISIBLE(c)); c = c->next); ++ for (; c && (c->isfloating || !ISVISIBLE(c) || HIDDEN(c)); c = c->next); + return c; + } + +@@ -1257,6 +1370,16 @@ propertynotify(XEvent *e) + void + quit(const Arg *arg) + { ++ // fix: reloading dwm keeps all the hidden clients hidden ++ Monitor *m; ++ Client *c; ++ for (m = mons; m; m = m->next) { ++ if (m) { ++ for (c = m->stack; c; c = c->next) ++ if (c && HIDDEN(c)) showwin(c); ++ } ++ } ++ + running = 0; + } + +@@ -1625,6 +1748,42 @@ seturgent(Client *c, int urg) + XFree(wmh); + } + ++void ++show(const Arg *arg) ++{ ++ if (selmon->hidsel) ++ selmon->hidsel = 0; ++ showwin(selmon->sel); ++} ++ ++void ++showall(const Arg *arg) ++{ ++ Client *c = NULL; ++ selmon->hidsel = 0; ++ for (c = selmon->clients; c; c = c->next) { ++ if (ISVISIBLE(c)) ++ showwin(c); ++ } ++ if (!selmon->sel) { ++ for (c = selmon->clients; c && !ISVISIBLE(c); c = c->next); ++ if (c) ++ focus(c); ++ } ++ restack(selmon); ++} ++ ++void ++showwin(Client *c) ++{ ++ if (!c || !HIDDEN(c)) ++ return; ++ ++ XMapWindow(dpy, c->win); ++ setclientstate(c, NormalState); ++ arrange(c->mon); ++} ++ + void + showhide(Client *c) + { +@@ -1761,6 +1920,25 @@ toggleview(const Arg *arg) + } + } + ++void ++togglewin(const Arg *arg) ++{ ++ Client *c = (Client*)arg->v; ++ ++ if (!c) ++ return; ++ if (c == selmon->sel) { ++ hidewin(c); ++ focus(NULL); ++ arrange(c->mon); ++ } else { ++ if (HIDDEN(c)) ++ showwin(c); ++ focus(c); ++ restack(selmon); ++ } ++} ++ + void + unfocus(Client *c, int setfocus) + { diff --git a/patches/dwm-winicon-6.6.diff b/patches/dwm-winicon-6.6.diff new file mode 100644 index 0000000..2667572 --- /dev/null +++ b/patches/dwm-winicon-6.6.diff @@ -0,0 +1,429 @@ +From 6aafee64cace33c1afc02c087397593350194866 Mon Sep 17 00:00:00 2001 +From: Bakkeby +Date: Thu, 18 Sep 2025 22:29:40 +0200 +Subject: [PATCH] Winicon patch (without imlib2 dependency) + +--- + config.def.h | 2 + + config.mk | 4 +- + drw.c | 11 +++ + drw.h | 3 + + dwm.c | 198 ++++++++++++++++++++++++++++++++++++++++++++++++++- + 5 files changed, 215 insertions(+), 3 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 9efa774..c2a6e47 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -5,6 +5,8 @@ static const unsigned int borderpx = 1; /* border pixel of windows */ + static const unsigned int snap = 32; /* snap pixel */ + static const int showbar = 1; /* 0 means no bar */ + static const int topbar = 1; /* 0 means bottom bar */ ++static int iconsize = 16; /* icon size */ ++static int iconspacing = 5; /* space between icon and title */ + static const char *fonts[] = { "monospace:size=10" }; + static const char dmenufont[] = "monospace:size=10"; + static const char col_gray1[] = "#222222"; +diff --git a/config.mk b/config.mk +index b469a2b..bbea09f 100644 +--- a/config.mk ++++ b/config.mk +@@ -10,6 +10,8 @@ MANPREFIX = ${PREFIX}/share/man + X11INC = /usr/X11R6/include + X11LIB = /usr/X11R6/lib + ++XRENDER = -lXrender ++ + # Xinerama, comment if you don't want it + XINERAMALIBS = -lXinerama + XINERAMAFLAGS = -DXINERAMA +@@ -23,7 +25,7 @@ FREETYPEINC = /usr/include/freetype2 + + # includes and libs + INCS = -I${X11INC} -I${FREETYPEINC} +-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ++LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} ${XRENDER} + + # flags + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} +diff --git a/drw.c b/drw.c +index c41e6af..cfa769f 100644 +--- a/drw.c ++++ b/drw.c +@@ -57,6 +57,7 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h + drw->w = w; + drw->h = h; + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); ++ drw->picture = XRenderCreatePicture(dpy, drw->drawable, XRenderFindVisualFormat(dpy, DefaultVisual(dpy, screen)), 0, NULL); + drw->gc = XCreateGC(dpy, root, 0, NULL); + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); + +@@ -71,6 +72,8 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) + + drw->w = w; + drw->h = h; ++ if (drw->picture) ++ XRenderFreePicture(drw->dpy, drw->picture); + if (drw->drawable) + XFreePixmap(drw->dpy, drw->drawable); + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); +@@ -392,6 +395,14 @@ drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) + XSync(drw->dpy, False); + } + ++void ++drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic) ++{ ++ if (!drw) ++ return; ++ XRenderComposite(drw->dpy, PictOpOver, pic, None, drw->picture, 0, 0, 0, 0, x, y, w, h); ++} ++ + unsigned int + drw_fontset_getwidth(Drw *drw, const char *text) + { +diff --git a/drw.h b/drw.h +index 6471431..ff75898 100644 +--- a/drw.h ++++ b/drw.h +@@ -21,6 +21,7 @@ typedef struct { + int screen; + Window root; + Drawable drawable; ++ Picture picture; + GC gc; + Clr *scheme; + Fnt *fonts; +@@ -56,3 +57,5 @@ int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned in + + /* Map functions */ + void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); ++void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic); ++ +diff --git a/dwm.c b/dwm.c +index 1443802..62ea0e6 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -59,7 +60,7 @@ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMState, NetWMCheck, ++enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +@@ -92,6 +93,8 @@ struct Client { + int bw, oldbw; + unsigned int tags; + int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; ++ unsigned int icw, ich; ++ Picture icon; + Client *next; + Client *snext; + Monitor *mon; +@@ -147,6 +150,7 @@ static void arrange(Monitor *m); + static void arrangemon(Monitor *m); + static void attach(Client *c); + static void attachstack(Client *c); ++static uint32_t *bilinear_scale(const uint32_t *src, int sw, int sh, uint32_t dw, uint32_t dh); + static void buttonpress(XEvent *e); + static void checkotherwm(void); + static void cleanup(void); +@@ -168,9 +172,11 @@ static void focus(Client *c); + static void focusin(XEvent *e); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); ++static void freeicon(Client *c); + static Atom getatomprop(Client *c, Atom prop); + static int getrootptr(int *x, int *y); + static long getstate(Window w); ++static Picture geticonprop(Display *dpy, Window w, int iconsize, unsigned int *icw, unsigned int *ich); + static int gettextprop(Window w, Atom atom, char *text, unsigned int size); + static void grabbuttons(Client *c, int focused); + static void grabkeys(void); +@@ -185,6 +191,7 @@ static void motionnotify(XEvent *e); + static void movemouse(const Arg *arg); + static Client *nexttiled(Client *c); + static void pop(Client *c); ++static uint32_t prealpha(uint32_t p); + static void propertynotify(XEvent *e); + static void quit(const Arg *arg); + static Monitor *recttomon(int x, int y, int w, int h); +@@ -219,6 +226,7 @@ static void updatebarpos(Monitor *m); + static void updatebars(void); + static void updateclientlist(void); + static int updategeom(void); ++static void updateicon(Client *c); + static void updatenumlockmask(void); + static void updatesizehints(Client *c); + static void updatestatus(void); +@@ -414,6 +422,57 @@ attachstack(Client *c) + c->mon->stack = c; + } + ++uint32_t * ++bilinear_scale(const uint32_t *src, int sw, int sh, unsigned int dw, unsigned int dh) ++{ ++ uint32_t *dst = calloc(dw * dh, sizeof(uint32_t)); ++ if (!dst) return NULL; ++ ++ for (int y = 0; y < dh; y++) { ++ /* Source position (in fixed-point) */ ++ float sy = (y + 0.5f) * sh / (float)dh - 0.5f; ++ int y0 = (sy < 0) ? (int)(sy - 1) : (int)(sy);; ++ int y1 = y0 + 1; ++ float fy = sy - y0; ++ if (y0 < 0) { y0 = 0; y1 = 0; fy = 0; } ++ if (y1 >= sh) { y1 = sh-1; y0 = y1; fy = 0; } ++ ++ for (int x = 0; x < dw; x++) { ++ float sx = (x + 0.5f) * sw / (float)dw - 0.5f; ++ int x0 = (sx < 0) ? (int)(sx - 1) : (int)(sx); ++ int x1 = x0 + 1; ++ float fx = sx - x0; ++ if (x0 < 0) { x0 = 0; x1 = 0; fx = 0; } ++ if (x1 >= sw) { x1 = sw-1; x0 = x1; fx = 0; } ++ ++ uint32_t p00 = prealpha(src[y0*sw + x0]); ++ uint32_t p10 = prealpha(src[y0*sw + x1]); ++ uint32_t p01 = prealpha(src[y1*sw + x0]); ++ uint32_t p11 = prealpha(src[y1*sw + x1]); ++ ++ /* Extract premultiplied channels */ ++ int a00 = p00 >> 24, r00 = (p00 >> 16) & 0xFF, g00 = (p00 >> 8) & 0xFF, b00 = p00 & 0xFF; ++ int a10 = p10 >> 24, r10 = (p10 >> 16) & 0xFF, g10 = (p10 >> 8) & 0xFF, b10 = p10 & 0xFF; ++ int a01 = p01 >> 24, r01 = (p01 >> 16) & 0xFF, g01 = (p01 >> 8) & 0xFF, b01 = p01 & 0xFF; ++ int a11 = p11 >> 24, r11 = (p11 >> 16) & 0xFF, g11 = (p11 >> 8) & 0xFF, b11 = p11 & 0xFF; ++ ++ /* Bilinear weights */ ++ float w00 = (1.0f - fx) * (1.0f - fy); ++ float w10 = fx * (1.0f - fy); ++ float w01 = (1.0f - fx) * fy; ++ float w11 = fx * fy; ++ ++ int a = (int)(a00*w00 + a10*w10 + a01*w01 + a11*w11 + 0.5f); ++ int r = (int)(r00*w00 + r10*w10 + r01*w01 + r11*w11 + 0.5f); ++ int g = (int)(g00*w00 + g10*w10 + g01*w01 + g11*w11 + 0.5f); ++ int b = (int)(b00*w00 + b10*w10 + b01*w01 + b11*w11 + 0.5f); ++ ++ dst[y*dw + x] = (a << 24) | (r << 16) | (g << 8) | b; ++ } ++ } ++ return dst; ++} ++ + void + buttonpress(XEvent *e) + { +@@ -736,7 +795,12 @@ drawbar(Monitor *m) + 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->icon) { ++ drw_text(drw, x, 0, w, bh, lrpad / 2 + m->sel->icw + iconspacing, m->sel->name, 0); ++ drw_pic(drw, x + lrpad / 2, (bh - m->sel->ich) / 2, m->sel->icw, m->sel->ich, m->sel->icon); ++ } else { ++ 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 { +@@ -860,6 +924,15 @@ focusstack(const Arg *arg) + } + } + ++void ++freeicon(Client *c) ++{ ++ if (c->icon) { ++ XRenderFreePicture(dpy, c->icon); ++ c->icon = None; ++ } ++} ++ + Atom + getatomprop(Client *c, Atom prop) + { +@@ -904,6 +977,104 @@ getstate(Window w) + return result; + } + ++/* geticonprop: read, scale, and return an XRender Picture */ ++Picture ++geticonprop(Display *dpy, Window win, int iconsize, unsigned int *icw, unsigned int *ich) { ++ Atom net_wm_icon = XInternAtom(dpy, "_NET_WM_ICON", False); ++ Atom actual_type; ++ int actual_format; ++ unsigned long nitems, bytes_after; ++ unsigned char *data = NULL; ++ Picture pict = None; ++ ++ if (XGetWindowProperty(dpy, win, net_wm_icon, 0, LONG_MAX, False, AnyPropertyType, ++ &actual_type, &actual_format, &nitems, &bytes_after, &data) != Success || !data) { ++ return None; ++ } ++ ++ unsigned long *p = (unsigned long *)data; ++ unsigned long *end = p + nitems; ++ ++ /* Pick icon closest to desired size */ ++ unsigned long *best = NULL; ++ unsigned long best_w = 0, best_h = 0; ++ unsigned long best_diff = ~0UL; ++ ++ while (p + 2 < end) { ++ unsigned long w0 = *p++; ++ unsigned long h0 = *p++; ++ if (w0 == 0 || h0 == 0 || p + w0*h0 > end) break; ++ ++ unsigned long diff = (w0 > (unsigned long)iconsize ? w0 - iconsize : iconsize - w0) ++ + (h0 > (unsigned long)iconsize ? h0 - iconsize : iconsize - h0); ++ ++ if (diff < best_diff) { ++ best_diff = diff; ++ best_w = w0; ++ best_h = h0; ++ best = p; ++ } ++ p += w0 * h0; ++ } ++ ++ if (!best) { ++ XFree(data); ++ return None; ++ } ++ ++ /* Copy into fixed 32-bit array */ ++ uint32_t *src = malloc(best_w * best_h * sizeof(uint32_t)); ++ for (size_t i = 0; i < best_w * best_h; i++) ++ src[i] = (uint32_t)best[i]; ++ ++ /* Scale */ ++ int dst_w, dst_h; ++ if (best_w > best_h) { ++ dst_w = iconsize; ++ dst_h = best_h * iconsize / best_w; ++ } else { ++ dst_h = iconsize; ++ dst_w = best_w * iconsize / best_h; ++ } ++ ++ uint32_t *scaled = bilinear_scale(src, best_w, best_h, dst_w, dst_h); ++ free(src); ++ XFree(data); ++ if (!scaled) return None; ++ ++ /* Create pixmap and upload pixels */ ++ Pixmap pm = XCreatePixmap(dpy, root, dst_w, dst_h, 32); ++ ++ XImage img; ++ memset(&img, 0, sizeof img); ++ img.width = dst_w; ++ img.height = dst_h; ++ img.format = ZPixmap; ++ img.data = (char*)scaled; ++ img.byte_order = LSBFirst; ++ img.bitmap_unit = 32; ++ img.bitmap_bit_order = LSBFirst; ++ img.bitmap_pad = 32; ++ img.depth = 32; ++ img.bits_per_pixel = 32; ++ img.bytes_per_line = dst_w * 4; ++ ++ XInitImage(&img); ++ GC gc = XCreateGC(dpy, pm, 0, NULL); ++ XPutImage(dpy, pm, gc, &img, 0, 0, 0, 0, dst_w, dst_h); ++ XFreeGC(dpy, gc); ++ ++ XRenderPictFormat *fmt = XRenderFindStandardFormat(dpy, PictStandardARGB32); ++ pict = XRenderCreatePicture(dpy, pm, fmt, 0, NULL); ++ XFreePixmap(dpy, pm); ++ ++ free(scaled); ++ ++ *icw = dst_w; ++ *ich = dst_h; ++ return pict; ++} ++ + int + gettextprop(Window w, Atom atom, char *text, unsigned int size) + { +@@ -1043,6 +1214,7 @@ manage(Window w, XWindowAttributes *wa) + c->h = c->oldh = wa->height; + c->oldbw = wa->border_width; + ++ updateicon(c); + updatetitle(c); + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { + c->mon = t->mon; +@@ -1217,6 +1389,14 @@ pop(Client *c) + arrange(c->mon); + } + ++uint32_t ++prealpha(uint32_t p) { ++ uint8_t a = p >> 24u; ++ uint32_t rb = (a * (p & 0xFF00FFu)) >> 8u; ++ uint32_t g = (a * (p & 0x00FF00u)) >> 8u; ++ return (rb & 0xFF00FFu) | (g & 0x00FF00u) | (a << 24u); ++} ++ + void + propertynotify(XEvent *e) + { +@@ -1249,6 +1429,11 @@ propertynotify(XEvent *e) + if (c == c->mon->sel) + drawbar(c->mon); + } ++ if (ev->atom == netatom[NetWMIcon]) { ++ updateicon(c); ++ if (c == c->mon->sel) ++ drawbar(c->mon); ++ } + if (ev->atom == netatom[NetWMWindowType]) + updatewindowtype(c); + } +@@ -1572,6 +1757,7 @@ setup(void) + netatom[NetActiveWindow] = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + netatom[NetSupported] = XInternAtom(dpy, "_NET_SUPPORTED", False); + netatom[NetWMName] = XInternAtom(dpy, "_NET_WM_NAME", False); ++ netatom[NetWMIcon] = XInternAtom(dpy, "_NET_WM_ICON", 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); +@@ -1782,6 +1968,7 @@ unmanage(Client *c, int destroyed) + + detach(c); + detachstack(c); ++ freeicon(c); + if (!destroyed) { + wc.border_width = c->oldbw; + XGrabServer(dpy); /* avoid race conditions */ +@@ -1863,6 +2050,13 @@ updateclientlist(void) + (unsigned char *) &(c->win), 1); + } + ++void ++updateicon(Client *c) ++{ ++ freeicon(c); ++ c->icon = geticonprop(dpy, c->win, iconsize, &c->icw, &c->ich); ++} ++ + int + updategeom(void) + { +-- +2.51.0 +