Compare commits
	
		
			69 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 5c440dfd8c | |||
| 2b0fa8c0cf | |||
| 0fe460dbd4 | |||
| dfbbf7f6e1 | |||
| ba1a347dca | |||
| bcbc1ef5c4 | |||
| 689d9bfcf6 | |||
| e42c036634 | |||
| 1d2b462acf | |||
| 7ec32fe494 | |||
| fce06f437d | |||
| 1e8c5b68f4 | |||
| 528d39b011 | |||
| 32db2b1251 | |||
| e35976f4a5 | |||
| 28fb3e2812 | |||
| fe5d5c6709 | |||
| e1e1de7b3b | |||
| 33685b06e9 | |||
| e4827b0c40 | |||
| e73651f12a | |||
| 31fa07b984 | |||
| 6818e07291 | |||
| b43ec0577f | |||
| 22511c41d5 | |||
| 77526f756e | |||
| 7269c5355d | |||
| 6be057f060 | |||
| 41fdabbf7c | |||
| 3a505cebe8 | |||
| 308fe78b83 | |||
| c4b656e0da | |||
| 3e39c526d2 | |||
| a9a3836861 | |||
| eb96af27f4 | |||
| d78ff08d99 | |||
| cd2133a5f6 | |||
| c585e8e498 | |||
| 523aa08f51 | |||
| 1a13d0465d | |||
| 9b38fda6fe | |||
| db6093f6ec | |||
| a9b1de384a | |||
| 43b0c2c3dd | |||
| f5036b90ef | |||
| 153aaf88bf | |||
| 65be875f5a | |||
| 7d19b2055d | |||
| 11a65377da | |||
| bbc464dc80 | |||
| a314412f4b | |||
| a9eae39e93 | |||
| 851b73d178 | |||
| 05c138f5b8 | |||
| cd132c8d5b | |||
| e75494b730 | |||
| 0f76dd2fb8 | |||
| 0b57480218 | |||
| 377bd37e21 | |||
| b6d2cc9aea | |||
| 2f398981fe | |||
| 23051d78dd | |||
| e2a280541e | |||
| 889512811d | |||
| 84a1bc5d0d | |||
| f0a5b75d6a | |||
| 1cabeda550 | |||
| 41379f7c39 | |||
| 64ab2801fb | 
							
								
								
									
										12
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								LICENSE
									
									
									
									
									
								
							| @ -1,13 +1,15 @@ | |||||||
| MIT/X Consortium License | MIT/X Consortium License | ||||||
|  |  | ||||||
| © 2006-2014 Anselm R Garbe <anselm@garbe.us> | © 2006-2019 Anselm R Garbe <anselm@garbe.ca> | ||||||
| © 2010-2012 Connor Lane Smith <cls@lubutu.com> | © 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com> | ||||||
|  | © 2006-2007 Michał Janeczek <janeczek@gmail.com> | ||||||
|  | © 2007 Kris Maglione <jg@suckless.org> | ||||||
| © 2009 Gottox <gottox@s01.de> | © 2009 Gottox <gottox@s01.de> | ||||||
| © 2009 Markus Schnalke <meillo@marmaro.de> | © 2009 Markus Schnalke <meillo@marmaro.de> | ||||||
| © 2009 Evan Gates <evan.gates@gmail.com> | © 2009 Evan Gates <evan.gates@gmail.com> | ||||||
| © 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com> | © 2010-2012 Connor Lane Smith <cls@lubutu.com> | ||||||
| © 2006-2007 Michał Janeczek <janeczek at gmail dot com> | © 2014-2022 Hiltjo Posthuma <hiltjo@codemadness.org> | ||||||
| © 2014-2015 Hiltjo Posthuma <hiltjo@codemadness.org> | © 2015-2019 Quentin Rameau <quinq@fifth.space> | ||||||
|  |  | ||||||
| Permission is hereby granted, free of charge, to any person obtaining a | Permission is hereby granted, free of charge, to any person obtaining a | ||||||
| copy of this software and associated documentation files (the "Software"), | copy of this software and associated documentation files (the "Software"), | ||||||
|  | |||||||
							
								
								
									
										78
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								Makefile
									
									
									
									
									
								
							| @ -4,71 +4,61 @@ | |||||||
