From 56445155eea88b13919a539b57e525f944cefe33 Mon Sep 17 00:00:00 2001 From: h3n3 Date: Tue, 30 Jan 2024 21:48:58 +0100 Subject: [PATCH] suckless --- dmenu | 1 - dmenu/LICENSE | 30 + dmenu/Makefile | 58 + dmenu/README | 24 + dmenu/arg.h | 49 + dmenu/config.def.h | 23 + dmenu/config.h | 23 + dmenu/config.mk | 32 + dmenu/dmenu | Bin 0 -> 42792 bytes dmenu/dmenu.1 | 194 ++ dmenu/dmenu.c | 796 ++++++ dmenu/dmenu.o | Bin 0 -> 32080 bytes dmenu/dmenu_path | 13 + dmenu/dmenu_run | 2 + dmenu/drw.c | 451 ++++ dmenu/drw.h | 58 + dmenu/drw.o | Bin 0 -> 11160 bytes dmenu/stest | Bin 0 -> 16856 bytes dmenu/stest.1 | 90 + dmenu/stest.c | 109 + dmenu/stest.o | Bin 0 -> 5312 bytes dmenu/util.c | 36 + dmenu/util.h | 9 + dmenu/util.o | Bin 0 -> 2240 bytes dwm | 1 - dwm/LICENSE | 38 + dwm/Makefile | 45 + dwm/README | 48 + dwm/config.def.aangepast | 139 ++ dwm/config.def.h | 139 ++ dwm/config.h | 139 ++ dwm/config.mk | 39 + dwm/config.old | 139 ++ dwm/drw.c | 450 ++++ dwm/drw.h | 58 + dwm/drw.o | Bin 0 -> 11144 bytes dwm/dwm | Bin 0 -> 71496 bytes dwm/dwm.1 | 176 ++ dwm/dwm.c | 2165 +++++++++++++++++ dwm/dwm.o | Bin 0 -> 59616 bytes dwm/dwm.png | Bin 0 -> 373 bytes dwm/transient.c | 42 + dwm/util.c | 36 + dwm/util.h | 8 + dwm/util.o | Bin 0 -> 2240 bytes slstatus | 1 - slstatus/LICENSE | 43 + slstatus/Makefile | 69 + slstatus/README | 65 + slstatus/arg.h | 33 + slstatus/components/battery.c | 247 ++ slstatus/components/battery.o | Bin 0 -> 5208 bytes slstatus/components/cat.c | 32 + slstatus/components/cat.o | Bin 0 -> 2128 bytes slstatus/components/cpu.c | 157 ++ slstatus/components/cpu.o | Bin 0 -> 3136 bytes slstatus/components/datetime.c | 20 + slstatus/components/datetime.o | Bin 0 -> 1936 bytes slstatus/components/disk.c | 59 + slstatus/components/disk.o | Bin 0 -> 3144 bytes slstatus/components/entropy.c | 29 + slstatus/components/entropy.o | Bin 0 -> 1792 bytes slstatus/components/hostname.c | 17 + slstatus/components/hostname.o | Bin 0 -> 1648 bytes slstatus/components/ip.c | 61 + slstatus/components/ip.o | Bin 0 -> 2568 bytes slstatus/components/kernel_release.c | 19 + slstatus/components/kernel_release.o | Bin 0 -> 1840 bytes slstatus/components/keyboard_indicators.c | 50 + slstatus/components/keyboard_indicators.o | Bin 0 -> 2416 bytes slstatus/components/keymap.c | 86 + slstatus/components/keymap.o | Bin 0 -> 4056 bytes slstatus/components/load_avg.c | 19 + slstatus/components/load_avg.o | Bin 0 -> 1872 bytes slstatus/components/netspeeds.c | 129 + slstatus/components/netspeeds.o | Bin 0 -> 2792 bytes slstatus/components/num_files.c | 32 + slstatus/components/num_files.o | Bin 0 -> 2160 bytes slstatus/components/ram.c | 212 ++ slstatus/components/ram.o | Bin 0 -> 3104 bytes slstatus/components/run_command.c | 31 + slstatus/components/run_command.o | Bin 0 -> 2144 bytes slstatus/components/swap.c | 274 +++ slstatus/components/swap.o | Bin 0 -> 3944 bytes slstatus/components/temperature.c | 73 + slstatus/components/temperature.o | Bin 0 -> 1712 bytes slstatus/components/uptime.c | 34 + slstatus/components/uptime.o | Bin 0 -> 1992 bytes slstatus/components/user.c | 33 + slstatus/components/user.o | Bin 0 -> 2200 bytes slstatus/components/volume.c | 219 ++ slstatus/components/volume.o | Bin 0 -> 3616 bytes slstatus/components/wifi.c | 267 ++ slstatus/components/wifi.o | Bin 0 -> 4296 bytes slstatus/config.def.h | 69 + slstatus/config.h | 69 + slstatus/config.mk | 22 + slstatus/slstatus | Bin 0 -> 31344 bytes slstatus/slstatus.1 | 47 + slstatus/slstatus.c | 134 ++ slstatus/slstatus.h | 84 + slstatus/slstatus.o | Bin 0 -> 5496 bytes slstatus/util.c | 141 ++ slstatus/util.h | 16 + slstatus/util.o | Bin 0 -> 5576 bytes st | 1 - st/FAQ | 253 ++ st/LEGACY | 17 + st/LICENSE | 34 + st/Makefile | 51 + st/README | 34 + st/TODO | 28 + st/arg.h | 50 + st/config.def.h | 474 ++++ st/config.h | 474 ++++ st/config.mk | 36 + st/st | Bin 0 -> 109224 bytes st/st.1 | 177 ++ st/st.c | 2674 +++++++++++++++++++++ st/st.h | 126 + st/st.info | 243 ++ st/st.o | Bin 0 -> 79088 bytes st/win.h | 41 + st/x.c | 2099 ++++++++++++++++ st/x.o | Bin 0 -> 75352 bytes 125 files changed, 15361 insertions(+), 4 deletions(-) delete mode 160000 dmenu create mode 100644 dmenu/LICENSE create mode 100644 dmenu/Makefile create mode 100644 dmenu/README create mode 100644 dmenu/arg.h create mode 100644 dmenu/config.def.h create mode 100644 dmenu/config.h create mode 100644 dmenu/config.mk create mode 100755 dmenu/dmenu create mode 100644 dmenu/dmenu.1 create mode 100644 dmenu/dmenu.c create mode 100644 dmenu/dmenu.o create mode 100755 dmenu/dmenu_path create mode 100755 dmenu/dmenu_run create mode 100644 dmenu/drw.c create mode 100644 dmenu/drw.h create mode 100644 dmenu/drw.o create mode 100755 dmenu/stest create mode 100644 dmenu/stest.1 create mode 100644 dmenu/stest.c create mode 100644 dmenu/stest.o create mode 100644 dmenu/util.c create mode 100644 dmenu/util.h create mode 100644 dmenu/util.o delete mode 160000 dwm create mode 100644 dwm/LICENSE create mode 100644 dwm/Makefile create mode 100644 dwm/README create mode 100644 dwm/config.def.aangepast create mode 100644 dwm/config.def.h create mode 100644 dwm/config.h create mode 100644 dwm/config.mk create mode 100644 dwm/config.old create mode 100644 dwm/drw.c create mode 100644 dwm/drw.h create mode 100644 dwm/drw.o create mode 100755 dwm/dwm create mode 100644 dwm/dwm.1 create mode 100644 dwm/dwm.c create mode 100644 dwm/dwm.o create mode 100644 dwm/dwm.png create mode 100644 dwm/transient.c create mode 100644 dwm/util.c create mode 100644 dwm/util.h create mode 100644 dwm/util.o delete mode 160000 slstatus create mode 100644 slstatus/LICENSE create mode 100644 slstatus/Makefile create mode 100644 slstatus/README create mode 100644 slstatus/arg.h create mode 100644 slstatus/components/battery.c create mode 100644 slstatus/components/battery.o create mode 100644 slstatus/components/cat.c create mode 100644 slstatus/components/cat.o create mode 100644 slstatus/components/cpu.c create mode 100644 slstatus/components/cpu.o create mode 100644 slstatus/components/datetime.c create mode 100644 slstatus/components/datetime.o create mode 100644 slstatus/components/disk.c create mode 100644 slstatus/components/disk.o create mode 100644 slstatus/components/entropy.c create mode 100644 slstatus/components/entropy.o create mode 100644 slstatus/components/hostname.c create mode 100644 slstatus/components/hostname.o create mode 100644 slstatus/components/ip.c create mode 100644 slstatus/components/ip.o create mode 100644 slstatus/components/kernel_release.c create mode 100644 slstatus/components/kernel_release.o create mode 100644 slstatus/components/keyboard_indicators.c create mode 100644 slstatus/components/keyboard_indicators.o create mode 100644 slstatus/components/keymap.c create mode 100644 slstatus/components/keymap.o create mode 100644 slstatus/components/load_avg.c create mode 100644 slstatus/components/load_avg.o create mode 100644 slstatus/components/netspeeds.c create mode 100644 slstatus/components/netspeeds.o create mode 100644 slstatus/components/num_files.c create mode 100644 slstatus/components/num_files.o create mode 100644 slstatus/components/ram.c create mode 100644 slstatus/components/ram.o create mode 100644 slstatus/components/run_command.c create mode 100644 slstatus/components/run_command.o create mode 100644 slstatus/components/swap.c create mode 100644 slstatus/components/swap.o create mode 100644 slstatus/components/temperature.c create mode 100644 slstatus/components/temperature.o create mode 100644 slstatus/components/uptime.c create mode 100644 slstatus/components/uptime.o create mode 100644 slstatus/components/user.c create mode 100644 slstatus/components/user.o create mode 100644 slstatus/components/volume.c create mode 100644 slstatus/components/volume.o create mode 100644 slstatus/components/wifi.c create mode 100644 slstatus/components/wifi.o create mode 100644 slstatus/config.def.h create mode 100644 slstatus/config.h create mode 100644 slstatus/config.mk create mode 100755 slstatus/slstatus create mode 100644 slstatus/slstatus.1 create mode 100644 slstatus/slstatus.c create mode 100644 slstatus/slstatus.h create mode 100644 slstatus/slstatus.o create mode 100644 slstatus/util.c create mode 100644 slstatus/util.h create mode 100644 slstatus/util.o delete mode 160000 st create mode 100644 st/FAQ create mode 100644 st/LEGACY create mode 100644 st/LICENSE create mode 100644 st/Makefile create mode 100644 st/README create mode 100644 st/TODO create mode 100644 st/arg.h create mode 100644 st/config.def.h create mode 100644 st/config.h create mode 100644 st/config.mk create mode 100755 st/st create mode 100644 st/st.1 create mode 100644 st/st.c create mode 100644 st/st.h create mode 100644 st/st.info create mode 100644 st/st.o create mode 100644 st/win.h create mode 100644 st/x.c create mode 100644 st/x.o diff --git a/dmenu b/dmenu deleted file mode 160000 index 8df553e..0000000 --- a/dmenu +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8df553e0048733bab4bc85a6b76bcfd44c046e71 diff --git a/dmenu/LICENSE b/dmenu/LICENSE new file mode 100644 index 0000000..2a64b28 --- /dev/null +++ b/dmenu/LICENSE @@ -0,0 +1,30 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2022 Hiltjo Posthuma +© 2015-2019 Quentin Rameau + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/dmenu/Makefile b/dmenu/Makefile new file mode 100644 index 0000000..458c524 --- /dev/null +++ b/dmenu/Makefile @@ -0,0 +1,58 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: dmenu stest + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(OBJ): arg.h config.h config.mk drw.h + +dmenu: dmenu.o drw.o util.o + $(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) + +stest: stest.o + $(CC) -o $@ stest.o $(LDFLAGS) + +clean: + rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz + +dist: clean + mkdir -p dmenu-$(VERSION) + cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ + drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ + dmenu-$(VERSION) + tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) + gzip dmenu-$(VERSION).tar + rm -rf dmenu-$(VERSION) + +install: all + mkdir -p $(DESTDIR)$(PREFIX)/bin + cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path + chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run + chmod 755 $(DESTDIR)$(PREFIX)/bin/stest + mkdir -p $(DESTDIR)$(MANPREFIX)/man1 + sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 + chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ + $(DESTDIR)$(PREFIX)/bin/dmenu_path\ + $(DESTDIR)$(PREFIX)/bin/dmenu_run\ + $(DESTDIR)$(PREFIX)/bin/stest\ + $(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ + $(DESTDIR)$(MANPREFIX)/man1/stest.1 + +.PHONY: all clean dist install uninstall diff --git a/dmenu/README b/dmenu/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/dmenu/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/dmenu/arg.h b/dmenu/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/dmenu/arg.h @@ -0,0 +1,49 @@ +/* + * Copy me if you can. + * by 20h + */ + +#ifndef ARG_H__ +#define ARG_H__ + +extern char *argv0; + +/* use main(int argc, char *argv[]) */ +#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ + argv[0] && argv[0][0] == '-'\ + && argv[0][1];\ + argc--, argv++) {\ + char argc_;\ + char **argv_;\ + int brk_;\ + if (argv[0][1] == '-' && argv[0][2] == '\0') {\ + argv++;\ + argc--;\ + break;\ + }\ + for (brk_ = 0, argv[0]++, argv_ = argv;\ + argv[0][0] && !brk_;\ + argv[0]++) {\ + if (argv_ != argv)\ + break;\ + argc_ = argv[0][0];\ + switch (argc_) + +#define ARGEND }\ + } + +#define ARGC() argc_ + +#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ + ((x), abort(), (char *)0) :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ + (char *)0 :\ + (brk_ = 1, (argv[0][1] != '\0')?\ + (&argv[0][1]) :\ + (argc--, argv++, argv[0]))) + +#endif diff --git a/dmenu/config.def.h b/dmenu/config.def.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/dmenu/config.def.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/dmenu/config.h b/dmenu/config.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/dmenu/config.h @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +/* Default settings; can be overriden by command line. */ + +static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ +/* -fn option overrides fonts[0]; default X11 font or font set */ +static const char *fonts[] = { + "monospace:size=10" +}; +static const char *prompt = NULL; /* -p option; prompt to the left of input field */ +static const char *colors[SchemeLast][2] = { + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +static unsigned int lines = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; diff --git a/dmenu/config.mk b/dmenu/config.mk new file mode 100644 index 0000000..566348b --- /dev/null +++ b/dmenu/config.mk @@ -0,0 +1,32 @@ +# dmenu version +VERSION = 5.2 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 +#MANPREFIX = ${PREFIX}/man + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# flags +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS) +CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) +LDFLAGS = $(LIBS) + +# compiler and linker +CC = cc diff --git a/dmenu/dmenu b/dmenu/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..71055b8e4b763d859d69cf783ca9323c2866b2af GIT binary patch literal 42792 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#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; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + 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/dmenu.o b/dmenu/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..d20d6707fb0c1094a3fad391fe04395db6fc19ab GIT binary patch literal 32080 zcmeI5dwf*YwfIjSV8A#NEm&I7j&-bwgqQ);gon+L37qHvfdq{_43o(qk-VDB5D-u> z3H00$>(3&!)+$zeYpu81mRcWNo)aeY?YjZ#7RSUE7Uiu{)=r$PDN0(w%P)2|Fh#g*LZiN8(IiVdn#lJ0U~6 zE0o>0reJs??OC+s^>F9qDw9sg&ffX=AzHHSJzr_c`=!oM8+2saT`Ct+qIB;KWskyi zJ$7GTpN)xSEToEiZRa(6=bJ<9z3HlOo!X|tgSL12Zb#pML8EHyG1@J0&b!+;e_?Pz zaAEL<-E+&qbJ?ePu1(pa9G9W2M-LWF^;(IlXO5>qxw`70G}<>Mf#-rxVzoboA5Y_l z9bJDEPdksEVW+J}!|l$dEL8w7e01QXO&;reKJgS4KCpudIJn8#^B3+ecmCmgWIJC` z#!5a-6}0dmlzIN{;M`!v-10e{=R=NG$D#Nz?_=t+w}O(`U8*v)K&bO#lnbS)6XmW7 zPGX@>)ySMZDlU|!?m{`ep`5pJdUJMD^9{UbJGvcr9Eg95Jf|xoG2HH)=o~p-40V{Y zr?7YRnYNAyyw_Vqmv(1aR`0*VXNC>wuNX9a8l_+L^<<)=44dj08qm&4U7=8&&(uJX zpiT=0)M+;K2B@-3>seLTFN_wYmbRLZ~6|%xKQOz7R4v(fhSDPF72G`&*>ebOmmLpJbV1SFZ%kN_ruORfdkNb_kY>f zH>0!0pXKc7ePTw(v3RxJrTYpDDcC*-8mtQybh?G*%IcdeT`;n}oZA8Pl$^H%2cV5` z`>UK6($1bAcn%`SI|JN84~`zpzueZ+Pg`p&E7?t`KT(;<&?zYIc5Qd`#lIfzO#hbM zw%a!Yy4CQovsVfCzV4PLKu==bQ0FUq`u5JHPEbysn&a)b3o_=G5x=zU&wa-`K7|&e zpSa#_@geY#-mPe;3qHretkn4u9_Y?T zv)5c@#S4OKCi|`UB_)CPrh1>q9aGf)Ufiz=TI_vdM6TUwb!OgCnE7yGcxm6B-owz} zdWV$ub@%R@7j}-)h#9<&M@f6_yB16{?6yq5HQ*W^HPqOjwtUbG4ngY<>&I+ZJ6Sx5 z+Wpxm0}rEB{%mCnOrLD3Y}?u6Ebyn>9q+}XG}Lt!r%*cz7Iv@B5B7D39Mz*{M30s3 zJeI~Cnq8_VWN(L^K5940#gIn;yqPRn1djxr1tb77IhfX!1p2@) zCD69_Dot2n;*v7aow9ES&O5X0jziw|Pf)IHlI3mhg{#1U!U})-En||IOtPH{|0uh2 z!v4OIN@XYB_U*ztex>IzK$9SWx8vt<)war?ZY9p)w9u5!x1GwY-V1C7VBv*wdw0w38aMC+jzUPfKw)*-yMseQ3@51)tiRXRwtTK>KtYj zz30z>PCB&IseeFKiZfNHyd3T`@v}KYXGxarlw|atVLNI_4^u66 zoE$_t>@;PSI@3Tp?5Ghr?5N=*sZ3P`Rr&D@>JLqSX>_u1Pdav`FH{=18?~X>3JxW2CvpnhdX5GGcLKG|>WmZ|pLwrasn? z7<+l%IBRURHFmKzw$@6tL~e{t^r;x%{IS)GYnLsAbDgiQwjtJnmrcH==EnM_I9@Mq z@GWj^P?z<-`o@OZcw_Uz;XWDWk8P;-MH}lN0unSV?!RskyhZRy}%f7mL^e zc4Nxw7-SQviPhw3?^(s+(z2^;_vK6hXjWxvSMoE>&SJcIg(yE*htTbx}+O>!SSp z%P+t3N<3r0^I`}V)J0=UQzROj*iw6QY)T;CQiJ|=Fhc1eI=B@dFlzXR@%cE+2%S~_ z%mHKXL4biBp-DCVOmEjnxG9Zx)2oi%nY%mOmAeO2!1GYQ&ca}HwtS5BEFKP#dT19i znf*g!@3hmRz3umaU8muM-{{S>0H7a@CC=MDb&#wC-bzwJ@EVfoZ7&*B&B&`eSB=rH z#yV-wg#V#j=Ad$CU)>oR!>?97*tbtNeV|PD|3GH*pfVnrSMRN-XEadYhW}1r%6}&? zdfgMz$S^kv!29%M`uCR5Uzu-VD&gw*P(9 zSkO>IK0U|nXvb0T2Fa=gf)3^8&PsSh=qG7MUm`gV8yPsjQ@y1A_?t-2@)HK4EqpQ?rP5EK&Z2_Oq6}?X z3C?558z`@os*m2`{nxzK#dGRcye|8uo^25%Xykf>nGRrdqCnqiN=Xy6>fc+2Fr}Ax`LHta+Yk}Vn zD{(2UV*)1whkCw5zr)H`5xyVL+LcUjS!v;(_@&{_I?&No;V&q4UJW}Rl8NE2kiUpu zw4jyl>D`hvextt(G8d6|U~51Y0%f_lx-la?)Va}Lsn7<01)KwYfkUCrd;JUGx+G;+ zNy_t~!Vgx@_ijJiyFHj5@^1fLYRdjl;r^g^)~-C{ghegAJRQPj4 zUDHxRU9(ca_HTfJ>9(^EE+eU-!k1Ui@@_90ie`jTj)n^Nfz>-hh3|yCGu{d191U(I ztGh!fuc@-mf|4JB*dw8w*MiSb?8{Il=mzP1yL<1oqlfIJhcfMyUhnqebno`^q0r7p z2X^pc$8f;I$EcQ;vLCuL#QFeH$I^W$YLvQ5*Q1Jrdk(oH)-r8PCJls2plGDx0U$K}Y{G$fuv_ktJu>)UNbYt>GzH9)(qh8h@qk zT0j2uGaaF;L&Lr8{>MQKvV|t6)|!$p+eI*^S2oxnv>Aogq$K+t*t;lLu_P59#5d8u z&al4>YBQ|Ww{_g*z&9X5Ht>hMYon)?*|MgT?*D-JI?a4v+AW!XjW!u1Bc0@{<5`ZzQgWg6t;(+_~& z14@DV81T%Xv0BUb%k-YpcIs?z`&_8Uwo~8owl|%D9rRKEK>$43&{_`+k{w^mK&&0r z|HOI`8XzWUJLUDZ{}D9Y`bgWUVcvFZlx?SA=JR!Rk(&59shr{b4c;@tU0eKj!>AkX z(nD9+`Nva5;9qPuIFw;fmO`7!gkiI??Nq9_Jr8vqPlG|$yM7M5g1{h`t%h!$=;W?+ zZ~I?B28Q7k-gTGY)s(J27{fY3cnzkaZhOjg$ksoJUjwo}u+9m`NHCBpjbIbS3~vwAWGK#%l%Au`6Q?)gsv(E7GF zzi@Pu>iM&XszBLo0?a}1ZwPc1KbxMWJ*~^8I|N_D?0f|&a(E$I&pJEaOYH3p{NIy$J1zqCZpnA|41w4+Z>G?YxDDK9h4t_d=dUKH zYr=P7Vdtf=qsNh+6R@5OS@#an>vm{-&k@yFj#on+4EL^o5hU!ckr-n~({F=sKcR_m zrIewQz7IixG^)b^tnude`H!_vt#PV7u^kjD@oFJcTKy+z+62tUYA| zMywkF5%yT@>!9Tv@A`DSKh3f!*qFQ>@1O!*b89B2LH`*F<>)UYKXde>ZAQm)-uAzL z-d}-%zHrVycr>Zn?D>-#f7Am5b;Zwu0Yb;D#Dee)0Y+;jL%Mr5^(P~w8^-`Cjrgkt z_?Atp=!AzXJ@4Vk_ge2m9KmzlpJRRWzt{HUU}mf)`RWRd4(8>{;k$!j95)iGy9XzA z1iT&ZewK8VnkgT_LcuG8x_Jq2c;=xWu!VM(w?p-KZEVkWv_)UT`Wck~=iGOqW=gD2 zJ@n zN)P@-#i@8RNoS-j@^-wB=Ia|>tFQN$Mr$3{`wcbr_R7l12|i*g!xOmzX>cZ zn?uc{J0Ds@(1v#a8`m+f+y>LE;9+Dbb4M?93}uDHpuEFF5^52CdwsDxcmR%f87JV|4oh4i;E}vvgcMO z8sZ6GAdoj9FMn(xp>6}Wj2oAiKOu*($`xNKaG@SM&h(jcvB~T}22TnVM$kFs<~df% z@~o8eGSA4k16l>9QExSHzLI2$;G9p_VQe{5Jz2M>PW6oHN-OpFHl&9<+3SV`J^5`z zr+LcK#-w=igP!c5#|IK1In@LG!}7xsCmsKW=b$HRU24!Xsx2+#DN23FGYUY^lYu)4 z?j^YkNvmeX^`8xbjY<0?MVGdWe>`A_L%nxO7{Ss`|~dK zl%@WV^PUV*Y6OGxYLrv@OHh9Z^%tZ5kSBk+r=ZkR6djleh_}O0iT;65hK?>v`e`cq z3G5?3m3j)UK@A09ZOl_J!&6l4DVPeD&-4_aJ8X5g+EY;ODY{0-{8VLngG!S6BTvCh zPtkmJV|%ja4fHko@F>}F2O0zAZA(r47jz=};!BGA4C9Ki9&D@!+v7`pi+zE<_TxAM z;d=a-mh`pqh4MA{;ucRf+$UXam4WC2O7{|;!G}MuBB3pr@ei zjOm^yePPd&MWvn-MQNK;Re?Y?h>FHzgPO-lC;Aw5Qu|YW1rMwnG9BArYWZ+gE2UiZ zC1-***%TjzUkW_7+8M}TqDm^6w9u|WpF}0ZPcdK*a8gu zGRhy+Vo5zyi7ztvG`L4S`P4Qpr1&{-Mm+_F{I$e)5NCT9z&+}jO?r4M;}Vj8#gJc0 ze80gPi0?Caf_S&ValAr1Rey(T_Ss~koy#c@s`dH!Wr{zB1fw66Bzbalz)Nb*w6TdHu5au ztREK$kUwm2oIj)f9k3XJf&KI>T%&xYqWyM05BDfvMvB!~3GASf&#-X$76VnY^^Uqu zu}ZPyKu}{QoKL`kdVWS+jfucc!h!j+-CVAJ5{Hut$$0YrW`m!lI8G{cVEK{6`JT_X zM2_~fP)BO#gm6x?UB%RW%et*iCwB+d1PjTH>&$9X% zUPXw1b0D@B6aTismlEgkkfZTs0`(U%pmT!wB!k~fe1^eqB|gjG?Zk1Mlffa5k@636}_1Fl`6ejaBzPQBp(AEM%!T9#UA z0Y1#&-J}QmlMH_(ezw8)6OTz@xc)2g5r+I>;@B@`zy&+BKVb0V#7hkRK5<-Vli_3H zIERD+fLI3$JtG z-*e$BUHEM-{QEBaZWsP@7yhUV-|50%aN%#b@V8y~CoVh%CI*AG&vRY)XcvBo3%}fj z7rXG;F8q2I9(CajF8mf3-s!?Ox$p;E_^(~~HW$9zg}>y&-*Vx-F8q`WKNDUZ2dj4v zJXZ$e=elsc5ic)lHN=+3n-dK&E1GC-X>7J&r({h_9JcpaweeVei>0kXDQ&5>5j)@q5>fx<{D?zOb)mrzQpJ`#^EiM7CvzgVOp(PT9@HpXGw z-m+M0V>4_oyfJ3sMNO=(wjR_pgJ3Oe^MwC&N-N&Dv>`8mr)7Ehq^+UGT1-NCZiqrz zY8zT&&2f-xs*XU~#s=7L2-_JgwI#E~YKb<-VhvVPq#5=d!oEw~f!Jgv;)@Hc<+V{O zFI=3D9|8OrhacndV*-9$jvrUx$Cda|fFFhU0lN+HQ>6(g`G86gP$>c`NkF9us6+vk zYMe?nPNz~?jnkh>!#Jg3oYF8(X&9$8j8huMDGlS5hVe?ncx~)>3$}S;y<=m*ZL?NQ zZH)HIT2)mo@kn%ORdmVHD%_}PRZfStaSd;rZHT}g%DPxgwTCL@wOG6g^uf;5_>%sn za#|GViN#BCi{~`8*S4~Jz%8_{Dr`_t{q-?yYgO3x)J1bFsxRszO;+XH25hTfkBVCi z6=hYHuV{!`mBn?9EwK=7X@#1OE@_6+MB^)(VpVb2Yug%YuEN4%37ex$E3k-IYG^HZ zs)HJ@kJV#qgO&r8j#AK8s-}l$O$~;tW=)$`UQ$t25u6$>slv#HXrv{E5|MagEwt!3 zD2J`K5LFX{_P-d4+5)!ejkhgf9jW%f3M0WV z+667q`YNK8r47(48iMi0daDxZw>eTDnVpC=uP9dy8myV7{H0h$|7BTYEhYpPstjqT zE!I$RU8JSOhFIRb%QB_KR^^OH)0{|cORUDKoD%WLDwx~o>TR$1KGunanFc?>nzLYKuVz)GvC zf@h7Qr>kZzXQZ%XOiMkU#DskLxZN(4J~ISbrwH zr=b2Rg7bSH%1iyDggnNv{rE*W24qnOn?JJk&wR;?pgmdL!R5g zJcF~}Vg}!@v|Cn#!8zZA;NKPbt`r>Yj<(do!S(VJLq0_Me`#>m^LwG^CZPx4 zlQ5uu*^lr&2?N?6goE>aS;$8O$G?onfO=}+V0qj@iGlsFO5a*ugLAvW_aqD~@7K51 zMF!6$o@?;Y!~?|fgx_7TJy#m?TS>mu;9QUR{)B<;VLKzl@l*!~>u)jS*`AvX&Uzf7 zN7{L(A-@lefN-zDU(ne4mBCs6<3fKu$g-V(Fyu2y&+`WN6F*>Z*8d;G@g(hk-H>NL zziV*L_alR||GzLe=d1tXx4&L?kez4IS9q9A*2^g3T#wv;zisdbNzbJQ?9;owdG^lhEaMu5V&@bEhUkrJ+|A^p4U=xp%e-|8WZHEKH zy9VccKQ=hm@0Y|)KWETD#O;d9J3;Uw$dB!rOx)B{EaWkc{eQK=+0HV9bH0^AzpR%V zT;!J;oXgv6aIUYF24{P^h@1{2ct5!8zYw8axF0Fg$K>&bQm(6mhPB$!^4}Bk(_G}QHTYq&{{|OcXYk*V{4IjxchmeH+esY# z&;keB|05xfaa-WPu-V{j&o2zl`To1$GT%QEH}n0gkeB(sA-L3Y%tg=VLSE`gp?w|P z9=Kd*3oiAK5?t!ZA#U3LT^ISdkdK2O+0V}#^6byUf=?ClZwWqGaNYue_TZi))_*2# zjX=Ik@C%5uA21$6w!vA?l?G?|X+r-ZNYC;!40+}Yg}i)ks&SD|2zhD$?Jn}0gnX;8 z=hue(-4KW2DT8lT6xLx}^gL(C^LX-+!MVIc&eRIg&lBNbewpCt%X_t$b&cRC&wj2H z9N9x8ztrHYCvI@gw?lBL|B&FQpXXP9H#qA*X>hjZp9W|7VVT-sZf~srYX)a|zrk7m z7=yF?ID@l%p}|*?---;rf%rn==z|(K*l(*0`NJf?LCDMYcAp{7oTx>@ig#8EyZIL`Sop!`ZWINucrA)tH-9L(1u zgkb9L)L6-#OP|q|tSkGp-#$f8f{eu`#emWd1{|AH+O!-|JTbIIlH5@Ge z9C6em+dt3CkmFH8-J1gHn&s9G)D_lE&F z)+y`%4slce1i_{Li9!$hpTji4uLj8aalal0Q~wP@9^Y*S%$XyG_XB zUbtL1Fz9`1y1ZeyXFa?R%CtxCJJa&02jem5{bicZfP2>Sv`e}4ezAT%EdR2wbEeRP zf24`Qw5M0dqdg;(lx69CURpoy z2Hc;80rPB!gYEptMZV94d#RJ7o(`eso5V30#&NzE3O-lJe@Ez%{uwLeQ4g1EyusNI z6AjMwGE?YZC-R*qd8%qv_+j&Ch*&y_MDfHka%YV~YH)i`UA&#h$wmXJsLY1-J=C%Cj{sIlJ2_Iz8cH{LGn*(>BxKm6^w3a>? z&wo;Iv~@JeYxeW&1e2zi;Wxo*kjz0Z)J47xBpDD?jj?m6E_4EdLpv}Nr!c$he^XL5b9A6_!# z*&bf!MB6utd|x-@S$~hgSq>73`Fn)?+d^L2|C!)Y&q!n4kLzo+!MVOJF*y4t&){5Nhfc;b8yt z81ijY-j589KE?34!B=T)4fW~^v=Q@Q`LhhZf#lB-9MjCe4+!579QABe6zav`tmk6E zrJh{DQ4g2v3WKwrpbMX6@Q28rYYonRh#LHHl5a8i?})E9IP3X=!D$+4ZFb?mHaP2f z-i049IQ!u(gR}oXFnBxJf6Cx2pOK{nxIM6buff^QfD4~ta2{6|3XXYjd#({2>z(Br z4bJ{tZE&{d9)ojvA22w}|JvYO-ror>+vj${(N5NLkT|lRigKMWJVj{8vIA z^X2E%aQrP52wX2U`qsM8;349d8GHfpFmbf=emL0wO9hwDsWpPj@uyShN1J#YyGzKU zFIoRRhCI*Hwg`?kvHi~&obBHs^lujWUl+VaaJW ziBqy9zl#7>SCapNo^SBI z#3vY>_dOIC{Ldsm+2D`RbGpdj|3UIx&+PxnG@r8#`8P;DY;fLJQD*RWNWQ}0y~J_N z69eb_0da0$%&(y5Iddl~4gHI>*wDA8qJaN!(}fTZ#J(-bOsz;C1BBT!U{Uo^SA%iSzyhwtpG< zv%rvlg!p8G=MXP4xSx2);2D&!ZSY@Exxxnj6>)ygW;^#$JyLT})&BtBz&V4iQQgl3 z=S$=$!{7HydAv@?j37`klz!yA4`7sv#ot8IdPb1E&)}npXB(V<6ENT4xrQFRC8Q)L zkUaA%>3+Gv`FGql8=QZaO^#!zi+^WL_DAIW`(?7-BIoZT7oYE-b`FFVR_fc5SW%^Qo=C!P-k4LJlJoxioeO|&>&9S;j zp8B)!yqZWnV&zr0v{-q~jd+9N0r=DZyrdA2NOK&1L_i}gMmH)2NE)|!>Myvhyl7)R z{4hb>%7gzmQ7jLBPIRpLn|Qjvu_2KMzjUDgxL7NXEvZ@ze{3C-1^Iuw_CYeN68icp zsicUmKBe)SBdGfL);bTa(%~pd5`ED$NuPb^>hss&glV}yaXF#W&(+W;Z(IH~XY)ARizxEL(|<8dv);~?gbk76@_6dNr6qm+Mv zR+IenC9m{FBb)}ye^f%pKTPtNKXwH(|8`)QKidhHDvY{FpRs+)z}3O$E{No7DHo#j zlHs)}i)#(ICSumVP3e6QCj;Bh?DueiI?eP;C_V3kVR +#include +#include +#include +#include + +#include "drw.h" +#include "util.h" + +#define UTF_INVALID 0xFFFD +#define UTF_SIZ 4 + +static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0}; +static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8}; +static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000}; +static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF}; + +static long +utf8decodebyte(const char c, size_t *i) +{ + for (*i = 0; *i < (UTF_SIZ + 1); ++(*i)) + if (((unsigned char)c & utfmask[*i]) == utfbyte[*i]) + return (unsigned char)c & ~utfmask[*i]; + return 0; +} + +static size_t +utf8validate(long *u, size_t i) +{ + if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF)) + *u = UTF_INVALID; + for (i = 1; *u > utfmax[i]; ++i) + ; + return i; +} + +static size_t +utf8decode(const char *c, long *u, size_t clen) +{ + size_t i, j, len, type; + long udecoded; + + *u = UTF_INVALID; + if (!clen) + return 0; + udecoded = utf8decodebyte(c[0], &len); + if (!BETWEEN(len, 1, UTF_SIZ)) + return 1; + for (i = 1, j = 1; i < clen && j < len; ++i, ++j) { + udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type); + if (type) + return j; + } + if (j < len) + return 0; + *u = udecoded; + utf8validate(u, len); + + 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, 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; + + 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); + 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, "..."); + while (1) { + ew = ellipsis_len = utf8strlen = 0; + utf8str = text; + nextfont = NULL; + while (*text) { + utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ); + 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) { + utf8strlen += utf8charlen; + text += utf8charlen; + ew += tmpw; + } else { + nextfont = curfont; + } + break; + } + } + + if (overflow || !charexists || nextfont) + 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 (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/dmenu/drw.h b/dmenu/drw.h new file mode 100644 index 0000000..fd7631b --- /dev/null +++ b/dmenu/drw.h @@ -0,0 +1,58 @@ +/* See LICENSE file for copyright and license details. */ + +typedef struct { + Cursor cursor; +} Cur; + +typedef struct Fnt { + Display *dpy; + unsigned int h; + XftFont *xfont; + FcPattern *pattern; + struct Fnt *next; +} Fnt; + +enum { ColFg, ColBg }; /* Clr scheme index */ +typedef XftColor Clr; + +typedef struct { + unsigned int w, h; + Display *dpy; + int screen; + Window root; + Drawable drawable; + GC gc; + Clr *scheme; + Fnt *fonts; +} Drw; + +/* Drawable abstraction */ +Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); +void drw_resize(Drw *drw, unsigned int w, unsigned int h); +void drw_free(Drw *drw); + +/* Fnt abstraction */ +Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); +void drw_fontset_free(Fnt* set); +unsigned int drw_fontset_getwidth(Drw *drw, const char *text); +unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); +void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); + +/* Colorscheme abstraction */ +void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); +Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); + +/* Cursor abstraction */ +Cur *drw_cur_create(Drw *drw, int shape); +void drw_cur_free(Drw *drw, Cur *cursor); + +/* Drawing context manipulation */ +void drw_setfontset(Drw *drw, Fnt *set); +void drw_setscheme(Drw *drw, Clr *scm); + +/* Drawing functions */ +void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert); +int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert); + +/* Map functions */ +void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h); diff --git a/dmenu/drw.o b/dmenu/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..4b2a23c6c79b1aec9c9260959498664c0b58389c GIT binary patch literal 11160 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 literal 0 HcmV?d00001 diff --git a/dmenu/stest b/dmenu/stest new file mode 100755 index 0000000000000000000000000000000000000000..37e493ad7ac32374ab4860f72f5be739579acc5a GIT binary patch literal 16856 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