diff options
| -rw-r--r-- | Makefile | 24 | ||||
| -rw-r--r-- | README | 11 | ||||
| -rw-r--r-- | config.def.h | 8 | ||||
| -rw-r--r-- | config.mk | 17 | ||||
| -rw-r--r-- | dmenu.1 | 98 | ||||
| -rw-r--r-- | dmenu.c | 222 | ||||
| -rwxr-xr-x | dmenu_path | 2 | 
7 files changed, 162 insertions, 220 deletions
| @@ -3,9 +3,6 @@ | |||
| 3 | 3 | ||
| 4 | include config.mk | 4 | include config.mk | 
| 5 | 5 | ||
| 6 | SRC = dmenu.c | ||
| 7 | OBJ = ${SRC:.c=.o} | ||
| 8 | |||
| 9 | all: options dmenu | 6 | all: options dmenu | 
| 10 | 7 | ||
| 11 | options: | 8 | options: | 
| @@ -14,34 +11,28 @@ options: | |||
| 14 | @echo "LDFLAGS = ${LDFLAGS}" | 11 | @echo "LDFLAGS = ${LDFLAGS}" | 
| 15 | @echo "CC = ${CC}" | 12 | @echo "CC = ${CC}" | 
| 16 | 13 | ||
| 17 | .c.o: | 14 | dmenu.o: dmenu.c config.mk | 
| 18 | @echo CC $< | 15 | @echo CC $< | 
| 19 | @${CC} -c ${CFLAGS} $< | 16 | @${CC} -c ${CFLAGS} $< | 
| 20 | 17 | ||
| 21 | ${OBJ}: config.h config.mk | 18 | dmenu: dmenu.o | 
| 22 | |||
| 23 | config.h: | ||
| 24 | @echo creating $@ from config.def.h | ||
| 25 | @cp config.def.h $@ | ||
| 26 | |||
| 27 | dmenu: ${OBJ} | ||
| 28 | @echo CC -o $@ | 19 | @echo CC -o $@ | 
| 29 | @${CC} -o $@ $+ ${LDFLAGS} | 20 | @${CC} -o $@ $+ ${LDFLAGS} | 
| 30 | 21 | ||
| 31 | clean: | 22 | clean: | 
| 32 | @echo cleaning | 23 | @echo cleaning | 
| 33 | @rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz | 24 | @rm -f dmenu dmenu.o dmenu-${VERSION}.tar.gz | 
| 34 | 25 | ||
| 35 | dist: clean | 26 | dist: clean | 
| 36 | @echo creating dist tarball | 27 | @echo creating dist tarball | 
| 37 | @mkdir -p dmenu-${VERSION} | 28 | @mkdir -p dmenu-${VERSION} | 
| 38 | @cp -R LICENSE Makefile README config.mk dmenu.1 config.def.h dmenu_path dmenu_run ${SRC} dmenu-${VERSION} | 29 | @cp -R LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path dmenu_run dmenu-${VERSION} | 
| 39 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} | 30 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} | 
| 40 | @gzip dmenu-${VERSION}.tar | 31 | @gzip dmenu-${VERSION}.tar | 
| 41 | @rm -rf dmenu-${VERSION} | 32 | @rm -rf dmenu-${VERSION} | 
| 42 | 33 | ||
| 43 | install: all | 34 | install: all | 
| 44 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin | 35 | @echo installing executables to ${DESTDIR}${PREFIX}/bin | 
| 45 | @mkdir -p ${DESTDIR}${PREFIX}/bin | 36 | @mkdir -p ${DESTDIR}${PREFIX}/bin | 
| 46 | @cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin | 37 | @cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin | 
| 47 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu | 38 | @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu | 
| @@ -53,8 +44,9 @@ install: all | |||
| 53 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 44 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 
| 54 | 45 | ||
| 55 | uninstall: | 46 | uninstall: | 
| 56 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin | 47 | @echo removing executables from ${DESTDIR}${PREFIX}/bin | 
| 57 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path | 48 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu | 
| 49 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path | ||
| 58 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run | 50 | @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run | 
| 59 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 | 51 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 | 
| 60 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 52 | @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 
| @@ -1,21 +1,22 @@ | |||
| 1 | dmenu - dynamic menu | 1 | dmenu - dynamic menu | 
| 2 | ==================== | 2 | ==================== | 
| 3 | dmenu is a generic and efficient menu for X. | 3 | dmenu is an efficient dynamic menu for X. | 
| 4 | 4 | ||
| 5 | 5 | ||
| 6 | Requirements | 6 | Requirements | 
| 7 | ------------ | 7 | ------------ | 
| 8 | In order to build dmenu you need the Xlib header files. | 8 | In order to build dmenu you need the Xlib header files. | 
| 9 | |||
| 9 | You also need libdraw, available from http://hg.suckless.org/libdraw | 10 | You also need libdraw, available from http://hg.suckless.org/libdraw | 
| 10 | 11 | ||
| 11 | 12 | ||
| 12 | Installation | 13 | Installation | 
| 13 | ------------ | 14 | ------------ | 
| 14 | Edit config.mk to match your local setup (dmenu is installed into the | 15 | Edit config.mk to match your local setup (dmenu is installed into | 
| 15 | /usr/local namespace by default). | 16 | the /usr/local namespace by default). | 
| 16 | 17 | ||
| 17 | Afterwards enter the following command to build and install dmenu (if | 18 | Afterwards enter the following command to build and install dmenu | 
| 18 | necessary as root): | 19 | (if necessary as root): | 
| 19 | 20 | ||
| 20 | make clean install | 21 | make clean install | 
| 21 | 22 | ||
| diff --git a/config.def.h b/config.def.h deleted file mode 100644 index eae3f08..0000000 --- a/config.def.h +++ /dev/null | |||
| @@ -1,8 +0,0 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | ||
| 2 | |||
| 3 | /* appearance */ | ||
| 4 | static const char *font = "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*"; | ||
| 5 | static const char *normbgcolor = "#cccccc"; | ||
| 6 | static const char *normfgcolor = "#000000"; | ||
| 7 | static const char *selbgcolor = "#0066ff"; | ||
| 8 | static const char *selfgcolor = "#ffffff"; | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | # dmenu version | 1 | # dmenu version | 
| 2 | VERSION = 4.1.1 | 2 | VERSION = 4.2 | 
| 3 | 3 | ||
| 4 | # Customize below to fit your system | 4 | # Customize below to fit your system | 
| 5 | 5 | ||
| @@ -7,25 +7,22 @@ VERSION = 4.1.1 | |||
| 7 | PREFIX = /usr/local | 7 | PREFIX = /usr/local | 
| 8 | MANPREFIX = ${PREFIX}/share/man | 8 | MANPREFIX = ${PREFIX}/share/man | 
| 9 | 9 | ||
| 10 | # Xlib | ||
| 10 | X11INC = /usr/X11R6/include | 11 | X11INC = /usr/X11R6/include | 
| 11 | X11LIB = /usr/X11R6/lib | 12 | X11LIB = /usr/X11R6/lib | 
| 12 | 13 | ||
| 13 | # Xinerama, comment if you don't want it | 14 | # Xinerama, comment if you don't want it | 
| 14 | XINERAMALIBS = -L${X11LIB} -lXinerama | 15 | XINERAMALIBS = -lXinerama | 
| 15 | XINERAMAFLAGS = -DXINERAMA | 16 | XINERAMAFLAGS = -DXINERAMA | 
| 16 | 17 | ||
| 17 | # includes and libs | 18 | # includes and libs | 
| 18 | INCS = -I. -I/usr/include -I${X11INC} | 19 | INCS = -I${X11INC} | 
| 19 | LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -ldraw ${XINERAMALIBS} | 20 | LIBS = -L${X11LIB} -ldraw -lX11 ${XINERAMALIBS} | 
| 20 | 21 | ||
| 21 | # flags | 22 | # flags | 
| 22 | CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | 23 | CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | 
| 23 | CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} | 24 | CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS} | 
| 24 | LDFLAGS = -s ${LIBS} | 25 | LDFLAGS = -s ${LIBS} | 
| 25 | |||
| 26 | # Solaris | ||
| 27 | #CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\" | ||
| 28 | #LDFLAGS = ${LIBS} | ||
| 29 | 26 | ||
| 30 | # compiler and linker | 27 | # compiler and linker | 
| 31 | CC = cc | 28 | CC = cc | 
| @@ -5,81 +5,76 @@ dmenu \- dynamic menu | |||
| 5 | .B dmenu | 5 | .B dmenu | 
| 6 | .RB [ \-b ] | 6 | .RB [ \-b ] | 
| 7 | .RB [ \-i ] | 7 | .RB [ \-i ] | 
| 8 | .RB [ \-l " <lines>]" | 8 | .RB [ \-l | 
| 9 | .RB [ \-p " <prompt>]" | 9 | .IR lines ] | 
| 10 | .RB [ \-fn " <font>]" | 10 | .RB [ \-p | 
| 11 | .RB [ \-nb " <color>]" | 11 | .IR prompt ] | 
| 12 | .RB [ \-nf " <color>]" | 12 | .RB [ \-fn | 
| 13 | .RB [ \-sb " <color>]" | 13 | .IR font ] | 
| 14 | .RB [ \-sf " <color>]" | 14 | .RB [ \-nb | 
| 15 | .IR color ] | ||
| 16 | .RB [ \-nf | ||
| 17 | .IR color ] | ||
| 18 | .RB [ \-sb | ||
| 19 | .IR color ] | ||
| 20 | .RB [ \-sf | ||
| 21 | .IR color ] | ||
| 15 | .RB [ \-v ] | 22 | .RB [ \-v ] | 
| 16 | 23 | .P | |
| 17 | .B dmenu_run | 24 | .BR dmenu_run " ..." | 
| 18 | .RB [ \-b ] | 25 | .P | 
| 19 | .RB [ \-i ] | ||
| 20 | .RB [ \-l " <lines>]" | ||
| 21 | .RB [ \-p " <prompt>]" | ||
| 22 | .RB [ \-fn " <font>]" | ||
| 23 | .RB [ \-nb " <color>]" | ||
| 24 | .RB [ \-nf " <color>]" | ||
| 25 | .RB [ \-sb " <color>]" | ||
| 26 | .RB [ \-sf " <color>]" | ||
| 27 | .RB [ \-v ] | ||
| 28 | |||
| 29 | .B dmenu_path | 26 | .B dmenu_path | 
| 30 | .SH DESCRIPTION | 27 | .SH DESCRIPTION | 
| 31 | .SS Overview | ||
| 32 | .B dmenu | 28 | .B dmenu | 
| 33 | is a generic menu for X, originally designed for | 29 | is a dynamic menu for X, originally designed for | 
| 34 | .BR dwm (1). | 30 | .BR dwm (1). | 
| 35 | It manages huge amounts (10000 and more) of user defined menu items efficiently. | 31 | It manages huge numbers of user-defined menu items efficiently. | 
| 32 | .P | ||
| 33 | dmenu reads a list of newline-separated items from standard input and creates a | ||
| 34 | menu. When the user selects an item or enters any text and presses Return, | ||
| 35 | their choice is printed to standard output and dmenu terminates. | ||
| 36 | .P | 36 | .P | 
| 37 | .B dmenu_run | 37 | .B dmenu_run | 
| 38 | is a dmenu script which lists programs in the user's PATH and executes | 38 | is a dmenu script used by dwm which lists programs in the user's PATH and | 
| 39 | the selected item. | 39 | executes the selected item. | 
| 40 | .P | 40 | .P | 
| 41 | .B dmenu_path | 41 | .B dmenu_path | 
| 42 | is a script used by | 42 | is a script used by dmenu_run to find and cache a list of programs. | 
| 43 | .I dmenu_run | 43 | .SH OPTIONS | 
| 44 | to find and cache a list of programs. | ||
| 45 | .SS Options | ||
| 46 | .TP | 44 | .TP | 
| 47 | .B \-b | 45 | .B \-b | 
| 48 | dmenu appears at the bottom of the screen. | 46 | dmenu appears at the bottom of the screen. | 
| 49 | .TP | 47 | .TP | 
| 50 | .B \-i | 48 | .B \-i | 
| 51 | dmenu matches menu entries case insensitively. | 49 | dmenu matches menu items case insensitively. | 
| 52 | .TP | 50 | .TP | 
| 53 | .B \-l <lines> | 51 | .BI \-l " lines" | 
| 54 | dmenu lists items vertically, with the given number of lines. | 52 | dmenu lists items vertically, with the given number of lines. | 
| 55 | .TP | 53 | .TP | 
| 56 | .B \-p <prompt> | 54 | .BI \-p " prompt" | 
| 57 | sets the prompt to be displayed to the left of the input area. | 55 | defines the prompt to be displayed to the left of the input area. | 
| 58 | .TP | 56 | .TP | 
| 59 | .B \-fn <font> | 57 | .BI \-fn " font" | 
| 60 | sets the font. | 58 | defines the font set used. | 
| 61 | .TP | 59 | .TP | 
| 62 | .B \-nb <color> | 60 | .BI \-nb " color" | 
| 63 | sets the background color (#RGB, #RRGGBB, and color names are supported). | 61 | defines the normal background color. | 
| 62 | .IR #RGB , | ||
| 63 | .IR #RRGGBB , | ||
| 64 | and color names are supported. | ||
| 64 | .TP | 65 | .TP | 
| 65 | .B \-nf <color> | 66 | .BI \-nf " color" | 
| 66 | sets the foreground color (#RGB, #RRGGBB, and color names are supported). | 67 | defines the normal foreground color. | 
| 67 | .TP | 68 | .TP | 
| 68 | .B \-sb <color> | 69 | .BI \-sb " color" | 
| 69 | sets the background color of selected items (#RGB, #RRGGBB, and color names are | 70 | defines the selected background color. | 
| 70 | supported). | ||
| 71 | .TP | 71 | .TP | 
| 72 | .B \-sf <color> | 72 | .BI \-sf " color" | 
| 73 | sets the foreground color of selected items (#RGB, #RRGGBB, and color names are | 73 | defines the selected foreground color. | 
| 74 | supported). | ||
| 75 | .TP | 74 | .TP | 
| 76 | .B \-v | 75 | .B \-v | 
| 77 | prints version information to standard output, then exits. | 76 | prints version information to standard output, then exits. | 
| 78 | .SH USAGE | 77 | .SH USAGE | 
| 79 | dmenu reads a list of newline-separated items from standard input and creates a | ||
| 80 | menu. When the user selects an item or enters any text and presses Return, | ||
| 81 | their choice is printed to standard output and dmenu terminates. | ||
| 82 | .P | ||
| 83 | dmenu is completely controlled by the keyboard. Besides standard Unix line | 78 | dmenu is completely controlled by the keyboard. Besides standard Unix line | 
| 84 | editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the | 79 | editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the | 
| 85 | following keys are recognized: | 80 | following keys are recognized: | 
| @@ -96,10 +91,9 @@ Confirm input. Prints the input text to standard output and exits, returning | |||
| 96 | success. | 91 | success. | 
| 97 | .TP | 92 | .TP | 
| 98 | .B Escape (Control\-c) | 93 | .B Escape (Control\-c) | 
| 99 | Quit without selecting an item, returning failure. | 94 | Exit without selecting an item, returning failure. | 
| 100 | .TP | 95 | .TP | 
| 101 | .B Control\-y | 96 | .B Control\-y | 
| 102 | Paste the current X selection into the input field. | 97 | Paste the current X selection into the input field. | 
| 103 | .SH SEE ALSO | 98 | .SH SEE ALSO | 
| 104 | .BR dwm (1), | 99 | .BR dwm (1) | 
| 105 | .BR wmii (1). | ||
| @@ -1,11 +1,9 @@ | |||
| 1 | /* See LICENSE file for copyright and license details. */ | 1 | /* See LICENSE file for copyright and license details. */ | 
| 2 | #include <ctype.h> | 2 | #include <ctype.h> | 
| 3 | #include <locale.h> | ||
| 4 | #include <stdio.h> | 3 | #include <stdio.h> | 
| 5 | #include <stdlib.h> | 4 | #include <stdlib.h> | 
| 6 | #include <string.h> | 5 | #include <string.h> | 
| 7 | #include <unistd.h> | 6 | #include <unistd.h> | 
| 8 | #include <X11/keysym.h> | ||
| 9 | #include <X11/Xatom.h> | 7 | #include <X11/Xatom.h> | 
| 10 | #include <X11/Xlib.h> | 8 | #include <X11/Xlib.h> | 
| 11 | #include <X11/Xutil.h> | 9 | #include <X11/Xutil.h> | 
| @@ -13,7 +11,6 @@ | |||
| 13 | #include <X11/extensions/Xinerama.h> | 11 | #include <X11/extensions/Xinerama.h> | 
| 14 | #endif | 12 | #endif | 
| 15 | #include <draw.h> | 13 | #include <draw.h> | 
| 16 | #include "config.h" | ||
| 17 | 14 | ||
| 18 | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) | 15 | #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) | 
| 19 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 16 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | 
| @@ -38,25 +35,27 @@ static void grabkeyboard(void); | |||
| 38 | static void insert(const char *s, ssize_t n); | 35 | static void insert(const char *s, ssize_t n); | 
| 39 | static void keypress(XKeyEvent *e); | 36 | static void keypress(XKeyEvent *e); | 
| 40 | static void match(void); | 37 | static void match(void); | 
| 41 | static void paste(Atom atom); | 38 | static void paste(void); | 
| 42 | static void readstdin(void); | 39 | static void readstdin(void); | 
| 43 | static void run(void); | 40 | static void run(void); | 
| 44 | static void setup(void); | 41 | static void setup(void); | 
| 45 | static void usage(void); | 42 | static void usage(void); | 
| 46 | 43 | ||
| 47 | static char *prompt; | ||
| 48 | static char text[4096]; | 44 | static char text[4096]; | 
| 49 | static int screen; | ||
| 50 | static size_t cursor = 0; | 45 | static size_t cursor = 0; | 
| 46 | static const char *prompt = NULL; | ||
| 47 | static const char *normbgcolor = "#cccccc"; | ||
| 48 | static const char *normfgcolor = "#000000"; | ||
| 49 | static const char *selbgcolor = "#0066ff"; | ||
| 50 | static const char *selfgcolor = "#ffffff"; | ||
| 51 | static unsigned int inputw = 0; | 51 | static unsigned int inputw = 0; | 
| 52 | static unsigned int lines = 0; | 52 | static unsigned int lines = 0; | 
| 53 | static unsigned int mw, mh; | 53 | static unsigned int mw, mh; | 
| 54 | static unsigned int promptw = 0; | ||
| 55 | static unsigned long normcol[ColLast]; | 54 | static unsigned long normcol[ColLast]; | 
| 56 | static unsigned long selcol[ColLast]; | 55 | static unsigned long selcol[ColLast]; | 
| 57 | static Atom utf8; | 56 | static Atom utf8; | 
| 58 | static Bool topbar = True; | 57 | static Bool topbar = True; | 
| 59 | static DC dc; | 58 | static DC *dc; | 
| 60 | static Item *allitems, *matches; | 59 | static Item *allitems, *matches; | 
| 61 | static Item *curr, *prev, *next, *sel; | 60 | static Item *curr, *prev, *next, *sel; | 
| 62 | static Window root, win; | 61 | static Window root, win; | 
| @@ -67,7 +66,7 @@ static void (*calcoffsets)(void) = calcoffsetsh; | |||
| 67 | 66 | ||
| 68 | void | 67 | void | 
| 69 | appenditem(Item *item, Item **list, Item **last) { | 68 | appenditem(Item *item, Item **list, Item **last) { | 
| 70 | if(!(*last)) | 69 | if(!*last) | 
| 71 | *list = item; | 70 | *list = item; | 
| 72 | else | 71 | else | 
| 73 | (*last)->right = item; | 72 | (*last)->right = item; | 
| @@ -80,12 +79,12 @@ void | |||
| 80 | calcoffsetsh(void) { | 79 | calcoffsetsh(void) { | 
| 81 | unsigned int w, x; | 80 | unsigned int w, x; | 
| 82 | 81 | ||
| 83 | w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">"); | 82 | w = (prompt ? textw(dc, prompt) : 0) + inputw + textw(dc, "<") + textw(dc, ">"); | 
| 84 | for(x = w, next = curr; next; next = next->right) | 83 | for(x = w, next = curr; next; next = next->right) | 
| 85 | if((x += MIN(textw(&dc, next->text), mw / 3)) > mw) | 84 | if((x += MIN(textw(dc, next->text), mw / 3)) > mw) | 
| 86 | break; | 85 | break; | 
| 87 | for(x = w, prev = curr; prev && prev->left; prev = prev->left) | 86 | for(x = w, prev = curr; prev && prev->left; prev = prev->left) | 
| 88 | if((x += MIN(textw(&dc, prev->left->text), mw / 3)) > mw) | 87 | if((x += MIN(textw(dc, prev->left->text), mw / 3)) > mw) | 
| 89 | break; | 88 | break; | 
| 90 | } | 89 | } | 
| 91 | 90 | ||
| @@ -96,101 +95,75 @@ calcoffsetsv(void) { | |||
| 96 | next = prev = curr; | 95 | next = prev = curr; | 
| 97 | for(i = 0; i < lines && next; i++) | 96 | for(i = 0; i < lines && next; i++) | 
| 98 | next = next->right; | 97 | next = next->right; | 
| 99 | mh = (dc.font.height + 2) * (i + 1); | ||
| 100 | for(i = 0; i < lines && prev && prev->left; i++) | 98 | for(i = 0; i < lines && prev && prev->left; i++) | 
| 101 | prev = prev->left; | 99 | prev = prev->left; | 
| 102 | } | 100 | } | 
| 103 | 101 | ||
| 104 | char * | 102 | char * | 
| 105 | cistrstr(const char *s, const char *sub) { | 103 | cistrstr(const char *s, const char *sub) { | 
| 106 | int c, csub; | 104 | size_t len; | 
| 107 | unsigned int len; | ||
| 108 | 105 | ||
| 109 | if(!sub) | 106 | for(len = strlen(sub); *s; s++) | 
| 110 | return (char *)s; | 107 | if(!strncasecmp(s, sub, len)) | 
| 111 | if((c = tolower(*sub++)) != '\0') { | 108 | return (char *)s; | 
| 112 | len = strlen(sub); | 109 | return NULL; | 
| 113 | do { | ||
| 114 | do { | ||
| 115 | if((csub = *s++) == '\0') | ||
| 116 | return NULL; | ||
| 117 | } | ||
| 118 | while(tolower(csub) != c); | ||
| 119 | } | ||
| 120 | while(strncasecmp(s, sub, len) != 0); | ||
| 121 | s--; | ||
| 122 | } | ||
| 123 | return (char *)s; | ||
| 124 | } | 110 | } | 
| 125 | 111 | ||
| 126 | void | 112 | void | 
| 127 | drawmenu(void) { | 113 | drawmenu(void) { | 
| 128 | dc.x = 0; | 114 | dc->x = 0; | 
| 129 | dc.y = 0; | 115 | dc->y = 0; | 
| 130 | dc.w = mw; | 116 | drawrect(dc, 0, 0, mw, mh, BG(dc, normcol)); | 
| 131 | dc.h = mh; | 117 | dc->h = dc->font.height + 2; | 
| 132 | drawbox(&dc, normcol); | 118 | dc->y = topbar ? 0 : mh - dc->h; | 
| 133 | dc.h = dc.font.height + 2; | ||
| 134 | dc.y = topbar ? 0 : mh - dc.h; | ||
| 135 | /* print prompt? */ | 119 | /* print prompt? */ | 
| 136 | if(prompt) { | 120 | if(prompt) { | 
| 137 | dc.w = promptw; | 121 | dc->w = textw(dc, prompt); | 
| 138 | drawbox(&dc, selcol); | 122 | drawtext(dc, prompt, selcol); | 
| 139 | drawtext(&dc, prompt, selcol); | 123 | dc->x = dc->w; | 
| 140 | dc.x += dc.w; | ||
| 141 | } | 124 | } | 
| 142 | dc.w = mw - dc.x; | 125 | dc->w = mw - dc->x; | 
| 143 | /* print input area */ | 126 | /* print input area */ | 
| 144 | if(matches && lines == 0 && textw(&dc, text) <= inputw) | 127 | if(matches && lines == 0 && textw(dc, text) <= inputw) | 
| 145 | dc.w = inputw; | 128 | dc->w = inputw; | 
| 146 | drawtext(&dc, text, normcol); | 129 | drawtext(dc, text, normcol); | 
| 147 | drawline(&dc, textnw(&dc, text, cursor) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); | 130 | drawrect(dc, textnw(dc, text, cursor) + dc->h/2 - 2, 2, 1, dc->h - 4, FG(dc, normcol)); | 
| 148 | if(lines > 0) | 131 | if(lines > 0) | 
| 149 | drawmenuv(); | 132 | drawmenuv(); | 
| 150 | else if(curr && (dc.w == inputw || curr->next)) | 133 | else if(curr && (dc->w == inputw || curr->next)) | 
| 151 | drawmenuh(); | 134 | drawmenuh(); | 
| 152 | commitdraw(&dc, win); | 135 | commitdraw(dc, win); | 
| 153 | } | 136 | } | 
| 154 | 137 | ||
| 155 | void | 138 | void | 
| 156 | drawmenuh(void) { | 139 | drawmenuh(void) { | 
| 157 | Item *item; | 140 | Item *item; | 
| 158 | 141 | ||
| 159 | dc.x += inputw; | 142 | dc->x += inputw; | 
| 160 | dc.w = textw(&dc, "<"); | 143 | dc->w = textw(dc, "<"); | 
| 161 | if(curr->left) | 144 | if(curr->left) | 
| 162 | drawtext(&dc, "<", normcol); | 145 | drawtext(dc, "<", normcol); | 
| 163 | dc.x += dc.w; | ||
| 164 | for(item = curr; item != next; item = item->right) { | 146 | for(item = curr; item != next; item = item->right) { | 
| 165 | dc.w = MIN(textw(&dc, item->text), mw / 3); | 147 | dc->x += dc->w; | 
| 166 | if(item == sel) | 148 | dc->w = MIN(textw(dc, item->text), mw / 3); | 
| 167 | drawbox(&dc, selcol); | 149 | drawtext(dc, item->text, (item == sel) ? selcol : normcol); | 
| 168 | drawtext(&dc, item->text, (item == sel) ? selcol : normcol); | ||
| 169 | dc.x += dc.w; | ||
| 170 | } | 150 | } | 
| 171 | dc.w = textw(&dc, ">"); | 151 | dc->w = textw(dc, ">"); | 
| 172 | dc.x = mw - dc.w; | 152 | dc->x = mw - dc->w; | 
| 173 | if(next) | 153 | if(next) | 
| 174 | drawtext(&dc, ">", normcol); | 154 | drawtext(dc, ">", normcol); | 
| 175 | } | 155 | } | 
| 176 | 156 | ||
| 177 | void | 157 | void | 
| 178 | drawmenuv(void) { | 158 | drawmenuv(void) { | 
| 179 | Item *item; | 159 | Item *item; | 
| 180 | XWindowAttributes wa; | ||
| 181 | 160 | ||
| 182 | dc.y = topbar ? dc.h : 0; | 161 | dc->y = topbar ? dc->h : 0; | 
| 183 | dc.w = mw - dc.x; | 162 | dc->w = mw - dc->x; | 
| 184 | for(item = curr; item != next; item = item->right) { | 163 | for(item = curr; item != next; item = item->right) { | 
| 185 | if(item == sel) | 164 | drawtext(dc, item->text, (item == sel) ? selcol : normcol); | 
| 186 | drawbox(&dc, selcol); | 165 | dc->y += dc->h; | 
| 187 | drawtext(&dc, item->text, (item == sel) ? selcol : normcol); | ||
| 188 | dc.y += dc.h; | ||
| 189 | } | 166 | } | 
| 190 | if(!XGetWindowAttributes(dc.dpy, win, &wa)) | ||
| 191 | eprintf("cannot get window attributes\n"); | ||
| 192 | if(wa.height != mh) | ||
| 193 | XMoveResizeWindow(dc.dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); | ||
| 194 | } | 167 | } | 
| 195 | 168 | ||
| 196 | void | 169 | void | 
| @@ -198,7 +171,7 @@ grabkeyboard(void) { | |||
| 198 | int i; | 171 | int i; | 
| 199 | 172 | ||
| 200 | for(i = 0; i < 1000; i++) { | 173 | for(i = 0; i < 1000; i++) { | 
| 201 | if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) | 174 | if(!XGrabKeyboard(dc->dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) | 
| 202 | return; | 175 | return; | 
| 203 | usleep(1000); | 176 | usleep(1000); | 
| 204 | } | 177 | } | 
| @@ -254,6 +227,7 @@ keypress(XKeyEvent *e) { | |||
| 254 | break; | 227 | break; | 
| 255 | case XK_k: /* delete right */ | 228 | case XK_k: /* delete right */ | 
| 256 | text[cursor] = '\0'; | 229 | text[cursor] = '\0'; | 
| 230 | match(); | ||
| 257 | break; | 231 | break; | 
| 258 | case XK_n: | 232 | case XK_n: | 
| 259 | ksym = XK_Down; | 233 | ksym = XK_Down; | 
| @@ -270,10 +244,10 @@ keypress(XKeyEvent *e) { | |||
| 270 | n = 0; | 244 | n = 0; | 
| 271 | while(cursor - n++ > 0 && text[cursor - n] == ' '); | 245 | while(cursor - n++ > 0 && text[cursor - n] == ' '); | 
| 272 | while(cursor - n++ > 0 && text[cursor - n] != ' '); | 246 | while(cursor - n++ > 0 && text[cursor - n] != ' '); | 
| 273 | insert(NULL, -(--n)); | 247 | insert(NULL, 1-n); | 
| 274 | break; | 248 | break; | 
| 275 | case XK_y: /* paste selection */ | 249 | case XK_y: /* paste selection */ | 
| 276 | XConvertSelection(dc.dpy, XA_PRIMARY, utf8, None, win, CurrentTime); | 250 | XConvertSelection(dc->dpy, XA_PRIMARY, utf8, None, win, CurrentTime); | 
| 277 | /* causes SelectionNotify event */ | 251 | /* causes SelectionNotify event */ | 
| 278 | return; | 252 | return; | 
| 279 | } | 253 | } | 
| @@ -348,7 +322,7 @@ keypress(XKeyEvent *e) { | |||
| 348 | break; | 322 | break; | 
| 349 | case XK_Return: | 323 | case XK_Return: | 
| 350 | case XK_KP_Enter: | 324 | case XK_KP_Enter: | 
| 351 | fputs(((e->state & ShiftMask) || sel) ? sel->text : text, stdout); | 325 | fputs((sel && !(e->state & ShiftMask)) ? sel->text : text, stdout); | 
| 352 | fflush(stdout); | 326 | fflush(stdout); | 
| 353 | exit(EXIT_SUCCESS); | 327 | exit(EXIT_SUCCESS); | 
| 354 | case XK_Right: | 328 | case XK_Right: | 
| @@ -418,15 +392,14 @@ match(void) { | |||
| 418 | } | 392 | } | 
| 419 | 393 | ||
| 420 | void | 394 | void | 
| 421 | paste(Atom atom) | 395 | paste(void) { | 
| 422 | { | ||
| 423 | char *p, *q; | 396 | char *p, *q; | 
| 424 | int di; | 397 | int di; | 
| 425 | unsigned long dl; | 398 | unsigned long dl; | 
| 426 | Atom da; | 399 | Atom da; | 
| 427 | 400 | ||
| 428 | XGetWindowProperty(dc.dpy, win, atom, 0, sizeof text - cursor, True, | 401 | XGetWindowProperty(dc->dpy, win, utf8, 0, sizeof text - cursor, True, | 
| 429 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | 402 | utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | 
| 430 | insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); | 403 | insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); | 
| 431 | XFree(p); | 404 | XFree(p); | 
| 432 | drawmenu(); | 405 | drawmenu(); | 
| @@ -434,24 +407,22 @@ paste(Atom atom) | |||
| 434 | 407 | ||
| 435 | void | 408 | void | 
| 436 | readstdin(void) { | 409 | readstdin(void) { | 
| 437 | char buf[sizeof text]; | 410 | char buf[sizeof text], *p; | 
| 438 | size_t len; | ||
| 439 | Item *item, *new; | 411 | Item *item, *new; | 
| 440 | 412 | ||
| 441 | allitems = NULL; | 413 | allitems = NULL; | 
| 442 | for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) { | 414 | for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) { | 
| 443 | len = strlen(buf); | 415 | if((p = strchr(buf, '\n'))) | 
| 444 | if(buf[len-1] == '\n') | 416 | *p = '\0'; | 
| 445 | buf[--len] = '\0'; | ||
| 446 | if(!(new = malloc(sizeof *new))) | 417 | if(!(new = malloc(sizeof *new))) | 
| 447 | eprintf("cannot malloc %u bytes\n", sizeof *new); | 418 | eprintf("cannot malloc %u bytes\n", sizeof *new); | 
| 448 | if(!(new->text = strdup(buf))) | 419 | if(!(new->text = strdup(buf))) | 
| 449 | eprintf("cannot strdup %u bytes\n", len); | 420 | eprintf("cannot strdup %u bytes\n", strlen(buf)); | 
| 450 | inputw = MAX(inputw, textw(&dc, new->text)); | 421 | inputw = MAX(inputw, textw(dc, new->text)); | 
| 451 | new->next = new->left = new->right = NULL; | 422 | new->next = new->left = new->right = NULL; | 
| 452 | if(item) | 423 | if(item) | 
| 453 | item->next = new; | 424 | item->next = new; | 
| 454 | else | 425 | else | 
| 455 | allitems = new; | 426 | allitems = new; | 
| 456 | } | 427 | } | 
| 457 | } | 428 | } | 
| @@ -460,7 +431,7 @@ void | |||
| 460 | run(void) { | 431 | run(void) { | 
| 461 | XEvent ev; | 432 | XEvent ev; | 
| 462 | 433 | ||
| 463 | while(!XNextEvent(dc.dpy, &ev)) | 434 | while(!XNextEvent(dc->dpy, &ev)) | 
| 464 | switch(ev.type) { | 435 | switch(ev.type) { | 
| 465 | case Expose: | 436 | case Expose: | 
| 466 | if(ev.xexpose.count == 0) | 437 | if(ev.xexpose.count == 0) | 
| @@ -470,39 +441,43 @@ run(void) { | |||
| 470 | keypress(&ev.xkey); | 441 | keypress(&ev.xkey); | 
| 471 | break; | 442 | break; | 
| 472 | case SelectionNotify: | 443 | case SelectionNotify: | 
| 473 | if(ev.xselection.property != None) | 444 | if(ev.xselection.property == utf8) | 
| 474 | paste(ev.xselection.property); | 445 | paste(); | 
| 475 | break; | 446 | break; | 
| 476 | case VisibilityNotify: | 447 | case VisibilityNotify: | 
| 477 | if(ev.xvisibility.state != VisibilityUnobscured) | 448 | if(ev.xvisibility.state != VisibilityUnobscured) | 
| 478 | XRaiseWindow(dc.dpy, win); | 449 | XRaiseWindow(dc->dpy, win); | 
| 479 | break; | 450 | break; | 
| 480 | } | 451 | } | 
| 481 | } | 452 | } | 
| 482 | 453 | ||
| 483 | void | 454 | void | 
| 484 | setup(void) { | 455 | setup(void) { | 
| 485 | int x, y; | 456 | int x, y, screen; | 
| 457 | XSetWindowAttributes wa; | ||
| 486 | #ifdef XINERAMA | 458 | #ifdef XINERAMA | 
| 487 | int i, n; | 459 | int n; | 
| 488 | XineramaScreenInfo *info; | 460 | XineramaScreenInfo *info; | 
| 489 | #endif | 461 | #endif | 
| 490 | XSetWindowAttributes wa; | ||
| 491 | 462 | ||
| 492 | normcol[ColBG] = getcolor(&dc, normbgcolor); | 463 | screen = DefaultScreen(dc->dpy); | 
| 493 | normcol[ColFG] = getcolor(&dc, normfgcolor); | 464 | root = RootWindow(dc->dpy, screen); | 
| 494 | selcol[ColBG] = getcolor(&dc, selbgcolor); | 465 | utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False); | 
| 495 | selcol[ColFG] = getcolor(&dc, selfgcolor); | 466 | |
| 467 | normcol[ColBG] = getcolor(dc, normbgcolor); | ||
| 468 | normcol[ColFG] = getcolor(dc, normfgcolor); | ||
| 469 | selcol[ColBG] = getcolor(dc, selbgcolor); | ||
| 470 | selcol[ColFG] = getcolor(dc, selfgcolor); | ||
| 496 | 471 | ||
| 497 | /* input window geometry */ | 472 | /* input window geometry */ | 
| 498 | mh = (dc.font.height + 2) * (lines + 1); | 473 | mh = (dc->font.height + 2) * (lines + 1); | 
| 499 | #ifdef XINERAMA | 474 | #ifdef XINERAMA | 
| 500 | if((info = XineramaQueryScreens(dc.dpy, &n))) { | 475 | if((info = XineramaQueryScreens(dc->dpy, &n))) { | 
| 501 | int di; | 476 | int i, di; | 
| 502 | unsigned int du; | 477 | unsigned int du; | 
| 503 | Window dw; | 478 | Window dw; | 
| 504 | 479 | ||
| 505 | XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du); | 480 | XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du); | 
| 506 | for(i = 0; i < n; i++) | 481 | for(i = 0; i < n; i++) | 
| 507 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | 482 | if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | 
| 508 | break; | 483 | break; | 
| @@ -515,31 +490,30 @@ setup(void) { | |||
| 515 | #endif | 490 | #endif | 
| 516 | { | 491 | { | 
| 517 | x = 0; | 492 | x = 0; | 
| 518 | y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh; | 493 | y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh; | 
| 519 | mw = DisplayWidth(dc.dpy, screen); | 494 | mw = DisplayWidth(dc->dpy, screen); | 
| 520 | } | 495 | } | 
| 521 | 496 | ||
| 522 | /* input window */ | 497 | /* input window */ | 
| 523 | wa.override_redirect = True; | 498 | wa.override_redirect = True; | 
| 524 | wa.background_pixmap = ParentRelative; | 499 | wa.background_pixmap = ParentRelative; | 
| 525 | wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | 500 | wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | 
| 526 | win = XCreateWindow(dc.dpy, root, x, y, mw, mh, 0, | 501 | win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0, | 
| 527 | DefaultDepth(dc.dpy, screen), CopyFromParent, | 502 | DefaultDepth(dc->dpy, screen), CopyFromParent, | 
| 528 | DefaultVisual(dc.dpy, screen), | 503 | DefaultVisual(dc->dpy, screen), | 
| 529 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | 504 | CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | 
| 530 | 505 | ||
| 531 | match(); | ||
| 532 | grabkeyboard(); | 506 | grabkeyboard(); | 
| 533 | setupdraw(&dc, win); | 507 | setcanvas(dc, win, mw, mh); | 
| 534 | inputw = MIN(inputw, mw / 3); | 508 | inputw = MIN(inputw, mw/3); | 
| 535 | utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False); | 509 | XMapRaised(dc->dpy, win); | 
| 536 | XMapRaised(dc.dpy, win); | 510 | match(); | 
| 537 | } | 511 | } | 
| 538 | 512 | ||
| 539 | void | 513 | void | 
| 540 | usage(void) { | 514 | usage(void) { | 
| 541 | fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n" | 515 | fputs("usage: dmenu [-b] [-i] [-l lines] [-p prompt] [-fn font] [-nb color]\n" | 
| 542 | " [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr); | 516 | " [-nf color] [-sb color] [-sf color] [-v]\n", stderr); | 
| 543 | exit(EXIT_FAILURE); | 517 | exit(EXIT_FAILURE); | 
| 544 | } | 518 | } | 
| 545 | 519 | ||
| @@ -548,8 +522,10 @@ main(int argc, char *argv[]) { | |||
| 548 | int i; | 522 | int i; | 
| 549 | 523 | ||
| 550 | progname = "dmenu"; | 524 | progname = "dmenu"; | 
| 525 | dc = initdraw(); | ||
| 526 | |||
| 551 | for(i = 1; i < argc; i++) | 527 | for(i = 1; i < argc; i++) | 
| 552 | /* 1-arg flags */ | 528 | /* single flags */ | 
| 553 | if(!strcmp(argv[i], "-v")) { | 529 | if(!strcmp(argv[i], "-v")) { | 
| 554 | fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout); | 530 | fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout); | 
| 555 | exit(EXIT_SUCCESS); | 531 | exit(EXIT_SUCCESS); | 
| @@ -562,17 +538,15 @@ main(int argc, char *argv[]) { | |||
| 562 | } | 538 | } | 
| 563 | else if(i == argc-1) | 539 | else if(i == argc-1) | 
| 564 | usage(); | 540 | usage(); | 
| 565 | /* 2-arg flags */ | 541 | /* double flags */ | 
| 566 | else if(!strcmp(argv[i], "-l")) { | 542 | else if(!strcmp(argv[i], "-l")) { | 
| 567 | if((lines = atoi(argv[++i])) > 0) | 543 | if((lines = atoi(argv[++i])) > 0) | 
| 568 | calcoffsets = calcoffsetsv; | 544 | calcoffsets = calcoffsetsv; | 
| 569 | } | 545 | } | 
| 570 | else if(!strcmp(argv[i], "-p")) { | 546 | else if(!strcmp(argv[i], "-p")) | 
| 571 | prompt = argv[++i]; | 547 | prompt = argv[++i]; | 
| 572 | promptw = MIN(textw(&dc, prompt), mw/5); | ||
| 573 | } | ||
| 574 | else if(!strcmp(argv[i], "-fn")) | 548 | else if(!strcmp(argv[i], "-fn")) | 
| 575 | font = argv[++i]; | 549 | initfont(dc, argv[i++]); | 
| 576 | else if(!strcmp(argv[i], "-nb")) | 550 | else if(!strcmp(argv[i], "-nb")) | 
| 577 | normbgcolor = argv[++i]; | 551 | normbgcolor = argv[++i]; | 
| 578 | else if(!strcmp(argv[i], "-nf")) | 552 | else if(!strcmp(argv[i], "-nf")) | 
| @@ -584,14 +558,6 @@ main(int argc, char *argv[]) { | |||
| 584 | else | 558 | else | 
| 585 | usage(); | 559 | usage(); | 
| 586 | 560 | ||
| 587 | if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||
| 588 | fputs("dmenu: warning: no locale support\n", stderr); | ||
| 589 | if(!(dc.dpy = XOpenDisplay(NULL))) | ||
| 590 | eprintf("cannot open display\n"); | ||
| 591 | screen = DefaultScreen(dc.dpy); | ||
| 592 | root = RootWindow(dc.dpy, screen); | ||
| 593 | initfont(&dc, font); | ||
| 594 | |||
| 595 | readstdin(); | 561 | readstdin(); | 
| 596 | setup(); | 562 | setup(); | 
| 597 | run(); | 563 | run(); | 
| @@ -19,7 +19,7 @@ then | |||
| 19 | do | 19 | do | 
| 20 | test -x "$file" && echo "$file" | 20 | test -x "$file" && echo "$file" | 
| 21 | done | 21 | done | 
| 22 | done | sort | uniq > "$CACHE".$$ && | 22 | done | sort -u > "$CACHE".$$ && | 
| 23 | mv "$CACHE".$$ "$CACHE" | 23 | mv "$CACHE".$$ "$CACHE" | 
| 24 | fi | 24 | fi | 
| 25 | 25 | ||
