diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f913c1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +dwm +config.h diff --git a/config.def.h b/config.def.h index 81f2d32..fa14f40 100644 --- a/config.def.h +++ b/config.def.h @@ -92,6 +92,7 @@ static const Key keys[] = { { MODKEY, XK_equal, setgaps, {.i = +5 } }, { MODKEY|ShiftMask, XK_minus, setgaps, {.i = 0 } }, { MODKEY, XK_F5, togglerainbow, {0} }, + { MODKEY|ShiftMask, XK_F5, togglerainbowbar, {0} }, { MODKEY, XK_0, view, {.ui = ~0 } }, { MODKEY|ShiftMask, XK_0, tag, {.ui = ~0 } }, { MODKEY, XK_comma, focusmon, {.i = -1 } }, @@ -131,4 +132,3 @@ static const Button buttons[] = { { ClkTagBar, MODKEY, Button1, tag, {0} }, { ClkTagBar, MODKEY, Button3, toggletag, {0} }, }; - diff --git a/drw.c b/drw.c index 9fdd1a4..98298a4 100644 --- a/drw.c +++ b/drw.c @@ -79,6 +79,8 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) void drw_free(Drw *drw) { + if (!drw) + return; XFreePixmap(drw->dpy, drw->drawable); XFreeGC(drw->dpy, drw->gc); drw_fontset_free(drw->fonts); @@ -160,9 +162,12 @@ drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount) void drw_fontset_free(Fnt *font) { - if (font) { - drw_fontset_free(font->next); + Fnt *next; + + while (font) { + next = font->next; xfont_free(font); + font = next; } } @@ -200,7 +205,6 @@ 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); } @@ -269,13 +273,17 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp 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 (!drw->nobg) { + 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)); + if (!d) + return x + w; x += lpad; w -= lpad; } @@ -331,17 +339,29 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp 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); + if (drw->nobg) { + static XftColor nobg_fg; + static int nobg_fg_init = 0; + if (!nobg_fg_init) { + if (XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), + DefaultColormap(drw->dpy, drw->screen), "#000000", &nobg_fg)) + nobg_fg_init = 1; + } + XftDrawStringUtf8(d, nobg_fg_init ? &nobg_fg : &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } else { + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); + } } x += ew; - w -= ew; + w = ew < w ? w - ew : 0; } if (utf8err && (!render || invalid_width < w)) { if (render) drw_text(drw, x, y, w, h, 0, invalid, invert); x += invalid_width; - w -= invalid_width; + w = invalid_width < w ? w - invalid_width : 0; } if (render && overflow) drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); @@ -365,6 +385,15 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint) goto no_match; + /* cap fallback fonts to prevent unbounded growth */ + { + int nfonts = 0; + for (curfont = drw->fonts; curfont; curfont = curfont->next) + nfonts++; + if (nfonts >= 64) + goto no_match; + } + fccharset = FcCharSetCreate(); FcCharSetAddChar(fccharset, utf8codepoint); @@ -412,7 +441,7 @@ drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) return; XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y); - XSync(drw->dpy, False); + XFlush(drw->dpy); } unsigned int diff --git a/drw.h b/drw.h index bda06f9..975d158 100644 --- a/drw.h +++ b/drw.h @@ -24,6 +24,7 @@ typedef struct { GC gc; Clr *scheme; Fnt *fonts; + int nobg; } Drw; /* Drawable abstraction */ diff --git a/dwm.c b/dwm.c index 34fe9a5..71bd8b9 100644 --- a/dwm.c +++ b/dwm.c @@ -170,6 +170,7 @@ static void detachstack(Client *c); static Monitor *dirtomon(int dir); static void drawbar(Monitor *m); static void drawbars(void); +static void drawrainbowbar(Monitor *m); static void drawrainbowborder(Client *c); static unsigned long hsvtorgb(float h, float s, float v); static void enternotify(XEvent *e); @@ -224,6 +225,7 @@ static void tile(Monitor *m); static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); static void togglerainbow(const Arg *arg); +static void togglerainbowbar(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unfocus(Client *c, int setfocus); @@ -286,7 +288,10 @@ static Window root, wmcheckwin; /* rainbow border state */ static float rainbow_hue = 0; +static XImage *rainbow_bar_img = NULL; +static int rainbow_bar_img_w = 0, rainbow_bar_img_h = 0; static int rainbow_enabled; +static int rainbow_bar_enabled; /* configuration, allows nested code to access above variables */ #include "config.h" @@ -532,6 +537,8 @@ cleanup(void) while (m->stack) unmanage(m->stack, 0); XUngrabKey(dpy, AnyKey, AnyModifier, root); + if (rainbow_bar_img) + XDestroyImage(rainbow_bar_img); while (mons) cleanupmon(mons); for (i = 0; i < CurLast; i++) @@ -774,6 +781,12 @@ drawbar(Monitor *m) if (!m->showbar) return; + /* paint rainbow gradient background when enabled */ + if (rainbow_bar_enabled) { + drawrainbowbar(m); + drw->nobg = 1; + } + /* draw status first so it can be overdrawn by tags later */ if (m == selmon) { /* status is only drawn on selected monitor */ char *text, *s, ch; @@ -803,9 +816,14 @@ drawbar(Monitor *m) } x = 0; for (i = 0; i < LENGTH(tags); i++) { + int sel = m->tagset[m->seltags] & 1 << i; w = TEXTW(tags[i]); - drw_setscheme(drw, scheme[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drw_setscheme(drw, scheme[sel ? SchemeSel : SchemeNorm]); + if (sel && drw->nobg) + drw->nobg = 0; drw_text(drw, x, 0, w, bh, lrpad / 2, tags[i], urg & 1 << i); + if (sel && rainbow_bar_enabled) + drw->nobg = 1; if (occ & 1 << i) drw_rect(drw, x + boxs, boxs, boxw, boxw, m == selmon && selmon->sel && selmon->sel->tags & 1 << i, @@ -822,11 +840,12 @@ drawbar(Monitor *m) 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 { + } else if (!rainbow_bar_enabled) { drw_setscheme(drw, scheme[SchemeNorm]); drw_rect(drw, x, 0, w, bh, 1, 1); } } + drw->nobg = 0; drw_map(drw, m->barwin, 0, 0, m->ww, bh); } @@ -861,6 +880,40 @@ hsvtorgb(float h, float s, float v) return (R << 16) | (G << 8) | B; } +void +drawrainbowbar(Monitor *m) +{ + int w = m->ww; + + if (!m->showbar) + return; + + if (!rainbow_bar_img || rainbow_bar_img_w < w || rainbow_bar_img_h != bh) { /* grow-only: no thrash on mixed-width monitors */ + if (rainbow_bar_img) + XDestroyImage(rainbow_bar_img); + char *data = calloc((size_t)w * bh, 4); + if (!data) + return; + rainbow_bar_img = XCreateImage(dpy, DefaultVisual(dpy, screen), + DefaultDepth(dpy, screen), ZPixmap, 0, data, w, bh, 32, 0); + if (!rainbow_bar_img) { + free(data); + return; + } + rainbow_bar_img_w = w; + rainbow_bar_img_h = bh; + } + + for (int x = 0; x < w; x++) { + float hue = fmodf(rainbow_hue + 360.0f * x / w, 360.0f); + unsigned long color = hsvtorgb(hue, 0.7f, 1.0f); + for (int y = 0; y < bh; y++) + XPutPixel(rainbow_bar_img, x, y, color); + } + + XPutImage(dpy, drw->drawable, drw->gc, rainbow_bar_img, 0, 0, 0, 0, w, bh); +} + void drawrainbowborder(Client *c) { @@ -1570,7 +1623,7 @@ run(void) FD_ZERO(&rfds); FD_SET(xfd, &rfds); tv.tv_sec = 0; tv.tv_usec = rainbow_interval * 1000L; - if (select(xfd + 1, &rfds, NULL, NULL, rainbow_enabled ? &tv : NULL) > 0) { + if (select(xfd + 1, &rfds, NULL, NULL, (rainbow_enabled || rainbow_bar_enabled) ? &tv : NULL) > 0) { while (XPending(dpy)) { @@ -1580,14 +1633,17 @@ run(void) } } - if (rainbow_enabled) { + /* rainbow animation: only tick at fixed interval */ + if (rainbow_enabled || rainbow_bar_enabled) { gettimeofday(&now, NULL); now_ms = now.tv_sec * 1000LL + now.tv_usec / 1000; if (now_ms - last_rainbow_ms >= rainbow_interval) { last_rainbow_ms = now_ms; rainbow_hue = fmodf(rainbow_hue + rainbow_step, 360.0f); - if (selmon && selmon->sel) + if (rainbow_enabled && selmon && selmon->sel) drawrainbowborder(selmon->sel); + if (rainbow_bar_enabled) + drawbars(); XFlush(dpy); } } @@ -1976,6 +2032,13 @@ togglerainbow(const Arg *arg) XSetWindowBorder(dpy, selmon->sel->win, scheme[SchemeSel][ColBorder].pixel); } +void +togglerainbowbar(const Arg *arg) +{ + rainbow_bar_enabled = !rainbow_bar_enabled; + drawbars(); +} + void toggletag(const Arg *arg) {