| include config.mk | include config.mk | ||||||
|  |  | ||||||
| SRC = drw.c dmenu.c stest.c util.c | SRC = drw.c dmenu.c stest.c util.c | ||||||
| OBJ = ${SRC:.c=.o} | OBJ = $(SRC:.c=.o) | ||||||
|  |  | ||||||
| all: options dmenu stest | all: options dmenu stest | ||||||
|  |  | ||||||
| options: | options: | ||||||
| 	@echo dmenu build options: | 	@echo dmenu build options: | ||||||
| 	@echo "CFLAGS   = ${CFLAGS}" | 	@echo "CFLAGS   = $(CFLAGS)" | ||||||
| 	@echo "LDFLAGS  = ${LDFLAGS}" | 	@echo "LDFLAGS  = $(LDFLAGS)" | ||||||
| 	@echo "CC       = ${CC}" | 	@echo "CC       = $(CC)" | ||||||
|  |  | ||||||
| .c.o: | .c.o: | ||||||
| 	@echo CC $< | 	$(CC) -c $(CFLAGS) $< | ||||||
| 	@${CC} -c ${CFLAGS} $< |  | ||||||
|  |  | ||||||
| config.h: | config.h: | ||||||
| 	@echo creating $@ from config.def.h | 	cp config.def.h $@ | ||||||
| 	@cp config.def.h $@ |  | ||||||
|  |  | ||||||
| ${OBJ}: arg.h config.h config.mk drw.h | $(OBJ): arg.h config.h config.mk drw.h | ||||||
|  |  | ||||||
| dmenu: dmenu.o drw.o util.o | dmenu: dmenu.o drw.o util.o | ||||||
| 	@echo CC -o $@ | 	$(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS) | ||||||
| 	@${CC} -o $@ dmenu.o drw.o util.o ${LDFLAGS} |  | ||||||
|  |  | ||||||
| stest: stest.o | stest: stest.o | ||||||
| 	@echo CC -o $@ | 	$(CC) -o $@ stest.o $(LDFLAGS) | ||||||
| 	@${CC} -o $@ stest.o ${LDFLAGS} |  | ||||||
|  |  | ||||||
| clean: | clean: | ||||||
| 	@echo cleaning | 	rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz | ||||||
| 	@rm -f dmenu stest ${OBJ} dmenu-${VERSION}.tar.gz |  | ||||||
|  |  | ||||||
| dist: clean | dist: clean | ||||||
| 	@echo creating dist tarball | 	mkdir -p dmenu-$(VERSION) | ||||||
| 	@mkdir -p dmenu-${VERSION} | 	cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\ | ||||||
| 	@cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1 \ | 		drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\ | ||||||
| 		drw.h util.h dmenu_path dmenu_run stest.1 ${SRC} \ | 		dmenu-$(VERSION) | ||||||
| 		dmenu-${VERSION} | 	tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION) | ||||||
| 	@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} | 	gzip dmenu-$(VERSION).tar | ||||||
| 	@gzip dmenu-${VERSION}.tar | 	rm -rf dmenu-$(VERSION) | ||||||
| 	@rm -rf dmenu-${VERSION} |  | ||||||
|  |  | ||||||
| install: all | install: all | ||||||
| 	@echo installing executables to ${DESTDIR}${PREFIX}/bin | 	mkdir -p $(DESTDIR)$(PREFIX)/bin | ||||||
| 	@mkdir -p ${DESTDIR}${PREFIX}/bin | 	cp -f dmenu dmenu_path dmenu_run stest $(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 | 	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path | ||||||
| 	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path | 	chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run | ||||||
| 	@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run | 	chmod 755 $(DESTDIR)$(PREFIX)/bin/stest | ||||||
| 	@chmod 755 ${DESTDIR}${PREFIX}/bin/stest | 	mkdir -p $(DESTDIR)$(MANPREFIX)/man1 | ||||||
| 	@echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 | 	sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 | ||||||
| 	@mkdir -p ${DESTDIR}${MANPREFIX}/man1 | 	sed "s/VERSION/$(VERSION)/g" < stest.1 > $(DESTDIR)$(MANPREFIX)/man1/stest.1 | ||||||
| 	@sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1 | 	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/dmenu.1 | ||||||
| 	@sed "s/VERSION/${VERSION}/g" < stest.1 > ${DESTDIR}${MANPREFIX}/man1/stest.1 | 	chmod 644 $(DESTDIR)$(MANPREFIX)/man1/stest.1 | ||||||
| 	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1 |  | ||||||
| 	@chmod 644 ${DESTDIR}${MANPREFIX}/man1/stest.1 |  | ||||||
|  |  | ||||||
| uninstall: | uninstall: | ||||||
| 	@echo removing executables from ${DESTDIR}${PREFIX}/bin | 	rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\ | ||||||
| 	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu | 		$(DESTDIR)$(PREFIX)/bin/dmenu_path\ | ||||||
| 	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path | 		$(DESTDIR)$(PREFIX)/bin/dmenu_run\ | ||||||
| 	@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run | 		$(DESTDIR)$(PREFIX)/bin/stest\ | ||||||
| 	@rm -f ${DESTDIR}${PREFIX}/bin/stest | 		$(DESTDIR)$(MANPREFIX)/man1/dmenu.1\ | ||||||
| 	@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 | 		$(DESTDIR)$(MANPREFIX)/man1/stest.1 | ||||||
| 	@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 |  | ||||||
| 	@rm -f ${DESTDIR}${MANPREFIX}/man1/stest.1 |  | ||||||
|  |  | ||||||
| .PHONY: all options clean dist install uninstall | .PHONY: all options clean dist install uninstall | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								config.def.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								config.def.h
									
									
									
									
									
								
							| @ -2,7 +2,10 @@ | |||||||
| /* Default settings; can be overriden by command line. */ | /* Default settings; can be overriden by command line. */ | ||||||
|  |  | ||||||
| static int topbar = 1;                      /* -b  option; if 0, dmenu appears at bottom     */ | static int topbar = 1;                      /* -b  option; if 0, dmenu appears at bottom     */ | ||||||
|  | static const unsigned int alpha = 0xf0; | ||||||
| /* -fn option overrides fonts[0]; default X11 font or font set */ | /* -fn option overrides fonts[0]; default X11 font or font set */ | ||||||
|  | static const int user_bt = 0;               /* add an defined amount of pixels to the bar height */ | ||||||
|  |  | ||||||
| static const char *fonts[] = { | static const char *fonts[] = { | ||||||
| 	"monospace:size=10" | 	"monospace:size=10" | ||||||
| }; | }; | ||||||
| @ -13,6 +16,13 @@ static const char *colors[SchemeLast][2] = { | |||||||
| 	[SchemeSel] = { "#eeeeee", "#005577" }, | 	[SchemeSel] = { "#eeeeee", "#005577" }, | ||||||
| 	[SchemeOut] = { "#000000", "#00ffff" }, | 	[SchemeOut] = { "#000000", "#00ffff" }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | static const unsigned int alphas[SchemeLast][2] = { | ||||||
|  | 	[SchemeNorm] = { OPAQUE, alpha }, | ||||||
|  | 	[SchemeSel] = { OPAQUE, alpha }, | ||||||
|  | 	[SchemeOut] = { OPAQUE, alpha }, | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ | /* -l option; if nonzero, dmenu uses vertical list with given number of lines */ | ||||||
| static unsigned int lines      = 0; | static unsigned int lines      = 0; | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										17
									
								
								config.mk
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								config.mk
									
									
									
									
									
								
							| @ -1,9 +1,9 @@ | |||||||
| # dmenu version | # dmenu version | ||||||
| VERSION = 4.7 | VERSION = 5.2 | ||||||
|  |  | ||||||
| # paths | # paths | ||||||
| PREFIX = /usr/local | PREFIX = /usr/local | ||||||
| MANPREFIX = ${PREFIX}/share/man | MANPREFIX = $(PREFIX)/share/man | ||||||
|  |  | ||||||
| X11INC = /usr/X11R6/include | X11INC = /usr/X11R6/include | ||||||
| X11LIB = /usr/X11R6/lib | X11LIB = /usr/X11R6/lib | ||||||
| @ -16,16 +16,17 @@ XINERAMAFLAGS = -DXINERAMA | |||||||
| FREETYPELIBS = -lfontconfig -lXft | FREETYPELIBS = -lfontconfig -lXft | ||||||
| FREETYPEINC = /usr/include/freetype2 | FREETYPEINC = /usr/include/freetype2 | ||||||
| # OpenBSD (uncomment) | # OpenBSD (uncomment) | ||||||
| #FREETYPEINC = ${X11INC}/freetype2 | #FREETYPEINC = $(X11INC)/freetype2 | ||||||
|  | #MANPREFIX = ${PREFIX}/man | ||||||
|  |  | ||||||
| # includes and libs | # includes and libs | ||||||
| INCS = -I${X11INC} -I${FREETYPEINC} | INCS = -I$(X11INC) -I$(FREETYPEINC) | ||||||
| LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} | LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) | ||||||
|  |  | ||||||
| # flags | # flags | ||||||
| CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} | 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} | CFLAGS   = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS) | ||||||
| LDFLAGS  = -s ${LIBS} | LDFLAGS  = $(LIBS) | ||||||
|  |  | ||||||
| # compiler and linker | # compiler and linker | ||||||
| CC = cc | CC = cc | ||||||
|  | |||||||
							
								
								
									
										68
									
								
								dmenu.1
									
									
									
									
									
								
							
							
						
						
									
										68
									
								
								dmenu.1
									
									
									
									
									
								
							| @ -41,8 +41,8 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. | |||||||
