dmenu-alpha-20250614-b1e217b.diff - sites - public wiki contents of suckless.org
(HTM) git clone git://git.suckless.org/sites
(DIR) Log
(DIR) Files
(DIR) Refs
---
dmenu-alpha-20250614-b1e217b.diff (53189B)
---
1 diff --git a/config.def.h b/config.def.h
2 index 1edb647..809c96e 100644
3 --- a/config.def.h
4 +++ b/config.def.h
5 @@ -2,6 +2,7 @@
6 /* Default settings; can be overriden by command line. */
7
8 static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
9 +static const unsigned int alpha = 0xff; /* Amount of opacity. 0xff is opaque */
10 /* -fn option overrides fonts[0]; default X11 font or font set */
11 static const char *fonts[] = {
12 "monospace:size=10"
13 @@ -13,6 +14,12 @@ static const char *colors[SchemeLast][2] = {
14 [SchemeSel] = { "#eeeeee", "#005577" },
15 [SchemeOut] = { "#000000", "#00ffff" },
16 };
17 +
18 +static const unsigned int alphas[SchemeLast][2] = {
19 + [SchemeNorm] = { OPAQUE, alpha },
20 + [SchemeSel] = { OPAQUE, alpha },
21 + [SchemeOut] = { OPAQUE, alpha },
22 +};
23 /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
24 static unsigned int lines = 0;
25
26 diff --git a/config.mk b/config.mk
27 index 137f7c8..64c3b06 100644
28 --- a/config.mk
29 +++ b/config.mk
30 @@ -21,7 +21,7 @@ FREETYPEINC = /usr/include/freetype2
31
32 # includes and libs
33 INCS = -I$(X11INC) -I$(FREETYPEINC)
34 -LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
35 +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lXrender
36
37 # flags
38 CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
39 diff --git a/dmenu.c b/dmenu.c
40 index fd49549..85bb73a 100644
41 --- a/dmenu.c
42 +++ b/dmenu.c
43 @@ -10,10 +10,12 @@
44
45 #include <X11/Xlib.h>
46 #include <X11/Xatom.h>
47 +#include <X11/Xproto.h>
48 #include <X11/Xutil.h>
49 #ifdef XINERAMA
50 #include <X11/extensions/Xinerama.h>
51 #endif
52 +#include <X11/extensions/Xrender.h>
53 #include <X11/Xft/Xft.h>
54
55 #include "drw.h"
56 @@ -24,6 +26,8 @@
57 * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
58 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
59
60 +#define OPAQUE 0xffu
61 +
62 /* enums */
63 enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
64
65 @@ -52,10 +56,16 @@ static XIC xic;
66 static Drw *drw;
67 static Clr *scheme[SchemeLast];
68
69 +static int useargb = 0;
70 +static Visual *visual;
71 +static int depth;
72 +static Colormap cmap;
73 +
74 #include "config.h"
75
76 static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
77 static char *(*fstrstr)(const char *, const char *) = strstr;
78 +static void xinitvisual();
79
80 static unsigned int
81 textw_clamp(const char *str, unsigned int n)
82 @@ -627,7 +637,7 @@ setup(void)
83 #endif
84 /* init appearance */
85 for (j = 0; j < SchemeLast; j++)
86 - scheme[j] = drw_scm_create(drw, colors[j], 2);
87 + scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2);
88
89 clip = XInternAtom(dpy, "CLIPBOARD", False);
90 utf8 = XInternAtom(dpy, "UTF8_STRING", False);
91 @@ -682,11 +692,12 @@ setup(void)
92
93 /* create menu window */
94 swa.override_redirect = True;
95 - swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
96 + swa.border_pixel = 0;
97 + swa.colormap = cmap;
98 swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
99 win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
100 - CopyFromParent, CopyFromParent, CopyFromParent,
101 - CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
102 + depth, CopyFromParent, visual,
103 + CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
104 XSetClassHint(dpy, win, &ch);
105
106 /* input methods */
107 @@ -771,7 +782,8 @@ main(int argc, char *argv[])
108 if (!XGetWindowAttributes(dpy, parentwin, &wa))
109 die("could not get embedding window attributes: 0x%lx",
110 parentwin);
111 - drw = drw_create(dpy, screen, root, wa.width, wa.height);
112 + xinitvisual();
113 + drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
114 if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
115 die("no fonts could be loaded.");
116 lrpad = drw->fonts->h;
117 @@ -793,3 +805,40 @@ main(int argc, char *argv[])
118
119 return 1; /* unreachable */
120 }
121 +
122 +void
123 +xinitvisual()
124 +{
125 + XVisualInfo *infos;
126 + XRenderPictFormat *fmt;
127 + int nitems;
128 + int i;
129 +
130 + XVisualInfo tpl = {
131 + .screen = screen,
132 + .depth = 32,
133 + .class = TrueColor
134 + };
135 + long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
136 +
137 + infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
138 + visual = NULL;
139 + for(i = 0; i < nitems; i ++) {
140 + fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
141 + if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
142 + visual = infos[i].visual;
143 + depth = infos[i].depth;
144 + cmap = XCreateColormap(dpy, root, visual, AllocNone);
145 + useargb = 1;
146 + break;
147 + }
148 + }
149 +
150 + XFree(infos);
151 +
152 + if (! visual) {
153 + visual = DefaultVisual(dpy, screen);
154 + depth = DefaultDepth(dpy, screen);
155 + cmap = DefaultColormap(dpy, screen);
156 + }
157 +}
158 diff --git a/dmenu.c.orig b/dmenu.c.orig
159 new file mode 100644
160 index 0000000..fd49549
161 --- /dev/null
162 +++ b/dmenu.c.orig
163 @@ -0,0 +1,795 @@
164 +/* See LICENSE file for copyright and license details. */
165 +#include <ctype.h>
166 +#include <locale.h>
167 +#include <stdio.h>
168 +#include <stdlib.h>
169 +#include <string.h>
170 +#include <strings.h>
171 +#include <time.h>
172 +#include <unistd.h>
173 +
174 +#include <X11/Xlib.h>
175 +#include <X11/Xatom.h>
176 +#include <X11/Xutil.h>
177 +#ifdef XINERAMA
178 +#include <X11/extensions/Xinerama.h>
179 +#endif
180 +#include <X11/Xft/Xft.h>
181 +
182 +#include "drw.h"
183 +#include "util.h"
184 +
185 +/* macros */
186 +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \
187 + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
188 +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
189 +
190 +/* enums */
191 +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
192 +
193 +struct item {
194 + char *text;
195 + struct item *left, *right;
196 + int out;
197 +};
198 +
199 +static char text[BUFSIZ] = "";
200 +static char *embed;
201 +static int bh, mw, mh;
202 +static int inputw = 0, promptw;
203 +static int lrpad; /* sum of left and right padding */
204 +static size_t cursor;
205 +static struct item *items = NULL;
206 +static struct item *matches, *matchend;
207 +static struct item *prev, *curr, *next, *sel;
208 +static int mon = -1, screen;
209 +
210 +static Atom clip, utf8;
211 +static Display *dpy;
212 +static Window root, parentwin, win;
213 +static XIC xic;
214 +
215 +static Drw *drw;
216 +static Clr *scheme[SchemeLast];
217 +
218 +#include "config.h"
219 +
220 +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
221 +static char *(*fstrstr)(const char *, const char *) = strstr;
222 +
223 +static unsigned int
224 +textw_clamp(const char *str, unsigned int n)
225 +{
226 + unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
227 + return MIN(w, n);
228 +}
229 +
230 +static void
231 +appenditem(struct item *item, struct item **list, struct item **last)
232 +{
233 + if (*last)
234 + (*last)->right = item;
235 + else
236 + *list = item;
237 +
238 + item->left = *last;
239 + item->right = NULL;
240 + *last = item;
241 +}
242 +
243 +static void
244 +calcoffsets(void)
245 +{
246 + int i, n;
247 +
248 + if (lines > 0)
249 + n = lines * bh;
250 + else
251 + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">"));
252 + /* calculate which items will begin the next page and previous page */
253 + for (i = 0, next = curr; next; next = next->right)
254 + if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)
255 + break;
256 + for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
257 + if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
258 + break;
259 +}
260 +
261 +static void
262 +cleanup(void)
263 +{
264 + size_t i;
265 +
266 + XUngrabKeyboard(dpy, CurrentTime);
267 + for (i = 0; i < SchemeLast; i++)
268 + free(scheme[i]);
269 + for (i = 0; items && items[i].text; ++i)
270 + free(items[i].text);
271 + free(items);
272 + drw_free(drw);
273 + XSync(dpy, False);
274 + XCloseDisplay(dpy);
275 +}
276 +
277 +static char *
278 +cistrstr(const char *h, const char *n)
279 +{
280 + size_t i;
281 +
282 + if (!n[0])
283 + return (char *)h;
284 +
285 + for (; *h; ++h) {
286 + for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
287 + tolower((unsigned char)h[i]); ++i)
288 + ;
289 + if (n[i] == '\0')
290 + return (char *)h;
291 + }
292 + return NULL;
293 +}
294 +
295 +static int
296 +drawitem(struct item *item, int x, int y, int w)
297 +{
298 + if (item == sel)
299 + drw_setscheme(drw, scheme[SchemeSel]);
300 + else if (item->out)
301 + drw_setscheme(drw, scheme[SchemeOut]);
302 + else
303 + drw_setscheme(drw, scheme[SchemeNorm]);
304 +
305 + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
306 +}
307 +
308 +static void
309 +drawmenu(void)
310 +{
311 + unsigned int curpos;
312 + struct item *item;
313 + int x = 0, y = 0, w;
314 +
315 + drw_setscheme(drw, scheme[SchemeNorm]);
316 + drw_rect(drw, 0, 0, mw, mh, 1, 1);
317 +
318 + if (prompt && *prompt) {
319 + drw_setscheme(drw, scheme[SchemeSel]);
320 + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
321 + }
322 + /* draw input field */
323 + w = (lines > 0 || !matches) ? mw - x : inputw;
324 + drw_setscheme(drw, scheme[SchemeNorm]);
325 + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
326 +
327 + curpos = TEXTW(text) - TEXTW(&text[cursor]);
328 + if ((curpos += lrpad / 2 - 1) < w) {
329 + drw_setscheme(drw, scheme[SchemeNorm]);
330 + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
331 + }
332 +
333 + if (lines > 0) {
334 + /* draw vertical list */
335 + for (item = curr; item != next; item = item->right)
336 + drawitem(item, x, y += bh, mw - x);
337 + } else if (matches) {
338 + /* draw horizontal list */
339 + x += inputw;
340 + w = TEXTW("<");
341 + if (curr->left) {
342 + drw_setscheme(drw, scheme[SchemeNorm]);
343 + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
344 + }
345 + x += w;
346 + for (item = curr; item != next; item = item->right)
347 + x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
348 + if (next) {
349 + w = TEXTW(">");
350 + drw_setscheme(drw, scheme[SchemeNorm]);
351 + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0);
352 + }
353 + }
354 + drw_map(drw, win, 0, 0, mw, mh);
355 +}
356 +
357 +static void
358 +grabfocus(void)
359 +{
360 + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 };
361 + Window focuswin;
362 + int i, revertwin;
363 +
364 + for (i = 0; i < 100; ++i) {
365 + XGetInputFocus(dpy, &focuswin, &revertwin);
366 + if (focuswin == win)
367 + return;
368 + XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
369 + nanosleep(&ts, NULL);
370 + }
371 + die("cannot grab focus");
372 +}
373 +
374 +static void
375 +grabkeyboard(void)
376 +{
377 + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };
378 + int i;
379 +
380 + if (embed)
381 + return;
382 + /* try to grab keyboard, we may have to wait for another process to ungrab */
383 + for (i = 0; i < 1000; i++) {
384 + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
385 + GrabModeAsync, CurrentTime) == GrabSuccess)
386 + return;
387 + nanosleep(&ts, NULL);
388 + }
389 + die("cannot grab keyboard");
390 +}
391 +
392 +static void
393 +match(void)
394 +{
395 + static char **tokv = NULL;
396 + static int tokn = 0;
397 +
398 + char buf[sizeof text], *s;
399 + int i, tokc = 0;
400 + size_t len, textsize;
401 + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
402 +
403 + strcpy(buf, text);
404 + /* separate input text into tokens to be matched individually */
405 + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
406 + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
407 + die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
408 + len = tokc ? strlen(tokv[0]) : 0;
409 +
410 + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
411 + textsize = strlen(text) + 1;
412 + for (item = items; item && item->text; item++) {
413 + for (i = 0; i < tokc; i++)
414 + if (!fstrstr(item->text, tokv[i]))
415 + break;
416 + if (i != tokc) /* not all tokens match */
417 + continue;
418 + /* exact matches go first, then prefixes, then substrings */
419 + if (!tokc || !fstrncmp(text, item->text, textsize))
420 + appenditem(item, &matches, &matchend);
421 + else if (!fstrncmp(tokv[0], item->text, len))
422 + appenditem(item, &lprefix, &prefixend);
423 + else
424 + appenditem(item, &lsubstr, &substrend);
425 + }
426 + if (lprefix) {
427 + if (matches) {
428 + matchend->right = lprefix;
429 + lprefix->left = matchend;
430 + } else
431 + matches = lprefix;
432 + matchend = prefixend;
433 + }
434 + if (lsubstr) {
435 + if (matches) {
436 + matchend->right = lsubstr;
437 + lsubstr->left = matchend;
438 + } else
439 + matches = lsubstr;
440 + matchend = substrend;
441 + }
442 + curr = sel = matches;
443 + calcoffsets();
444 +}
445 +
446 +static void
447 +insert(const char *str, ssize_t n)
448 +{
449 + if (strlen(text) + n > sizeof text - 1)
450 + return;
451 + /* move existing text out of the way, insert new text, and update cursor */
452 + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
453 + if (n > 0)
454 + memcpy(&text[cursor], str, n);
455 + cursor += n;
456 + match();
457 +}
458 +
459 +static size_t
460 +nextrune(int inc)
461 +{
462 + ssize_t n;
463 +
464 + /* return location of next utf8 rune in the given direction (+1 or -1) */
465 + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
466 + ;
467 + return n;
468 +}
469 +
470 +static void
471 +movewordedge(int dir)
472 +{
473 + if (dir < 0) { /* move cursor to the start of the word*/
474 + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
475 + cursor = nextrune(-1);
476 + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
477 + cursor = nextrune(-1);
478 + } else { /* move cursor to the end of the word */
479 + while (text[cursor] && strchr(worddelimiters, text[cursor]))
480 + cursor = nextrune(+1);
481 + while (text[cursor] && !strchr(worddelimiters, text[cursor]))
482 + cursor = nextrune(+1);
483 + }
484 +}
485 +
486 +static void
487 +keypress(XKeyEvent *ev)
488 +{
489 + char buf[64];
490 + int len;
491 + KeySym ksym = NoSymbol;
492 + Status status;
493 +
494 + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
495 + switch (status) {
496 + default: /* XLookupNone, XBufferOverflow */
497 + return;
498 + case XLookupChars: /* composed string from input method */
499 + goto insert;
500 + case XLookupKeySym:
501 + case XLookupBoth: /* a KeySym and a string are returned: use keysym */
502 + break;
503 + }
504 +
505 + if (ev->state & ControlMask) {
506 + switch(ksym) {
507 + case XK_a: ksym = XK_Home; break;
508 + case XK_b: ksym = XK_Left; break;
509 + case XK_c: ksym = XK_Escape; break;
510 + case XK_d: ksym = XK_Delete; break;
511 + case XK_e: ksym = XK_End; break;
512 + case XK_f: ksym = XK_Right; break;
513 + case XK_g: ksym = XK_Escape; break;
514 + case XK_h: ksym = XK_BackSpace; break;
515 + case XK_i: ksym = XK_Tab; break;
516 + case XK_j: /* fallthrough */
517 + case XK_J: /* fallthrough */
518 + case XK_m: /* fallthrough */
519 + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
520 + case XK_n: ksym = XK_Down; break;
521 + case XK_p: ksym = XK_Up; break;
522 +
523 + case XK_k: /* delete right */
524 + text[cursor] = '\0';
525 + match();
526 + break;
527 + case XK_u: /* delete left */
528 + insert(NULL, 0 - cursor);
529 + break;
530 + case XK_w: /* delete word */
531 + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
532 + insert(NULL, nextrune(-1) - cursor);
533 + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
534 + insert(NULL, nextrune(-1) - cursor);
535 + break;
536 + case XK_y: /* paste selection */
537 + case XK_Y:
538 + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
539 + utf8, utf8, win, CurrentTime);
540 + return;
541 + case XK_Left:
542 + case XK_KP_Left:
543 + movewordedge(-1);
544 + goto draw;
545 + case XK_Right:
546 + case XK_KP_Right:
547 + movewordedge(+1);
548 + goto draw;
549 + case XK_Return:
550 + case XK_KP_Enter:
551 + break;
552 + case XK_bracketleft:
553 + cleanup();
554 + exit(1);
555 + default:
556 + return;
557 + }
558 + } else if (ev->state & Mod1Mask) {
559 + switch(ksym) {
560 + case XK_b:
561 + movewordedge(-1);
562 + goto draw;
563 + case XK_f:
564 + movewordedge(+1);
565 + goto draw;
566 + case XK_g: ksym = XK_Home; break;
567 + case XK_G: ksym = XK_End; break;
568 + case XK_h: ksym = XK_Up; break;
569 + case XK_j: ksym = XK_Next; break;
570 + case XK_k: ksym = XK_Prior; break;
571 + case XK_l: ksym = XK_Down; break;
572 + default:
573 + return;
574 + }
575 + }
576 +
577 + switch(ksym) {
578 + default:
579 +insert:
580 + if (!iscntrl((unsigned char)*buf))
581 + insert(buf, len);
582 + break;
583 + case XK_Delete:
584 + case XK_KP_Delete:
585 + if (text[cursor] == '\0')
586 + return;
587 + cursor = nextrune(+1);
588 + /* fallthrough */
589 + case XK_BackSpace:
590 + if (cursor == 0)
591 + return;
592 + insert(NULL, nextrune(-1) - cursor);
593 + break;
594 + case XK_End:
595 + case XK_KP_End:
596 + if (text[cursor] != '\0') {
597 + cursor = strlen(text);
598 + break;
599 + }
600 + if (next) {
601 + /* jump to end of list and position items in reverse */
602 + curr = matchend;
603 + calcoffsets();
604 + curr = prev;
605 + calcoffsets();
606 + while (next && (curr = curr->right))
607 + calcoffsets();
608 + }
609 + sel = matchend;
610 + break;
611 + case XK_Escape:
612 + cleanup();
613 + exit(1);
614 + case XK_Home:
615 + case XK_KP_Home:
616 + if (sel == matches) {
617 + cursor = 0;
618 + break;
619 + }
620 + sel = curr = matches;
621 + calcoffsets();
622 + break;
623 + case XK_Left:
624 + case XK_KP_Left:
625 + if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
626 + cursor = nextrune(-1);
627 + break;
628 + }
629 + if (lines > 0)
630 + return;
631 + /* fallthrough */
632 + case XK_Up:
633 + case XK_KP_Up:
634 + if (sel && sel->left && (sel = sel->left)->right == curr) {
635 + curr = prev;
636 + calcoffsets();
637 + }
638 + break;
639 + case XK_Next:
640 + case XK_KP_Next:
641 + if (!next)
642 + return;
643 + sel = curr = next;
644 + calcoffsets();
645 + break;
646 + case XK_Prior:
647 + case XK_KP_Prior:
648 + if (!prev)
649 + return;
650 + sel = curr = prev;
651 + calcoffsets();
652 + break;
653 + case XK_Return:
654 + case XK_KP_Enter:
655 + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
656 + if (!(ev->state & ControlMask)) {
657 + cleanup();
658 + exit(0);
659 + }
660 + if (sel)
661 + sel->out = 1;
662 + break;
663 + case XK_Right:
664 + case XK_KP_Right:
665 + if (text[cursor] != '\0') {
666 + cursor = nextrune(+1);
667 + break;
668 + }
669 + if (lines > 0)
670 + return;
671 + /* fallthrough */
672 + case XK_Down:
673 + case XK_KP_Down:
674 + if (sel && sel->right && (sel = sel->right) == next) {
675 + curr = next;
676 + calcoffsets();
677 + }
678 + break;
679 + case XK_Tab:
680 + if (!sel)
681 + return;
682 + cursor = strnlen(sel->text, sizeof text - 1);
683 + memcpy(text, sel->text, cursor);
684 + text[cursor] = '\0';
685 + match();
686 + break;
687 + }
688 +
689 +draw:
690 + drawmenu();
691 +}
692 +
693 +static void
694 +paste(void)
695 +{
696 + char *p, *q;
697 + int di;
698 + unsigned long dl;
699 + Atom da;
700 +
701 + /* we have been given the current selection, now insert it into input */
702 + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
703 + utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
704 + == Success && p) {
705 + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
706 + XFree(p);
707 + }
708 + drawmenu();
709 +}
710 +
711 +static void
712 +readstdin(void)
713 +{
714 + char *line = NULL;
715 + size_t i, itemsiz = 0, linesiz = 0;
716 + ssize_t len;
717 +
718 + /* read each line from stdin and add it to the item list */
719 + for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
720 + if (i + 1 >= itemsiz) {
721 + itemsiz += 256;
722 + if (!(items = realloc(items, itemsiz * sizeof(*items))))
723 + die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
724 + }
725 + if (line[len - 1] == '\n')
726 + line[len - 1] = '\0';
727 + if (!(items[i].text = strdup(line)))
728 + die("strdup:");
729 +
730 + items[i].out = 0;
731 + }
732 + free(line);
733 + if (items)
734 + items[i].text = NULL;
735 + lines = MIN(lines, i);
736 +}
737 +
738 +static void
739 +run(void)
740 +{
741 + XEvent ev;
742 +
743 + while (!XNextEvent(dpy, &ev)) {
744 + if (XFilterEvent(&ev, win))
745 + continue;
746 + switch(ev.type) {
747 + case DestroyNotify:
748 + if (ev.xdestroywindow.window != win)
749 + break;
750 + cleanup();
751 + exit(1);
752 + case Expose:
753 + if (ev.xexpose.count == 0)
754 + drw_map(drw, win, 0, 0, mw, mh);
755 + break;
756 + case FocusIn:
757 + /* regrab focus from parent window */
758 + if (ev.xfocus.window != win)
759 + grabfocus();
760 + break;
761 + case KeyPress:
762 + keypress(&ev.xkey);
763 + break;
764 + case SelectionNotify:
765 + if (ev.xselection.property == utf8)
766 + paste();
767 + break;
768 + case VisibilityNotify:
769 + if (ev.xvisibility.state != VisibilityUnobscured)
770 + XRaiseWindow(dpy, win);
771 + break;
772 + }
773 + }
774 +}
775 +
776 +static void
777 +setup(void)
778 +{
779 + int x, y, i, j;
780 + unsigned int du;
781 + XSetWindowAttributes swa;
782 + XIM xim;
783 + Window w, dw, *dws;
784 + XWindowAttributes wa;
785 + XClassHint ch = {"dmenu", "dmenu"};
786 +#ifdef XINERAMA
787 + XineramaScreenInfo *info;
788 + Window pw;
789 + int a, di, n, area = 0;
790 +#endif
791 + /* init appearance */
792 + for (j = 0; j < SchemeLast; j++)
793 + scheme[j] = drw_scm_create(drw, colors[j], 2);
794 +
795 + clip = XInternAtom(dpy, "CLIPBOARD", False);
796 + utf8 = XInternAtom(dpy, "UTF8_STRING", False);
797 +
798 + /* calculate menu geometry */
799 + bh = drw->fonts->h + 2;
800 + lines = MAX(lines, 0);
801 + mh = (lines + 1) * bh;
802 +#ifdef XINERAMA
803 + i = 0;
804 + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
805 + XGetInputFocus(dpy, &w, &di);
806 + if (mon >= 0 && mon < n)
807 + i = mon;
808 + else if (w != root && w != PointerRoot && w != None) {
809 + /* find top-level window containing current input focus */
810 + do {
811 + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
812 + XFree(dws);
813 + } while (w != root && w != pw);
814 + /* find xinerama screen with which the window intersects most */
815 + if (XGetWindowAttributes(dpy, pw, &wa))
816 + for (j = 0; j < n; j++)
817 + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
818 + area = a;
819 + i = j;
820 + }
821 + }
822 + /* no focused window is on screen, so use pointer location instead */
823 + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
824 + for (i = 0; i < n; i++)
825 + if (INTERSECT(x, y, 1, 1, info[i]) != 0)
826 + break;
827 +
828 + x = info[i].x_org;
829 + y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
830 + mw = info[i].width;
831 + XFree(info);
832 + } else
833 +#endif
834 + {
835 + if (!XGetWindowAttributes(dpy, parentwin, &wa))
836 + die("could not get embedding window attributes: 0x%lx",
837 + parentwin);
838 + x = 0;
839 + y = topbar ? 0 : wa.height - mh;
840 + mw = wa.width;
841 + }
842 + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
843 + inputw = mw / 3; /* input width: ~33% of monitor width */
844 + match();
845 +
846 + /* create menu window */
847 + swa.override_redirect = True;
848 + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
849 + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
850 + win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
851 + CopyFromParent, CopyFromParent, CopyFromParent,
852 + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
853 + XSetClassHint(dpy, win, &ch);
854 +
855 + /* input methods */
856 + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
857 + die("XOpenIM failed: could not open input device");
858 +
859 + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
860 + XNClientWindow, win, XNFocusWindow, win, NULL);
861 +
862 + XMapRaised(dpy, win);
863 + if (embed) {
864 + XReparentWindow(dpy, win, parentwin, x, y);
865 + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
866 + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
867 + for (i = 0; i < du && dws[i] != win; ++i)
868 + XSelectInput(dpy, dws[i], FocusChangeMask);
869 + XFree(dws);
870 + }
871 + grabfocus();
872 + }
873 + drw_resize(drw, mw, mh);
874 + drawmenu();
875 +}
876 +
877 +static void
878 +usage(void)
879 +{
880 + die("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
881 + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]");
882 +}
883 +
884 +int
885 +main(int argc, char *argv[])
886 +{
887 + XWindowAttributes wa;
888 + int i, fast = 0;
889 +
890 + for (i = 1; i < argc; i++)
891 + /* these options take no arguments */
892 + if (!strcmp(argv[i], "-v")) { /* prints version information */
893 + puts("dmenu-"VERSION);
894 + exit(0);
895 + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
896 + topbar = 0;
897 + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
898 + fast = 1;
899 + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
900 + fstrncmp = strncasecmp;
901 + fstrstr = cistrstr;
902 + } else if (i + 1 == argc)
903 + usage();
904 + /* these options take one argument */
905 + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */
906 + lines = atoi(argv[++i]);
907 + else if (!strcmp(argv[i], "-m"))
908 + mon = atoi(argv[++i]);
909 + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */
910 + prompt = argv[++i];
911 + else if (!strcmp(argv[i], "-fn")) /* font or font set */
912 + fonts[0] = argv[++i];
913 + else if (!strcmp(argv[i], "-nb")) /* normal background color */
914 + colors[SchemeNorm][ColBg] = argv[++i];
915 + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
916 + colors[SchemeNorm][ColFg] = argv[++i];
917 + else if (!strcmp(argv[i], "-sb")) /* selected background color */
918 + colors[SchemeSel][ColBg] = argv[++i];
919 + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */
920 + colors[SchemeSel][ColFg] = argv[++i];
921 + else if (!strcmp(argv[i], "-w")) /* embedding window id */
922 + embed = argv[++i];
923 + else
924 + usage();
925 +
926 + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
927 + fputs("warning: no locale support\n", stderr);
928 + if (!(dpy = XOpenDisplay(NULL)))
929 + die("cannot open display");
930 + screen = DefaultScreen(dpy);
931 + root = RootWindow(dpy, screen);
932 + if (!embed || !(parentwin = strtol(embed, NULL, 0)))
933 + parentwin = root;
934 + if (!XGetWindowAttributes(dpy, parentwin, &wa))
935 + die("could not get embedding window attributes: 0x%lx",
936 + parentwin);
937 + drw = drw_create(dpy, screen, root, wa.width, wa.height);
938 + if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
939 + die("no fonts could be loaded.");
940 + lrpad = drw->fonts->h;
941 +
942 +#ifdef __OpenBSD__
943 + if (pledge("stdio rpath", NULL) == -1)
944 + die("pledge");
945 +#endif
946 +
947 + if (fast && !isatty(0)) {
948 + grabkeyboard();
949 + readstdin();
950 + } else {
951 + readstdin();
952 + grabkeyboard();
953 + }
954 + setup();
955 + run();
956 +
957 + return 1; /* unreachable */
958 +}
959 diff --git a/dmenu.c.rej b/dmenu.c.rej
960 new file mode 100644
961 index 0000000..ea19a32
962 --- /dev/null
963 +++ b/dmenu.c.rej
964 @@ -0,0 +1,19 @@
965 +--- dmenu.c
966 ++++ dmenu.c
967 +@@ -692,11 +702,13 @@ setup(void)
968 +
969 + /* create menu window */
970 + swa.override_redirect = True;
971 +- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
972 ++ swa.background_pixel = 0;
973 ++ swa.border_pixel = 0;
974 ++ swa.colormap = cmap;
975 + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
976 + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
977 +- CopyFromParent, CopyFromParent, CopyFromParent,
978 +- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
979 ++ depth, CopyFromParent, visual,
980 ++ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
981 + XSetClassHint(dpy, win, &ch);
982 +
983 +
984 diff --git a/drw.c b/drw.c
985 index c41e6af..f8d4ac2 100644
986 --- a/drw.c
987 +++ b/drw.c
988 @@ -47,7 +47,7 @@ utf8decode(const char *s_in, long *u, int *err)
989 }
990
991 Drw *
992 -drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
993 +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
994 {
995 Drw *drw = ecalloc(1, sizeof(Drw));
996
997 @@ -56,8 +56,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
998 drw->root = root;
999 drw->w = w;
1000 drw->h = h;
1001 - drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
1002 - drw->gc = XCreateGC(dpy, root, 0, NULL);
1003 + drw->visual = visual;
1004 + drw->depth = depth;
1005 + drw->cmap = cmap;
1006 + drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
1007 + drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
1008 XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
1009
1010 return drw;
1011 @@ -73,7 +76,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
1012 drw->h = h;
1013 if (drw->drawable)
1014 XFreePixmap(drw->dpy, drw->drawable);
1015 - drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
1016 + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
1017 }
1018
1019 void
1020 @@ -167,21 +170,22 @@ drw_fontset_free(Fnt *font)
1021 }
1022
1023 void
1024 -drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
1025 +drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
1026 {
1027 if (!drw || !dest || !clrname)
1028 return;
1029
1030 - if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
1031 - DefaultColormap(drw->dpy, drw->screen),
1032 + if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
1033 clrname, dest))
1034 die("error, cannot allocate color '%s'", clrname);
1035 +
1036 + dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
1037 }
1038
1039 /* Wrapper to create color schemes. The caller has to call free(3) on the
1040 * returned color scheme when done using it. */
1041 Clr *
1042 -drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
1043 +drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
1044 {
1045 size_t i;
1046 Clr *ret;
1047 @@ -191,7 +195,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
1048 return NULL;
1049
1050 for (i = 0; i < clrcount; i++)
1051 - drw_clr_create(drw, &ret[i], clrnames[i]);
1052 + drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
1053 return ret;
1054 }
1055
1056 @@ -248,11 +252,9 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
1057 } else {
1058 XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
1059 XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
1060 + d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
1061 if (w < lpad)
1062 return x + w;
1063 - d = XftDrawCreate(drw->dpy, drw->drawable,
1064 - DefaultVisual(drw->dpy, drw->screen),
1065 - DefaultColormap(drw->dpy, drw->screen));
1066 x += lpad;
1067 w -= lpad;
1068 }
1069 diff --git a/drw.c.orig b/drw.c.orig
1070 new file mode 100644
1071 index 0000000..c41e6af
1072 --- /dev/null
1073 +++ b/drw.c.orig
1074 @@ -0,0 +1,448 @@
1075 +/* See LICENSE file for copyright and license details. */
1076 +#include <stdio.h>
1077 +#include <stdlib.h>
1078 +#include <string.h>
1079 +#include <X11/Xlib.h>
1080 +#include <X11/Xft/Xft.h>
1081 +
1082 +#include "drw.h"
1083 +#include "util.h"
1084 +
1085 +#define UTF_INVALID 0xFFFD
1086 +
1087 +static int
1088 +utf8decode(const char *s_in, long *u, int *err)
1089 +{
1090 + static const unsigned char lens[] = {
1091 + /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1092 + /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
1093 + /* 110XX */ 2, 2, 2, 2,
1094 + /* 1110X */ 3, 3,
1095 + /* 11110 */ 4,
1096 + /* 11111 */ 0, /* invalid */
1097 + };
1098 + static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
1099 + static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
1100 +
1101 + const unsigned char *s = (const unsigned char *)s_in;
1102 + int len = lens[*s >> 3];
1103 + *u = UTF_INVALID;
1104 + *err = 1;
1105 + if (len == 0)
1106 + return 1;
1107 +
1108 + long cp = s[0] & leading_mask[len - 1];
1109 + for (int i = 1; i < len; ++i) {
1110 + if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
1111 + return i;
1112 + cp = (cp << 6) | (s[i] & 0x3F);
1113 + }
1114 + /* out of range, surrogate, overlong encoding */
1115 + if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
1116 + return len;
1117 +
1118 + *err = 0;
1119 + *u = cp;
1120 + return len;
1121 +}
1122 +
1123 +Drw *
1124 +drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
1125 +{
1126 + Drw *drw = ecalloc(1, sizeof(Drw));
1127 +
1128 + drw->dpy = dpy;
1129 + drw->screen = screen;
1130 + drw->root = root;
1131 + drw->w = w;
1132 + drw->h = h;
1133 + drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
1134 + drw->gc = XCreateGC(dpy, root, 0, NULL);
1135 + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
1136 +
1137 + return drw;
1138 +}
1139 +
1140 +void
1141 +drw_resize(Drw *drw, unsigned int w, unsigned int h)
1142 +{
1143 + if (!drw)
1144 + return;
1145 +
1146 + drw->w = w;
1147 + drw->h = h;
1148 + if (drw->drawable)
1149 + XFreePixmap(drw->dpy, drw->drawable);
1150 + drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
1151 +}
1152 +
1153 +void
1154 +drw_free(Drw *drw)
1155 +{
1156 + XFreePixmap(drw->dpy, drw->drawable);
1157 + XFreeGC(drw->dpy, drw->gc);
1158 + drw_fontset_free(drw->fonts);
1159 + free(drw);
1160 +}
1161 +
1162 +/* This function is an implementation detail. Library users should use
1163 + * drw_fontset_create instead.
1164 + */
1165 +static Fnt *
1166 +xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
1167 +{
1168 + Fnt *font;
1169 + XftFont *xfont = NULL;
1170 + FcPattern *pattern = NULL;
1171 +
1172 + if (fontname) {
1173 + /* Using the pattern found at font->xfont->pattern does not yield the
1174 + * same substitution results as using the pattern returned by
1175 + * FcNameParse; using the latter results in the desired fallback
1176 + * behaviour whereas the former just results in missing-character
1177 + * rectangles being drawn, at least with some fonts. */
1178 + if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
1179 + fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
1180 + return NULL;
1181 + }
1182 + if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
1183 + fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
1184 + XftFontClose(drw->dpy, xfont);
1185 + return NULL;
1186 + }
1187 + } else if (fontpattern) {
1188 + if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
1189 + fprintf(stderr, "error, cannot load font from pattern.\n");
1190 + return NULL;
1191 + }
1192 + } else {
1193 + die("no font specified.");
1194 + }
1195 +
1196 + font = ecalloc(1, sizeof(Fnt));
1197 + font->xfont = xfont;
1198 + font->pattern = pattern;
1199 + font->h = xfont->ascent + xfont->descent;
1200 + font->dpy = drw->dpy;
1201 +
1202 + return font;
1203 +}
1204 +
1205 +static void
1206 +xfont_free(Fnt *font)
1207 +{
1208 + if (!font)
1209 + return;
1210 + if (font->pattern)
1211 + FcPatternDestroy(font->pattern);
1212 + XftFontClose(font->dpy, font->xfont);
1213 + free(font);
1214 +}
1215 +
1216 +Fnt*
1217 +drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
1218 +{
1219 + Fnt *cur, *ret = NULL;
1220 + size_t i;
1221 +
1222 + if (!drw || !fonts)
1223 + return NULL;
1224 +
1225 + for (i = 1; i <= fontcount; i++) {
1226 + if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
1227 + cur->next = ret;
1228 + ret = cur;
1229 + }
1230 + }
1231 + return (drw->fonts = ret);
1232 +}
1233 +
1234 +void
1235 +drw_fontset_free(Fnt *font)
1236 +{
1237 + if (font) {
1238 + drw_fontset_free(font->next);
1239 + xfont_free(font);
1240 + }
1241 +}
1242 +
1243 +void
1244 +drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
1245 +{
1246 + if (!drw || !dest || !clrname)
1247 + return;
1248 +
1249 + if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
1250 + DefaultColormap(drw->dpy, drw->screen),
1251 + clrname, dest))
1252 + die("error, cannot allocate color '%s'", clrname);
1253 +}
1254 +
1255 +/* Wrapper to create color schemes. The caller has to call free(3) on the
1256 + * returned color scheme when done using it. */
1257 +Clr *
1258 +drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
1259 +{
1260 + size_t i;
1261 + Clr *ret;
1262 +
1263 + /* need at least two colors for a scheme */
1264 + if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
1265 + return NULL;
1266 +
1267 + for (i = 0; i < clrcount; i++)
1268 + drw_clr_create(drw, &ret[i], clrnames[i]);
1269 + return ret;
1270 +}
1271 +
1272 +void
1273 +drw_setfontset(Drw *drw, Fnt *set)
1274 +{
1275 + if (drw)
1276 + drw->fonts = set;
1277 +}
1278 +
1279 +void
1280 +drw_setscheme(Drw *drw, Clr *scm)
1281 +{
1282 + if (drw)
1283 + drw->scheme = scm;
1284 +}
1285 +
1286 +void
1287 +drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
1288 +{
1289 + if (!drw || !drw->scheme)
1290 + return;
1291 + XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
1292 + if (filled)
1293 + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
1294 + else
1295 + XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
1296 +}
1297 +
1298 +int
1299 +drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
1300 +{
1301 + int ty, ellipsis_x = 0;
1302 + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
1303 + XftDraw *d = NULL;
1304 + Fnt *usedfont, *curfont, *nextfont;
1305 + int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
1306 + long utf8codepoint = 0;
1307 + const char *utf8str;
1308 + FcCharSet *fccharset;
1309 + FcPattern *fcpattern;
1310 + FcPattern *match;
1311 + XftResult result;
1312 + int charexists = 0, overflow = 0;
1313 + /* keep track of a couple codepoints for which we have no match. */
1314 + static unsigned int nomatches[128], ellipsis_width, invalid_width;
1315 + static const char invalid[] = "�";
1316 +
1317 + if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
1318 + return 0;
1319 +
1320 + if (!render) {
1321 + w = invert ? invert : ~invert;
1322 + } else {
1323 + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
1324 + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
1325 + if (w < lpad)
1326 + return x + w;
1327 + d = XftDrawCreate(drw->dpy, drw->drawable,
1328 + DefaultVisual(drw->dpy, drw->screen),
1329 + DefaultColormap(drw->dpy, drw->screen));
1330 + x += lpad;
1331 + w -= lpad;
1332 + }
1333 +
1334 + usedfont = drw->fonts;
1335 + if (!ellipsis_width && render)
1336 + ellipsis_width = drw_fontset_getwidth(drw, "...");
1337 + if (!invalid_width && render)
1338 + invalid_width = drw_fontset_getwidth(drw, invalid);
1339 + while (1) {
1340 + ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
1341 + utf8str = text;
1342 + nextfont = NULL;
1343 + while (*text) {
1344 + utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
1345 + for (curfont = drw->fonts; curfont; curfont = curfont->next) {
1346 + charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
1347 + if (charexists) {
1348 + drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
1349 + if (ew + ellipsis_width <= w) {
1350 + /* keep track where the ellipsis still fits */
1351 + ellipsis_x = x + ew;
1352 + ellipsis_w = w - ew;
1353 + ellipsis_len = utf8strlen;
1354 + }
1355 +
1356 + if (ew + tmpw > w) {
1357 + overflow = 1;
1358 + /* called from drw_fontset_getwidth_clamp():
1359 + * it wants the width AFTER the overflow
1360 + */
1361 + if (!render)
1362 + x += tmpw;
1363 + else
1364 + utf8strlen = ellipsis_len;
1365 + } else if (curfont == usedfont) {
1366 + text += utf8charlen;
1367 + utf8strlen += utf8err ? 0 : utf8charlen;
1368 + ew += utf8err ? 0 : tmpw;
1369 + } else {
1370 + nextfont = curfont;
1371 + }
1372 + break;
1373 + }
1374 + }
1375 +
1376 + if (overflow || !charexists || nextfont || utf8err)
1377 + break;
1378 + else
1379 + charexists = 0;
1380 + }
1381 +
1382 + if (utf8strlen) {
1383 + if (render) {
1384 + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
1385 + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
1386 + usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
1387 + }
1388 + x += ew;
1389 + w -= ew;
1390 + }
1391 + if (utf8err && (!render || invalid_width < w)) {
1392 + if (render)
1393 + drw_text(drw, x, y, w, h, 0, invalid, invert);
1394 + x += invalid_width;
1395 + w -= invalid_width;
1396 + }
1397 + if (render && overflow)
1398 + drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
1399 +
1400 + if (!*text || overflow) {
1401 + break;
1402 + } else if (nextfont) {
1403 + charexists = 0;
1404 + usedfont = nextfont;
1405 + } else {
1406 + /* Regardless of whether or not a fallback font is found, the
1407 + * character must be drawn. */
1408 + charexists = 1;
1409 +
1410 + hash = (unsigned int)utf8codepoint;
1411 + hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
1412 + hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
1413 + h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
1414 + h1 = (hash >> 17) % LENGTH(nomatches);
1415 + /* avoid expensive XftFontMatch call when we know we won't find a match */
1416 + if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
1417 + goto no_match;
1418 +
1419 + fccharset = FcCharSetCreate();
1420 + FcCharSetAddChar(fccharset, utf8codepoint);
1421 +
1422 + if (!drw->fonts->pattern) {
1423 + /* Refer to the comment in xfont_create for more information. */
1424 + die("the first font in the cache must be loaded from a font string.");
1425 + }
1426 +
1427 + fcpattern = FcPatternDuplicate(drw->fonts->pattern);
1428 + FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
1429 + FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
1430 +
1431 + FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
1432 + FcDefaultSubstitute(fcpattern);
1433 + match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
1434 +
1435 + FcCharSetDestroy(fccharset);
1436 + FcPatternDestroy(fcpattern);
1437 +
1438 + if (match) {
1439 + usedfont = xfont_create(drw, NULL, match);
1440 + if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
1441 + for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
1442 + ; /* NOP */
1443 + curfont->next = usedfont;
1444 + } else {
1445 + xfont_free(usedfont);
1446 + nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
1447 +no_match:
1448 + usedfont = drw->fonts;
1449 + }
1450 + }
1451 + }
1452 + }
1453 + if (d)
1454 + XftDrawDestroy(d);
1455 +
1456 + return x + (render ? w : 0);
1457 +}
1458 +
1459 +void
1460 +drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
1461 +{
1462 + if (!drw)
1463 + return;
1464 +
1465 + XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
1466 + XSync(drw->dpy, False);
1467 +}
1468 +
1469 +unsigned int
1470 +drw_fontset_getwidth(Drw *drw, const char *text)
1471 +{
1472 + if (!drw || !drw->fonts || !text)
1473 + return 0;
1474 + return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
1475 +}
1476 +
1477 +unsigned int
1478 +drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
1479 +{
1480 + unsigned int tmp = 0;
1481 + if (drw && drw->fonts && text && n)
1482 + tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
1483 + return MIN(n, tmp);
1484 +}
1485 +
1486 +void
1487 +drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
1488 +{
1489 + XGlyphInfo ext;
1490 +
1491 + if (!font || !text)
1492 + return;
1493 +
1494 + XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
1495 + if (w)
1496 + *w = ext.xOff;
1497 + if (h)
1498 + *h = font->h;
1499 +}
1500 +
1501 +Cur *
1502 +drw_cur_create(Drw *drw, int shape)
1503 +{
1504 + Cur *cur;
1505 +
1506 + if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
1507 + return NULL;
1508 +
1509 + cur->cursor = XCreateFontCursor(drw->dpy, shape);
1510 +
1511 + return cur;
1512 +}
1513 +
1514 +void
1515 +drw_cur_free(Drw *drw, Cur *cursor)
1516 +{
1517 + if (!cursor)
1518 + return;
1519 +
1520 + XFreeCursor(drw->dpy, cursor->cursor);
1521 + free(cursor);
1522 +}
1523 diff --git a/drw.c.rej b/drw.c.rej
1524 new file mode 100644
1525 index 0000000..a2390e4
1526 --- /dev/null
1527 +++ b/drw.c.rej
1528 @@ -0,0 +1,13 @@
1529 +--- drw.c
1530 ++++ drw.c
1531 +@@ -267,9 +271,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
1532 + } else {
1533 + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
1534 + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
1535 +- d = XftDrawCreate(drw->dpy, drw->drawable,
1536 +- DefaultVisual(drw->dpy, drw->screen),
1537 +- DefaultColormap(drw->dpy, drw->screen));
1538 ++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
1539 + x += lpad;
1540 + w -= lpad;
1541 + }
1542 diff --git a/drw.h b/drw.h
1543 index fd7631b..48f2f93 100644
1544 --- a/drw.h
1545 +++ b/drw.h
1546 @@ -20,6 +20,9 @@ typedef struct {
1547 Display *dpy;
1548 int screen;
1549 Window root;
1550 + Visual *visual;
1551 + unsigned int depth;
1552 + Colormap cmap;
1553 Drawable drawable;
1554 GC gc;
1555 Clr *scheme;
1556 @@ -27,7 +30,7 @@ typedef struct {
1557 } Drw;
1558
1559 /* Drawable abstraction */
1560 -Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
1561 +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap);
1562 void drw_resize(Drw *drw, unsigned int w, unsigned int h);
1563 void drw_free(Drw *drw);
1564
1565 @@ -39,8 +42,8 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int
1566 void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
1567
1568 /* Colorscheme abstraction */
1569 -void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
1570 -Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
1571 +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
1572 +Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
1573
1574 /* Cursor abstraction */
1575 Cur *drw_cur_create(Drw *drw, int shape);
1576 diff --git a/patch/dmenu-alpha-20230110-5.2.diff b/patch/dmenu-alpha-20230110-5.2.diff
1577 new file mode 100644
1578 index 0000000..e3b1493
1579 --- /dev/null
1580 +++ b/patch/dmenu-alpha-20230110-5.2.diff
1581 @@ -0,0 +1,293 @@
1582 +From 4709ed81c8b8df043420ca9de016054088beb934 Mon Sep 17 00:00:00 2001
1583 +From: Andrew Slice <edward.andrew.slice@gmail.com>
1584 +Date: Tue, 10 Jan 2023 17:22:44 -0500
1585 +Subject: [PATCH] Adds alpha transparency. This also fixes a crash that happens
1586 + when using '-w' to embed dmenu in a window that has alpha transparency.
1587 +
1588 +Based on the original patch by Marcin Lukow <marcin@nerdy.cat>
1589 +---
1590 + config.def.h | 7 ++++++
1591 + config.mk | 2 +-
1592 + dmenu.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++-----
1593 + drw.c | 26 ++++++++++++-----------
1594 + drw.h | 9 +++++---
1595 + 5 files changed, 83 insertions(+), 21 deletions(-)
1596 +
1597 +diff --git a/config.def.h b/config.def.h
1598 +index 1edb647..809c96e 100644
1599 +--- a/config.def.h
1600 ++++ b/config.def.h
1601 +@@ -2,6 +2,7 @@
1602 + /* Default settings; can be overriden by command line. */
1603 +
1604 + static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
1605 ++static const unsigned int alpha = 0xff; /* Amount of opacity. 0xff is opaque */
1606 + /* -fn option overrides fonts[0]; default X11 font or font set */
1607 + static const char *fonts[] = {
1608 + "monospace:size=10"
1609 +@@ -13,6 +14,12 @@ static const char *colors[SchemeLast][2] = {
1610 + [SchemeSel] = { "#eeeeee", "#005577" },
1611 + [SchemeOut] = { "#000000", "#00ffff" },
1612 + };
1613 ++
1614 ++static const unsigned int alphas[SchemeLast][2] = {
1615 ++ [SchemeNorm] = { OPAQUE, alpha },
1616 ++ [SchemeSel] = { OPAQUE, alpha },
1617 ++ [SchemeOut] = { OPAQUE, alpha },
1618 ++};
1619 + /* -l option; if nonzero, dmenu uses vertical list with given number of lines */
1620 + static unsigned int lines = 0;
1621 +
1622 +diff --git a/config.mk b/config.mk
1623 +index 566348b..fa2b4fc 100644
1624 +--- a/config.mk
1625 ++++ b/config.mk
1626 +@@ -21,7 +21,7 @@ FREETYPEINC = /usr/include/freetype2
1627 +
1628 + # includes and libs
1629 + INCS = -I$(X11INC) -I$(FREETYPEINC)
1630 +-LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
1631 ++LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lXrender
1632 +
1633 + # flags
1634 + CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
1635 +diff --git a/dmenu.c b/dmenu.c
1636 +index 27b7a30..a20302f 100644
1637 +--- a/dmenu.c
1638 ++++ b/dmenu.c
1639 +@@ -10,10 +10,12 @@
1640 +
1641 + #include <X11/Xlib.h>
1642 + #include <X11/Xatom.h>
1643 ++#include <X11/Xproto.h>
1644 + #include <X11/Xutil.h>
1645 + #ifdef XINERAMA
1646 + #include <X11/extensions/Xinerama.h>
1647 + #endif
1648 ++#include <X11/extensions/Xrender.h>
1649 + #include <X11/Xft/Xft.h>
1650 +
1651 + #include "drw.h"
1652 +@@ -25,6 +27,8 @@
1653 + #define LENGTH(X) (sizeof X / sizeof X[0])
1654 + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
1655 +
1656 ++#define OPAQUE 0xffu
1657 ++
1658 + /* enums */
1659 + enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
1660 +
1661 +@@ -53,10 +57,16 @@ static XIC xic;
1662 + static Drw *drw;
1663 + static Clr *scheme[SchemeLast];
1664 +
1665 ++static int useargb = 0;
1666 ++static Visual *visual;
1667 ++static int depth;
1668 ++static Colormap cmap;
1669 ++
1670 + #include "config.h"
1671 +
1672 + static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
1673 + static char *(*fstrstr)(const char *, const char *) = strstr;
1674 ++static void xinitvisual();
1675 +
1676 + static unsigned int
1677 + textw_clamp(const char *str, unsigned int n)
1678 +@@ -627,7 +637,7 @@ setup(void)
1679 + #endif
1680 + /* init appearance */
1681 + for (j = 0; j < SchemeLast; j++)
1682 +- scheme[j] = drw_scm_create(drw, colors[j], 2);
1683 ++ scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2);
1684 +
1685 + clip = XInternAtom(dpy, "CLIPBOARD", False);
1686 + utf8 = XInternAtom(dpy, "UTF8_STRING", False);
1687 +@@ -682,11 +692,13 @@ setup(void)
1688 +
1689 + /* create menu window */
1690 + swa.override_redirect = True;
1691 +- swa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
1692 ++ swa.background_pixel = 0;
1693 ++ swa.border_pixel = 0;
1694 ++ swa.colormap = cmap;
1695 + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
1696 + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
1697 +- CopyFromParent, CopyFromParent, CopyFromParent,
1698 +- CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
1699 ++ depth, CopyFromParent, visual,
1700 ++ CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
1701 + XSetClassHint(dpy, win, &ch);
1702 +
1703 +
1704 +@@ -771,7 +783,8 @@ main(int argc, char *argv[])
1705 + if (!XGetWindowAttributes(dpy, parentwin, &wa))
1706 + die("could not get embedding window attributes: 0x%lx",
1707 + parentwin);
1708 +- drw = drw_create(dpy, screen, root, wa.width, wa.height);
1709 ++ xinitvisual();
1710 ++ drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
1711 + if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
1712 + die("no fonts could be loaded.");
1713 + lrpad = drw->fonts->h;
1714 +@@ -793,3 +806,40 @@ main(int argc, char *argv[])
1715 +
1716 + return 1; /* unreachable */
1717 + }
1718 ++
1719 ++void
1720 ++xinitvisual()
1721 ++{
1722 ++ XVisualInfo *infos;
1723 ++ XRenderPictFormat *fmt;
1724 ++ int nitems;
1725 ++ int i;
1726 ++
1727 ++ XVisualInfo tpl = {
1728 ++ .screen = screen,
1729 ++ .depth = 32,
1730 ++ .class = TrueColor
1731 ++ };
1732 ++ long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
1733 ++
1734 ++ infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
1735 ++ visual = NULL;
1736 ++ for(i = 0; i < nitems; i ++) {
1737 ++ fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
1738 ++ if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
1739 ++ visual = infos[i].visual;
1740 ++ depth = infos[i].depth;
1741 ++ cmap = XCreateColormap(dpy, root, visual, AllocNone);
1742 ++ useargb = 1;
1743 ++ break;
1744 ++ }
1745 ++ }
1746 ++
1747 ++ XFree(infos);
1748 ++
1749 ++ if (! visual) {
1750 ++ visual = DefaultVisual(dpy, screen);
1751 ++ depth = DefaultDepth(dpy, screen);
1752 ++ cmap = DefaultColormap(dpy, screen);
1753 ++ }
1754 ++}
1755 +diff --git a/drw.c b/drw.c
1756 +index a58a2b4..42700e5 100644
1757 +--- a/drw.c
1758 ++++ b/drw.c
1759 +@@ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen)
1760 + }
1761 +
1762 + Drw *
1763 +-drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
1764 ++drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap)
1765 + {
1766 + Drw *drw = ecalloc(1, sizeof(Drw));
1767 +
1768 +@@ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h
1769 + drw->root = root;
1770 + drw->w = w;
1771 + drw->h = h;
1772 +- drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
1773 +- drw->gc = XCreateGC(dpy, root, 0, NULL);
1774 ++ drw->visual = visual;
1775 ++ drw->depth = depth;
1776 ++ drw->cmap = cmap;
1777 ++ drw->drawable = XCreatePixmap(dpy, root, w, h, depth);
1778 ++ drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL);
1779 + XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
1780 +
1781 + return drw;
1782 +@@ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h)
1783 + drw->h = h;
1784 + if (drw->drawable)
1785 + XFreePixmap(drw->dpy, drw->drawable);
1786 +- drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
1787 ++ drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth);
1788 + }
1789 +
1790 + void
1791 +@@ -181,21 +184,22 @@ drw_fontset_free(Fnt *font)
1792 + }
1793 +
1794 + void
1795 +-drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
1796 ++drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha)
1797 + {
1798 + if (!drw || !dest || !clrname)
1799 + return;
1800 +
1801 +- if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
1802 +- DefaultColormap(drw->dpy, drw->screen),
1803 ++ if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
1804 + clrname, dest))
1805 + die("error, cannot allocate color '%s'", clrname);
1806 ++
1807 ++ dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
1808 + }
1809 +
1810 + /* Wrapper to create color schemes. The caller has to call free(3) on the
1811 + * returned color scheme when done using it. */
1812 + Clr *
1813 +-drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
1814 ++drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount)
1815 + {
1816 + size_t i;
1817 + Clr *ret;
1818 +@@ -205,7 +209,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
1819 + return NULL;
1820 +
1821 + for (i = 0; i < clrcount; i++)
1822 +- drw_clr_create(drw, &ret[i], clrnames[i]);
1823 ++ drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]);
1824 + return ret;
1825 + }
1826 +
1827 +@@ -263,9 +267,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
1828 + } else {
1829 + XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
1830 + XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
1831 +- d = XftDrawCreate(drw->dpy, drw->drawable,
1832 +- DefaultVisual(drw->dpy, drw->screen),
1833 +- DefaultColormap(drw->dpy, drw->screen));
1834 ++ d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap);
1835 + x += lpad;
1836 + w -= lpad;
1837 + }
1838 +diff --git a/drw.h b/drw.h
1839 +index fd7631b..48f2f93 100644
1840 +--- a/drw.h
1841 ++++ b/drw.h
1842 +@@ -20,6 +20,9 @@ typedef struct {
1843 + Display *dpy;
1844 + int screen;
1845 + Window root;
1846 ++ Visual *visual;
1847 ++ unsigned int depth;
1848 ++ Colormap cmap;
1849 + Drawable drawable;
1850 + GC gc;
1851 + Clr *scheme;
1852 +@@ -27,7 +30,7 @@ typedef struct {
1853 + } Drw;
1854 +
1855 + /* Drawable abstraction */
1856 +-Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
1857 ++Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap);
1858 + void drw_resize(Drw *drw, unsigned int w, unsigned int h);
1859 + void drw_free(Drw *drw);
1860 +
1861 +@@ -39,8 +42,8 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int
1862 + void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
1863 +
1864 + /* Colorscheme abstraction */
1865 +-void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
1866 +-Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
1867 ++void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha);
1868 ++Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount);
1869 +
1870 + /* Cursor abstraction */
1871 + Cur *drw_cur_create(Drw *drw, int shape);
1872 +--
1873 +2.37.4
1874 +