From ee6a9f05130f18bbf79bfa8c0089755e94996e29 Mon Sep 17 00:00:00 2001 From: horhik Date: Sun, 30 Aug 2020 14:59:33 +0300 Subject: [PATCH] add dmenu source --- dmenu-4.9/LICENSE | 30 ++ dmenu-4.9/Makefile | 64 +++ dmenu-4.9/README | 24 + dmenu-4.9/arg.h | 49 ++ dmenu-4.9/border.diff | 25 + dmenu-4.9/center.diff | 120 +++++ dmenu-4.9/config.def.h | 31 ++ dmenu-4.9/config.def.h.orig | 29 ++ dmenu-4.9/config.def.h.rej | 9 + dmenu-4.9/config.h | 31 ++ dmenu-4.9/config.mk | 31 ++ dmenu-4.9/dmenu | Bin 0 -> 44232 bytes dmenu-4.9/dmenu.1 | 222 +++++++++ dmenu-4.9/dmenu.1.orig | 202 ++++++++ dmenu-4.9/dmenu.c | 855 ++++++++++++++++++++++++++++++++++ dmenu-4.9/dmenu.c.orig | 802 +++++++++++++++++++++++++++++++ dmenu-4.9/dmenu.c.rej | 14 + dmenu-4.9/dmenu.o | Bin 0 -> 35824 bytes dmenu-4.9/dmenu_path | 13 + dmenu-4.9/dmenu_run | 2 + dmenu-4.9/drw.c | 435 +++++++++++++++++ dmenu-4.9/drw.h | 57 +++ dmenu-4.9/drw.o | Bin 0 -> 10600 bytes dmenu-4.9/fuzzyhighlight.diff | 152 ++++++ dmenu-4.9/grid.diff | 107 +++++ dmenu-4.9/stest | Bin 0 -> 17744 bytes dmenu-4.9/stest.1 | 90 ++++ dmenu-4.9/stest.c | 109 +++++ dmenu-4.9/stest.o | Bin 0 -> 5280 bytes dmenu-4.9/util.c | 35 ++ dmenu-4.9/util.h | 8 + dmenu-4.9/util.o | Bin 0 -> 2264 bytes 32 files changed, 3546 insertions(+) create mode 100644 dmenu-4.9/LICENSE create mode 100644 dmenu-4.9/Makefile create mode 100644 dmenu-4.9/README create mode 100644 dmenu-4.9/arg.h create mode 100644 dmenu-4.9/border.diff create mode 100644 dmenu-4.9/center.diff create mode 100644 dmenu-4.9/config.def.h create mode 100644 dmenu-4.9/config.def.h.orig create mode 100644 dmenu-4.9/config.def.h.rej create mode 100644 dmenu-4.9/config.h create mode 100644 dmenu-4.9/config.mk create mode 100755 dmenu-4.9/dmenu create mode 100644 dmenu-4.9/dmenu.1 create mode 100644 dmenu-4.9/dmenu.1.orig create mode 100644 dmenu-4.9/dmenu.c create mode 100644 dmenu-4.9/dmenu.c.orig create mode 100644 dmenu-4.9/dmenu.c.rej create mode 100644 dmenu-4.9/dmenu.o create mode 100644 dmenu-4.9/dmenu_path create mode 100755 dmenu-4.9/dmenu_run create mode 100644 dmenu-4.9/drw.c create mode 100644 dmenu-4.9/drw.h create mode 100644 dmenu-4.9/drw.o create mode 100644 dmenu-4.9/fuzzyhighlight.diff create mode 100644 dmenu-4.9/grid.diff create mode 100755 dmenu-4.9/stest create mode 100644 dmenu-4.9/stest.1 create mode 100644 dmenu-4.9/stest.c create mode 100644 dmenu-4.9/stest.o create mode 100644 dmenu-4.9/util.c create mode 100644 dmenu-4.9/util.h create mode 100644 dmenu-4.9/util.o diff --git a/dmenu-4.9/LICENSE b/dmenu-4.9/LICENSE new file mode 100644 index 0000000..6ed8ad3 --- /dev/null +++ b/dmenu-4.9/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-2019 Hiltjo Posthuma +© 2015-2018 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-4.9/Makefile b/dmenu-4.9/Makefile new file mode 100644 index 0000000..a03a95c --- /dev/null +++ b/dmenu-4.9/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/dmenu-4.9/README b/dmenu-4.9/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/dmenu-4.9/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-4.9/arg.h b/dmenu-4.9/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/dmenu-4.9/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-4.9/border.diff b/dmenu-4.9/border.diff new file mode 100644 index 0000000..89b4437 --- /dev/null +++ b/dmenu-4.9/border.diff @@ -0,0 +1,25 @@ +diff -up dmenu-4.9-b/config.def.h dmenu-4.9-a/config.def.h +--- dmenu-4.9-b/config.def.h 2019-02-02 13:55:02.000000000 +0100 ++++ dmenu-4.9-a/config.def.h 2019-05-19 02:10:12.740040403 +0200 +@@ -21,3 +21,6 @@ static unsigned int lines = 0; + * for example: " /?\"&[]" + */ + static const char worddelimiters[] = " "; ++ ++/* Size of the window border */ ++static const unsigned int border_width = 5; +diff -up dmenu-4.9-b/dmenu.c dmenu-4.9-a/dmenu.c +--- dmenu-4.9-b/dmenu.c 2019-02-02 13:55:02.000000000 +0100 ++++ dmenu-4.9-a/dmenu.c 2019-05-19 02:11:20.966710117 +0200 +@@ -654,9 +654,10 @@ setup(void) + 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, ++ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); ++ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); + XSetClassHint(dpy, win, &ch); + + /* open input methods */ diff --git a/dmenu-4.9/center.diff b/dmenu-4.9/center.diff new file mode 100644 index 0000000..af249a6 --- /dev/null +++ b/dmenu-4.9/center.diff @@ -0,0 +1,120 @@ +From 8cd37e1ab9e7cb025224aeb3543f1a5be8bceb93 Mon Sep 17 00:00:00 2001 +From: Nihal Jere +Date: Sat, 11 Jan 2020 21:16:08 -0600 +Subject: [PATCH] center patch now has adjustable minimum width + +--- + config.def.h | 2 ++ + dmenu.1 | 3 +++ + dmenu.c | 39 ++++++++++++++++++++++++++++++++------- + 3 files changed, 37 insertions(+), 7 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1edb647..88ef264 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -2,6 +2,8 @@ + /* Default settings; can be overriden by command line. */ + + static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ ++static int centered = 0; /* -c option; centers dmenu on screen */ ++static int min_width = 500; /* minimum width when centered */ + /* -fn option overrides fonts[0]; default X11 font or font set */ + static const char *fonts[] = { + "monospace:size=10" +diff --git a/dmenu.1 b/dmenu.1 +index 323f93c..c036baa 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -40,6 +40,9 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. + .B \-b + dmenu appears at the bottom of the screen. + .TP ++.B \-c ++dmenu appears centered on 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. +diff --git a/dmenu.c b/dmenu.c +index 65f25ce..041c7f8 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -89,6 +89,15 @@ calcoffsets(void) + break; + } + ++static int ++max_textw(void) ++{ ++ int len = 0; ++ for (struct item *item = items; item && item->text; item++) ++ len = MAX(TEXTW(item->text), len); ++ return len; ++} ++ + static void + cleanup(void) + { +@@ -611,6 +620,7 @@ setup(void) + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; ++ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + #ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { +@@ -637,9 +647,16 @@ setup(void) + if (INTERSECT(x, y, 1, 1, info[i])) + break; + +- x = info[i].x_org; +- y = info[i].y_org + (topbar ? 0 : info[i].height - mh); +- mw = info[i].width; ++ if (centered) { ++ mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); ++ x = info[i].x_org + ((info[i].width - mw) / 2); ++ y = info[i].y_org + ((info[i].height - mh) / 2); ++ } else { ++ x = info[i].x_org; ++ y = info[i].y_org + (topbar ? 0 : info[i].height - mh); ++ mw = info[i].width; ++ } ++ + XFree(info); + } else + #endif +@@ -647,11 +664,17 @@ setup(void) + 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; ++ ++ if (centered) { ++ mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); ++ x = (wa.width - mw) / 2; ++ y = (wa.height - mh) / 2; ++ } else { ++ x = 0; ++ y = topbar ? 0 : wa.height - mh; ++ mw = wa.width; ++ } + } +- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = MIN(inputw, mw/3); + match(); + +@@ -709,6 +732,8 @@ main(int argc, char *argv[]) + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; ++ else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ ++ centered = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; +-- +2.24.1 + diff --git a/dmenu-4.9/config.def.h b/dmenu-4.9/config.def.h new file mode 100644 index 0000000..24eb8db --- /dev/null +++ b/dmenu-4.9/config.def.h @@ -0,0 +1,31 @@ +/* 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 */ +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ +/* -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" }, + [SchemeSelHighlight] = { "#ffc978", "#005577" }, + [SchemeNormHighlight] = { "#ffc978", "#222222" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l and -g options; controls number of lines and columns in grid if > 0 */ +static unsigned int lines = 0; +static unsigned int columns = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; + +/* Size of the window border */ +static const unsigned int border_width = 5; diff --git a/dmenu-4.9/config.def.h.orig b/dmenu-4.9/config.def.h.orig new file mode 100644 index 0000000..6f248d0 --- /dev/null +++ b/dmenu-4.9/config.def.h.orig @@ -0,0 +1,29 @@ +/* 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 */ +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ +/* -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 and -g options; controls number of lines and columns in grid if > 0 */ +static unsigned int lines = 0; +static unsigned int columns = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; + +/* Size of the window border */ +static const unsigned int border_width = 5; diff --git a/dmenu-4.9/config.def.h.rej b/dmenu-4.9/config.def.h.rej new file mode 100644 index 0000000..1875e56 --- /dev/null +++ b/dmenu-4.9/config.def.h.rej @@ -0,0 +1,9 @@ +--- config.def.h 2019-02-02 13:55:02.000000000 +0100 ++++ config.def.h 2019-05-19 02:10:12.740040403 +0200 +@@ -21,3 +21,6 @@ static unsigned int lines = 0; + * for example: " /?\"&[]" + */ + static const char worddelimiters[] = " "; ++ ++/* Size of the window border */ ++static const unsigned int border_width = 5; diff --git a/dmenu-4.9/config.h b/dmenu-4.9/config.h new file mode 100644 index 0000000..b901a92 --- /dev/null +++ b/dmenu-4.9/config.h @@ -0,0 +1,31 @@ +/* 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 */ +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ +/* -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] = { "#f8f8f2", "#222222" }, + [SchemeSel] = { "#f8f8f2", "#44475a" }, + [SchemeSelHighlight] = { "#ffb86c", "#44475a" }, + [SchemeNormHighlight] = { "#ffb86c", "#222222" }, + [SchemeOut] = { "#000000", "#00ffff" }, +}; +/* -l and -g options; controls number of lines and columns in grid if > 0 */ +static unsigned int lines = 0; +static unsigned int columns = 0; + +/* + * Characters not considered part of a word while deleting words + * for example: " /?\"&[]" + */ +static const char worddelimiters[] = " "; + +/* Size of the window border */ +static const unsigned int border_width = 5; diff --git a/dmenu-4.9/config.mk b/dmenu-4.9/config.mk new file mode 100644 index 0000000..0929b4a --- /dev/null +++ b/dmenu-4.9/config.mk @@ -0,0 +1,31 @@ +# dmenu version +VERSION = 4.9 + +# 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 + +# 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-4.9/dmenu b/dmenu-4.9/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..493e4c0a2ce90930b4b482eb10be6a39b2883ab1 GIT binary patch literal 44232 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 AUjP6A literal 0 HcmV?d00001 diff --git a/dmenu-4.9/dmenu.1 b/dmenu-4.9/dmenu.1 new file mode 100644 index 0000000..f6ae948 --- /dev/null +++ b/dmenu-4.9/dmenu.1 @@ -0,0 +1,222 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-g +.IR columns ] +.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 [ \-nhb +.IR color ] +.RB [ \-nhf +.IR color ] +.RB [ \-shb +.IR color ] +.RB [ \-shf +.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 \-c +dmenu appears centered on 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 \-g " columns" +dmenu lists items in a grid with the given number of columns. +.TP +.BI \-l " lines" +dmenu lists items in a grid 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 +.BI \-nhb " color" +defines the normal highlight background color. +.TP +.BI \-nhf " color" +defines the normal highlight foreground color. +.TP +.BI \-shb " color" +defines the selected highlight background color. +.TP +.BI \-shf " color" +defines the selected highlight 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/dmenu-4.9/dmenu.1.orig b/dmenu-4.9/dmenu.1.orig new file mode 100644 index 0000000..57ea184 --- /dev/null +++ b/dmenu-4.9/dmenu.1.orig @@ -0,0 +1,202 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-g +.IR columns ] +.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 \-c +dmenu appears centered on 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 \-g " columns" +dmenu lists items in a grid with the given number of columns. +.TP +.BI \-l " lines" +dmenu lists items in a grid 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/dmenu-4.9/dmenu.c b/dmenu-4.9/dmenu.c new file mode 100644 index 0000000..b607411 --- /dev/null +++ b/dmenu-4.9/dmenu.c @@ -0,0 +1,855 @@ +/* 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, SchemeNormHighlight, SchemeSelHighlight, + 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 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 * columns * 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 : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + return NULL; +} + +static void +drawhighlights(struct item *item, int x, int y, int maxw) +{ + int i, indent; + char *highlight; + char c; + + if (!(strlen(item->text) && strlen(text))) + return; + + drw_setscheme(drw, scheme[item == sel + ? SchemeSelHighlight + : SchemeNormHighlight]); + for (i = 0, highlight = item->text; *highlight && text[i];) { + if (*highlight == text[i]) { + /* get indentation */ + c = *highlight; + *highlight = '\0'; + indent = TEXTW(item->text); + *highlight = c; + + /* highlight character */ + c = highlight[1]; + highlight[1] = '\0'; + drw_text( + drw, + x + indent - (lrpad / 2), + y, + MIN(maxw - indent, TEXTW(highlight) - lrpad), + bh, 0, highlight, 0 + ); + highlight[1] = c; + i++; + } + highlight++; + } +} + + +static int +drawitem(struct item *item, int x, int y, int w) +{ + int r; + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); + drawhighlights(item, x, y, w); + return r; +} + +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 grid */ + int i = 0; + for (item = curr; item != next; item = item->right, i++) + drawitem( + item, + x + ((i / lines) * ((mw - x) / columns)), + y + (((i % lines) + 1) * bh), + (mw - x) / columns + ); + } 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, MIN(TEXTW(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 %u 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: + movewordedge(-1); + goto draw; + case XK_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(*buf)) + insert(buf, len); + break; + case XK_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: + 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: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_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: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + 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 buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + die("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = NULL; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, None)) + continue; + switch(ev.type) { + 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; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; +#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])) + break; + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / 2); + } else { + 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); + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + } + inputw = MIN(inputw, mw/3); + 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, border_width, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); + XSetClassHint(dpy, win, &ch); + + /* open input methods */ + xim = XOpenIM(dpy, NULL, NULL, NULL); + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask); + 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]\n" + " [-nhb color] [-nhf color] [-shb color] [-shf 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], "-c")) /* centers dmenu on screen */ + centered = 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], "-g")) { /* number of columns in grid */ + columns = atoi(argv[++i]); + if (lines == 0) lines = 1; + } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */ + lines = atoi(argv[++i]); + if (columns == 0) columns = 1; + } 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], "-nhb")) /* normal hi background color */ + colors[SchemeNormHighlight][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ + colors[SchemeNormHighlight][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ + colors[SchemeSelHighlight][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ + colors[SchemeSelHighlight][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 (!XSetLocaleModifiers("")) + fputs("warning: no locale modifiers 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-4.9/dmenu.c.orig b/dmenu-4.9/dmenu.c.orig new file mode 100644 index 0000000..d245075 --- /dev/null +++ b/dmenu-4.9/dmenu.c.orig @@ -0,0 +1,802 @@ +/* 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 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 * columns * 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 : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + 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 grid */ + int i = 0; + for (item = curr; item != next; item = item->right, i++) + drawitem( + item, + x + ((i / lines) * ((mw - x) / columns)), + y + (((i % lines) + 1) * bh), + (mw - x) / columns + ); + } 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, MIN(TEXTW(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 %u 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: + movewordedge(-1); + goto draw; + case XK_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(*buf)) + insert(buf, len); + break; + case XK_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: + 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: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_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: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + 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 buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + die("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = NULL; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, None)) + continue; + switch(ev.type) { + 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; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; +#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])) + break; + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / 2); + } else { + 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); + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + } + inputw = MIN(inputw, mw/3); + 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, border_width, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); + XSetClassHint(dpy, win, &ch); + + /* open input methods */ + xim = XOpenIM(dpy, NULL, NULL, NULL); + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask); + 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], "-c")) /* centers dmenu on screen */ + centered = 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], "-g")) { /* number of columns in grid */ + columns = atoi(argv[++i]); + if (lines == 0) lines = 1; + } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */ + lines = atoi(argv[++i]); + if (columns == 0) columns = 1; + } 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 (!XSetLocaleModifiers("")) + fputs("warning: no locale modifiers 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-4.9/dmenu.c.rej b/dmenu-4.9/dmenu.c.rej new file mode 100644 index 0000000..dd978c8 --- /dev/null +++ b/dmenu-4.9/dmenu.c.rej @@ -0,0 +1,14 @@ +--- dmenu.c 2019-02-02 13:55:02.000000000 +0100 ++++ dmenu.c 2019-05-19 02:11:20.966710117 +0200 +@@ -654,9 +654,10 @@ setup(void) + 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, ++ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); ++ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); + XSetClassHint(dpy, win, &ch); + + /* open input methods */ diff --git a/dmenu-4.9/dmenu.o b/dmenu-4.9/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..522a7d98cba4224e776672e9328a51e285827a79 GIT binary patch literal 35824 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 INVNa{HzHdr8~^|S literal 0 HcmV?d00001 diff --git a/dmenu-4.9/dmenu_path b/dmenu-4.9/dmenu_path new file mode 100644 index 0000000..3a7cda7 --- /dev/null +++ b/dmenu-4.9/dmenu_path @@ -0,0 +1,13 @@ +#!/bin/sh + +cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" +cache="$cachedir/dmenu_run" + +[ ! -e "$cachedir" ] && mkdir -p "$cachedir" + +IFS=: +if stest -dqr -n "$cache" $PATH; then + stest -flx $PATH | sort -u | tee "$cache" +else + cat "$cache" +fi diff --git a/dmenu-4.9/dmenu_run b/dmenu-4.9/dmenu_run new file mode 100755 index 0000000..834ede5 --- /dev/null +++ b/dmenu-4.9/dmenu_run @@ -0,0 +1,2 @@ +#!/bin/sh +dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} & diff --git a/dmenu-4.9/drw.c b/dmenu-4.9/drw.c new file mode 100644 index 0000000..8fd1ca4 --- /dev/null +++ b/dmenu-4.9/drw.c @@ -0,0 +1,435 @@ +/* See LICENSE file for copyright and license details. */ +#include +#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); + 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) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + 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; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } 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; + while (1) { + 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) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + 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; + + 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); + 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); +} + +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-4.9/drw.h b/dmenu-4.9/drw.h new file mode 100644 index 0000000..4c67419 --- /dev/null +++ b/dmenu-4.9/drw.h @@ -0,0 +1,57 @@ +/* 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); +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-4.9/drw.o b/dmenu-4.9/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..2b2a07fed2ab490087f88d41239c7fc2bb9805d2 GIT binary patch literal 10600 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 Fe*m!DWat0@ literal 0 HcmV?d00001 diff --git a/dmenu-4.9/fuzzyhighlight.diff b/dmenu-4.9/fuzzyhighlight.diff new file mode 100644 index 0000000..58d5c6f --- /dev/null +++ b/dmenu-4.9/fuzzyhighlight.diff @@ -0,0 +1,152 @@ +Author: Chris Noxz +note: This patch is meant to be used together with fuzzymatch + +diff -upN dmenu-4.9/config.def.h dmenu-4.9-fuzzyhighlight/config.def.h +--- dmenu-4.9/config.def.h 2019-02-02 13:55:02.000000000 +0100 ++++ dmenu-4.9-fuzzyhighlight/config.def.h 2020-04-04 10:26:36.990890854 +0200 +@@ -11,6 +11,8 @@ static const char *colors[SchemeLast][2] + /* fg bg */ + [SchemeNorm] = { "#bbbbbb", "#222222" }, + [SchemeSel] = { "#eeeeee", "#005577" }, ++ [SchemeSelHighlight] = { "#ffc978", "#005577" }, ++ [SchemeNormHighlight] = { "#ffc978", "#222222" }, + [SchemeOut] = { "#000000", "#00ffff" }, + }; + /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ +diff -upN dmenu-4.9/dmenu.1 dmenu-4.9-fuzzyhighlight/dmenu.1 +--- dmenu-4.9/dmenu.1 2019-02-02 13:55:02.000000000 +0100 ++++ dmenu-4.9-fuzzyhighlight/dmenu.1 2020-04-04 10:30:16.430054933 +0200 +@@ -20,6 +20,14 @@ dmenu \- dynamic menu + .IR color ] + .RB [ \-sf + .IR color ] ++.RB [ \-nhb ++.IR color ] ++.RB [ \-nhf ++.IR color ] ++.RB [ \-shb ++.IR color ] ++.RB [ \-shf ++.IR color ] + .RB [ \-w + .IR windowid ] + .P +@@ -75,6 +83,18 @@ defines the selected background color. + .BI \-sf " color" + defines the selected foreground color. + .TP ++.BI \-nhb " color" ++defines the normal highlight background color. ++.TP ++.BI \-nhf " color" ++defines the normal highlight foreground color. ++.TP ++.BI \-shb " color" ++defines the selected highlight background color. ++.TP ++.BI \-shf " color" ++defines the selected highlight foreground color. ++.TP + .B \-v + prints version information to stdout, then exits. + .TP +diff -upN dmenu-4.9/dmenu.c dmenu-4.9-fuzzyhighlight/dmenu.c +--- dmenu-4.9/dmenu.c 2019-02-02 13:55:02.000000000 +0100 ++++ dmenu-4.9-fuzzyhighlight/dmenu.c 2020-04-04 10:27:43.888026309 +0200 +@@ -26,7 +26,9 @@ + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +-enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ ++enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight, ++ SchemeOut, SchemeLast }; /* color schemes */ ++ + + struct item { + char *text; +@@ -113,9 +115,49 @@ cistrstr(const char *s, const char *sub) + return NULL; + } + ++static void ++drawhighlights(struct item *item, int x, int y, int maxw) ++{ ++ int i, indent; ++ char *highlight; ++ char c; ++ ++ if (!(strlen(item->text) && strlen(text))) ++ return; ++ ++ drw_setscheme(drw, scheme[item == sel ++ ? SchemeSelHighlight ++ : SchemeNormHighlight]); ++ for (i = 0, highlight = item->text; *highlight && text[i];) { ++ if (*highlight == text[i]) { ++ /* get indentation */ ++ c = *highlight; ++ *highlight = '\0'; ++ indent = TEXTW(item->text); ++ *highlight = c; ++ ++ /* highlight character */ ++ c = highlight[1]; ++ highlight[1] = '\0'; ++ drw_text( ++ drw, ++ x + indent - (lrpad / 2), ++ y, ++ MIN(maxw - indent, TEXTW(highlight) - lrpad), ++ bh, 0, highlight, 0 ++ ); ++ highlight[1] = c; ++ i++; ++ } ++ highlight++; ++ } ++} ++ ++ + static int + drawitem(struct item *item, int x, int y, int w) + { ++ int r; + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) +@@ -123,7 +165,9 @@ drawitem(struct item *item, int x, int y + else + drw_setscheme(drw, scheme[SchemeNorm]); + +- return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); ++ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); ++ drawhighlights(item, x, y, w); ++ return r; + } + + static void +@@ -683,7 +727,8 @@ 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); ++ " [-nb color] [-nf color] [-sb color] [-sf color]\n" ++ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr); + exit(1); + } + +@@ -724,6 +769,14 @@ main(int argc, char *argv[]) + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; ++ else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ ++ colors[SchemeNormHighlight][ColBg] = argv[++i]; ++ else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ ++ colors[SchemeNormHighlight][ColFg] = argv[++i]; ++ else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ ++ colors[SchemeSelHighlight][ColBg] = argv[++i]; ++ else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ ++ colors[SchemeSelHighlight][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else diff --git a/dmenu-4.9/grid.diff b/dmenu-4.9/grid.diff new file mode 100644 index 0000000..c27689b --- /dev/null +++ b/dmenu-4.9/grid.diff @@ -0,0 +1,107 @@ +From 39ab9676914bd0d8105d0f96bbd7611a53077438 Mon Sep 17 00:00:00 2001 +From: Miles Alan +Date: Sat, 4 Jul 2020 11:19:04 -0500 +Subject: [PATCH] Add -g option to display entries in the given number of grid + columns + +This option can be used in conjunction with -l to format dmenu's options in +arbitrary size grids. For example, to create a 4 column by 6 line grid, you +could use: dmenu -g 4 -l 6 +--- + config.def.h | 3 ++- + dmenu.1 | 7 ++++++- + dmenu.c | 22 ++++++++++++++++------ + 3 files changed, 24 insertions(+), 8 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1edb647..96cf3c9 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -13,8 +13,9 @@ static const char *colors[SchemeLast][2] = { + [SchemeSel] = { "#eeeeee", "#005577" }, + [SchemeOut] = { "#000000", "#00ffff" }, + }; +-/* -l option; if nonzero, dmenu uses vertical list with given number of lines */ ++/* -l and -g options; controls number of lines and columns in grid if > 0 */ + static unsigned int lines = 0; ++static unsigned int columns = 0; + + /* + * Characters not considered part of a word while deleting words +diff --git a/dmenu.1 b/dmenu.1 +index 323f93c..d0a734a 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -4,6 +4,8 @@ dmenu \- dynamic menu + .SH SYNOPSIS + .B dmenu + .RB [ \-bfiv ] ++.RB [ \-g ++.IR columns ] + .RB [ \-l + .IR lines ] + .RB [ \-m +@@ -47,8 +49,11 @@ is faster, but will lock up X until stdin reaches end\-of\-file. + .B \-i + dmenu matches menu items case insensitively. + .TP ++.BI \-g " columns" ++dmenu lists items in a grid with the given number of columns. ++.TP + .BI \-l " lines" +-dmenu lists items vertically, with the given number of lines. ++dmenu lists items in a grid with the given number of lines. + .TP + .BI \-m " monitor" + dmenu is displayed on the monitor number supplied. Monitor numbers are starting +diff --git a/dmenu.c b/dmenu.c +index 6b8f51b..d79b6bb 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -77,7 +77,7 @@ calcoffsets(void) + int i, n; + + if (lines > 0) +- n = lines * bh; ++ n = lines * columns * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ +@@ -152,9 +152,15 @@ drawmenu(void) + } + + if (lines > 0) { +- /* draw vertical list */ +- for (item = curr; item != next; item = item->right) +- drawitem(item, x, y += bh, mw - x); ++ /* draw grid */ ++ int i = 0; ++ for (item = curr; item != next; item = item->right, i++) ++ drawitem( ++ item, ++ x + ((i / lines) * ((mw - x) / columns)), ++ y + (((i % lines) + 1) * bh), ++ (mw - x) / columns ++ ); + } else if (matches) { + /* draw horizontal list */ + x += inputw; +@@ -708,9 +714,13 @@ main(int argc, char *argv[]) + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ +- else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ ++ else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */ ++ columns = atoi(argv[++i]); ++ if (lines == 0) lines = 1; ++ } else if (!strcmp(argv[i], "-l")) { /* number of lines in grid */ + lines = atoi(argv[++i]); +- else if (!strcmp(argv[i], "-m")) ++ if (columns == 0) columns = 1; ++ } 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]; +-- +2.23.1 + diff --git a/dmenu-4.9/stest b/dmenu-4.9/stest new file mode 100755 index 0000000000000000000000000000000000000000..c93eed0aabd6b4ee4f747c9cb34b7bb157cf0469 GIT binary patch literal 17744 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 literal 0 HcmV?d00001 diff --git a/dmenu-4.9/stest.1 b/dmenu-4.9/stest.1 new file mode 100644 index 0000000..2667d8a --- /dev/null +++ b/dmenu-4.9/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/dmenu-4.9/stest.c b/dmenu-4.9/stest.c new file mode 100644 index 0000000..7a7b0bc --- /dev/null +++ b/dmenu-4.9/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 (n && 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/dmenu-4.9/stest.o b/dmenu-4.9/stest.o new file mode 100644 index 0000000000000000000000000000000000000000..9408acc32b22a0b093552dc7cacd070a1e9a4780 GIT binary patch literal 5280 zcmbW4Z){sv6~K?}v@uQbOV?5|%9y*o3|CuBT)~u<73C#y`#c<4=${ESo#WciNmR$q z?B^zfp=OHR$#vaLV*><;!p4URCNVUie3&|GZP$XBR0e`C1NFniW_eSKG_`1KGv{32 z8{gdeec?pU_nzN9=iGDeJ?~GBg`+#&E*A)K!B?QI2?}tvx7{CR-C@wc2YbxQ4pPnP zm#7KV%*v;z^A7#I;ubw)U!?s0ezW|M`S`m-7(Z!Ny7~h+YL<(700BByZQaYob5C)i~~Orp?ieImd5(f z4YPbrvtKc%p15jQTFl~u^W+%~39YN!75aeJ!URt#1DAq{PiQp8izO11W2I~~2T@mv?h zUe3jStNuN-G?yOJhd?`ZqU~j~g?Y8&(Kaje^Hu+z%EKNwM@>7J_DoItT@B};BX48- zkM*q>V;ftReV5p{?ogBM@+HP8QndUwBPQ+*)kD&}w^bnasuq9z0@ckSW8yqox22Ge zPg&dj{dxbvf%xG>(n?KDr{^BYJ8u)uc+QW1XT8`%}ZmtmolRcLfVJcaaKHLsiqY_ zLbNzEpq1Xo&WSy);u~7APV9q$!n(x;HxzozrSsnA>-E2obF&sHzZs^I^o&>T(~YGY zF5^TPiZ$)oSB>J|wBpauDzfcOtwc4Zox5H=@bp;q=;@^oT=Q{b@jEUkXycW%?j0|^ zS6IV#G19!)SlxU*vUI`wK!)G4W|buwEnhRW@O5Sudc!E!B8k@wcF1AO{h3#d@`fku zEoj9o8h4tl4PqLu&OSY!xG{RE>nt56CLL*Z>Gd!cHO^L@(7pAWSO%C!w~1PBV{;Vm z_P$2itd4az7l+mqJg?!3E05`3v;1)@X6rd(RvG&zXQY&B$1b;$c+{+Xne`&&i|-DAGKR}AKi8k)?H!!g6uz1B%RG*pu?JRj>y?cMDBD}b z18Z)9t-Nnt?|sa26~3KUN{I74D>2hNfQ6vQ{isuIsX|Y?b<+L|&r#%^?7!*2;|@II zz*7!<)`908_<{p3Bfm{L7j`coJLO>itONhC1Aoqe|H6U)%7LGC;IBCF%MKj!ww1So z31BZqAO%wC_!MNUdB~=dFdMfM(-4e~4$@DEezu}h(#qu^o|`&42(ew!-6KXcwtMH! zz2SYaea1*M9D~$cZYE==z*?BGA)ZKB`8=facHD-!f}Mw04Eu>AvBdO|SSmh~#(8Ek zFo#jIISijmOjDCYHf7oAnT!RgoMl0F&dMZba*(s)Noo`6Y~G?yKEvn3b&`ntj2>m{ z@%oTnEhs%Sh(et1eZj?Zl{obUr~93R;%&rB@Vg135dSn@f{Xi#c%Q_n&PgafjUD9A zgGUIW2p;47L<=wT1n18ye2Vjz6@G;Ciwe(j{#VW^7h0pp|E6M}=k`6k6?x8ceS^ZE z??Q=%xfw)cChM@BrsxT`%&ug9`r<=i8+?avcsU_BC!V-fxu8CcMPC zC#5*_9U-`Qv(Pa4Oz=1lVxNS#e7}ggB2Jfx^A7e&L>!uf4?_BukVVWd+6f#Kec5i2`^${_sx5sF5L1s!_@5ys zNkskvrLog#{qzqfiP>s^)=woR{6+nAnwwk#{6Nm|bD-^{y-@w>KNq!j5WyAxFU_Ax WkwjfTJ +#include +#include +#include + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +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); +} diff --git a/dmenu-4.9/util.h b/dmenu-4.9/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/dmenu-4.9/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/dmenu-4.9/util.o b/dmenu-4.9/util.o new file mode 100644 index 0000000000000000000000000000000000000000..4607dbd27ccff1daf63334f1a4c614aad4f343e1 GIT binary patch literal 2264 zcmbVNJxo(k6h5UW6-!%z0fR$_r=bbHv?NASl@uuO)ED_RB7?QGuc5Jjd3`l#OvH#o z(==glaByI7F>x@_(S$|^S4SsdG{FG}6AdQyocHd9o9FAoA-CVT-}%nH=iNUZPpFe^ zE*Eff!3Z4oI1BLN*r6XY-57L%7v`1fzVhV94pK_hkcO4&f)rD#s-&pZxHO}px47Og zqW(}^y6X?AQpq2BF0G;5A9^WmnD|QCH1Un}*2MQx!^98L7sRwuYd<@Hj)DUYL>fx% z3U;vfO{uR-do0kW)EA_HQjbgF&v7Z>K^n0)%)-I%=)l?~P-+**GX|CFHWlnK14<3O zwMXt|w9>N`#JI^unkMXaZgp~Mi`K?uXRX9%jfz*Pn-!VF(x+51mn){C08``R(cr+; zjhmrhBrFffR15Ud51Q-Y9Jp2ku6}QaX9EZAL+*?j0r*2W>I*z=yXMzEW)!7a|n@&-;;|>G3@z2TJIFggOK{ynGzzrQj@6dI_|211ot8tDWCNr-Kv0JF$ zTr1>GVGA&g2(+$FyxfAn*n%Hv!N*(h*IV#2$W#5iZgW48n{A2@ zl#R5e>yVgIr^iOs#PsCk{KP_HVRTHLNWlF}NzWFH465{;R)9>YVx*vi2}K>W)vN)U zbv&Vh^O3b-JBjUY)3Vn@qS21iT66v#k!W`PM~gP~KJ>Z%tHApOep}#aU$}l(;6?o} zfu}v@`h9`#5%>-|_-Irooh`2SGM>JRZZqeD=#!D28n?^L;XL0jkjoG9MsgX^&@Eiy zp04GRvZ1XSAg7Z?66ED_8Dzak9g;`n2*{~oKCcx}QYadljIYD_vXM;PLmg#nD~XJr z%xk=iBV!OV>67g@D25zaH(L*@XKA@l*pH3YDuzw8-|UI-uzXrea{0}s`$l#S8}arR zWzIG)Y02#LAaXS3QS&2g%ppY4`24(H9^>