| dmenu appears at the bottom of the screen. | dmenu appears at the bottom of the screen. | ||||||
| .TP | .TP | ||||||
| .B \-f | .B \-f | ||||||
| dmenu grabs the keyboard before reading stdin.  This is faster, but will lock up | dmenu grabs the keyboard before reading stdin if not reading from a tty. This | ||||||
| X until stdin reaches end\-of\-file. | is faster, but will lock up X until stdin reaches end\-of\-file. | ||||||
| .TP | .TP | ||||||
| .B \-i | .B \-i | ||||||
| dmenu matches menu items case insensitively. | dmenu matches menu items case insensitively. | ||||||
| @ -100,82 +100,94 @@ Confirm input.  Prints the input text to stdout and exits, returning success. | |||||||
| .B Escape | .B Escape | ||||||
| Exit without selecting an item, returning failure. | Exit without selecting an item, returning failure. | ||||||
| .TP | .TP | ||||||
| C\-a | .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 | Home | ||||||
| .TP | .TP | ||||||
| C\-b | .B C\-b | ||||||
| Left | Left | ||||||
| .TP | .TP | ||||||
| C\-c | .B C\-c | ||||||
| Escape | Escape | ||||||
| .TP | .TP | ||||||
| C\-d | .B C\-d | ||||||
| Delete | Delete | ||||||
| .TP | .TP | ||||||
| C\-e | .B C\-e | ||||||
| End | End | ||||||
| .TP | .TP | ||||||
| C\-f | .B C\-f | ||||||
| Right | Right | ||||||
| .TP | .TP | ||||||
| C\-g | .B C\-g | ||||||
| Escape | Escape | ||||||
| .TP | .TP | ||||||
| C\-h | .B C\-h | ||||||
| Backspace | Backspace | ||||||
| .TP | .TP | ||||||
| C\-i | .B C\-i | ||||||
| Tab | Tab | ||||||
| .TP | .TP | ||||||
| C\-j | .B C\-j | ||||||
| Return | Return | ||||||
| .TP | .TP | ||||||
| C\-J | .B C\-J | ||||||
| Shift-Return | Shift-Return | ||||||
| .TP | .TP | ||||||
| C\-k | .B C\-k | ||||||
| Delete line right | Delete line right | ||||||
| .TP | .TP | ||||||
| C\-m | .B C\-m | ||||||
| Return | Return | ||||||
| .TP | .TP | ||||||
| C\-M | .B C\-M | ||||||
| Shift-Return | Shift-Return | ||||||
| .TP | .TP | ||||||
| C\-n | .B C\-n | ||||||
| Down | Down | ||||||
| .TP | .TP | ||||||
| C\-p | .B C\-p | ||||||
| Up | Up | ||||||
| .TP | .TP | ||||||
| C\-u | .B C\-u | ||||||
| Delete line left | Delete line left | ||||||
| .TP | .TP | ||||||
| C\-w | .B C\-w | ||||||
| Delete word left | Delete word left | ||||||
| .TP | .TP | ||||||
| C\-y | .B C\-y | ||||||
| Paste from primary X selection | Paste from primary X selection | ||||||
| .TP | .TP | ||||||
| C\-Y | .B C\-Y | ||||||
| Paste from X clipboard | Paste from X clipboard | ||||||
| .TP | .TP | ||||||
| M\-g | .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 | Home | ||||||
| .TP | .TP | ||||||
| M\-G | .B M\-G | ||||||
| End | End | ||||||
| .TP | .TP | ||||||
| M\-h | .B M\-h | ||||||
| Up | Up | ||||||
| .TP | .TP | ||||||
| M\-j | .B M\-j | ||||||
| Page down | Page down | ||||||
| .TP | .TP | ||||||
| M\-k | .B M\-k | ||||||
| Page up | Page up | ||||||
| .TP | .TP | ||||||
| M\-l | .B M\-l | ||||||
| Down | Down | ||||||
| .SH SEE ALSO | .SH SEE ALSO | ||||||
| .IR dwm (1), | .IR dwm (1), | ||||||
|  | |||||||
							
								
								
									
										251
									
								
								dmenu.c
									
									
									
									
									
								
							
							
						
						
									
										251
									
								
								dmenu.c
									
									
									
									
									
								
							| @ -6,9 +6,11 @@ | |||||||
