sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

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:
Atools.suckless.org/dmenu/patches/alpha/dmenu-alpha-20250614-b1e217b.diff | 1874+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtools.suckless.org/dmenu/patches/alpha/index.md | 1+
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 -------