diff --git a/config.def.h.orig b/config.def.h.orig index 6fff8ea..d749030 100644 --- a/config.def.h.orig +++ b/config.def.h.orig @@ -15,6 +15,8 @@ static const unsigned int gappov = 30; /* vert outer gap between window static int smartgaps = 0; /* 1 means no outer gap when there is only one window */ 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"; @@ -26,6 +28,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/drw.c.orig b/drw.c.orig new file mode 100644 index 0000000..ecc61b2 --- /dev/null +++ b/drw.c.orig @@ -0,0 +1,482 @@ +/* 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->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); + + return drw; +} + +void +drw_resize(Drw *drw, unsigned int w, unsigned int h) +{ + if (!drw) + return; + + 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)); +} + +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); +} + +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) +{ + 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.orig b/drw.h.orig new file mode 100644 index 0000000..e929714 --- /dev/null +++ b/drw.h.orig @@ -0,0 +1,63 @@ +/* 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; + Picture picture; + 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); +void drw_pic(Drw *drw, int x, int y, unsigned int w, unsigned int h, Picture pic); + diff --git a/dwm b/dwm index ebab94c..a3ccd6c 100755 Binary files a/dwm and b/dwm differ diff --git a/dwm.c b/dwm.c index ec0c5c3..ca16a6a 100644 --- a/dwm.c +++ b/dwm.c @@ -998,7 +998,7 @@ drawbar(Monitor *m) } remainder--; } - drw_text(drw, x, 0, tabw, bh, lrpad / 2, c->name, 0); + drw_text(drw, x, 0, tabw > m->ww - tw - stw - x ? m->ww - tw - stw - x : tabw, bh, lrpad / 2, c->name, 0); x += tabw; } } else { diff --git a/dwm.c.orig b/dwm.c.orig index 9b3a0fe..a24e352 100644 --- a/dwm.c.orig +++ b/dwm.c.orig @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,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,8 +78,8 @@ /* enums */ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ -enum { SchemeNorm, SchemeSel }; /* color schemes */ -enum { NetSupported, NetWMName, NetWMState, NetWMCheck, +enum { SchemeNorm, SchemeSel, SchemeHid }; /* color schemes */ +enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck, NetSystemTray, NetSystemTrayOP, NetSystemTrayOrientation, NetSystemTrayOrientationHorz, NetWMFullscreen, NetActiveWindow, NetWMWindowType, NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetDesktopNames, NetDesktopViewport, NetNumberOfDesktops, NetCurrentDesktop, NetLast }; /* EWMH atoms */ @@ -113,6 +115,8 @@ struct Client { int bw, oldbw; unsigned int tags; int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen; + unsigned int icw, ich; + Picture icon; int issteam; int fakefullscreen; Client *next; @@ -139,6 +143,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 +156,7 @@ struct Monitor { unsigned int tagset[2]; int showbar; int topbar; + int hidsel; Client *clients; Client *sel; Client *stack; @@ -181,6 +188,7 @@ static void arrangemon(Monitor *m); static void attach(Client *c); static void attachaside(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); @@ -203,14 +211,20 @@ 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 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 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); @@ -225,6 +239,7 @@ static Client *nexttagged(Client *c); static Client *nexttiled(Client *c); static void placedir(const Arg *arg); 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); @@ -253,6 +268,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 +285,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); @@ -275,6 +294,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); @@ -539,6 +559,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) { @@ -565,10 +636,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 +663,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 +994,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 +1015,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 +1037,41 @@ 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--; + } + if (c->icon) { + drw_text(drw, x, 0, w > m->ww - tw - stw - x ? m->ww - tw - stw - x : w, bh, lrpad / 2 + c->ich + iconspacing, c->name, 0); + drw_pic(drw, x + lrpad / 2, (bh - c->ich) / 2, c->icw, c->ich, c->icon); + } else { + drw_text(drw, x, 0, tabw > m->ww - tw - stw - x ? m->ww - tw - stw - x : 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 +1120,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 +1240,62 @@ 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; + } + } +} + +void +freeicon(Client *c) +{ + if (c->icon) { + XRenderFreePicture(dpy, c->icon); + c->icon = None; } } @@ -1209,6 +1361,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) { @@ -1281,6 +1531,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) { @@ -1360,6 +1640,7 @@ manage(Window w, XWindowAttributes *wa) c->oldbw = wa->border_width; c->cfact = 1.0; + updateicon(c); updatetitle(c); if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { c->mon = t->mon; @@ -1415,14 +1696,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 +1843,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; } @@ -1670,6 +1953,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) { @@ -1713,6 +2004,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); } @@ -1721,6 +2017,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; } @@ -2188,6 +2494,7 @@ setup(void) netatom[NetSystemTrayOrientation] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION", False); netatom[NetSystemTrayOrientationHorz] = XInternAtom(dpy, "_NET_SYSTEM_TRAY_ORIENTATION_HORZ", 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); @@ -2261,6 +2568,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 +2824,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) { @@ -2502,6 +2864,7 @@ unmanage(Client *c, int destroyed) detach(c); detachstack(c); + freeicon(c); if (!destroyed) { wc.border_width = c->oldbw; XGrabServer(dpy); /* avoid race conditions */ @@ -2604,6 +2967,13 @@ void updatecurrentdesktop(void){ XChangeProperty(dpy, root, netatom[NetCurrentDesktop], XA_CARDINAL, 32, PropModeReplace, (unsigned char *)data, 1); } +void +updateicon(Client *c) +{ + freeicon(c); + c->icon = geticonprop(dpy, c->win, iconsize, &c->icw, &c->ich); +} + int updategeom(void) { diff --git a/dwm.c.rej b/dwm.c.rej index fc2a28a..e8b02c3 100644 --- a/dwm.c.rej +++ b/dwm.c.rej @@ -1,226 +1,46 @@ --- dwm.c +++ 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) -@@ -170,13 +174,17 @@ static void expose(XEvent *e); - static void focus(Client *c); +@@ -59,7 +58,7 @@ + /* enums */ + enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ +-enum { NetSupported, NetWMName, NetWMIcon, NetWMState, NetWMCheck, ++enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, + NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */ + enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */ +@@ -92,8 +91,6 @@ 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; +@@ -170,11 +166,9 @@ 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 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); -+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); - +@@ -740,12 +681,7 @@ 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->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; -+ } + if (m->sel) { + drw_setscheme(drw, scheme[m == selmon ? SchemeSel : SchemeNorm]); +- 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); +- } ++ 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); - } - } -+ 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 b07d6d5..9ec1f01 100644 Binary files a/dwm.o and b/dwm.o differ diff --git a/patches/dwm-winicon-6.6.diff b/patches/dwm-winicon-6.6.diff deleted file mode 100644 index 2667572..0000000 --- a/patches/dwm-winicon-6.6.diff +++ /dev/null @@ -1,429 +0,0 @@ -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 -