| #include <string.h> | #include <string.h> | ||||||
| #include <strings.h> | #include <strings.h> | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  |  | ||||||
| #include <X11/Xlib.h> | #include <X11/Xlib.h> | ||||||
| #include <X11/Xatom.h> | #include <X11/Xatom.h> | ||||||
|  | #include <X11/Xproto.h> | ||||||
| #include <X11/Xutil.h> | #include <X11/Xutil.h> | ||||||
| #ifdef XINERAMA | #ifdef XINERAMA | ||||||
| #include <X11/extensions/Xinerama.h> | #include <X11/extensions/Xinerama.h> | ||||||
| @ -24,6 +26,8 @@ | |||||||
| #define LENGTH(X)             (sizeof X / sizeof X[0]) | #define LENGTH(X)             (sizeof X / sizeof X[0]) | ||||||
| #define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad) | #define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad) | ||||||
|  |  | ||||||
|  | #define OPAQUE                0xffU | ||||||
|  |  | ||||||
| /* enums */ | /* enums */ | ||||||
| enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ | enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ | ||||||
|  |  | ||||||
| @ -52,10 +56,23 @@ static XIC xic; | |||||||
| static Drw *drw; | static Drw *drw; | ||||||
| static Clr *scheme[SchemeLast]; | static Clr *scheme[SchemeLast]; | ||||||
|  |  | ||||||
|  | static int useargb = 0; | ||||||
|  | static Visual *visual; | ||||||
|  | static int depth; | ||||||
|  | static Colormap cmap; | ||||||
|  |  | ||||||
| #include "config.h" | #include "config.h" | ||||||
|  |  | ||||||
| static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | ||||||
| static char *(*fstrstr)(const char *, const char *) = strstr; | static char *(*fstrstr)(const char *, const char *) = strstr; | ||||||
|  | static void xinitvisual(); | ||||||
|  |  | ||||||
|  | static unsigned int | ||||||
|  | textw_clamp(const char *str, unsigned int n) | ||||||
|  | { | ||||||
|  | 	unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; | ||||||
|  | 	return MIN(w, n); | ||||||
|  | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| appenditem(struct item *item, struct item **list, struct item **last) | appenditem(struct item *item, struct item **list, struct item **last) | ||||||
| @ -81,10 +98,10 @@ calcoffsets(void) | |||||||
| 		n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); | 		n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); | ||||||
| 	/* calculate which items will begin the next page and previous page */ | 	/* calculate which items will begin the next page and previous page */ | ||||||
| 	for (i = 0, next = curr; next; next = next->right) | 	for (i = 0, next = curr; next; next = next->right) | ||||||
| 		if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) | 		if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) | ||||||
| 			break; | 			break; | ||||||
| 	for (i = 0, prev = curr; prev && prev->left; prev = prev->left) | 	for (i = 0, prev = curr; prev && prev->left; prev = prev->left) | ||||||
| 		if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) | 		if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) | ||||||
| 			break; | 			break; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -96,19 +113,29 @@ cleanup(void) | |||||||
| 	XUngrabKey(dpy, AnyKey, AnyModifier, root); | 	XUngrabKey(dpy, AnyKey, AnyModifier, root); | ||||||
| 	for (i = 0; i < SchemeLast; i++) | 	for (i = 0; i < SchemeLast; i++) | ||||||
| 		free(scheme[i]); | 		free(scheme[i]); | ||||||
|  | 	for (i = 0; items && items[i].text; ++i) | ||||||
|  | 		free(items[i].text); | ||||||
|  | 	free(items); | ||||||
| 	drw_free(drw); | 	drw_free(drw); | ||||||
| 	XSync(dpy, False); | 	XSync(dpy, False); | ||||||
| 	XCloseDisplay(dpy); | 	XCloseDisplay(dpy); | ||||||
| } | } | ||||||
|  |  | ||||||
| static char * | static char * | ||||||
| cistrstr(const char *s, const char *sub) | cistrstr(const char *h, const char *n) | ||||||
| { | { | ||||||
| 	size_t len; | 	size_t i; | ||||||
|  |  | ||||||
| 	for (len = strlen(sub); *s; s++) | 	if (!n[0]) | ||||||
| 		if (!strncasecmp(s, sub, len)) | 		return (char *)h; | ||||||
| 			return (char *)s; |  | ||||||
|  | 	for (; *h; ++h) { | ||||||
|  | 		for (i = 0; n[i] && tolower((unsigned char)n[i]) == | ||||||
|  | 		            tolower((unsigned char)h[i]); ++i) | ||||||
|  | 			; | ||||||
|  | 		if (n[i] == '\0') | ||||||
|  | 			return (char *)h; | ||||||
|  | 	} | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -144,7 +171,7 @@ drawmenu(void) | |||||||
| 	drw_setscheme(drw, scheme[SchemeNorm]); | 	drw_setscheme(drw, scheme[SchemeNorm]); | ||||||
| 	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); | 	drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); | ||||||
|  |  | ||||||
| 	drw_font_getexts(drw->fonts, text, cursor, &curpos, NULL); | 	curpos = TEXTW(text) - TEXTW(&text[cursor]); | ||||||
| 	if ((curpos += lrpad / 2 - 1) < w) { | 	if ((curpos += lrpad / 2 - 1) < w) { | ||||||
| 		drw_setscheme(drw, scheme[SchemeNorm]); | 		drw_setscheme(drw, scheme[SchemeNorm]); | ||||||
| 		drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); | 		drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); | ||||||
| @ -164,7 +191,7 @@ drawmenu(void) | |||||||
| 		} | 		} | ||||||
| 		x += w; | 		x += w; | ||||||
| 		for (item = curr; item != next; item = item->right) | 		for (item = curr; item != next; item = item->right) | ||||||
| 			x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); | 			x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); | ||||||
| 		if (next) { | 		if (next) { | ||||||
| 			w = TEXTW(">"); | 			w = TEXTW(">"); | ||||||
| 			drw_setscheme(drw, scheme[SchemeNorm]); | 			drw_setscheme(drw, scheme[SchemeNorm]); | ||||||
| @ -224,7 +251,7 @@ match(void) | |||||||
| 	/* separate input text into tokens to be matched individually */ | 	/* separate input text into tokens to be matched individually */ | ||||||
| 	for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) | 	for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) | ||||||
| 		if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) | 		if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) | ||||||
| 			die("cannot realloc %u bytes:", tokn * sizeof *tokv); | 			die("cannot realloc %zu bytes:", tokn * sizeof *tokv); | ||||||
| 	len = tokc ? strlen(tokv[0]) : 0; | 	len = tokc ? strlen(tokv[0]) : 0; | ||||||
|  |  | ||||||
| 	matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; | 	matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; | ||||||
| @ -287,18 +314,42 @@ nextrune(int inc) | |||||||
| 	return n; | 	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 | static void | ||||||
| keypress(XKeyEvent *ev) | keypress(XKeyEvent *ev) | ||||||
| { | { | ||||||
| 	char buf[32]; | 	char buf[64]; | ||||||
| 	int len; | 	int len; | ||||||
| 	KeySym ksym = NoSymbol; | 	KeySym ksym = NoSymbol; | ||||||
| 	Status status; | 	Status status; | ||||||
|  |  | ||||||
| 	len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); | 	len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); | ||||||
| 	if (status == XBufferOverflow) | 	switch (status) { | ||||||
|  | 	default: /* XLookupNone, XBufferOverflow */ | ||||||
| 		return; | 		return; | ||||||
| 	if (ev->state & ControlMask) | 	case XLookupChars: /* composed string from input method */ | ||||||
|  | 		goto insert; | ||||||
|  | 	case XLookupKeySym: | ||||||
|  | 	case XLookupBoth: /* a KeySym and a string are returned: use keysym */ | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (ev->state & ControlMask) { | ||||||
| 		switch(ksym) { | 		switch(ksym) { | ||||||
| 		case XK_a: ksym = XK_Home;      break; | 		case XK_a: ksym = XK_Home;      break; | ||||||
| 		case XK_b: ksym = XK_Left;      break; | 		case XK_b: ksym = XK_Left;      break; | ||||||
| @ -334,6 +385,14 @@ keypress(XKeyEvent *ev) | |||||||
| 			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, | 			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, | ||||||
| 			                  utf8, utf8, win, CurrentTime); | 			                  utf8, utf8, win, CurrentTime); | ||||||
| 			return; | 			return; | ||||||
|  | 		case XK_Left: | ||||||
|  | 		case XK_KP_Left: | ||||||
|  | 			movewordedge(-1); | ||||||
|  | 			goto draw; | ||||||
|  | 		case XK_Right: | ||||||
|  | 		case XK_KP_Right: | ||||||
|  | 			movewordedge(+1); | ||||||
|  | 			goto draw; | ||||||
| 		case XK_Return: | 		case XK_Return: | ||||||
| 		case XK_KP_Enter: | 		case XK_KP_Enter: | ||||||
| 			break; | 			break; | ||||||
| @ -343,8 +402,14 @@ keypress(XKeyEvent *ev) | |||||||
| 		default: | 		default: | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
| 	else if (ev->state & Mod1Mask) | 	} else if (ev->state & Mod1Mask) { | ||||||
| 		switch(ksym) { | 		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_Home;  break; | ||||||
| 		case XK_G: ksym = XK_End;   break; | 		case XK_G: ksym = XK_End;   break; | ||||||
| 		case XK_h: ksym = XK_Up;    break; | 		case XK_h: ksym = XK_Up;    break; | ||||||
| @ -354,12 +419,16 @@ keypress(XKeyEvent *ev) | |||||||
| 		default: | 		default: | ||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	switch(ksym) { | 	switch(ksym) { | ||||||
| 	default: | 	default: | ||||||
| 		if (!iscntrl(*buf)) | insert: | ||||||
|  | 		if (!iscntrl((unsigned char)*buf)) | ||||||
| 			insert(buf, len); | 			insert(buf, len); | ||||||
| 		break; | 		break; | ||||||
| 	case XK_Delete: | 	case XK_Delete: | ||||||
|  | 	case XK_KP_Delete: | ||||||
| 		if (text[cursor] == '\0') | 		if (text[cursor] == '\0') | ||||||
| 			return; | 			return; | ||||||
| 		cursor = nextrune(+1); | 		cursor = nextrune(+1); | ||||||
| @ -370,6 +439,7 @@ keypress(XKeyEvent *ev) | |||||||
| 		insert(NULL, nextrune(-1) - cursor); | 		insert(NULL, nextrune(-1) - cursor); | ||||||
| 		break; | 		break; | ||||||
| 	case XK_End: | 	case XK_End: | ||||||
|  | 	case XK_KP_End: | ||||||
| 		if (text[cursor] != '\0') { | 		if (text[cursor] != '\0') { | ||||||
| 			cursor = strlen(text); | 			cursor = strlen(text); | ||||||
| 			break; | 			break; | ||||||
| @ -389,6 +459,7 @@ keypress(XKeyEvent *ev) | |||||||
| 		cleanup(); | 		cleanup(); | ||||||
| 		exit(1); | 		exit(1); | ||||||
| 	case XK_Home: | 	case XK_Home: | ||||||
|  | 	case XK_KP_Home: | ||||||
| 		if (sel == matches) { | 		if (sel == matches) { | ||||||
| 			cursor = 0; | 			cursor = 0; | ||||||
| 			break; | 			break; | ||||||
| @ -397,6 +468,7 @@ keypress(XKeyEvent *ev) | |||||||
| 		calcoffsets(); | 		calcoffsets(); | ||||||
| 		break; | 		break; | ||||||
| 	case XK_Left: | 	case XK_Left: | ||||||
|  | 	case XK_KP_Left: | ||||||
| 		if (cursor > 0 && (!sel || !sel->left || lines > 0)) { | 		if (cursor > 0 && (!sel || !sel->left || lines > 0)) { | ||||||
| 			cursor = nextrune(-1); | 			cursor = nextrune(-1); | ||||||
| 			break; | 			break; | ||||||
| @ -405,18 +477,21 @@ keypress(XKeyEvent *ev) | |||||||
| 			return; | 			return; | ||||||
| 		/* fallthrough */ | 		/* fallthrough */ | ||||||
| 	case XK_Up: | 	case XK_Up: | ||||||
|  | 	case XK_KP_Up: | ||||||
| 		if (sel && sel->left && (sel = sel->left)->right == curr) { | 		if (sel && sel->left && (sel = sel->left)->right == curr) { | ||||||
| 			curr = prev; | 			curr = prev; | ||||||
| 			calcoffsets(); | 			calcoffsets(); | ||||||
| 		} | 		} | ||||||
| 		break; | 		break; | ||||||
| 	case XK_Next: | 	case XK_Next: | ||||||
|  | 	case XK_KP_Next: | ||||||
| 		if (!next) | 		if (!next) | ||||||
| 			return; | 			return; | ||||||
| 		sel = curr = next; | 		sel = curr = next; | ||||||
| 		calcoffsets(); | 		calcoffsets(); | ||||||
| 		break; | 		break; | ||||||
| 	case XK_Prior: | 	case XK_Prior: | ||||||
|  | 	case XK_KP_Prior: | ||||||
| 		if (!prev) | 		if (!prev) | ||||||
| 			return; | 			return; | ||||||
| 		sel = curr = prev; | 		sel = curr = prev; | ||||||
| @ -433,6 +508,7 @@ keypress(XKeyEvent *ev) | |||||||
| 			sel->out = 1; | 			sel->out = 1; | ||||||
| 		break; | 		break; | ||||||
| 	case XK_Right: | 	case XK_Right: | ||||||
|  | 	case XK_KP_Right: | ||||||
| 		if (text[cursor] != '\0') { | 		if (text[cursor] != '\0') { | ||||||
| 			cursor = nextrune(+1); | 			cursor = nextrune(+1); | ||||||
| 			break; | 			break; | ||||||
| @ -441,6 +517,7 @@ keypress(XKeyEvent *ev) | |||||||
| 			return; | 			return; | ||||||
| 		/* fallthrough */ | 		/* fallthrough */ | ||||||
| 	case XK_Down: | 	case XK_Down: | ||||||
|  | 	case XK_KP_Down: | ||||||
| 		if (sel && sel->right && (sel = sel->right) == next) { | 		if (sel && sel->right && (sel = sel->right) == next) { | ||||||
| 			curr = next; | 			curr = next; | ||||||
| 			calcoffsets(); | 			calcoffsets(); | ||||||
| @ -449,12 +526,14 @@ keypress(XKeyEvent *ev) | |||||||
| 	case XK_Tab: | 	case XK_Tab: | ||||||
| 		if (!sel) | 		if (!sel) | ||||||
| 			return; | 			return; | ||||||
| 		strncpy(text, sel->text, sizeof text - 1); | 		cursor = strnlen(sel->text, sizeof text - 1); | ||||||
| 		text[sizeof text - 1] = '\0'; | 		memcpy(text, sel->text, cursor); | ||||||
| 		cursor = strlen(text); | 		text[cursor] = '\0'; | ||||||
| 		match(); | 		match(); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | draw: | ||||||
| 	drawmenu(); | 	drawmenu(); | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -467,39 +546,39 @@ paste(void) | |||||||
| 	Atom da; | 	Atom da; | ||||||
|  |  | ||||||
| 	/* we have been given the current selection, now insert it into input */ | 	/* we have been given the current selection, now insert it into input */ | ||||||
| 	XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, | 	if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, | ||||||
| 	                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | 	                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p) | ||||||
|  | 	    == Success && p) { | ||||||
| 		insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); | 		insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); | ||||||
| 		XFree(p); | 		XFree(p); | ||||||
|  | 	} | ||||||
| 	drawmenu(); | 	drawmenu(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| readstdin(void) | readstdin(void) | ||||||
| { | { | ||||||
| 	char buf[sizeof text], *p; | 	char *line = NULL; | ||||||
| 	size_t i, imax = 0, size = 0; | 	size_t i, itemsiz = 0, linesiz = 0; | ||||||
| 	unsigned int tmpmax = 0; | 	ssize_t len; | ||||||
|  |  | ||||||
| 	/* read each line from stdin and add it to the item list */ | 	/* read each line from stdin and add it to the item list */ | ||||||
| 	for (i = 0; fgets(buf, sizeof buf, stdin); i++) { | 	for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { | ||||||
| 		if (i + 1 >= size / sizeof *items) | 		if (i + 1 >= itemsiz) { | ||||||
| 			if (!(items = realloc(items, (size += BUFSIZ)))) | 			itemsiz += 256; | ||||||
| 				die("cannot realloc %u bytes:", size); | 			if (!(items = realloc(items, itemsiz * sizeof(*items)))) | ||||||
| 		if ((p = strchr(buf, '\n'))) | 				die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); | ||||||
| 			*p = '\0'; | 		} | ||||||
| 		if (!(items[i].text = strdup(buf))) | 		if (line[len - 1] == '\n') | ||||||
| 			die("cannot strdup %u bytes:", strlen(buf) + 1); | 			line[len - 1] = '\0'; | ||||||
|  | 		if (!(items[i].text = strdup(line))) | ||||||
|  | 			die("strdup:"); | ||||||
|  |  | ||||||
| 		items[i].out = 0; | 		items[i].out = 0; | ||||||
| 		drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); |  | ||||||
| 		if (tmpmax > inputw) { |  | ||||||
| 			inputw = tmpmax; |  | ||||||
| 			imax = i; |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  | 	free(line); | ||||||
| 	if (items) | 	if (items) | ||||||
| 		items[i].text = NULL; | 		items[i].text = NULL; | ||||||
| 	inputw = items ? TEXTW(items[imax].text) : 0; |  | ||||||
| 	lines = MIN(lines, i); | 	lines = MIN(lines, i); | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -512,6 +591,11 @@ run(void) | |||||||
| 		if (XFilterEvent(&ev, win)) | 		if (XFilterEvent(&ev, win)) | ||||||
| 			continue; | 			continue; | ||||||
| 		switch(ev.type) { | 		switch(ev.type) { | ||||||
|  | 		case DestroyNotify: | ||||||
|  | 			if (ev.xdestroywindow.window != win) | ||||||
|  | 				break; | ||||||
|  | 			cleanup(); | ||||||
|  | 			exit(1); | ||||||
| 		case Expose: | 		case Expose: | ||||||
| 			if (ev.xexpose.count == 0) | 			if (ev.xexpose.count == 0) | ||||||
| 				drw_map(drw, win, 0, 0, mw, mh); | 				drw_map(drw, win, 0, 0, mw, mh); | ||||||
| @ -539,31 +623,32 @@ run(void) | |||||||
| static void | static void | ||||||
| setup(void) | setup(void) | ||||||
| { | { | ||||||
| 	int x, y, i = 0; | 	int x, y, i, j; | ||||||
| 	unsigned int du; | 	unsigned int du; | ||||||
| 	XSetWindowAttributes swa; | 	XSetWindowAttributes swa; | ||||||
| 	XIM xim; | 	XIM xim; | ||||||
| 	Window w, dw, *dws; | 	Window w, dw, *dws; | ||||||
| 	XWindowAttributes wa; | 	XWindowAttributes wa; | ||||||
|  | 	XClassHint ch = {"dmenu", "dmenu"}; | ||||||
| #ifdef XINERAMA | #ifdef XINERAMA | ||||||
| 	XineramaScreenInfo *info; | 	XineramaScreenInfo *info; | ||||||
| 	Window pw; | 	Window pw; | ||||||
| 	int a, j, di, n, area = 0; | 	int a, di, n, area = 0; | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 	/* init appearance */ | 	/* init appearance */ | ||||||
| 	scheme[SchemeNorm] = drw_scm_create(drw, colors[SchemeNorm], 2); | 	for (j = 0; j < SchemeLast; j++) | ||||||
| 	scheme[SchemeSel] = drw_scm_create(drw, colors[SchemeSel], 2); | 		scheme[j] = drw_scm_create(drw, colors[j], alphas[i], 2); | ||||||
| 	scheme[SchemeOut] = drw_scm_create(drw, colors[SchemeOut], 2); |  | ||||||
|  |  | ||||||
| 	clip = XInternAtom(dpy, "CLIPBOARD",   False); | 	clip = XInternAtom(dpy, "CLIPBOARD",   False); | ||||||
| 	utf8 = XInternAtom(dpy, "UTF8_STRING", False); | 	utf8 = XInternAtom(dpy, "UTF8_STRING", False); | ||||||
|  |  | ||||||
| 	/* calculate menu geometry */ | 	/* calculate menu geometry */ | ||||||
| 	bh = drw->fonts->h + 2; | 	bh = drw->fonts->h; | ||||||
|  | 	bh = user_bh ? bh + user_bh : bh + 2; | ||||||
| 	lines = MAX(lines, 0); | 	lines = MAX(lines, 0); | ||||||
| 	mh = (lines + 1) * bh; | 	mh = (lines + 1) * bh; | ||||||
| #ifdef XINERAMA | #ifdef XINERAMA | ||||||
|  | 	i = 0; | ||||||
| 	if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { | 	if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { | ||||||
| 		XGetInputFocus(dpy, &w, &di); | 		XGetInputFocus(dpy, &w, &di); | ||||||
| 		if (mon >= 0 && mon < n) | 		if (mon >= 0 && mon < n) | ||||||
| @ -585,12 +670,13 @@ setup(void) | |||||||
| 		/* no focused window is on screen, so use pointer location instead */ | 		/* 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)) | 		if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) | ||||||
| 			for (i = 0; i < n; i++) | 			for (i = 0; i < n; i++) | ||||||
| 				if (INTERSECT(x, y, 1, 1, info[i])) | 				if (INTERSECT(x, y, 1, 1, info[i]) != 0) | ||||||
| 					break; | 					break; | ||||||
|  |  | ||||||
| 		x = info[i].x_org; | 		x = info[i].x_org; | ||||||
| 		y = info[i].y_org + (topbar ? 0 : info[i].height - mh); | 		y = info[i].y_org + (topbar ? 0 : info[i].height - mh); | ||||||
| 		mw = info[i].width; | 		mw = info[i].width; | ||||||
|  |  | ||||||
| 		XFree(info); | 		XFree(info); | ||||||
| 	} else | 	} else | ||||||
| #endif | #endif | ||||||
| @ -603,25 +689,32 @@ setup(void) | |||||||
| 		mw = wa.width; | 		mw = wa.width; | ||||||
| 	} | 	} | ||||||
| 	promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; | 	promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; | ||||||
| 	inputw = MIN(inputw, mw/3); | 	inputw = mw / 3; /* input width: ~33% of monitor width */ | ||||||
| 	match(); | 	match(); | ||||||
|  |  | ||||||
| 	/* create menu window */ | 	/* create menu window */ | ||||||
| 	swa.override_redirect = True; | 	swa.override_redirect = True; | ||||||
| 	swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; | 	swa.background_pixel = 0; | ||||||
|  | 	swa.border_pixel = 0; | ||||||
|  | 	swa.colormap = cmap; | ||||||
| 	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | 	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, | 	                    depth, CopyFromParent, visual, | ||||||
| 	                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); | 	                    CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa); | ||||||
|  | 	XSetClassHint(dpy, win, &ch); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	/* input methods */ | ||||||
|  | 	if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) | ||||||
|  | 		die("XOpenIM failed: could not open input device"); | ||||||
|  |  | ||||||
| 	/* open input methods */ |  | ||||||
| 	xim = XOpenIM(dpy, NULL, NULL, NULL); |  | ||||||
| 	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, | 	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, | ||||||
| 	                XNClientWindow, win, XNFocusWindow, win, NULL); | 	                XNClientWindow, win, XNFocusWindow, win, NULL); | ||||||
|  |  | ||||||
| 	XMapRaised(dpy, win); | 	XMapRaised(dpy, win); | ||||||
| 	if (embed) { | 	if (embed) { | ||||||
| 		XSelectInput(dpy, parentwin, FocusChangeMask); | 		XReparentWindow(dpy, win, parentwin, x, y); | ||||||
|  | 		XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); | ||||||
| 		if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { | 		if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { | ||||||
| 			for (i = 0; i < du && dws[i] != win; ++i) | 			for (i = 0; i < du && dws[i] != win; ++i) | ||||||
| 				XSelectInput(dpy, dws[i], FocusChangeMask); | 				XSelectInput(dpy, dws[i], FocusChangeMask); | ||||||
| @ -636,9 +729,8 @@ setup(void) | |||||||
| static void | static void | ||||||
| usage(void) | usage(void) | ||||||
| { | { | ||||||
| 	fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" | 	die("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] [-w windowid]"); | ||||||
| 	exit(1); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| int | int | ||||||
| @ -694,12 +786,18 @@ main(int argc, char *argv[]) | |||||||
| 	if (!XGetWindowAttributes(dpy, parentwin, &wa)) | 	if (!XGetWindowAttributes(dpy, parentwin, &wa)) | ||||||
| 		die("could not get embedding window attributes: 0x%lx", | 		die("could not get embedding window attributes: 0x%lx", | ||||||
| 		    parentwin); | 		    parentwin); | ||||||
| 	drw = drw_create(dpy, screen, root, wa.width, wa.height); | 	xinitvisual(); | ||||||
|  | 	drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); | ||||||
| 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) | 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) | ||||||
| 		die("no fonts could be loaded."); | 		die("no fonts could be loaded."); | ||||||
| 	lrpad = drw->fonts->h; | 	lrpad = drw->fonts->h; | ||||||
|  |  | ||||||
| 	if (fast) { | #ifdef __OpenBSD__ | ||||||
|  | 	if (pledge("stdio rpath", NULL) == -1) | ||||||
|  | 		die("pledge"); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (fast && !isatty(0)) { | ||||||
| 		grabkeyboard(); | 		grabkeyboard(); | ||||||
| 		readstdin(); | 		readstdin(); | ||||||
| 	} else { | 	} else { | ||||||
| @ -711,3 +809,40 @@ main(int argc, char *argv[]) | |||||||
|  |  | ||||||
| 	return 1; /* unreachable */ | 	return 1; /* unreachable */ | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  void | ||||||
|  | xinitvisual() | ||||||
|  | { | ||||||
|  | 	XVisualInfo *infos; | ||||||
|  | 	XRenderPictFormat *fmt; | ||||||
|  | 	int nitems; | ||||||
|  | 	int i; | ||||||
|  |  | ||||||
|  | 	XVisualInfo tpl = { | ||||||
|  | 		.screen = screen, | ||||||
|  | 		.depth = 32, | ||||||
|  | 		.class = TrueColor | ||||||
|  | 	}; | ||||||
|  | 	long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; | ||||||
|  |  | ||||||
|  | 	infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); | ||||||
|  | 	visual = NULL; | ||||||
|  | 	for(i = 0; i < nitems; i ++) { | ||||||
|  | 		fmt = XRenderFindVisualFormat(dpy, infos[i].visual); | ||||||
|  | 		if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { | ||||||
|  | 			visual = infos[i].visual; | ||||||
|  | 			depth = infos[i].depth; | ||||||
|  | 			cmap = XCreateColormap(dpy, root, visual, AllocNone); | ||||||
|  | 			useargb = 1; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	XFree(infos); | ||||||
|  |  | ||||||
|  | 	if (! visual) { | ||||||
|  | 		visual = DefaultVisual(dpy, screen); | ||||||
|  | 		depth = DefaultDepth(dpy, screen); | ||||||
|  | 		cmap = DefaultColormap(dpy, screen); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										12
									
								
								dmenu_path
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							
							
						
						
									
										12
									
								
								dmenu_path
									
									
									
									
									
										
										
										Normal file → Executable file
									
								
							| @ -1,10 +1,10 @@ | |||||||
| #!/bin/sh | #!/bin/sh | ||||||
| cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"} |  | ||||||
| if [ -d "$cachedir" ]; then | cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}" | ||||||
| 	cache=$cachedir/dmenu_run | cache="$cachedir/dmenu_run" | ||||||
| else |  | ||||||
| 	cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~ | [ ! -e "$cachedir" ] && mkdir -p "$cachedir" | ||||||
| fi |  | ||||||
| IFS=: | IFS=: | ||||||
| if stest -dqr -n "$cache" $PATH; then | if stest -dqr -n "$cache" $PATH; then | ||||||
| 	stest -flx $PATH | sort -u | tee "$cache" | 	stest -flx $PATH | sort -u | tee "$cache" | ||||||
|  | |||||||
							
								
								
									
										105
									
								
								drw.c
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								drw.c
									
									
									
									
									
								
							| @ -61,7 +61,7 @@ utf8decode(const char *c, long *u, size_t clen) | |||||||
| } | } | ||||||
|  |  | ||||||
| Drw * | Drw * | ||||||
| drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h) | drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h, Visual *visual, unsigned int depth, Colormap cmap) | ||||||
| { | { | ||||||
| 	Drw *drw = ecalloc(1, sizeof(Drw)); | 	Drw *drw = ecalloc(1, sizeof(Drw)); | ||||||
|  |  | ||||||
| @ -70,8 +70,11 @@ drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h | |||||||
| 	drw->root = root; | 	drw->root = root; | ||||||
| 	drw->w = w; | 	drw->w = w; | ||||||
| 	drw->h = h; | 	drw->h = h; | ||||||
| 	drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen)); | 	drw->visual = visual; | ||||||
| 	drw->gc = XCreateGC(dpy, root, 0, NULL); | 	drw->depth = depth; | ||||||
|  | 	drw->cmap = cmap; | ||||||
|  | 	drw->drawable = XCreatePixmap(dpy, root, w, h, depth); | ||||||
|  | 	drw->gc = XCreateGC(dpy, drw->drawable, 0, NULL); | ||||||
| 	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | 	XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter); | ||||||
|  |  | ||||||
| 	return drw; | 	return drw; | ||||||
| @ -87,7 +90,7 @@ drw_resize(Drw *drw, unsigned int w, unsigned int h) | |||||||
| 	drw->h = h; | 	drw->h = h; | ||||||
| 	if (drw->drawable) | 	if (drw->drawable) | ||||||
| 		XFreePixmap(drw->dpy, drw->drawable); | 		XFreePixmap(drw->dpy, drw->drawable); | ||||||
| 	drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen)); | 	drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, drw->depth); | ||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| @ -95,6 +98,7 @@ drw_free(Drw *drw) | |||||||
| { | { | ||||||
| 	XFreePixmap(drw->dpy, drw->drawable); | 	XFreePixmap(drw->dpy, drw->drawable); | ||||||
| 	XFreeGC(drw->dpy, drw->gc); | 	XFreeGC(drw->dpy, drw->gc); | ||||||
|  | 	drw_fontset_free(drw->fonts); | ||||||
| 	free(drw); | 	free(drw); | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -180,21 +184,22 @@ drw_fontset_free(Fnt *font) | |||||||
| } | } | ||||||
|  |  | ||||||
| void | void | ||||||
| drw_clr_create(Drw *drw, Clr *dest, const char *clrname) | drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha) | ||||||
| { | { | ||||||
| 	if (!drw || !dest || !clrname) | 	if (!drw || !dest || !clrname) | ||||||
| 		return; | 		return; | ||||||
|  |  | ||||||
| 	if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen), | 	if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap, | ||||||
| 	                       DefaultColormap(drw->dpy, drw->screen), |  | ||||||
| 	                       clrname, dest)) | 	                       clrname, dest)) | ||||||
| 		die("error, cannot allocate color '%s'", clrname); | 		die("error, cannot allocate color '%s'", clrname); | ||||||
|  |  | ||||||
|  | 	dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24); | ||||||
| } | } | ||||||
|  |  | ||||||
| /* Wrapper to create color schemes. The caller has to call free(3) on the | /* Wrapper to create color schemes. The caller has to call free(3) on the | ||||||
|  * returned color scheme when done using it. */ |  * returned color scheme when done using it. */ | ||||||
| Clr * | Clr * | ||||||
| drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) | drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount) | ||||||
| { | { | ||||||
| 	size_t i; | 	size_t i; | ||||||
| 	Clr *ret; | 	Clr *ret; | ||||||
| @ -204,7 +209,7 @@ drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount) | |||||||
| 		return NULL; | 		return NULL; | ||||||
|  |  | ||||||
| 	for (i = 0; i < clrcount; i++) | 	for (i = 0; i < clrcount; i++) | ||||||
| 		drw_clr_create(drw, &ret[i], clrnames[i]); | 		drw_clr_create(drw, &ret[i], clrnames[i], alphas[i]); | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -237,12 +242,10 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int | |||||||
| int | int | ||||||
| drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert) | 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 i, ty, ellipsis_x = 0; | ||||||
| 	int ty; | 	unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len; | ||||||
| 	unsigned int ew; |  | ||||||
| 	XftDraw *d = NULL; | 	XftDraw *d = NULL; | ||||||
| 	Fnt *usedfont, *curfont, *nextfont; | 	Fnt *usedfont, *curfont, *nextfont; | ||||||
| 	size_t i, len; |  | ||||||
| 	int utf8strlen, utf8charlen, render = x || y || w || h; | 	int utf8strlen, utf8charlen, render = x || y || w || h; | ||||||
| 	long utf8codepoint = 0; | 	long utf8codepoint = 0; | ||||||
| 	const char *utf8str; | 	const char *utf8str; | ||||||
| @ -250,26 +253,30 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp | |||||||
| 	FcPattern *fcpattern; | 	FcPattern *fcpattern; | ||||||
| 	FcPattern *match; | 	FcPattern *match; | ||||||
| 	XftResult result; | 	XftResult result; | ||||||
| 	int charexists = 0; | 	int charexists = 0, overflow = 0; | ||||||
|  | 	/* keep track of a couple codepoints for which we have no match. */ | ||||||
|  | 	enum { nomatches_len = 64 }; | ||||||
|  | 	static struct { long codepoint[nomatches_len]; unsigned int idx; } nomatches; | ||||||
|  | 	static unsigned int ellipsis_width = 0; | ||||||
|  |  | ||||||
| 	if (!drw || (render && !drw->scheme) || !text || !drw->fonts) | 	if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts) | ||||||
| 		return 0; | 		return 0; | ||||||
|  |  | ||||||
| 	if (!render) { | 	if (!render) { | ||||||
| 		w = ~w; | 		w = invert ? invert : ~invert; | ||||||
| 	} else { | 	} else { | ||||||
| 		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); | 		XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel); | ||||||
| 		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | 		XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h); | ||||||
| 		d = XftDrawCreate(drw->dpy, drw->drawable, | 		d = XftDrawCreate(drw->dpy, drw->drawable, drw->visual, drw->cmap); | ||||||
| 		                  DefaultVisual(drw->dpy, drw->screen), |  | ||||||
| 		                  DefaultColormap(drw->dpy, drw->screen)); |  | ||||||
| 		x += lpad; | 		x += lpad; | ||||||
| 		w -= lpad; | 		w -= lpad; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	usedfont = drw->fonts; | 	usedfont = drw->fonts; | ||||||
|  | 	if (!ellipsis_width && render) | ||||||
|  | 		ellipsis_width = drw_fontset_getwidth(drw, "..."); | ||||||
| 	while (1) { | 	while (1) { | ||||||
| 		utf8strlen = 0; | 		ew = ellipsis_len = utf8strlen = 0; | ||||||
| 		utf8str = text; | 		utf8str = text; | ||||||
| 		nextfont = NULL; | 		nextfont = NULL; | ||||||
| 		while (*text) { | 		while (*text) { | ||||||
| @ -277,9 +284,27 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp | |||||||
| 			for (curfont = drw->fonts; curfont; curfont = curfont->next) { | 			for (curfont = drw->fonts; curfont; curfont = curfont->next) { | ||||||
| 				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); | 				charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint); | ||||||
| 				if (charexists) { | 				if (charexists) { | ||||||
| 					if (curfont == usedfont) { | 					drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL); | ||||||
|  | 					if (ew + ellipsis_width <= w) { | ||||||
|  | 						/* keep track where the ellipsis still fits */ | ||||||
|  | 						ellipsis_x = x + ew; | ||||||
|  | 						ellipsis_w = w - ew; | ||||||
|  | 						ellipsis_len = utf8strlen; | ||||||
|  | 					} | ||||||
|  |  | ||||||
|  | 					if (ew + tmpw > w) { | ||||||
|  | 						overflow = 1; | ||||||
|  | 						/* called from drw_fontset_getwidth_clamp(): | ||||||
|  | 						 * it wants the width AFTER the overflow | ||||||
|  | 						 */ | ||||||
|  | 						if (!render) | ||||||
|  | 							x += tmpw; | ||||||
|  | 						else | ||||||
|  | 							utf8strlen = ellipsis_len; | ||||||
|  | 					} else if (curfont == usedfont) { | ||||||
| 						utf8strlen += utf8charlen; | 						utf8strlen += utf8charlen; | ||||||
| 						text += utf8charlen; | 						text += utf8charlen; | ||||||
|  | 						ew += tmpw; | ||||||
| 					} else { | 					} else { | ||||||
| 						nextfont = curfont; | 						nextfont = curfont; | ||||||
| 					} | 					} | ||||||
| @ -287,36 +312,25 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			if (!charexists || nextfont) | 			if (overflow || !charexists || nextfont) | ||||||
| 				break; | 				break; | ||||||
| 			else | 			else | ||||||
| 				charexists = 0; | 				charexists = 0; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if (utf8strlen) { | 		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) { | 			if (render) { | ||||||
| 				ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; | 				ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; | ||||||
| 				XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], | 				XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], | ||||||
| 					                  usedfont->xfont, x, ty, (XftChar8 *)buf, len); | 				                  usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen); | ||||||
| 			} | 			} | ||||||
| 			x += ew; | 			x += ew; | ||||||
| 			w -= ew; | 			w -= ew; | ||||||
| 		} | 		} | ||||||
| 		} | 		if (render && overflow) | ||||||
|  | 			drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert); | ||||||
|  |  | ||||||
| 		if (!*text) { | 		if (!*text || overflow) { | ||||||
| 			break; | 			break; | ||||||
| 		} else if (nextfont) { | 		} else if (nextfont) { | ||||||
| 			charexists = 0; | 			charexists = 0; | ||||||
| @ -326,6 +340,12 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp | |||||||
| 			 * character must be drawn. */ | 			 * character must be drawn. */ | ||||||
| 			charexists = 1; | 			charexists = 1; | ||||||
|  |  | ||||||
|  | 			for (i = 0; i < nomatches_len; ++i) { | ||||||
|  | 				/* avoid calling XftFontMatch if we know we won't find a match */ | ||||||
|  | 				if (utf8codepoint == nomatches.codepoint[i]) | ||||||
|  | 					goto no_match; | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			fccharset = FcCharSetCreate(); | 			fccharset = FcCharSetCreate(); | ||||||
| 			FcCharSetAddChar(fccharset, utf8codepoint); | 			FcCharSetAddChar(fccharset, utf8codepoint); | ||||||
|  |  | ||||||
| @ -353,6 +373,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp | |||||||
| 					curfont->next = usedfont; | 					curfont->next = usedfont; | ||||||
| 				} else { | 				} else { | ||||||
| 					xfont_free(usedfont); | 					xfont_free(usedfont); | ||||||
|  | 					nomatches.codepoint[++nomatches.idx % nomatches_len] = utf8codepoint; | ||||||
|  | no_match: | ||||||
| 					usedfont = drw->fonts; | 					usedfont = drw->fonts; | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| @ -382,6 +404,15 @@ drw_fontset_getwidth(Drw *drw, const char *text) | |||||||
| 	return drw_text(drw, 0, 0, 0, 0, 0, text, 0); | 	return drw_text(drw, 0, 0, 0, 0, 0, text, 0); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | unsigned int | ||||||
|  | drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n) | ||||||
|  | { | ||||||
|  | 	unsigned int tmp = 0; | ||||||
|  | 	if (drw && drw->fonts && text && n) | ||||||
|  | 		tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n); | ||||||
|  | 	return MIN(n, tmp); | ||||||
|  | } | ||||||
|  |  | ||||||
| void | void | ||||||
| drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) | drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h) | ||||||
| { | { | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								drw.h
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								drw.h
									
									
									
									
									
								
							| @ -20,6 +20,9 @@ typedef struct { | |||||||
| 	Display *dpy; | 	Display *dpy; | ||||||
| 	int screen; | 	int screen; | ||||||
| 	Window root; | 	Window root; | ||||||
|  | 	Visual *visual; | ||||||
|  | 	unsigned int depth; | ||||||
|  | 	Colormap cmap; | ||||||
| 	Drawable drawable; | 	Drawable drawable; | ||||||
| 	GC gc; | 	GC gc; | ||||||
| 	Clr *scheme; | 	Clr *scheme; | ||||||
| @ -27,7 +30,7 @@ typedef struct { | |||||||
| } Drw; | } Drw; | ||||||
|  |  | ||||||
| /* Drawable abstraction */ | /* Drawable abstraction */ | ||||||
| Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h); | Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h, Visual*, unsigned int, Colormap); | ||||||
| void drw_resize(Drw *drw, unsigned int w, unsigned int h); | void drw_resize(Drw *drw, unsigned int w, unsigned int h); | ||||||
| void drw_free(Drw *drw); | void drw_free(Drw *drw); | ||||||
|  |  | ||||||
| @ -35,11 +38,12 @@ void drw_free(Drw *drw); | |||||||
| Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); | Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount); | ||||||
| void drw_fontset_free(Fnt* set); | void drw_fontset_free(Fnt* set); | ||||||
| unsigned int drw_fontset_getwidth(Drw *drw, const char *text); | unsigned int drw_fontset_getwidth(Drw *drw, const char *text); | ||||||
|  | unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n); | ||||||
| void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); | void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); | ||||||
|  |  | ||||||
| /* Colorscheme abstraction */ | /* Colorscheme abstraction */ | ||||||
| void drw_clr_create(Drw *drw, Clr *dest, const char *clrname); | void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); | ||||||
| Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount); | Clr *drw_scm_create(Drw *drw, const char *clrnames[], const unsigned int alphas[], size_t clrcount); | ||||||
|  |  | ||||||
| /* Cursor abstraction */ | /* Cursor abstraction */ | ||||||
| Cur *drw_cur_create(Drw *drw, int shape); | Cur *drw_cur_create(Drw *drw, int shape); | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								stest.c
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								stest.c
									
									
									
									
									
								
							| @ -84,7 +84,7 @@ main(int argc, char *argv[]) | |||||||
