commit b818296c822c79ed785e91b079dbab0591e0f11a
parent e80b9c745e66bb8d559b3b09e371f85a44aedfdb
Author: gunslingerzach <gunslingerzach@protonmail.com>
Date: Sat, 14 Jun 2025 15:28:27 -0500
updated the alpha patch so that it seamlessly patches
Diffstat:
2 files changed, 1875 insertions(+), 0 deletions(-)
diff --git a/tools.suckless.org/dmenu/patches/alpha/dmenu-alpha-20250614-b1e217b.diff b/tools.suckless.org/dmenu/patches/alpha/dmenu-alpha-20250614-b1e217b.diff
@@ -0,0 +1,1874 @@
+diff --git a/config.def.h b/config.def.h
+index 1edb647..809c96e 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,7 @@
+ /* Default settings; can be overriden by command line. */
+
+ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
++static const unsigned int alpha = 0xff; /* Amount of opacity. 0xff is opaque */
+ /* -fn option overrides fonts[0]; default X11 font or font set */
+ static const char *fonts[] = {
+ "monospace:size=10"
+@@ -13,6 +14,12 @@ static const char *colors[SchemeLast][2] = {
+ [SchemeSel] = { "#eeeeee", "#005577" },
+ [SchemeOut] = { "#000000", "#00ffff" },
+ };
++
++static const unsigned int alphas[SchemeLast][2] = {
++ [SchemeNorm] = { OPAQUE, alpha },
++ [SchemeSel] = { OPAQUE, alpha },
++ [SchemeOut] = { OPAQUE, alpha },
++};
+ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
+ static unsigned int lines = 0;
+
+diff --git a/config.mk b/config.mk
+index 137f7c8..64c3b06 100644
+--- a/config.mk
++++ b/config.mk
+@@ -21,7 +21,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) -lXrender
+
+ # flags
+ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
+diff --git a/dmenu.c b/dmenu.c
+index fd49549..85bb73a 100644
+--- a/dmenu.c
++++ b/dmenu.c
+@@ -10,10 +10,12 @@
+
+ #include <X11/Xlib.h>
+ #include <X11/Xatom.h>
++#include <X11/Xproto.h>
+ #include <X11/Xutil.h>
+ #ifdef XINERAMA
+ #include <X11/extensions/Xinerama.h>
+ #endif
++#include <X11/extensions/Xrender.h>
+ #include <X11/Xft/Xft.h>
+
+ #include "drw.h"
+@@ -24,6 +26,8 @@
+ * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
+ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
+
++#define OPAQUE 0xffu
++
+ /* enums */
+ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
+
+@@ -52,10 +56,16 @@ static XIC xic;
+ static Drw *drw;
+ static Clr *scheme[SchemeLast];
+
++static int useargb = 0;
++static Visual *visual;
++static int depth;
++static Colormap cmap;
++
+ #include "config.h"
+
+ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
+ static char *(*fstrstr)(const char *, const char *) = strstr;
++static void xinitvisual();
+
+ static unsigned int
+ textw_clamp(const char *str, unsigned int n)
+@@ -627,7 +637,7 @@ setup(void)
+ #endif
+ /* init appearance */
+ for (j = 0; j < SchemeLast; j++)
+- scheme[j] = drw_scm_create(drw, colors[j], 2);
++ scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2);
+
+ clip = XInternAtom(dpy, "CLIPBOARD", False);
+ utf8 = XInternAtom(dpy, "UTF8_STRING", False);
+@@ -682,11 +692,12 @@ setup(void)
+
+ /* create menu window */
+ swa.override_redirect = True;
+- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ swa.border_pixel = 0;
++ swa.colormap = cmap;
+ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
+- CopyFromParent, CopyFromParent, CopyFromParent,
+- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
++ depth, CopyFromParent, visual,
++ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
+ XSetClassHint(dpy, win, &ch);
+
+ /* input methods */
+@@ -771,7 +782,8 @@ main(int argc, char *argv[])
+ if (!XGetWindowAttributes(dpy, parentwin, &wa))
+ die("could not get embedding window attributes: 0x%lx",
+ parentwin);
+- drw = drw_create(dpy, screen, root, wa.width, wa.height);
++ xinitvisual();
++ drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
+ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
+ die("no fonts could be loaded.");
+ lrpad = drw->fonts->h;
+@@ -793,3 +805,40 @@ main(int argc, char *argv[])
+
+ return 1; /* unreachable */
+ }
++
++void
++xinitvisual()
++{
++ XVisualInfo *infos;
++ XRenderPictFormat *fmt;
++ int nitems;
++ int i;
++
++ XVisualInfo tpl = {
++ .screen = screen,
++ .depth = 32,
++ .class = TrueColor
++ };
++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
++
++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
++ visual = NULL;
++ for(i = 0; i < nitems; i ++) {
++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
++ visual = infos[i].visual;
++ depth = infos[i].depth;
++ cmap = XCreateColormap(dpy, root, visual, AllocNone);
++ useargb = 1;
++ break;
++ }
++ }
++
++ XFree(infos);
++
++ if (! visual) {
++ visual = DefaultVisual(dpy, screen);
++ depth = DefaultDepth(dpy, screen);
++ cmap = DefaultColormap(dpy, screen);
++ }
++}
+diff --git a/dmenu.c.orig b/dmenu.c.orig
+new file mode 100644
+index 0000000..fd49549
+--- /dev/null
++++ b/dmenu.c.orig
+@@ -0,0 +1,795 @@
++/* See LICENSE file for copyright and license details. */
++#include <ctype.h>
++#include <locale.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <strings.h>
++#include <time.h>
++#include <unistd.h>
++
++#include <X11/Xlib.h>
++#include <X11/Xatom.h>
++#include <X11/Xutil.h>
++#ifdef XINERAMA
++#include <X11/extensions/Xinerama.h>
++#endif
++#include <X11/Xft/Xft.h>
++
++#include "drw.h"
++#include "util.h"
++
++/* macros */
++#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
++ * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
++#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
++
++/* enums */
++enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
++
++struct item {
++ char *text;
++ struct item *left, *right;
++ int out;
++};
++
++static char text[BUFSIZ] = "";
++static char *embed;
++static int bh, mw, mh;
++static int inputw = 0, promptw;
++static int lrpad; /* sum of left and right padding */
++static size_t cursor;
++static struct item *items = NULL;
++static struct item *matches, *matchend;
++static struct item *prev, *curr, *next, *sel;
++static int mon = -1, screen;
++
++static Atom clip, utf8;
++static Display *dpy;
++static Window root, parentwin, win;
++static XIC xic;
++
++static Drw *drw;
++static Clr *scheme[SchemeLast];
++
++#include "config.h"
++
++static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
++static char *(*fstrstr)(const char *, const char *) = strstr;
++
++static unsigned int
++textw_clamp(const char *str, unsigned int n)
++{
++ unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
++ return MIN(w, n);
++}
++
++static void
++appenditem(struct item *item, struct item **list, struct item **last)
++{
++ if (*last)
++ (*last)->right = item;
++ else
++ *list = item;
++
++ item->left = *last;
++ item->right = NULL;
++ *last = item;
++}
++
++static void
++calcoffsets(void)
++{
++ int i, n;
++
++ if (lines > 0)
++ n = lines * bh;
++ else
++ n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
++ /* calculate which items will begin the next page and previous page */
++ for (i = 0, next = curr; next; next = next->right)
++ if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)
++ break;
++ for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
++ if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
++ break;
++}
++
++static void
++cleanup(void)
++{
++ size_t i;
++
++ XUngrabKeyboard(dpy, CurrentTime);
++ for (i = 0; i < SchemeLast; i++)
++ free(scheme[i]);
++ for (i = 0; items && items[i].text; ++i)
++ free(items[i].text);
++ free(items);
++ drw_free(drw);
++ XSync(dpy, False);
++ XCloseDisplay(dpy);
++}
++
++static char *
++cistrstr(const char *h, const char *n)
++{
++ size_t i;
++
++ if (!n[0])
++ return (char *)h;
++
++ for (; *h; ++h) {
++ for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
++ tolower((unsigned char)h[i]); ++i)
++ ;
++ if (n[i] == '\0')
++ return (char *)h;
++ }
++ return NULL;
++}
++
++static int
++drawitem(struct item *item, int x, int y, int w)
++{
++ if (item == sel)
++ drw_setscheme(drw, scheme[SchemeSel]);
++ else if (item->out)
++ drw_setscheme(drw, scheme[SchemeOut]);
++ else
++ drw_setscheme(drw, scheme[SchemeNorm]);
++
++ return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
++}
++
++static void
++drawmenu(void)
++{
++ unsigned int curpos;
++ struct item *item;
++ int x = 0, y = 0, w;
++
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_rect(drw, 0, 0, mw, mh, 1, 1);
++
++ if (prompt && *prompt) {
++ drw_setscheme(drw, scheme[SchemeSel]);
++ x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
++ }
++ /* draw input field */
++ w = (lines > 0 || !matches) ? mw - x : inputw;
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
++
++ curpos = TEXTW(text) - TEXTW(&text[cursor]);
++ if ((curpos += lrpad / 2 - 1) < w) {
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
++ }
++
++ if (lines > 0) {
++ /* draw vertical list */
++ for (item = curr; item != next; item = item->right)
++ drawitem(item, x, y += bh, mw - x);
++ } else if (matches) {
++ /* draw horizontal list */
++ x += inputw;
++ w = TEXTW("<");
++ if (curr->left) {
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
++ }
++ x += w;
++ for (item = curr; item != next; item = item->right)
++ x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
++ if (next) {
++ w = TEXTW(">");
++ drw_setscheme(drw, scheme[SchemeNorm]);
++ drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
++ }
++ }
++ drw_map(drw, win, 0, 0, mw, mh);
++}
++
++static void
++grabfocus(void)
++{
++ struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 };
++ Window focuswin;
++ int i, revertwin;
++
++ for (i = 0; i < 100; ++i) {
++ XGetInputFocus(dpy, &focuswin, &revertwin);
++ if (focuswin == win)
++ return;
++ XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
++ nanosleep(&ts, NULL);
++ }
++ die("cannot grab focus");
++}
++
++static void
++grabkeyboard(void)
++{
++ struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
++ int i;
++
++ if (embed)
++ return;
++ /* try to grab keyboard, we may have to wait for another process to ungrab */
++ for (i = 0; i < 1000; i++) {
++ if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
++ GrabModeAsync, CurrentTime) == GrabSuccess)
++ return;
++ nanosleep(&ts, NULL);
++ }
++ die("cannot grab keyboard");
++}
++
++static void
++match(void)
++{
++ static char **tokv = NULL;
++ static int tokn = 0;
++
++ char buf[sizeof text], *s;
++ int i, tokc = 0;
++ size_t len, textsize;
++ struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
++
++ strcpy(buf, text);
++ /* separate input text into tokens to be matched individually */
++ for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
++ if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
++ die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
++ len = tokc ? strlen(tokv[0]) : 0;
++
++ matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
++ textsize = strlen(text) + 1;
++ for (item = items; item && item->text; item++) {
++ for (i = 0; i < tokc; i++)
++ if (!fstrstr(item->text, tokv[i]))
++ break;
++ if (i != tokc) /* not all tokens match */
++ continue;
++ /* exact matches go first, then prefixes, then substrings */
++ if (!tokc || !fstrncmp(text, item->text, textsize))
++ appenditem(item, &matches, &matchend);
++ else if (!fstrncmp(tokv[0], item->text, len))
++ appenditem(item, &lprefix, &prefixend);
++ else
++ appenditem(item, &lsubstr, &substrend);
++ }
++ if (lprefix) {
++ if (matches) {
++ matchend->right = lprefix;
++ lprefix->left = matchend;
++ } else
++ matches = lprefix;
++ matchend = prefixend;
++ }
++ if (lsubstr) {
++ if (matches) {
++ matchend->right = lsubstr;
++ lsubstr->left = matchend;
++ } else
++ matches = lsubstr;
++ matchend = substrend;
++ }
++ curr = sel = matches;
++ calcoffsets();
++}
++
++static void
++insert(const char *str, ssize_t n)
++{
++ if (strlen(text) + n > sizeof text - 1)
++ return;
++ /* move existing text out of the way, insert new text, and update cursor */
++ memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
++ if (n > 0)
++ memcpy(&text[cursor], str, n);
++ cursor += n;
++ match();
++}
++
++static size_t
++nextrune(int inc)
++{
++ ssize_t n;
++
++ /* return location of next utf8 rune in the given direction (+1 or -1) */
++ for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
++ ;
++ return n;
++}
++
++static void
++movewordedge(int dir)
++{
++ if (dir < 0) { /* move cursor to the start of the word*/
++ while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
++ cursor = nextrune(-1);
++ while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
++ cursor = nextrune(-1);
++ } else { /* move cursor to the end of the word */
++ while (text[cursor] && strchr(worddelimiters, text[cursor]))
++ cursor = nextrune(+1);
++ while (text[cursor] && !strchr(worddelimiters, text[cursor]))
++ cursor = nextrune(+1);
++ }
++}
++
++static void
++keypress(XKeyEvent *ev)
++{
++ char buf[64];
++ int len;
++ KeySym ksym = NoSymbol;
++ Status status;
++
++ len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
++ switch (status) {
++ default: /* XLookupNone, XBufferOverflow */
++ return;
++ case XLookupChars: /* composed string from input method */
++ goto insert;
++ case XLookupKeySym:
++ case XLookupBoth: /* a KeySym and a string are returned: use keysym */
++ break;
++ }
++
++ if (ev->state & ControlMask) {
++ switch(ksym) {
++ case XK_a: ksym = XK_Home; break;
++ case XK_b: ksym = XK_Left; break;
++ case XK_c: ksym = XK_Escape; break;
++ case XK_d: ksym = XK_Delete; break;
++ case XK_e: ksym = XK_End; break;
++ case XK_f: ksym = XK_Right; break;
++ case XK_g: ksym = XK_Escape; break;
++ case XK_h: ksym = XK_BackSpace; break;
++ case XK_i: ksym = XK_Tab; break;
++ case XK_j: /* fallthrough */
++ case XK_J: /* fallthrough */
++ case XK_m: /* fallthrough */
++ case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
++ case XK_n: ksym = XK_Down; break;
++ case XK_p: ksym = XK_Up; break;
++
++ case XK_k: /* delete right */
++ text[cursor] = '\0';
++ match();
++ break;
++ case XK_u: /* delete left */
++ insert(NULL, 0 - cursor);
++ break;
++ case XK_w: /* delete word */
++ while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
++ insert(NULL, nextrune(-1) - cursor);
++ while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
++ insert(NULL, nextrune(-1) - cursor);
++ break;
++ case XK_y: /* paste selection */
++ case XK_Y:
++ XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
++ utf8, utf8, win, CurrentTime);
++ return;
++ case XK_Left:
++ case XK_KP_Left:
++ movewordedge(-1);
++ goto draw;
++ case XK_Right:
++ case XK_KP_Right:
++ movewordedge(+1);
++ goto draw;
++ case XK_Return:
++ case XK_KP_Enter:
++ break;
++ case XK_bracketleft:
++ cleanup();
++ exit(1);
++ default:
++ return;
++ }
++ } else if (ev->state & Mod1Mask) {
++ switch(ksym) {
++ case XK_b:
++ movewordedge(-1);
++ goto draw;
++ case XK_f:
++ movewordedge(+1);
++ goto draw;
++ case XK_g: ksym = XK_Home; break;
++ case XK_G: ksym = XK_End; break;
++ case XK_h: ksym = XK_Up; break;
++ case XK_j: ksym = XK_Next; break;
++ case XK_k: ksym = XK_Prior; break;
++ case XK_l: ksym = XK_Down; break;
++ default:
++ return;
++ }
++ }
++
++ switch(ksym) {
++ default:
++insert:
++ if (!iscntrl((unsigned char)*buf))
++ insert(buf, len);
++ break;
++ case XK_Delete:
++ case XK_KP_Delete:
++ if (text[cursor] == '\0')
++ return;
++ cursor = nextrune(+1);
++ /* fallthrough */
++ case XK_BackSpace:
++ if (cursor == 0)
++ return;
++ insert(NULL, nextrune(-1) - cursor);
++ break;
++ case XK_End:
++ case XK_KP_End:
++ if (text[cursor] != '\0') {
++ cursor = strlen(text);
++ break;
++ }
++ if (next) {
++ /* jump to end of list and position items in reverse */
++ curr = matchend;
++ calcoffsets();
++ curr = prev;
++ calcoffsets();
++ while (next && (curr = curr->right))
++ calcoffsets();
++ }
++ sel = matchend;
++ break;
++ case XK_Escape:
++ cleanup();
++ exit(1);
++ case XK_Home:
++ case XK_KP_Home:
++ if (sel == matches) {
++ cursor = 0;
++ break;
++ }
++ sel = curr = matches;
++ calcoffsets();
++ break;
++ case XK_Left:
++ case XK_KP_Left:
++ if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
++ cursor = nextrune(-1);
++ break;
++ }
++ if (lines > 0)
++ return;
++ /* fallthrough */
++ case XK_Up:
++ case XK_KP_Up:
++ if (sel && sel->left && (sel = sel->left)->right == curr) {
++ curr = prev;
++ calcoffsets();
++ }
++ break;
++ case XK_Next:
++ case XK_KP_Next:
++ if (!next)
++ return;
++ sel = curr = next;
++ calcoffsets();
++ break;
++ case XK_Prior:
++ case XK_KP_Prior:
++ if (!prev)
++ return;
++ sel = curr = prev;
++ calcoffsets();
++ break;
++ case XK_Return:
++ case XK_KP_Enter:
++ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
++ if (!(ev->state & ControlMask)) {
++ cleanup();
++ exit(0);
++ }
++ if (sel)
++ sel->out = 1;
++ break;
++ case XK_Right:
++ case XK_KP_Right:
++ if (text[cursor] != '\0') {
++ cursor = nextrune(+1);
++ break;
++ }
++ if (lines > 0)
++ return;
++ /* fallthrough */
++ case XK_Down:
++ case XK_KP_Down:
++ if (sel && sel->right && (sel = sel->right) == next) {
++ curr = next;
++ calcoffsets();
++ }
++ break;
++ case XK_Tab:
++ if (!sel)
++ return;
++ cursor = strnlen(sel->text, sizeof text - 1);
++ memcpy(text, sel->text, cursor);
++ text[cursor] = '\0';
++ match();
++ break;
++ }
++
++draw:
++ drawmenu();
++}
++
++static void
++paste(void)
++{
++ char *p, *q;
++ int di;
++ unsigned long dl;
++ Atom da;
++
++ /* we have been given the current selection, now insert it into input */
++ if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
++ utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
++ == Success && p) {
++ insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
++ XFree(p);
++ }
++ drawmenu();
++}
++
++static void
++readstdin(void)
++{
++ char *line = NULL;
++ size_t i, itemsiz = 0, linesiz = 0;
++ ssize_t len;
++
++ /* read each line from stdin and add it to the item list */
++ for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
++ if (i + 1 >= itemsiz) {
++ itemsiz += 256;
++ if (!(items = realloc(items, itemsiz * sizeof(*items))))
++ die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
++ }
++ if (line[len - 1] == '\n')
++ line[len - 1] = '\0';
++ if (!(items[i].text = strdup(line)))
++ die("strdup:");
++
++ items[i].out = 0;
++ }
++ free(line);
++ if (items)
++ items[i].text = NULL;
++ lines = MIN(lines, i);
++}
++
++static void
++run(void)
++{
++ XEvent ev;
++
++ while (!XNextEvent(dpy, &ev)) {
++ if (XFilterEvent(&ev, win))
++ continue;
++ switch(ev.type) {
++ case DestroyNotify:
++ if (ev.xdestroywindow.window != win)
++ break;
++ cleanup();
++ exit(1);
++ case Expose:
++ if (ev.xexpose.count == 0)
++ drw_map(drw, win, 0, 0, mw, mh);
++ break;
++ case FocusIn:
++ /* regrab focus from parent window */
++ if (ev.xfocus.window != win)
++ grabfocus();
++ break;
++ case KeyPress:
++ keypress(&ev.xkey);
++ break;
++ case SelectionNotify:
++ if (ev.xselection.property == utf8)
++ paste();
++ break;
++ case VisibilityNotify:
++ if (ev.xvisibility.state != VisibilityUnobscured)
++ XRaiseWindow(dpy, win);
++ break;
++ }
++ }
++}
++
++static void
++setup(void)
++{
++ int x, y, i, j;
++ unsigned int du;
++ XSetWindowAttributes swa;
++ XIM xim;
++ Window w, dw, *dws;
++ XWindowAttributes wa;
++ XClassHint ch = {"dmenu", "dmenu"};
++#ifdef XINERAMA
++ XineramaScreenInfo *info;
++ Window pw;
++ int a, di, n, area = 0;
++#endif
++ /* init appearance */
++ for (j = 0; j < SchemeLast; j++)
++ scheme[j] = drw_scm_create(drw, colors[j], 2);
++
++ clip = XInternAtom(dpy, "CLIPBOARD", False);
++ utf8 = XInternAtom(dpy, "UTF8_STRING", False);
++
++ /* calculate menu geometry */
++ bh = drw->fonts->h + 2;
++ lines = MAX(lines, 0);
++ mh = (lines + 1) * bh;
++#ifdef XINERAMA
++ i = 0;
++ if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
++ XGetInputFocus(dpy, &w, &di);
++ if (mon >= 0 && mon < n)
++ i = mon;
++ else if (w != root && w != PointerRoot && w != None) {
++ /* find top-level window containing current input focus */
++ do {
++ if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
++ XFree(dws);
++ } while (w != root && w != pw);
++ /* find xinerama screen with which the window intersects most */
++ if (XGetWindowAttributes(dpy, pw, &wa))
++ for (j = 0; j < n; j++)
++ if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
++ area = a;
++ i = j;
++ }
++ }
++ /* no focused window is on screen, so use pointer location instead */
++ if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
++ for (i = 0; i < n; i++)
++ if (INTERSECT(x, y, 1, 1, info[i]) != 0)
++ break;
++
++ x = info[i].x_org;
++ y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
++ mw = info[i].width;
++ XFree(info);
++ } else
++#endif
++ {
++ if (!XGetWindowAttributes(dpy, parentwin, &wa))
++ die("could not get embedding window attributes: 0x%lx",
++ parentwin);
++ x = 0;
++ y = topbar ? 0 : wa.height - mh;
++ mw = wa.width;
++ }
++ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
++ inputw = mw / 3; /* input width: ~33% of monitor width */
++ match();
++
++ /* create menu window */
++ swa.override_redirect = True;
++ swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
++ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
++ CopyFromParent, CopyFromParent, CopyFromParent,
++ CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
++ XSetClassHint(dpy, win, &ch);
++
++ /* input methods */
++ if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
++ die("XOpenIM failed: could not open input device");
++
++ xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
++ XNClientWindow, win, XNFocusWindow, win, NULL);
++
++ XMapRaised(dpy, win);
++ if (embed) {
++ XReparentWindow(dpy, win, parentwin, x, y);
++ XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
++ if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
++ for (i = 0; i < du && dws[i] != win; ++i)
++ XSelectInput(dpy, dws[i], FocusChangeMask);
++ XFree(dws);
++ }
++ grabfocus();
++ }
++ drw_resize(drw, mw, mh);
++ drawmenu();
++}
++
++static void
++usage(void)
++{
++ die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
++ " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
++}
++
++int
++main(int argc, char *argv[])
++{
++ XWindowAttributes wa;
++ int i, fast = 0;
++
++ for (i = 1; i < argc; i++)
++ /* these options take no arguments */
++ if (!strcmp(argv[i], "-v")) { /* prints version information */
++ puts("dmenu-"VERSION);
++ exit(0);
++ } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
++ topbar = 0;
++ else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
++ fast = 1;
++ else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
++ fstrncmp = strncasecmp;
++ fstrstr = cistrstr;
++ } else if (i + 1 == argc)
++ usage();
++ /* these options take one argument */
++ else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
++ lines = atoi(argv[++i]);
++ else if (!strcmp(argv[i], "-m"))
++ mon = atoi(argv[++i]);
++ else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
++ prompt = argv[++i];
++ else if (!strcmp(argv[i], "-fn")) /* font or font set */
++ fonts[0] = argv[++i];
++ else if (!strcmp(argv[i], "-nb")) /* normal background color */
++ colors[SchemeNorm][ColBg] = argv[++i];
++ else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
++ colors[SchemeNorm][ColFg] = argv[++i];
++ else if (!strcmp(argv[i], "-sb")) /* selected background color */
++ colors[SchemeSel][ColBg] = argv[++i];
++ else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
++ colors[SchemeSel][ColFg] = argv[++i];
++ else if (!strcmp(argv[i], "-w")) /* embedding window id */
++ embed = argv[++i];
++ else
++ usage();
++
++ if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
++ fputs("warning: no locale support\n", stderr);
++ if (!(dpy = XOpenDisplay(NULL)))
++ die("cannot open display");
++ screen = DefaultScreen(dpy);
++ root = RootWindow(dpy, screen);
++ if (!embed || !(parentwin = strtol(embed, NULL, 0)))
++ parentwin = root;
++ if (!XGetWindowAttributes(dpy, parentwin, &wa))
++ die("could not get embedding window attributes: 0x%lx",
++ parentwin);
++ drw = drw_create(dpy, screen, root, wa.width, wa.height);
++ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
++ die("no fonts could be loaded.");
++ lrpad = drw->fonts->h;
++
++#ifdef __OpenBSD__
++ if (pledge("stdio rpath", NULL) == -1)
++ die("pledge");
++#endif
++
++ if (fast && !isatty(0)) {
++ grabkeyboard();
++ readstdin();
++ } else {
++ readstdin();
++ grabkeyboard();
++ }
++ setup();
++ run();
++
++ return 1; /* unreachable */
++}
+diff --git a/dmenu.c.rej b/dmenu.c.rej
+new file mode 100644
+index 0000000..ea19a32
+--- /dev/null
++++ b/dmenu.c.rej
+@@ -0,0 +1,19 @@
++--- dmenu.c
+++++ dmenu.c
++@@ -692,11 +702,13 @@ setup(void)
++
++ /* create menu window */
++ swa.override_redirect = True;
++- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+++ swa.background_pixel = 0;
+++ swa.border_pixel = 0;
+++ swa.colormap = cmap;
++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
++ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
++- CopyFromParent, CopyFromParent, CopyFromParent,
++- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+++ depth, CopyFromParent, visual,
+++ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
++ XSetClassHint(dpy, win, &ch);
++
++
+diff --git a/drw.c b/drw.c
+index c41e6af..f8d4ac2 100644
+--- a/drw.c
++++ b/drw.c
+@@ -47,7 +47,7 @@ utf8decode(const char *s_in, long *u, int *err)
+ }
+
+ Drw *
+-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
+ {
+ Drw *drw = ecalloc(1, sizeof(Drw));
+
+@@ -56,8 +56,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
+ drw->root = root;
+ drw->w = w;
+ drw->h = h;
+- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
+- drw->gc = XCreateGC(dpy, root, 0, NULL);
++ drw->visual = visual;
++ drw->depth = depth;
++ drw->cmap = cmap;
++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
+ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
+
+ return drw;
+@@ -73,7 +76,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
+ drw->h = h;
+ if (drw->drawable)
+ XFreePixmap(drw->dpy, drw->drawable);
+- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
+ }
+
+ void
+@@ -167,21 +170,22 @@ drw_fontset_free(Fnt *font)
+ }
+
+ void
+-drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
+ {
+ if (!drw || !dest || !clrname)
+ return;
+
+- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
+- DefaultColormap(drw->dpy, drw->screen),
++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
+ clrname, dest))
+ die("error, cannot allocate color '%s'", clrname);
++
++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
+ }
+
+ /* Wrapper to create color schemes. The caller has to call free(3) on the
+ * returned color scheme when done using it. */
+ Clr *
+-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
++drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
+ {
+ size_t i;
+ Clr *ret;
+@@ -191,7 +195,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+ return NULL;
+
+ for (i = 0; i < clrcount; i++)
+- drw_clr_create(drw, &ret[i], clrnames[i]);
++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
+ return ret;
+ }
+
+@@ -248,11 +252,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
+ } else {
+ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
+ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
+ 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;
+ }
+diff --git a/drw.c.orig b/drw.c.orig
+new file mode 100644
+index 0000000..c41e6af
+--- /dev/null
++++ b/drw.c.orig
+@@ -0,0 +1,448 @@
++/* See LICENSE file for copyright and license details. */
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <X11/Xlib.h>
++#include <X11/Xft/Xft.h>
++
++#include "drw.h"
++#include "util.h"
++
++#define UTF_INVALID 0xFFFD
++
++static int
++utf8decode(const char *s_in, long *u, int *err)
++{
++ static const unsigned char lens[] = {
++ /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
++ /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
++ /* 110XX */ 2, 2, 2, 2,
++ /* 1110X */ 3, 3,
++ /* 11110 */ 4,
++ /* 11111 */ 0, /* invalid */
++ };
++ static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
++ static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
++
++ const unsigned char *s = (const unsigned char *)s_in;
++ int len = lens[*s >> 3];
++ *u = UTF_INVALID;
++ *err = 1;
++ if (len == 0)
++ return 1;
++
++ long cp = s[0] & leading_mask[len - 1];
++ for (int i = 1; i < len; ++i) {
++ if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
++ return i;
++ cp = (cp << 6) | (s[i] & 0x3F);
++ }
++ /* out of range, surrogate, overlong encoding */
++ if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
++ return len;
++
++ *err = 0;
++ *u = cp;
++ return len;
++}
++
++Drw *
++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
++{
++ Drw *drw = ecalloc(1, sizeof(Drw));
++
++ drw->dpy = dpy;
++ drw->screen = screen;
++ drw->root = root;
++ drw->w = w;
++ drw->h = h;
++ drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
++ drw->gc = XCreateGC(dpy, root, 0, NULL);
++ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
++
++ return drw;
++}
++
++void
++drw_resize(Drw *drw, unsigned int w, unsigned int h)
++{
++ if (!drw)
++ return;
++
++ drw->w = w;
++ drw->h = h;
++ if (drw->drawable)
++ XFreePixmap(drw->dpy, drw->drawable);
++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
++}
++
++void
++drw_free(Drw *drw)
++{
++ XFreePixmap(drw->dpy, drw->drawable);
++ XFreeGC(drw->dpy, drw->gc);
++ drw_fontset_free(drw->fonts);
++ free(drw);
++}
++
++/* This function is an implementation detail. Library users should use
++ * drw_fontset_create instead.
++ */
++static Fnt *
++xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
++{
++ Fnt *font;
++ XftFont *xfont = NULL;
++ FcPattern *pattern = NULL;
++
++ if (fontname) {
++ /* Using the pattern found at font->xfont->pattern does not yield the
++ * same substitution results as using the pattern returned by
++ * FcNameParse; using the latter results in the desired fallback
++ * behaviour whereas the former just results in missing-character
++ * rectangles being drawn, at least with some fonts. */
++ if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
++ fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
++ return NULL;
++ }
++ if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
++ fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
++ XftFontClose(drw->dpy, xfont);
++ return NULL;
++ }
++ } else if (fontpattern) {
++ if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
++ fprintf(stderr, "error, cannot load font from pattern.\n");
++ return NULL;
++ }
++ } else {
++ die("no font specified.");
++ }
++
++ font = ecalloc(1, sizeof(Fnt));
++ font->xfont = xfont;
++ font->pattern = pattern;
++ font->h = xfont->ascent + xfont->descent;
++ font->dpy = drw->dpy;
++
++ return font;
++}
++
++static void
++xfont_free(Fnt *font)
++{
++ if (!font)
++ return;
++ if (font->pattern)
++ FcPatternDestroy(font->pattern);
++ XftFontClose(font->dpy, font->xfont);
++ free(font);
++}
++
++Fnt*
++drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
++{
++ Fnt *cur, *ret = NULL;
++ size_t i;
++
++ if (!drw || !fonts)
++ return NULL;
++
++ for (i = 1; i <= fontcount; i++) {
++ if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
++ cur->next = ret;
++ ret = cur;
++ }
++ }
++ return (drw->fonts = ret);
++}
++
++void
++drw_fontset_free(Fnt *font)
++{
++ if (font) {
++ drw_fontset_free(font->next);
++ xfont_free(font);
++ }
++}
++
++void
++drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
++{
++ if (!drw || !dest || !clrname)
++ return;
++
++ if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
++ DefaultColormap(drw->dpy, drw->screen),
++ clrname, dest))
++ die("error, cannot allocate color '%s'", clrname);
++}
++
++/* Wrapper to create color schemes. The caller has to call free(3) on the
++ * returned color scheme when done using it. */
++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(XftColor))))
++ return NULL;
++
++ for (i = 0; i < clrcount; i++)
++ drw_clr_create(drw, &ret[i], clrnames[i]);
++ return ret;
++}
++
++void
++drw_setfontset(Drw *drw, Fnt *set)
++{
++ if (drw)
++ drw->fonts = set;
++}
++
++void
++drw_setscheme(Drw *drw, Clr *scm)
++{
++ if (drw)
++ drw->scheme = scm;
++}
++
++void
++drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
++{
++ if (!drw || !drw->scheme)
++ return;
++ XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
++ if (filled)
++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
++ else
++ XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
++}
++
++int
++drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
++{
++ int ty, ellipsis_x = 0;
++ unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
++ XftDraw *d = NULL;
++ Fnt *usedfont, *curfont, *nextfont;
++ int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
++ long utf8codepoint = 0;
++ const char *utf8str;
++ FcCharSet *fccharset;
++ FcPattern *fcpattern;
++ FcPattern *match;
++ XftResult result;
++ int charexists = 0, overflow = 0;
++ /* keep track of a couple codepoints for which we have no match. */
++ static unsigned int nomatches[128], ellipsis_width, invalid_width;
++ static const char invalid[] = "�";
++
++ if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
++ return 0;
++
++ if (!render) {
++ w = invert ? invert : ~invert;
++ } else {
++ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
++ if (w < lpad)
++ return x + w;
++ d = XftDrawCreate(drw->dpy, drw->drawable,
++ DefaultVisual(drw->dpy, drw->screen),
++ DefaultColormap(drw->dpy, drw->screen));
++ x += lpad;
++ w -= lpad;
++ }
++
++ usedfont = drw->fonts;
++ if (!ellipsis_width && render)
++ ellipsis_width = drw_fontset_getwidth(drw, "...");
++ if (!invalid_width && render)
++ invalid_width = drw_fontset_getwidth(drw, invalid);
++ while (1) {
++ ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
++ utf8str = text;
++ nextfont = NULL;
++ while (*text) {
++ utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
++ for (curfont = drw->fonts; curfont; curfont = curfont->next) {
++ charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
++ if (charexists) {
++ drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
++ if (ew + ellipsis_width <= w) {
++ /* keep track where the ellipsis still fits */
++ ellipsis_x = x + ew;
++ ellipsis_w = w - ew;
++ ellipsis_len = utf8strlen;
++ }
++
++ if (ew + tmpw > w) {
++ overflow = 1;
++ /* called from drw_fontset_getwidth_clamp():
++ * it wants the width AFTER the overflow
++ */
++ if (!render)
++ x += tmpw;
++ else
++ utf8strlen = ellipsis_len;
++ } else if (curfont == usedfont) {
++ text += utf8charlen;
++ utf8strlen += utf8err ? 0 : utf8charlen;
++ ew += utf8err ? 0 : tmpw;
++ } else {
++ nextfont = curfont;
++ }
++ break;
++ }
++ }
++
++ if (overflow || !charexists || nextfont || utf8err)
++ break;
++ else
++ charexists = 0;
++ }
++
++ if (utf8strlen) {
++ if (render) {
++ ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
++ XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
++ usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
++ }
++ x += ew;
++ w -= ew;
++ }
++ if (utf8err && (!render || invalid_width < w)) {
++ if (render)
++ drw_text(drw, x, y, w, h, 0, invalid, invert);
++ x += invalid_width;
++ w -= invalid_width;
++ }
++ if (render && overflow)
++ drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
++
++ if (!*text || overflow) {
++ break;
++ } else if (nextfont) {
++ charexists = 0;
++ usedfont = nextfont;
++ } else {
++ /* Regardless of whether or not a fallback font is found, the
++ * character must be drawn. */
++ charexists = 1;
++
++ hash = (unsigned int)utf8codepoint;
++ hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
++ hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
++ h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
++ h1 = (hash >> 17) % LENGTH(nomatches);
++ /* avoid expensive XftFontMatch call when we know we won't find a match */
++ if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
++ goto no_match;
++
++ fccharset = FcCharSetCreate();
++ FcCharSetAddChar(fccharset, utf8codepoint);
++
++ if (!drw->fonts->pattern) {
++ /* Refer to the comment in xfont_create for more information. */
++ die("the first font in the cache must be loaded from a font string.");
++ }
++
++ fcpattern = FcPatternDuplicate(drw->fonts->pattern);
++ FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
++ FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
++
++ FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
++ FcDefaultSubstitute(fcpattern);
++ match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
++
++ FcCharSetDestroy(fccharset);
++ FcPatternDestroy(fcpattern);
++
++ if (match) {
++ usedfont = xfont_create(drw, NULL, match);
++ if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
++ for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
++ ; /* NOP */
++ curfont->next = usedfont;
++ } else {
++ xfont_free(usedfont);
++ nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
++no_match:
++ usedfont = drw->fonts;
++ }
++ }
++ }
++ }
++ if (d)
++ XftDrawDestroy(d);
++
++ return x + (render ? w : 0);
++}
++
++void
++drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
++{
++ if (!drw)
++ return;
++
++ XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
++ XSync(drw->dpy, False);
++}
++
++unsigned int
++drw_fontset_getwidth(Drw *drw, const char *text)
++{
++ if (!drw || !drw->fonts || !text)
++ return 0;
++ return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
++}
++
++unsigned int
++drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
++{
++ unsigned int tmp = 0;
++ if (drw && drw->fonts && text && n)
++ tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
++ return MIN(n, tmp);
++}
++
++void
++drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
++{
++ XGlyphInfo ext;
++
++ if (!font || !text)
++ return;
++
++ XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
++ if (w)
++ *w = ext.xOff;
++ if (h)
++ *h = font->h;
++}
++
++Cur *
++drw_cur_create(Drw *drw, int shape)
++{
++ Cur *cur;
++
++ if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
++ return NULL;
++
++ cur->cursor = XCreateFontCursor(drw->dpy, shape);
++
++ return cur;
++}
++
++void
++drw_cur_free(Drw *drw, Cur *cursor)
++{
++ if (!cursor)
++ return;
++
++ XFreeCursor(drw->dpy, cursor->cursor);
++ free(cursor);
++}
+diff --git a/drw.c.rej b/drw.c.rej
+new file mode 100644
+index 0000000..a2390e4
+--- /dev/null
++++ b/drw.c.rej
+@@ -0,0 +1,13 @@
++--- drw.c
+++++ drw.c
++@@ -267,9 +271,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
++ } else {
++ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
++- d = XftDrawCreate(drw->dpy, drw->drawable,
++- DefaultVisual(drw->dpy, drw->screen),
++- DefaultColormap(drw->dpy, drw->screen));
+++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
++ x += lpad;
++ w -= lpad;
++ }
+diff --git a/drw.h b/drw.h
+index fd7631b..48f2f93 100644
+--- a/drw.h
++++ b/drw.h
+@@ -20,6 +20,9 @@ typedef struct {
+ Display *dpy;
+ int screen;
+ Window root;
++ Visual *visual;
++ unsigned int depth;
++ Colormap cmap;
+ Drawable drawable;
+ GC gc;
+ Clr *scheme;
+@@ -27,7 +30,7 @@ typedef struct {
+ } Drw;
+
+ /* Drawable abstraction */
+-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap);
+ void drw_resize(Drw *drw, unsigned int w, unsigned int h);
+ void drw_free(Drw *drw);
+
+@@ -39,8 +42,8 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int
+ 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);
+-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
++Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
+
+ /* Cursor abstraction */
+ Cur *drw_cur_create(Drw *drw, int shape);
+diff --git a/patch/dmenu-alpha-20230110-5.2.diff b/patch/dmenu-alpha-20230110-5.2.diff
+new file mode 100644
+index 0000000..e3b1493
+--- /dev/null
++++ b/patch/dmenu-alpha-20230110-5.2.diff
+@@ -0,0 +1,293 @@
++From 4709ed81c8b8df043420ca9de016054088beb934 Mon Sep 17 00:00:00 2001
++From: Andrew Slice <edward.andrew.slice@gmail.com>
++Date: Tue, 10 Jan 2023 17:22:44 -0500
++Subject: [PATCH] Adds alpha transparency. This also fixes a crash that happens
++ when using '-w' to embed dmenu in a window that has alpha transparency.
++
++Based on the original patch by Marcin Lukow <marcin@nerdy.cat>
++---
++ config.def.h | 7 ++++++
++ config.mk | 2 +-
++ dmenu.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++-----
++ drw.c | 26 ++++++++++++-----------
++ drw.h | 9 +++++---
++ 5 files changed, 83 insertions(+), 21 deletions(-)
++
++diff --git a/config.def.h b/config.def.h
++index 1edb647..809c96e 100644
++--- a/config.def.h
+++++ b/config.def.h
++@@ -2,6 +2,7 @@
++ /* Default settings; can be overriden by command line. */
++
++ static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
+++static const unsigned int alpha = 0xff; /* Amount of opacity. 0xff is opaque */
++ /* -fn option overrides fonts[0]; default X11 font or font set */
++ static const char *fonts[] = {
++ "monospace:size=10"
++@@ -13,6 +14,12 @@ static const char *colors[SchemeLast][2] = {
++ [SchemeSel] = { "#eeeeee", "#005577" },
++ [SchemeOut] = { "#000000", "#00ffff" },
++ };
+++
+++static const unsigned int alphas[SchemeLast][2] = {
+++ [SchemeNorm] = { OPAQUE, alpha },
+++ [SchemeSel] = { OPAQUE, alpha },
+++ [SchemeOut] = { OPAQUE, alpha },
+++};
++ /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
++ static unsigned int lines = 0;
++
++diff --git a/config.mk b/config.mk
++index 566348b..fa2b4fc 100644
++--- a/config.mk
+++++ b/config.mk
++@@ -21,7 +21,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) -lXrender
++
++ # flags
++ CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
++diff --git a/dmenu.c b/dmenu.c
++index 27b7a30..a20302f 100644
++--- a/dmenu.c
+++++ b/dmenu.c
++@@ -10,10 +10,12 @@
++
++ #include <X11/Xlib.h>
++ #include <X11/Xatom.h>
+++#include <X11/Xproto.h>
++ #include <X11/Xutil.h>
++ #ifdef XINERAMA
++ #include <X11/extensions/Xinerama.h>
++ #endif
+++#include <X11/extensions/Xrender.h>
++ #include <X11/Xft/Xft.h>
++
++ #include "drw.h"
++@@ -25,6 +27,8 @@
++ #define LENGTH(X) (sizeof X / sizeof X[0])
++ #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
++
+++#define OPAQUE 0xffu
+++
++ /* enums */
++ enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
++
++@@ -53,10 +57,16 @@ static XIC xic;
++ static Drw *drw;
++ static Clr *scheme[SchemeLast];
++
+++static int useargb = 0;
+++static Visual *visual;
+++static int depth;
+++static Colormap cmap;
+++
++ #include "config.h"
++
++ static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
++ static char *(*fstrstr)(const char *, const char *) = strstr;
+++static void xinitvisual();
++
++ static unsigned int
++ textw_clamp(const char *str, unsigned int n)
++@@ -627,7 +637,7 @@ setup(void)
++ #endif
++ /* init appearance */
++ for (j = 0; j < SchemeLast; j++)
++- scheme[j] = drw_scm_create(drw, colors[j], 2);
+++ scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2);
++
++ clip = XInternAtom(dpy, "CLIPBOARD", False);
++ utf8 = XInternAtom(dpy, "UTF8_STRING", False);
++@@ -682,11 +692,13 @@ setup(void)
++
++ /* create menu window */
++ swa.override_redirect = True;
++- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
+++ swa.background_pixel = 0;
+++ swa.border_pixel = 0;
+++ swa.colormap = cmap;
++ swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
++ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
++- CopyFromParent, CopyFromParent, CopyFromParent,
++- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
+++ depth, CopyFromParent, visual,
+++ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
++ XSetClassHint(dpy, win, &ch);
++
++
++@@ -771,7 +783,8 @@ main(int argc, char *argv[])
++ if (!XGetWindowAttributes(dpy, parentwin, &wa))
++ die("could not get embedding window attributes: 0x%lx",
++ parentwin);
++- drw = drw_create(dpy, screen, root, wa.width, wa.height);
+++ xinitvisual();
+++ drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
++ if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
++ die("no fonts could be loaded.");
++ lrpad = drw->fonts->h;
++@@ -793,3 +806,40 @@ main(int argc, char *argv[])
++
++ return 1; /* unreachable */
++ }
+++
+++void
+++xinitvisual()
+++{
+++ XVisualInfo *infos;
+++ XRenderPictFormat *fmt;
+++ int nitems;
+++ int i;
+++
+++ XVisualInfo tpl = {
+++ .screen = screen,
+++ .depth = 32,
+++ .class = TrueColor
+++ };
+++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
+++
+++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
+++ visual = NULL;
+++ for(i = 0; i < nitems; i ++) {
+++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
+++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
+++ visual = infos[i].visual;
+++ depth = infos[i].depth;
+++ cmap = XCreateColormap(dpy, root, visual, AllocNone);
+++ useargb = 1;
+++ break;
+++ }
+++ }
+++
+++ XFree(infos);
+++
+++ if (! visual) {
+++ visual = DefaultVisual(dpy, screen);
+++ depth = DefaultDepth(dpy, screen);
+++ cmap = DefaultColormap(dpy, screen);
+++ }
+++}
++diff --git a/drw.c b/drw.c
++index a58a2b4..42700e5 100644
++--- a/drw.c
+++++ b/drw.c
++@@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen)
++ }
++
++ Drw *
++-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
+++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
++ {
++ Drw *drw = ecalloc(1, sizeof(Drw));
++
++@@ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
++ drw->root = root;
++ drw->w = w;
++ drw->h = h;
++- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
++- drw->gc = XCreateGC(dpy, root, 0, NULL);
+++ drw->visual = visual;
+++ drw->depth = depth;
+++ drw->cmap = cmap;
+++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
+++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
++ XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
++
++ return drw;
++@@ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
++ drw->h = h;
++ if (drw->drawable)
++ XFreePixmap(drw->dpy, drw->drawable);
++- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
+++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
++ }
++
++ void
++@@ -181,21 +184,22 @@ drw_fontset_free(Fnt *font)
++ }
++
++ void
++-drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
+++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
++ {
++ if (!drw || !dest || !clrname)
++ return;
++
++- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
++- DefaultColormap(drw->dpy, drw->screen),
+++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
++ clrname, dest))
++ die("error, cannot allocate color '%s'", clrname);
+++
+++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
++ }
++
++ /* Wrapper to create color schemes. The caller has to call free(3) on the
++ * returned color scheme when done using it. */
++ Clr *
++-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
+++drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
++ {
++ size_t i;
++ Clr *ret;
++@@ -205,7 +209,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
++ return NULL;
++
++ for (i = 0; i < clrcount; i++)
++- drw_clr_create(drw, &ret[i], clrnames[i]);
+++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
++ return ret;
++ }
++
++@@ -263,9 +267,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
++ } else {
++ XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
++ XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
++- d = XftDrawCreate(drw->dpy, drw->drawable,
++- DefaultVisual(drw->dpy, drw->screen),
++- DefaultColormap(drw->dpy, drw->screen));
+++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
++ x += lpad;
++ w -= lpad;
++ }
++diff --git a/drw.h b/drw.h
++index fd7631b..48f2f93 100644
++--- a/drw.h
+++++ b/drw.h
++@@ -20,6 +20,9 @@ typedef struct {
++ Display *dpy;
++ int screen;
++ Window root;
+++ Visual *visual;
+++ unsigned int depth;
+++ Colormap cmap;
++ Drawable drawable;
++ GC gc;
++ Clr *scheme;
++@@ -27,7 +30,7 @@ typedef struct {
++ } Drw;
++
++ /* Drawable abstraction */
++-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
+++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap);
++ void drw_resize(Drw *drw, unsigned int w, unsigned int h);
++ void drw_free(Drw *drw);
++
++@@ -39,8 +42,8 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int
++ 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);
++-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
+++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
+++Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
++
++ /* Cursor abstraction */
++ Cur *drw_cur_create(Drw *drw, int shape);
++--
++2.37.4
++
diff --git a/tools.suckless.org/dmenu/patches/alpha/index.md b/tools.suckless.org/dmenu/patches/alpha/index.md
@@ -12,6 +12,7 @@ Download
--------
* [dmenu-alpha-20210605-1a13d04.diff](dmenu-alpha-20210605-1a13d04.diff) (2021-06-05)
* [dmenu-alpha-20230110-5.2.diff](dmenu-alpha-20230110-5.2.diff) (2023-01-10)
+* [dmenu-alpha-20250614-b1e217b.diff](dmenu-alpha-20250614-b1e217b.diff) (2025-06-14)
Authors
-------