From c8968c30d8e2ed8daba8e01b52066ab9ac74a8dd Mon Sep 17 00:00:00 2001 From: kaoplo Date: Fri, 30 Dec 2022 23:20:02 +0100 Subject: [PATCH] meow --- dmenu | 1 - dmenubuild/LICENSE | 30 ++ dmenubuild/Makefile | 64 ++++ dmenubuild/README | 24 ++ dmenubuild/arg.h | 49 +++ dmenubuild/config.def.h | 23 ++ dmenubuild/config.h | 23 ++ dmenubuild/config.mk | 32 ++ dmenubuild/dmenu | Bin 0 -> 47984 bytes dmenubuild/dmenu.1 | 194 ++++++++++ dmenubuild/dmenu.c | 792 ++++++++++++++++++++++++++++++++++++++++ dmenubuild/dmenu.o | Bin 0 -> 32024 bytes dmenubuild/dmenu_path | 13 + dmenubuild/dmenu_run | 2 + dmenubuild/drw.c | 464 +++++++++++++++++++++++ dmenubuild/drw.h | 58 +++ dmenubuild/drw.o | Bin 0 -> 11472 bytes dmenubuild/stest | Bin 0 -> 21544 bytes dmenubuild/stest.1 | 90 +++++ dmenubuild/stest.c | 109 ++++++ dmenubuild/stest.o | Bin 0 -> 5232 bytes dmenubuild/util.c | 36 ++ dmenubuild/util.h | 8 + dmenubuild/util.o | Bin 0 -> 2216 bytes 24 files changed, 2011 insertions(+), 1 deletion(-) delete mode 160000 dmenu create mode 100644 dmenubuild/LICENSE create mode 100644 dmenubuild/Makefile create mode 100644 dmenubuild/README create mode 100644 dmenubuild/arg.h create mode 100644 dmenubuild/config.def.h create mode 100644 dmenubuild/config.h create mode 100644 dmenubuild/config.mk create mode 100755 dmenubuild/dmenu create mode 100644 dmenubuild/dmenu.1 create mode 100644 dmenubuild/dmenu.c create mode 100644 dmenubuild/dmenu.o create mode 100755 dmenubuild/dmenu_path create mode 100755 dmenubuild/dmenu_run create mode 100644 dmenubuild/drw.c create mode 100644 dmenubuild/drw.h create mode 100644 dmenubuild/drw.o create mode 100755 dmenubuild/stest create mode 100644 dmenubuild/stest.1 create mode 100644 dmenubuild/stest.c create mode 100644 dmenubuild/stest.o create mode 100644 dmenubuild/util.c create mode 100644 dmenubuild/util.h create mode 100644 dmenubuild/util.o diff --git a/dmenu b/dmenu deleted file mode 160000 index 1e8c5b6..0000000 --- a/dmenu +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1e8c5b68f4881bd4ae257c780fd41f129c79f419 diff --git a/dmenubuild/LICENSE b/dmenubuild/LICENSE new file mode 100644 index 0000000..2a64b28 --- /dev/null +++ b/dmenubuild/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/dmenubuild/Makefile b/dmenubuild/Makefile new file mode 100644 index 0000000..a03a95c --- /dev/null +++ b/dmenubuild/Makefile @@ -0,0 +1,64 @@ +# 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: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.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 options clean dist install uninstall diff --git a/dmenubuild/README b/dmenubuild/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/dmenubuild/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/dmenubuild/arg.h b/dmenubuild/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/dmenubuild/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/dmenubuild/config.def.h b/dmenubuild/config.def.h new file mode 100644 index 0000000..1edb647 --- /dev/null +++ b/dmenubuild/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/dmenubuild/config.h b/dmenubuild/config.h new file mode 100644 index 0000000..c0d7d74 --- /dev/null +++ b/dmenubuild/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=14" +}; +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/dmenubuild/config.mk b/dmenubuild/config.mk new file mode 100644 index 0000000..b0bd246 --- /dev/null +++ b/dmenubuild/config.mk @@ -0,0 +1,32 @@ +# dmenu version +VERSION = 5.1 + +# 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/dmenubuild/dmenu b/dmenubuild/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..7ac26cc36e29e0ca37b670b28352d9d482e74d74 GIT binary patch literal 47984 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 IAca-_7j*%p&;S4c literal 0 HcmV?d00001 diff --git a/dmenubuild/dmenu.1 b/dmenubuild/dmenu.1 new file mode 100644 index 0000000..323f93c --- /dev/null +++ b/dmenubuild/dmenu.1 @@ -0,0 +1,194 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-l +.IR lines ] +.RB [ \-m +.IR monitor ] +.RB [ \-p +.IR prompt ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-sb +.IR color ] +.RB [ \-sf +.IR color ] +.RB [ \-w +.IR windowid ] +.P +.BR dmenu_run " ..." +.SH DESCRIPTION +.B dmenu +is a dynamic menu for X, which reads a list of newline\-separated items from +stdin. When the user selects an item and presses Return, their choice is printed +to stdout and dmenu terminates. Entering text will narrow the items to those +matching the tokens in the input. +.P +.B dmenu_run +is a script used by +.IR dwm (1) +which lists programs in the user's $PATH and runs the result in their $SHELL. +.SH OPTIONS +.TP +.B \-b +dmenu appears at the bottom of the screen. +.TP +.B \-f +dmenu grabs the keyboard before reading stdin if not reading from a tty. This +is faster, but will lock up X until stdin reaches end\-of\-file. +.TP +.B \-i +dmenu matches menu items case insensitively. +.TP +.BI \-l " lines" +dmenu lists items vertically, with the given number of lines. +.TP +.BI \-m " monitor" +dmenu is displayed on the monitor number supplied. Monitor numbers are starting +from 0. +.TP +.BI \-p " prompt" +defines the prompt to be displayed to the left of the input field. +.TP +.BI \-fn " font" +defines the font or font set used. +.TP +.BI \-nb " color" +defines the normal background color. +.IR #RGB , +.IR #RRGGBB , +and X color names are supported. +.TP +.BI \-nf " color" +defines the normal foreground color. +.TP +.BI \-sb " color" +defines the selected background color. +.TP +.BI \-sf " color" +defines the selected foreground color. +.TP +.B \-v +prints version information to stdout, then exits. +.TP +.BI \-w " windowid" +embed into windowid. +.SH USAGE +dmenu is completely controlled by the keyboard. Items are selected using the +arrow keys, page up, page down, home, and end. +.TP +.B Tab +Copy the selected item to the input field. +.TP +.B Return +Confirm selection. Prints the selected item to stdout and exits, returning +success. +.TP +.B Ctrl-Return +Confirm selection. Prints the selected item to stdout and continues. +.TP +.B Shift\-Return +Confirm input. Prints the input text to stdout and exits, returning success. +.TP +.B Escape +Exit without selecting an item, returning failure. +.TP +.B Ctrl-Left +Move cursor to the start of the current word +.TP +.B Ctrl-Right +Move cursor to the end of the current word +.TP +.B C\-a +Home +.TP +.B C\-b +Left +.TP +.B C\-c +Escape +.TP +.B C\-d +Delete +.TP +.B C\-e +End +.TP +.B C\-f +Right +.TP +.B C\-g +Escape +.TP +.B C\-h +Backspace +.TP +.B C\-i +Tab +.TP +.B C\-j +Return +.TP +.B C\-J +Shift-Return +.TP +.B C\-k +Delete line right +.TP +.B C\-m +Return +.TP +.B C\-M +Shift-Return +.TP +.B C\-n +Down +.TP +.B C\-p +Up +.TP +.B C\-u +Delete line left +.TP +.B C\-w +Delete word left +.TP +.B C\-y +Paste from primary X selection +.TP +.B C\-Y +Paste from X clipboard +.TP +.B M\-b +Move cursor to the start of the current word +.TP +.B M\-f +Move cursor to the end of the current word +.TP +.B M\-g +Home +.TP +.B M\-G +End +.TP +.B M\-h +Up +.TP +.B M\-j +Page down +.TP +.B M\-k +Page up +.TP +.B M\-l +Down +.SH SEE ALSO +.IR dwm (1), +.IR stest (1) diff --git a/dmenubuild/dmenu.c b/dmenubuild/dmenu.c new file mode 100644 index 0000000..818313a --- /dev/null +++ b/dmenubuild/dmenu.c @@ -0,0 +1,792 @@ +/* See LICENSE file for copyright and license details. */ +#include +#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 LENGTH(X) (sizeof X / sizeof X[0]) +#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[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + 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, junk, size = 0; + ssize_t len; + + /* read each line from stdin and add it to the item list */ + for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %zu bytes:", size); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + items[i].text = line; + items[i].out = 0; + } + 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, parentwin, 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) { + 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) +{ + fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); +} + +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/dmenubuild/dmenu.o b/dmenubuild/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..196c0962bc2e31a913310ae0c32ae2ae05e0a646 GIT binary patch literal 32024 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 Z +#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."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + 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 i, ty, ellipsis_x = 0; + unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; + 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. */ + enum { nomatches_len = 64 }; + static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; + static unsigned int ellipsis_width = 0; + + 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; + + for (i = 0; i < nomatches_len; ++i) { + /* avoid calling XftFontMatch if we know we won't find a match */ + if (utf8codepoint == nomatches.codepoint[i]) + 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); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + 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.codepoint[++nomatches.idx % nomatches_len] = 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/dmenubuild/drw.h b/dmenubuild/drw.h new file mode 100644 index 0000000..fd7631b --- /dev/null +++ b/dmenubuild/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/dmenubuild/drw.o b/dmenubuild/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..84d0b323d51f79b4c67d407d2cabc57841a19e66 GIT binary patch literal 11472 zcmb_hdvsgHnZL4SM|mKr;1ZQbD}o?S1F8ifI6N9%`zpRllwe|;I0-Z;vK$*^%SXB* z4uq3X*(o8|GHnsWA+7nQtjH;_*=6>}S)qF+mh?~D5-~WkO(4nV3*c}B)TPtJH%+EFR zlZ5#}!n_@he;+*h=9GXzBrur`pia$-Hrhn6f@F`5V#J8Ejy7^nj3h-7Z*lYW#K?j- z)PG~#oz7H1osbsWas=V#k<7bBf5E8<9Jo%ZOL$E z_W67cf?PLTp8RvUJZi%mgOcs}da_>(L>SBG2G~jskeIz7d{#v(MTXkz7%48zM{D^8EmiElA6+Y0--_SaP7Sd4s!sYuB zsaP5}&ybd;xOoc0-|dH7+d%l>%2?#xg!$ewtM0vo`C+`gPv7&Nn=Me?^%A3sFFL7P zSg^Sxa1F%n3$Wl+zsFny+Kr79wFX2Xn-Ge>jvOD5Hx)2bAiX$?#(Yos(^5@&Z1uo|{*yH>>6WwN*6_>DDrY@+YLw_7cToabGZU zN-Q+2tb+)+;>jRV+AN;Z5}^Q=0o;5Y1>rRm->)(;l;9R8%oh_QT4)xNxP16sW4iAJ zEmWtP?vOtG2cy<%_LwnvNDCoN&bQN~SnbfwLY=#|-A<_YoxGp*h5sYT9{tS>Ja5od_(sDE z)`EHdND0pd5aShOjk*)hfiO}MEUNjLX8t?Hv9%Eau*HK#;nuCXDn2J201LsG`8&$< z>Mfdi0=|p3YItg#1o=M|gYyePDTC;i-ilzeS5uKCd+ZA^Mn}G*N=!^r5E-g`Zo`NgCZ#y?y#_ZA*rZrv4x1%)?Z z$@m(K{r092_qR!Kehxg)pcH1g+OY`PE;&9|vw&$yNyHdEln=3=qS`or30g)-fAzRtU1+GTTBWwbP>sgf zsExQAbuycUqS~ph)`rnCZnEsP#&+fpTlqY))j`8*g)cP7Ofa_2H53m)tXY~w5@#pA z6Mv7UZ77SWt&JctfT>&Gcln0zKrM0@12*bdcfvGaH)f{(#BADN?Lg8;9#X0xby2`K zbQOs&mOT2Ry@e0##d8#E0@-#yPIC-ktFSm2PzqkY$MJ(7>#zm`j4FYHfqg8DoBP1c zKsGNN7KcPSq_K0|B!JB>Qsz?Zlca?$6;Z^_nu$;s@B1*$=^9^-LJR^X>GqSGS@;I<|hAFmu82Un2gG&pw59 z$wFTr`8wubDVOY=QarX;H!s>tOKE(EJw?Wh$>~ADp&0hX5YO}16+wd~8aZ$WruUY}DK-S!_D3V7(a|(^+T z<2-YS=R&OQ*()OlD6Jj1g_v!wHLK~!u;_@dWfX@Len`6*=QC{oY|-uZz#LnFeeicj z-X8baYx+HoSM$>tcI;EeH?U*>FWU~Rg}h&!f5~-Ilyoka&0P^pCHwocMlh32b_Kh$ z{YJ1mm+cGoC;QUZ1?OItpF2Z2n{XhR%cpHhA_^MW;6Tza(z*Vr=^cVOZFq*#pS7jr z2hyqD?%s4)SV?6w*_=aPGLy-sl14hnI+;mII2=}t9$3+v%fn!@y0+RnFRXsEgL&>L-ndJIR`l}8qm+3sHC|h_JWp^rNWOe&(_uH+% z{M;%?Xlhx(sz)hpJ@(P*txD-;G}oKUxXsLeg-R z2(!8AeRjJJeNrs^J_`4BrrUdUM64>PRk5n~z13RiU zZ*ZGi^VSz@G;g$ezRO#$dV{Jr0E#9r4S{Za8T_bRNslF7f3Ye#N}U>-_Y;P)iyq*5 zuqx&aJWw6;21nd6Z~cy%xVK?jZQR>foThrCRsG&Z&D)@Q>zlkm7+|Zp0<4FKA7vFi zTD<-pRhl=jt-9G8EV`+xGK197mkufljYl}tS)K|59hSw9Dsd_yN0PW=c z8>xRPn_A$3!O`Gl&m`uwxF)0QM=zo+2XVYYglG8Z-Ab#=Nao6~10QgEn&^$iMX$Oe`Hu8A{ zbrEqEt?FUnWzXhjkc@Hq_F9|%L7GQCCdl7G^SG^6qcOF3ORj2<%RA=My@yIy-ThRPO?f+eCV2LXl$M6)nq8Pa(c9T{Y$+8#N0AA=OI8nyq}Go#lsqJ zKqm?c$)${reDn~fQ%MdRH&)TMYBugoB7D(Bar8W={T9(Of412Uq;`cuQ}0$j=2_`% zqH3`i&W4q0mQ~IMDZL@LOn@RsoDamM)G9r^hwaFyf0N^V4*YVoNsi!Wc?H<+pP4;Z&Lu0uL7|SQn`(p?Ft!TU8 z9}kZ|%C%JNan>kw9g|BA4f0QvgEZ$%7 z%-iDiBUY(VYA2^mbYXrc^$PGYR49Kq%Rc~^3jdc1__G!8mnz_|Rlwh?fS;^@e^ddd zPqR{?*f0=~Zj{$>UI zcm@2+3b+q(FqNIb3i#C(@RkaAdj&iVoc!#Y#oRWr|7_)Woa3t9tvpyk&fjqQ?VO(N z#o+nJ3iL`>Zeuv56pZf1u5>Efm0rKeNK>;fnZJ|XtUXA#ly#|G8dn)5oM>*KM}!_% z(c@}*EKqE!?p&JDT%)8jncjhXZ+_jz-Y%mj%;*;~*}eU?ib33?`jQ52J$YOgAmmzl zG}2>{(%Ee+f!dn}()}xNS5xvv7cO&3_W0b2=b{uLBN`6gX>}J(3gk1;ZEwzrBenodPz2y-jK@{ z`n#a4Hqv#0HHt zM%jV*Vl>oAeI(U(;sh(X0^BTEh@=^p+}L1m-WEm-ZS4BXWN{H4zXz_$~KLUJy^PvG>alM3M% z;wSLGB@o3a|7ja$_;l0-{VzD~lwXo?IwJ`Bwv$q zxcFB)$)T-H;NtvB^ptA_J_qLkDnw7ISm1LxPV_+uS0(%s3Gb8eITHS$gkLJ*k4d=f zA3EnyA-$L2C+u;PfhdGuj-SA1OZZ#~Unt>pClK^~68>!oe^|n0`CjZtR7md?_z5}l zBwUuWP{P9!{bC81Z9}UxKfFkS>^s6~edYkbR_#G0iN%&?-PMw4o zC3-n;JS5>UiTEN!-z(9}`Re5g^lwPGF3CA6 z;c*H7M8cO!_!$XbCgC;w{y=`dRKgcacvQkSaC~CEc)sg*;`Vk|zRQVo`bQ-Bay*Pl z_#8=Z6*&uq?3ClGu>ziwa5>N1BjGC`OU&0}65cA|k8^uTZ%D$$cRwfoQ(Ckrgv;^v zBmpS&lJm)!gkOt3Vb3{IzM?y*z#o_F3`uwpbt*J3^b=(PuLxY+X9pa(`0lmCfs5}M zuRCz@9m381BjoSm%A*ckd~dkbfs5}A^u20#BksM;WL6KjZvPyTtqne*1g*SNV@{{v(cwaq=4wG*$fki~E!iRCCUqfBtX&|DTzEX6~K)n0sSa&nBB;2p#O=azSpe-bC6|#?*UM0;El} zidw|WMWd(!?~s_8-)0izn&D|%F?B244obZSCd$yWOju~DYe-7HN}+9znWL$wGkNMc znN)l!on}K2G}ZZq@_aH|h!Q$P0R&C0dNg^oNoI?qfns`E`NbzIg*;U+!ql5kdJ{@d zQ=isTRQr=Y;nAx6SxVcG$IvEYAxiazlwK+ARPvg#42GRAWTXF`^0q6zYkks;D5Yyn zSZJ!tyApb;iQ7&{fbXe*f2jig(F*wX3OGm2JaLAYXhQ-)&-;6kCO+9lbOslp?%pk-Xe=G;k7u&6 zbnljpiBvMy8{V6UiBPD2Un&{OWW(ufC}fJ370e_D)A3}sPb46l6`5=_o)m+*Y(@;m z(&4A}L#zF3)`~)MO(8i0uJ&I{?_&1jFk>@C z&Mq+<<}*2`qLk-+c8IevdGvYgfjd5q>FpF3DC}#JcZk)BUl5Pi@@x`NeD2J6HOt)$ zodVX!N;PU(`rE!k3 z;M#Z0T4BM}QiQNrWx;DCVn(eNe69s=v*761LI_%LPXQHTy9M`J@T)Ai9!t#LW5MSU zP+_3WOwe!E^X;GoS3|@UoQ-EaFzbO?56pVt|D*^0;63Nf;K+;a;F$CNW+8${CbPEu z6Ty+=ZmyJt$Y1Ar8^HMu~ zpX9$lo|o9^3CVw&JTKqVM?d{m%x=r$F^1S3uuaf+qk>{m$+9&zF2stb-)1xKAjuoxV( z!?)hUry)Ds+T=a*G7`6LHx57LJ^VbeH=A{nAZ79<6ZOy2nD)Mx%3wKz2OC6MmX@3RJbSUK`wa_5kFmW=df zcKb-+gJ>$OUY5P&~d+K{&xgj`K1K*CG=ox+GK`yp?M)Ok*Pz&U}LLB&V&**Q6 z^r1c{C!ov9T}|e5Et4aGK3asAf@62e&W{E!Tp&k7);YD2W#4q1gXEgs?bo*NZojTQ z^zc#6x0aW>N1wbdIQr}0$n&SR_jX~-JO+2(0ge2^D{u$=YM(Yv z&fw@DF~3^j=c4(w0s!>JvyJYlUTDDqu9nH^M*xom(ikfuS2cWt@MnKF66hzJtsCoX z^eq>sgb0W|$bNDpu;XnRK4g4rBoKI8MjJBj840YAmTN`==Shi`C+k~Y4~{-NbqA`8 zD*SeO2+gw;G>kEEBnoUK@ERH@vNf-FHZF5`5A*yq5_o}F4(48!nO!Gag5wV~-UX0G zd!$kCkw)4)+;|^gRsRW^tp$AYI6CldcHsKX#y#HK-@wEe39!3Gc0RI}A!TNb|4L?k zRAw!(R1t&Hd*mUR`5|VqItQgx~!fW(5*((;kOMkeG5Bir;KI!79dNMq(H1!b_qWqZvsemK1bHr6{0sAkU{2|UkH zhu+OkeesQa{s+!Cz?+s{o*D%bgl{+;FyqYgFJ|16#BVyzVROxHUcCy}Hk|Fus~N!A zFzbO?56pUC)&sL1nDxM{2mYUXfNvvmnQ(vXQs457@0zCY-bggo*FTULygr@D?LRQI z8(`Ac7f-~PO6e2rb$S{` z?CgK9W zPwR;53eO3jeYwrk3aH()0wNur2B>t<)@+#dz^n&mJuvHmSr5#5VAcb(9+>sOtOsU2 zKo98evh;UYeCW;aoJk7bGQCv7zg5D|Q;J%j-;y!RHA&&avRcJIqduNGL-G21u_cPv zw=HKXUVk^HKP=Vxuf3H|QDl;Dd=Yr>$55v}o>D|iseJv7)J|1Dyc&|BK~d|sUWAmI zm{Q+~@m7pMzJUhS_m6yG%&^!b;%QYujZdkBuJPgXY!9-fDBe;?{w}4D z_c4;!e#Y&s#d6UUnlze@zR3V$-8JFHYxgEMf()pujtK+eq7PNRrDK* zKBDMP75$B(uPa)mJ~CTm{n!kZ8E!Lcw0YykOMNTwmGeqp%WD5>|0-3{i$KjjHhW6% zOOVe1l#G>dJle!1h;`3ebIoT6Hc^XVSw8>WQh)1AzN)|ewouQL^4da}`qvlq7b$%` z51v<-r@btVwQ{|-f^|5C+PMBr)#@f7uG3!D)K)c2 z^*0uLiiKBwm&~8)-wjQ-aQqgW!N}GGjrl)D>MWPTaNh>b{USxH4AZ3hpJ?A@m^R%! zi@Dr}hn?hbpQdEBvG814b$^k0bIiO3;kX303`Vv)5V>|y^7n^20wwNwtJ7m#)>^L`96kh&kl_2={NMY_Ju^-Jd~;OD-K=-D|k*$L?r zH8yy9?vp^DtC^3asQybt1m&RfifZaJNAfOF&5mOCsPSL(2F%pCe!+22^(*ALeqH?o zn1dzG`5rCl5(|C_*yZ57u0EscdSukTNV(eg!-7$}g$>1N!?qHMg+6zO=kqlAV@S9= zJ)F6sY7~<0F86$pxmBx??cP-LcOZ4bF$7XQ4k3PKpz5~hO_b=~Y^Nbdwf$~cErT&M2`GwuH;S7QhH@u=|nY+85brK}@Mp099 zd5s+(NIA}zh;tDXF0Hw&W_=9>s%kB4D;7PjdI0an>dUQioD^hEwPAGE8_=1v43?UK z)YKbxPfd-qw#?0}xl(DFOPjA<0c&SL)HBaOg=_3fmn^NrQpl_7h09A_y&+AY99o#C z>L-iEKq?fW^6M9y;;28x7dTLb%WG;-N3&4`7b=rVX3<(p8*edcY+O^pm(~i|tTG$U zposPz>Zp7NB~iC{*WwxXmqD(EaVO4j_=HUuhVe%a&cg`gB(M~tRR}I;ZN^fY*T!x( zWF)F>`a)`NHMZI7{2k|AzNPWJK#P5DcEE<6T*e^?(|DRn<&0NzP!8ZYfa3@bj&NQ? zAHZ=O$3NhZ1<&b3%HRaF$qUh|NZIYEqoW#;eE}jT7wc7&bMXS^UX2t>e-~)k(hZhw zHj5KP$S9(;{<98Q1x-UnaERV_V&7o&^yC7~xah%`)i%BSu|Fk@D{%HPjO+f3#c=6i z$Oz{2>d1ZptkKcH^a{y-p~C$B=FGuNG&Y!N#xA@NcIC+pNdBFod2cSBh&K0gUruu- z9Z{)BCdd79zV@9y?9f>+%&kKrlZt!*TY}P|cqV0GlHv3q_88@`rdhK(gw0Cv8y{-pc$)!L#A#ff-Og(c$6GAH|>ij4vMZs-@3J-_odQN zksOGoO~FCffKis06DScorFQJ>x-ztJXJ}JT`{vNrZJ`}qJA2!A^oF)=4Q=1i)wN}N zZ>XcYcc#!JHZ-M@{XQA7Ix}1@nThu&V^LX4v~OLAjc-|}n$e0@yhCX_yEkub?=cHQ zGpH((;eFhVg<@J(x0)qIQ#o!7%V7JJY$%Kt%8d+!WkW-UV(FAfhNzx7G*fr98)!6^ zi4Zl5Fr9DuOr~YQFdq$P!^-Q00*c%pO^8rODwPnbw(KO`;jvT_C1no|#@?yPTe>&z zXz%SRcJR>Zi)BB`zT%+T*wUizP<&1M!q{mRFt>(+%jS}?Kz|Gy%Hk1UQ@^ii+iG7E z_NoQ?lR0QL^?|moZ;B_mtqp84)ijvMA`u&k#0InRR5AlbZj5V+B*K}2ronV78;dXp z8Ie8^5&-wb(*3cfa3ax!O>0n1W@#gx%3;r2vL7|f4is!EYpFCUmFB3CRfwea zVN)MXLAD9|>XIoigK^fdY?0YSrfFX)8cPJ?$#^y%PBg`aVi9v-$Qgtlpu5-udR)x( z1qOLxFs@lG(wW%xp-2W3F5I69?Zy7TLT)RP;l1%tHrzj>j@^IeT-qw9k?P@@{4hI= zV+y+yw_)PL3!xnL6No8Oe7(ySz;Yn+~xj@@>|dQU9hS`p_Q{&ACik zo}{9&y}5qNsjAo*>ob_j#D|*Uj0`zVV~IX%Wphc$;KaBeTa~dxxgdtIm2JjBN_n0Q zW!dp$B$128OmV^4kns<2FD2#?24yxm!|`ND7DWBG!Xaexd~D zl9%}BlpQQYW!Q+gTs^k}XFVsd{<4Pc3V)&m=c<76`uU>PIfw+yeYDL)=wAk|Wehx3 z)6e@Pi0Df=Xh%P<)cCiMFhqS}zp2J6)&FNwe!j>q(~>i^Tzg-T+4Bnj4bal3kudPI zPd^vbxCad}uw%6J`PX-V`$U5nYSWS?T@2j1?@{OSwtPO?t5=HWtBi}J9W6COFRNgO zw=(7I?=jXuB)s?f* z*DA80uAaP0t;2XOwSRR5c|JfW zXXh4W=e`bApd#ZHPHQ z_|$V+`K=S$ypp&LXIBilqF}kg&0MazFu)Z7KM=@}$`8y087y;gHBZJ4h*Tmfu;Pde z@Vb%7=CE%hf(y6)oj@qNFN8kUcI$_OZP^he}K@Y{|>+(0hIhc zf&W7&%IEJ7iU8S!cm5+o@gj$RQ7E>+zd2~1mi)_sOKNp%r2dgmtXlL}g`zC=YlR|A zt|N+&PzVJUJQ&TSLIdGs6n6{ZbpQTU7##dfLy5-TOh#3y^bZjQ!L|Cw5Cuf~7C(6? z{5qm22NlDjwg@tR1yKaaA5|2PHEYz5Dhh~MhP(?=znv(~D*Z=AAs2s$kpHZZV@wEt z=HR|;crR!+ZPEdq!kR0FTT#DUFXC3gkM%l!mytbaf#eNu6YecdsrHWUCN7Wt6d%AH zgYZWWCQ*b*vuQJDe=MEBJwy=`LS8zS2vb3&a5EzOoC?@N6&pg#8HBu)Y_>l(pymu$ z-UZIoFei_xq0=yo8@UKdO~Do-zbwEshK?3~-g;naqOx2PLob}{$Anj`y#Ll&d-tYe z`*n`|Er`m2_pC5RPk~P-@nj%_MfCqb;DaMv;;`m1Lj%r-44UfqpeAa{>NM$jIQU9X z>$fU>P1}%X4f^_~v2{pT^@o(crj1%qQN13}aCeEmULR}f(}IeY>c0>8>v)%`>#yJM zYPv${RVi9p|1fYok1VubzyH;gkKP&BM*3On^O+$-90!}M%h&IdHQh-iODJK*^~mG9 zTCK0&&uYr2WDL6gT29jsA&>8VHLu_QYRdICLuvg@)O;RDpK5*m{#nygN>G$tj}b9`GvY5(7iOsl?6>1ir6Ee2F}p_tE> z>Z|pIUS}LDx!=?J+T@o@^z}Mf(^@U4Xlebwru4P{g-;18`hLY~QmX$QWU&2O|5~3Z zsp(piEyShzd^2g)Kk73@HJvJGnhD*18vY3q9vo`g7w)h0f6YqeHLvNj(B-QNt*`Ir z?&2eG1TANBGrY!6B7+Aa|{hvCYaty~mgVxji$j97lzt-3P5t?kKv=A#yQi!#_ zrf(p}s;}?!rj-8Cf~J|!dYaF6s;F&~nP@J_ XEG?g>+Y%9}nBu|!VSkB22`l~sitG^O literal 0 HcmV?d00001 diff --git a/dmenubuild/stest.1 b/dmenubuild/stest.1 new file mode 100644 index 0000000..2667d8a --- /dev/null +++ b/dmenubuild/stest.1 @@ -0,0 +1,90 @@ +.TH STEST 1 dmenu\-VERSION +.SH NAME +stest \- filter a list of files by properties +.SH SYNOPSIS +.B stest +.RB [ -abcdefghlpqrsuwx ] +.RB [ -n +.IR file ] +.RB [ -o +.IR file ] +.RI [ file ...] +.SH DESCRIPTION +.B stest +takes a list of files and filters by the files' properties, analogous to +.IR test (1). +Files which pass all tests are printed to stdout. If no files are given, stest +reads files from stdin. +.SH OPTIONS +.TP +.B \-a +Test hidden files. +.TP +.B \-b +Test that files are block specials. +.TP +.B \-c +Test that files are character specials. +.TP +.B \-d +Test that files are directories. +.TP +.B \-e +Test that files exist. +.TP +.B \-f +Test that files are regular files. +.TP +.B \-g +Test that files have their set-group-ID flag set. +.TP +.B \-h +Test that files are symbolic links. +.TP +.B \-l +Test the contents of a directory given as an argument. +.TP +.BI \-n " file" +Test that files are newer than +.IR file . +.TP +.BI \-o " file" +Test that files are older than +.IR file . +.TP +.B \-p +Test that files are named pipes. +.TP +.B \-q +No files are printed, only the exit status is returned. +.TP +.B \-r +Test that files are readable. +.TP +.B \-s +Test that files are not empty. +.TP +.B \-u +Test that files have their set-user-ID flag set. +.TP +.B \-v +Invert the sense of tests, only failing files pass. +.TP +.B \-w +Test that files are writable. +.TP +.B \-x +Test that files are executable. +.SH EXIT STATUS +.TP +.B 0 +At least one file passed all tests. +.TP +.B 1 +No files passed all tests. +.TP +.B 2 +An error occurred. +.SH SEE ALSO +.IR dmenu (1), +.IR test (1) diff --git a/dmenubuild/stest.c b/dmenubuild/stest.c new file mode 100644 index 0000000..e27d3a5 --- /dev/null +++ b/dmenubuild/stest.c @@ -0,0 +1,109 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include +#include +#include +#include +#include +#include + +#include "arg.h" +char *argv0; + +#define FLAG(x) (flag[(x)-'a']) + +static void test(const char *, const char *); +static void usage(void); + +static int match = 0; +static int flag[26]; +static struct stat old, new; + +static void +test(const char *path, const char *name) +{ + struct stat st, ln; + + if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */ + && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */ + && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */ + && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */ + && (!FLAG('e') || access(path, F_OK) == 0) /* exists */ + && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */ + && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */ + && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */ + && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */ + && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */ + && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */ + && (!FLAG('r') || access(path, R_OK) == 0) /* readable */ + && (!FLAG('s') || st.st_size > 0) /* not empty */ + && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */ + && (!FLAG('w') || access(path, W_OK) == 0) /* writable */ + && (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */ + if (FLAG('q')) + exit(0); + match = 1; + puts(name); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] " + "[-n file] [-o file] [file...]\n", argv0); + exit(2); /* like test(1) return > 1 on error */ +} + +int +main(int argc, char *argv[]) +{ + struct dirent *d; + char path[PATH_MAX], *line = NULL, *file; + size_t linesiz = 0; + ssize_t n; + DIR *dir; + int r; + + ARGBEGIN { + case 'n': /* newer than file */ + case 'o': /* older than file */ + file = EARGF(usage()); + if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old)))) + perror(file); + break; + default: + /* miscellaneous operators */ + if (strchr("abcdefghlpqrsuvwx", ARGC())) + FLAG(ARGC()) = 1; + else + usage(); /* unknown flag */ + } ARGEND; + + if (!argc) { + /* read list from stdin */ + while ((n = getline(&line, &linesiz, stdin)) > 0) { + if (line[n - 1] == '\n') + line[n - 1] = '\0'; + test(line, line); + } + free(line); + } else { + for (; argc; argc--, argv++) { + if (FLAG('l') && (dir = opendir(*argv))) { + /* test directory contents */ + while ((d = readdir(dir))) { + r = snprintf(path, sizeof path, "%s/%s", + *argv, d->d_name); + if (r >= 0 && (size_t)r < sizeof path) + test(path, d->d_name); + } + closedir(dir); + } else { + test(*argv, *argv); + } + } + } + return match ? 0 : 1; +} diff --git a/dmenubuild/stest.o b/dmenubuild/stest.o new file mode 100644 index 0000000000000000000000000000000000000000..2f98e41c651acafddcb5d4a3a7bbed0b21eddac4 GIT binary patch literal 5232 zcmbuCeQaA-6~M3UH1V3vFKtQ*>;rFgHcu>%xO^-f>J%sL_Pn_?qGUs~S>o6)AJnmp z{oG_@Q!~Zxa6NYu6xNA9+KGRP5K;w*O?(tj?Y4@s3HJ9iDl`GXOf3_u#+YW#x$hkN z=3(;>N4mcE{O&pD-h1x7?>!&c6T8RlatRifxLdR|rb1-+6B9;dH>yVU z^H_yyM&)DJ_$&VW#?7rsqx{}}qx^5<@xPBi_@q(k8Vo|GQ7#680Kl(m1-#O$RI_?Q zwe^<)ugTb=(7pPY(5=6bYV}nj0so-5BE)VnBShg3?K=F>@j|!caV(gO3vTotT^@JKS%dbdoN4hMD0lC5BkTgDRpAT!3%^$yj$L7G)d5FJ33i{gUpG_pqx%q`u_8Yl;;qdax6u`89ZZTzIE5j`Wx~@-c7XRfT2J@d8%!}}Y zv8p_J;RnK#;jc_qGmZU5^&u}D-ykG;!|@#e5URleE=K}h{OL0)-A38o%8JJ*)wJUK zFz3~gA+2;78pkGF#W%F#+sHl`Dr{L@af?E)v3B0ucy0Z4phlw>Ex&1$J)zp(${m5V zE3WXdNdZ3JeX)2^E1m+A=&m=l()XaDoqM->&$lDh!>88XcP-C^S08hUg0{Dk3V8RH z-YIOB>5DdAs&_YDi>_Vpu6~d27!cYORVfAHQfP2TPg?bLVb z!vZg(9|~>-aN!oN8Zt~Hip!yIv$1eJ@@3&#>2qCwZIAb9FouD|4b*oV)?r^?5UOEf zQM{4Bj=Cehfi=&4zTdk&ce;H8fWkg6tQ@)zlI=dU9rN`)<&OFWj(f&@{%Y5lFZkr9 zuy6Qicf>cc)^oq_lHW7v_Kg4v`+{IH<{JQ)F^u)U1lQFi{?TY><7uvXW3NYMX(PJ^ zRLE5m$~A)q`vxKpwruea<_+!HPUZvHC!n|>{tSg$AcvcX`$>PA>RX7{9Q;<|7byez$yuB-n3GSX;aMQOjBgC zW;(f;6FD=U#5$46<|59H9G`K?uTpn)tVD+~f)3xW6cDKTrG@Hg57m z#Q6h+?K8yRAbXr=KNOzldyY7HvcHCEj2c-X$YG)o^ci8`k^glT``_~;@eqZ3)a6ur>_#f}U zZzaxoI7@ML2me0t5had#4)1o@*U0`p#UB6o@V?9{e7nN=4Tar!ZVytN2((dGsA&Wwp0f&Bp(ip+ zOJ*9p(izLt;p=rszIjNWPZ#uT4qhuc>tS*?7pCWO@g}LgqtIxVzbqyUBuMK( z6~}PqZ$B`MISfS=lOBxt?o@@x=NkX3a9g!o;RmY<+t{Xn^+0iM7yCQs#q;N|MT+V4 zKSSf6VMD5&{`ei+E`KNSp$}r%l{|lVkJ+EkAFkawHo8pEh7Qm^;x`%=r$7FGa-Kav eVgWlqerZ?DUuUlIo!HL*63zd3)6;G^{r>~UK44z} literal 0 HcmV?d00001 diff --git a/dmenubuild/util.c b/dmenubuild/util.c new file mode 100644 index 0000000..96b82c9 --- /dev/null +++ b/dmenubuild/util.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include + +#include "util.h" + +void +die(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + + if (fmt[0] && fmt[strlen(fmt)-1] == ':') { + fputc(' ', stderr); + perror(NULL); + } else { + fputc('\n', stderr); + } + + exit(1); +} + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} diff --git a/dmenubuild/util.h b/dmenubuild/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/dmenubuild/util.h @@ -0,0 +1,8 @@ +/* See LICENSE file for copyright and license details. */ + +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/dmenubuild/util.o b/dmenubuild/util.o new file mode 100644 index 0000000000000000000000000000000000000000..6ab501f771232ee94bd1d5e3f8348ea759683421 GIT binary patch literal 2216 zcmbtVJ!n%=6h5!5Hr6zWZHG!7B0MF5`e+J5NmZha_9;PXRn#t;CNDOaKVNPnRl$mN z2qnHbf`g!=(4d2>qmy+M>QDy{=7Uon>yE} zX&_C5Q?NUeEI?$>uAks;0s_zvm(9ku`S`~-q|64>C(OnbJz+Ldx|wPu^_diU*Oyx? z9*idSTft~buLPsd^d*!BqtEqKjxY2zj<5AM9N+0Jj_>u)h*@)`=STz{6AMOSEpz20 zPH^_sY%c3tA~0+=ujnDOnbgNVCH0UGX~dy15e|Px2bLzmTscl28#Nmn)Ub~S%oX&O z9(K3mwZZi;#yJ~nbJ*-#?~~Lz?G0xyIf+kOHNV;99dTmrV8YuBI5K_l zYBU@hH^vR>1$uCVanN>R2)aOP5hwRt6Z3t@kqtQj+WlG3>LG2!-|JgN@i6j-MC>== z=|Jd7*M-2Ah)sytK@qza@Cn8nX#F1e+4hQd?{v2tb*d?sLWKGe2NXT;4Y>n2LPDbg z{SGhwa0fopfj2tvblP6^@qBonvOm60$W3<8LyhGNMh1-3)EH#*7F1c*vTeARtJwJx z%i-KESS83+YAgd443%xL>Ujp1b0M%US2x@N^gU;*k7X!MpVz6n(Fv z|Dy18MzYR^!qeT7{HDUI`dx-lsLyU3QtuZ${Vp1k52H^;dK%JB z2uJere88wK7Fl{8k=YI|NYAzkX@gmH21Yi`(qPP2t6mb}a5HD8i64*ouK$p!3#N2+-BVi3EoYQB2P8{H9v;ARC64MYI1$q zuZ+p{B_=RHKL3bH5#pQ>3@RMCGEd@pg;S+PDNsod?G5DUovC+~QYs?f-Ix3K+(j)( z-T7-Ge};-F=FTr*%HO(^%Y6F2l6kF7JpEsgO$eknO(os=RMUO-NI^u9 aXK13BD7VAV_uni3rRe{`j?!`5`F{Z;iVAT6 literal 0 HcmV?d00001