| 	if (!argc) { | 	if (!argc) { | ||||||
| 		/* read list from stdin */ | 		/* read list from stdin */ | ||||||
| 		while ((n = getline(&line, &linesiz, stdin)) > 0) { | 		while ((n = getline(&line, &linesiz, stdin)) > 0) { | ||||||
| 			if (n && line[n - 1] == '\n') | 			if (line[n - 1] == '\n') | ||||||
| 				line[n - 1] = '\0'; | 				line[n - 1] = '\0'; | ||||||
| 			test(line, line); | 			test(line, line); | ||||||
| 		} | 		} | ||||||
|  | |||||||
							
								
								
									
										23
									
								
								util.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								util.c
									
									
									
									
									
								
							| @ -6,18 +6,9 @@ | |||||||
|  |  | ||||||
| #include "util.h" | #include "util.h" | ||||||
|  |  | ||||||
| void * |  | ||||||
| ecalloc(size_t nmemb, size_t size) |  | ||||||
| { |  | ||||||
| 	void *p; |  | ||||||
|  |  | ||||||
| 	if (!(p = calloc(nmemb, size))) |  | ||||||
| 		die("calloc:"); |  | ||||||
| 	return p; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void | void | ||||||
| die(const char *fmt, ...) { | die(const char *fmt, ...) | ||||||
|  | { | ||||||
| 	va_list ap; | 	va_list ap; | ||||||
|  |  | ||||||
| 	va_start(ap, fmt); | 	va_start(ap, fmt); | ||||||
| @ -33,3 +24,13 @@ die(const char *fmt, ...) { | |||||||
|  |  | ||||||
| 	exit(1); | 	exit(1); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void * | ||||||
|  | ecalloc(size_t nmemb, size_t size) | ||||||
|  | { | ||||||
|  | 	void *p; | ||||||
|  |  | ||||||
|  | 	if (!(p = calloc(nmemb, size))) | ||||||
|  | 		die("calloc:"); | ||||||
|  | 	return p; | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	