updated manpage, added paste, cleaned up, new libdraw
							parent
							
								
									7d5fe17391
								
							
						
					
					
						commit
						a3606ecb0e
					
				
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							|  | @ -6,7 +6,7 @@ include config.mk | |||
| SRC = dmenu.c | ||||
| OBJ = ${SRC:.c=.o} | ||||
| 
 | ||||
| all: options dinput dmenu | ||||
| all: options dmenu | ||||
| 
 | ||||
| options: | ||||
| 	@echo dmenu build options: | ||||
|  |  | |||
							
								
								
									
										6
									
								
								README
								
								
								
								
							
							
						
						
									
										6
									
								
								README
								
								
								
								
							|  | @ -8,13 +8,11 @@ Requirements | |||
| In order to build dmenu you need the Xlib header files. | ||||
| You also need libdraw, available from http://hg.suckless.org/libdraw | ||||
| 
 | ||||
| Pasting the X selection to dmenu requires the sselp utility at runtime. | ||||
| 
 | ||||
| 
 | ||||
| Installation | ||||
| ------------ | ||||
| Edit config.mk to match your local setup (dmenu is installed into | ||||
| the /usr/local namespace by default). | ||||
| Edit config.mk to match your local setup (dmenu is installed into the | ||||
| /usr/local namespace by default). | ||||
| 
 | ||||
| Afterwards enter the following command to build and install dmenu (if | ||||
| necessary as root): | ||||
|  |  | |||
							
								
								
									
										102
									
								
								dmenu.1
								
								
								
								
							
							
						
						
									
										102
									
								
								dmenu.1
								
								
								
								
							|  | @ -3,99 +3,103 @@ | |||
| dmenu \- dynamic menu | ||||
| .SH SYNOPSIS | ||||
| .B dmenu | ||||
| .RB [ \-i ] | ||||
| .RB [ \-b ] | ||||
| .RB [ \-e " <xid>]" | ||||
| .RB [ \-i ] | ||||
| .RB [ \-l " <lines>]" | ||||
| .RB [ \-p " <prompt>]" | ||||
| .RB [ \-fn " <font>]" | ||||
| .RB [ \-nb " <color>]" | ||||
| .RB [ \-nf " <color>]" | ||||
| .RB [ \-p " <prompt>]" | ||||
| .RB [ \-sb " <color>]" | ||||
| .RB [ \-sf " <color>]" | ||||
| .RB [ \-v ] | ||||
| 
 | ||||
| .B dmenu_run | ||||
| [<options...>] | ||||
| .RB [ \-b ] | ||||
| .RB [ \-i ] | ||||
| .RB [ \-l " <lines>]" | ||||
| .RB [ \-p " <prompt>]" | ||||
| .RB [ \-fn " <font>]" | ||||
| .RB [ \-nb " <color>]" | ||||
| .RB [ \-nf " <color>]" | ||||
| .RB [ \-sb " <color>]" | ||||
| .RB [ \-sf " <color>]" | ||||
| .RB [ \-v ] | ||||
| 
 | ||||
| .B dmenu_path | ||||
| .SH DESCRIPTION | ||||
| .SS Overview | ||||
| dmenu is a generic menu for X, originally designed for | ||||
| .B dmenu | ||||
| is a generic menu for X, originally designed for | ||||
| .BR dwm (1). | ||||
| It manages huge amounts (up to 10.000 and more) of user defined menu items | ||||
| efficiently. | ||||
| 
 | ||||
| dmenu_run is a dmenu script used by dwm which lists executables in the user's PATH | ||||
| and executes the selected item. | ||||
| 
 | ||||
| dmenu_path is a script used by dmenu_run to find and cache a list of executables. | ||||
| It manages huge amounts (10000 and more) of user defined menu items efficiently. | ||||
| .P | ||||
| .B dmenu_run | ||||
| is a dmenu script which lists programs in the user's PATH and executes | ||||
| the selected item. | ||||
| .P | ||||
| .B dmenu_path | ||||
| is a script used by | ||||
| .I dmenu_run | ||||
| to find and cache a list of programs. | ||||
| .SS Options | ||||
| .TP | ||||
| .B \-i | ||||
| makes dmenu match menu entries case insensitively. | ||||
| .TP | ||||
| .B \-b | ||||
| defines that dmenu appears at the bottom. | ||||
| dmenu appears at the bottom of the screen. | ||||
| .TP | ||||
| .B \-e <xid> | ||||
| reparents dmenu to the window specified by xid. | ||||
| .B \-i | ||||
| dmenu matches menu entries case insensitively. | ||||
| .TP | ||||
| .B \-l <lines> | ||||
| activates vertical list mode. | ||||
| The given number of lines will be displayed. Window height will be adjusted. | ||||
| .TP | ||||
| .B \-fn <font> | ||||
| defines the font. | ||||
| .TP | ||||
| .B \-nb <color> | ||||
| defines the normal background color (#RGB, #RRGGBB, and color names are supported). | ||||
| .TP | ||||
| .B \-nf <color> | ||||
| defines the normal foreground color (#RGB, #RRGGBB, and color names are supported). | ||||
| dmenu lists items vertically, with the given number of lines. | ||||
| .TP | ||||
| .B \-p <prompt> | ||||
| defines a prompt to be displayed before the input area. | ||||
| sets the prompt to be displayed to the left of the input area. | ||||
| .TP | ||||
| .B \-fn <font> | ||||
| sets the font. | ||||
| .TP | ||||
| .B \-nb <color> | ||||
| sets the background color (#RGB, #RRGGBB, and color names are supported). | ||||
| .TP | ||||
| .B \-nf <color> | ||||
| sets the foreground color (#RGB, #RRGGBB, and color names are supported). | ||||
| .TP | ||||
| .B \-sb <color> | ||||
| defines the selected background color (#RGB, #RRGGBB, and color names are supported). | ||||
| sets the background color of selected items (#RGB, #RRGGBB, and color names are | ||||
| supported). | ||||
| .TP | ||||
| .B \-sf <color> | ||||
| defines the selected foreground color (#RGB, #RRGGBB, and color names are supported). | ||||
| sets the foreground color of selected items (#RGB, #RRGGBB, and color names are | ||||
| supported). | ||||
| .TP | ||||
| .B \-v | ||||
| prints version information to standard output, then exits. | ||||
| .SH USAGE | ||||
| dmenu reads a list of newline-separated items from standard input and creates a | ||||
| menu.  When the user selects an item or enters any text and presses Return, his/her | ||||
| choice is printed to standard output and dmenu terminates. | ||||
| menu.  When the user selects an item or enters any text and presses Return, | ||||
| their choice is printed to standard output and dmenu terminates. | ||||
| .P | ||||
| dmenu is completely controlled by the keyboard. Besides standard Unix line editing, | ||||
| and item selection (Up/Down or Left/Right, PageUp/PageDown, Home/End), the following | ||||
| keys are recognized: | ||||
| dmenu is completely controlled by the keyboard.  Besides standard Unix line | ||||
| editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the | ||||
| following keys are recognized: | ||||
| .TP | ||||
| .B Tab (Control\-i) | ||||
| Copy the selected item to the input field. | ||||
| .TP | ||||
| .B Return (Control\-j) | ||||
| Confirm selection and quit (print the selected item to standard output). Returns | ||||
| .B 0 | ||||
| on termination. | ||||
| Confirm selection.  Prints the selected item to standard output and exits, | ||||
| returning success. | ||||
| .TP | ||||
| .B Shift\-Return (Control\-Shift\-j) | ||||
| Confirm input and quit (print the text in the input field to standard output). | ||||
| Returns | ||||
| .B 0 | ||||
| on termination. | ||||
| Confirm input.  Prints the input text to standard output and exits, returning | ||||
| success. | ||||
| .TP | ||||
| .B Escape (Control\-c) | ||||
| Quit without selecting an item. Returns | ||||
| .B 1 | ||||
| on termination. | ||||
| Quit without selecting an item, returning failure. | ||||
| .TP | ||||
| .B Control\-y | ||||
| Pastes the X selection into the input field. This requires | ||||
| .BR sselp (1). | ||||
| Paste the current X selection into the input field. | ||||
| .SH SEE ALSO | ||||
| .BR dwm (1), | ||||
| .BR wmii (1). | ||||
|  |  | |||
							
								
								
									
										490
									
								
								dmenu.c
								
								
								
								
							
							
						
						
									
										490
									
								
								dmenu.c
								
								
								
								
							|  | @ -6,6 +6,7 @@ | |||
| #include <string.h> | ||||
| #include <unistd.h> | ||||
| #include <X11/keysym.h> | ||||
| #include <X11/Xatom.h> | ||||
| #include <X11/Xlib.h> | ||||
| #include <X11/Xutil.h> | ||||
| #ifdef XINERAMA | ||||
|  | @ -14,78 +15,72 @@ | |||
| #include <draw.h> | ||||
| #include "config.h" | ||||
| 
 | ||||
| #define INRECT(x,y,rx,ry,rw,rh) ((rx) < (x) && (x) < (rx)+(rw) && (ry) < (y) && (y) < (ry)+(rh)) | ||||
| #define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh)) | ||||
| #define MIN(a,b)                ((a) < (b) ? (a) : (b)) | ||||
| #define MAX(a,b)                ((a) > (b) ? (a) : (b)) | ||||
| #define IS_UTF8_1ST_CHAR(c)     (((c) & 0xc0) == 0xc0 || ((c) & 0x80) == 0x00) | ||||
| #define UTF8_CODEPOINT(c)       (((c) & 0xc0) != 0x80) | ||||
| 
 | ||||
| typedef struct Item Item; | ||||
| struct Item { | ||||
| 	char *text; | ||||
| 	Item *next;         /* traverses all items */ | ||||
| 	Item *left, *right; /* traverses items matching current search pattern */ | ||||
| 	Item *next;          /* traverses all items */ | ||||
| 	Item *left, *right;  /* traverses matching items */ | ||||
| }; | ||||
| 
 | ||||
| static void appenditem(Item *i, Item **list, Item **last); | ||||
| static void appenditem(Item *item, Item **list, Item **last); | ||||
| static void calcoffsetsh(void); | ||||
| static void calcoffsetsv(void); | ||||
| static char *cistrstr(const char *s, const char *sub); | ||||
| static void cleanup(void); | ||||
| static void drawitem(const char *s, unsigned long col[ColLast]); | ||||
| static void drawmenu(void); | ||||
| static void drawmenuh(void); | ||||
| static void drawmenuv(void); | ||||
| static void grabkeyboard(void); | ||||
| static void insert(const char *s, ssize_t n); | ||||
| static void keypress(XKeyEvent *e); | ||||
| static void match(void); | ||||
| static void paste(Atom atom); | ||||
| static void readstdin(void); | ||||
| static void run(void); | ||||
| static void setup(void); | ||||
| static void usage(void); | ||||
| 
 | ||||
| static char **argp = NULL; | ||||
| static char *maxname = NULL; | ||||
| static char *prompt; | ||||
| static char text[4096]; | ||||
| static int promptw; | ||||
| static int screen; | ||||
| static size_t cur = 0; | ||||
| static unsigned int cmdw = 0; | ||||
| static size_t cursor = 0; | ||||
| static unsigned int inputw = 0; | ||||
| static unsigned int lines = 0; | ||||
| static unsigned int numlockmask; | ||||
| static unsigned int mw, mh; | ||||
| static unsigned int promptw = 0; | ||||
| static unsigned long normcol[ColLast]; | ||||
| static unsigned long selcol[ColLast]; | ||||
| static Atom utf8; | ||||
| static Bool topbar = True; | ||||
| static DC dc; | ||||
| static Display *dpy; | ||||
| static Item *allitems = NULL;  /* first of all items */ | ||||
| static Item *item = NULL;      /* first of pattern matching items */ | ||||
| static Item *sel = NULL; | ||||
| static Item *next = NULL; | ||||
| static Item *prev = NULL; | ||||
| static Item *curr = NULL; | ||||
| static Window win, root; | ||||
| static Item *allitems, *matches; | ||||
| static Item *curr, *prev, *next, *sel; | ||||
| static Window root, win; | ||||
| 
 | ||||
| static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; | ||||
| static char *(*fstrstr)(const char *, const char *) = strstr; | ||||
| static void (*calcoffsets)(void) = calcoffsetsh; | ||||
| 
 | ||||
| void | ||||
| appenditem(Item *i, Item **list, Item **last) { | ||||
| appenditem(Item *item, Item **list, Item **last) { | ||||
| 	if(!(*last)) | ||||
| 		*list = i; | ||||
| 		*list = item; | ||||
| 	else | ||||
| 		(*last)->right = i; | ||||
| 	i->left = *last; | ||||
| 	i->right = NULL; | ||||
| 	*last = i; | ||||
| 		(*last)->right = item; | ||||
| 	item->left = *last; | ||||
| 	item->right = NULL; | ||||
| 	*last = item; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| calcoffsetsh(void) { | ||||
| 	unsigned int w, x; | ||||
| 
 | ||||
| 	w = promptw + cmdw + textw(&dc, "<") + textw(&dc, ">"); | ||||
| 	w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">"); | ||||
| 	for(x = w, next = curr; next; next = next->right) | ||||
| 		if((x += MIN(textw(&dc, next->text), mw / 3)) > mw) | ||||
| 			break; | ||||
|  | @ -128,33 +123,6 @@ cistrstr(const char *s, const char *sub) { | |||
| 	return (char *)s; | ||||
| } | ||||
| 
 | ||||
| void | ||||
| cleanup(void) { | ||||
| 	Item *itm; | ||||
| 
 | ||||
| 	while(allitems) { | ||||
| 		itm = allitems->next; | ||||
| 		free(allitems->text); | ||||
| 		free(allitems); | ||||
| 		allitems = itm; | ||||
| 	} | ||||
| 	cleanupdraw(&dc); | ||||
| 	XDestroyWindow(dpy, win); | ||||
| 	XUngrabKeyboard(dpy, CurrentTime); | ||||
| 	XCloseDisplay(dpy); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| drawitem(const char *s, unsigned long col[ColLast]) { | ||||
| 	const char *p; | ||||
| 	unsigned int w = textnw(&dc, text, strlen(text)); | ||||
| 
 | ||||
| 	drawbox(&dc, col); | ||||
| 	drawtext(&dc, s, col); | ||||
| 	for(p = fstrstr(s, text); *text && (p = fstrstr(p, text)); p++) | ||||
| 		drawline(&dc, textnw(&dc, s, p-s) + dc.h/2 - 1, dc.h-2, w, 1, col); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| drawmenu(void) { | ||||
| 	dc.x = 0; | ||||
|  | @ -172,82 +140,89 @@ drawmenu(void) { | |||
| 		dc.x += dc.w; | ||||
| 	} | ||||
| 	dc.w = mw - dc.x; | ||||
| 	/* print command */ | ||||
| 	if(cmdw && item && lines == 0) | ||||
| 		dc.w = cmdw; | ||||
| 	/* print input area */ | ||||
| 	if(matches && lines == 0 && textw(&dc, text) <= inputw) | ||||
| 		dc.w = inputw; | ||||
| 	drawtext(&dc, text, normcol); | ||||
| 	drawline(&dc, textnw(&dc, text, cur) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); | ||||
| 	drawline(&dc, textnw(&dc, text, cursor) + dc.h/2 - 2, 2, 1, dc.h-4, normcol); | ||||
| 	if(lines > 0) | ||||
| 		drawmenuv(); | ||||
| 	else if(curr) | ||||
| 	else if(curr && (dc.w == inputw || curr->next)) | ||||
| 		drawmenuh(); | ||||
| 	commitdraw(&dc, win); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| drawmenuh(void) { | ||||
| 	Item *i; | ||||
| 	Item *item; | ||||
| 
 | ||||
| 	dc.x += cmdw; | ||||
| 	dc.x += inputw; | ||||
| 	dc.w = textw(&dc, "<"); | ||||
| 	drawtext(&dc, curr->left ? "<" : NULL, normcol); | ||||
| 	if(curr->left) | ||||
| 		drawtext(&dc, "<", normcol); | ||||
| 	dc.x += dc.w; | ||||
| 	for(i = curr; i != next; i = i->right) { | ||||
| 		dc.w = MIN(textw(&dc, i->text), mw / 3); | ||||
| 		drawitem(i->text, (sel == i) ? selcol : normcol); | ||||
| 	for(item = curr; item != next; item = item->right) { | ||||
| 		dc.w = MIN(textw(&dc, item->text), mw / 3); | ||||
| 		if(item == sel) | ||||
| 			drawbox(&dc, selcol); | ||||
| 		drawtext(&dc, item->text, (item == sel) ? selcol : normcol); | ||||
| 		dc.x += dc.w; | ||||
| 	} | ||||
| 	dc.w = textw(&dc, ">"); | ||||
| 	dc.x = mw - dc.w; | ||||
| 	drawtext(&dc, next ? ">" : NULL, normcol); | ||||
| 	if(next) | ||||
| 		drawtext(&dc, ">", normcol); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| drawmenuv(void) { | ||||
| 	Item *i; | ||||
| 	Item *item; | ||||
| 	XWindowAttributes wa; | ||||
| 
 | ||||
| 	dc.y = topbar ? dc.h : 0; | ||||
| 	dc.w = mw - dc.x; | ||||
| 	for(i = curr; i != next; i = i->right) { | ||||
| 		drawitem(i->text, (sel == i) ? selcol : normcol); | ||||
| 	for(item = curr; item != next; item = item->right) { | ||||
| 		if(item == sel) | ||||
| 			drawbox(&dc, selcol); | ||||
| 		drawtext(&dc, item->text, (item == sel) ? selcol : normcol); | ||||
| 		dc.y += dc.h; | ||||
| 	} | ||||
| 	if(!XGetWindowAttributes(dpy, win, &wa)) | ||||
| 		eprint("cannot get window attributes"); | ||||
| 	XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); | ||||
| 	if(!XGetWindowAttributes(dc.dpy, win, &wa)) | ||||
| 		eprintf("cannot get window attributes\n"); | ||||
| 	if(wa.height != mh) | ||||
| 		XMoveResizeWindow(dc.dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| grabkeyboard(void) { | ||||
| 	unsigned int n; | ||||
| 	int i; | ||||
| 
 | ||||
| 	for(n = 0; n < 1000; n++) { | ||||
| 		if(!XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) | ||||
| 	for(i = 0; i < 1000; i++) { | ||||
| 		if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime)) | ||||
| 			return; | ||||
| 		usleep(1000); | ||||
| 	} | ||||
| 	exit(EXIT_FAILURE); | ||||
| 	eprintf("cannot grab keyboard\n"); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| insert(const char *s, ssize_t n) { | ||||
| 	memmove(text + cursor + n, text + cursor, sizeof text - cursor - n); | ||||
| 	if(n > 0) | ||||
| 		memcpy(text + cursor, s, n); | ||||
| 	cursor += n; | ||||
| 	match(); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| keypress(XKeyEvent *e) { | ||||
| 	char buf[sizeof text]; | ||||
| 	int num; | ||||
| 	unsigned int i, len; | ||||
| 	int n; | ||||
| 	size_t len; | ||||
| 	KeySym ksym; | ||||
| 
 | ||||
| 	len = strlen(text); | ||||
| 	num = XLookupString(e, buf, sizeof buf, &ksym, NULL); | ||||
| 	if(ksym == XK_KP_Enter) | ||||
| 		ksym = XK_Return; | ||||
| 	else if(ksym >= XK_KP_0 && ksym <= XK_KP_9) | ||||
| 		ksym = (ksym - XK_KP_0) + XK_0; | ||||
| 	else if(IsFunctionKey(ksym) || IsKeypadKey(ksym) | ||||
| 	|| IsMiscFunctionKey(ksym) || IsPFKey(ksym) | ||||
| 	|| IsPrivateKeypadKey(ksym)) | ||||
| 		return; | ||||
| 	/* first check if a control mask is omitted */ | ||||
| 	XLookupString(e, buf, sizeof buf, &ksym, NULL); | ||||
| 	if(e->state & ControlMask) { | ||||
| 		switch(tolower(ksym)) { | ||||
| 		default: | ||||
|  | @ -277,8 +252,8 @@ keypress(XKeyEvent *e) { | |||
| 		case XK_m: | ||||
| 			ksym = XK_Return; | ||||
| 			break; | ||||
| 		case XK_k: | ||||
| 			text[cur] = '\0'; | ||||
| 		case XK_k:  /* delete right */ | ||||
| 			text[cursor] = '\0'; | ||||
| 			break; | ||||
| 		case XK_n: | ||||
| 			ksym = XK_Down; | ||||
|  | @ -286,66 +261,44 @@ keypress(XKeyEvent *e) { | |||
| 		case XK_p: | ||||
| 			ksym = XK_Up; | ||||
| 			break; | ||||
| 		case XK_u: | ||||
| 			memmove(text, text + cur, sizeof text - cur + 1); | ||||
| 			cur = 0; | ||||
| 			match(); | ||||
| 		case XK_u:  /* delete left */ | ||||
| 			insert(NULL, -cursor); | ||||
| 			break; | ||||
| 		case XK_w: | ||||
| 			if(cur == 0) | ||||
| 		case XK_w:  /* delete word */ | ||||
| 			if(cursor == 0) | ||||
| 				return; | ||||
| 			i = cur; | ||||
| 			while(i-- > 0 && text[i] == ' '); | ||||
| 			while(i-- > 0 && text[i] != ' '); | ||||
| 			memmove(text + i + 1, text + cur, sizeof text - cur + 1); | ||||
| 			cur = i + 1; | ||||
| 			match(); | ||||
| 			break; | ||||
| 		case XK_y: | ||||
| 			{ | ||||
| 				FILE *fp; | ||||
| 				char *s; | ||||
| 				if(!(fp = fopen("sselp", "r"))) | ||||
| 					eprint("cannot popen sselp\n"); | ||||
| 				s = fgets(buf, sizeof buf, fp); | ||||
| 				fclose(fp); | ||||
| 				if(!s) | ||||
| 					return; | ||||
| 			} | ||||
| 			num = strlen(buf); | ||||
| 			if(num && buf[num-1] == '\n') | ||||
| 				buf[--num] = '\0'; | ||||
| 			n = 0; | ||||
| 			while(cursor - n++ > 0 && text[cursor - n] == ' '); | ||||
| 			while(cursor - n++ > 0 && text[cursor - n] != ' '); | ||||
| 			insert(NULL, -(--n)); | ||||
| 			break; | ||||
| 		case XK_y:  /* paste selection */ | ||||
| 			XConvertSelection(dc.dpy, XA_PRIMARY, utf8, None, win, CurrentTime); | ||||
| 			/* causes SelectionNotify event */ | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 	switch(ksym) { | ||||
| 	default: | ||||
| 		num = MIN(num, sizeof text); | ||||
| 		if(num && !iscntrl((int) buf[0])) { | ||||
| 			memmove(text + cur + num, text + cur, sizeof text - cur - num); | ||||
| 			memcpy(text + cur, buf, num); | ||||
| 			cur += num; | ||||
| 			match(); | ||||
| 		} | ||||
| 		if(!iscntrl((int)*buf)) | ||||
| 			insert(buf, MIN(strlen(buf), sizeof text - cursor)); | ||||
| 		break; | ||||
| 	case XK_BackSpace: | ||||
| 		if(cur == 0) | ||||
| 		if(cursor == 0) | ||||
| 			return; | ||||
| 		for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[cur - i]); i++); | ||||
| 		memmove(text + cur - i, text + cur, sizeof text - cur + i); | ||||
| 		cur -= i; | ||||
| 		match(); | ||||
| 		for(n = 1; cursor - n > 0 && !UTF8_CODEPOINT(text[cursor - n]); n++); | ||||
| 		insert(NULL, -n); | ||||
| 		break; | ||||
| 	case XK_Delete: | ||||
| 		if(cur == len) | ||||
| 		if(cursor == len) | ||||
| 			return; | ||||
| 		for(i = 1; cur + i < len && !IS_UTF8_1ST_CHAR(text[cur + i]); i++); | ||||
| 		memmove(text + cur, text + cur + i, sizeof text - cur); | ||||
| 		match(); | ||||
| 		for(n = 1; cursor + n < len && !UTF8_CODEPOINT(text[cursor + n]); n++); | ||||
| 		cursor += n; | ||||
| 		insert(NULL, -n); | ||||
| 		break; | ||||
| 	case XK_End: | ||||
| 		if(cur < len) { | ||||
| 			cur = len; | ||||
| 		if(cursor < len) { | ||||
| 			cursor = len; | ||||
| 			break; | ||||
| 		} | ||||
| 		while(next) { | ||||
|  | @ -358,19 +311,19 @@ keypress(XKeyEvent *e) { | |||
| 	case XK_Escape: | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	case XK_Home: | ||||
| 		if(sel == item) { | ||||
| 			cur = 0; | ||||
| 		if(sel == matches) { | ||||
| 			cursor = 0; | ||||
| 			break; | ||||
| 		} | ||||
| 		sel = curr = item; | ||||
| 		sel = curr = matches; | ||||
| 		calcoffsets(); | ||||
| 		break; | ||||
| 	case XK_Left: | ||||
| 		if(cur > 0 && (!sel || !sel->left || lines > 0)) { | ||||
| 			while(cur-- > 0 && !IS_UTF8_1ST_CHAR(text[cur])); | ||||
| 		if(cursor > 0 && (!sel || !sel->left || lines > 0)) { | ||||
| 			while(cursor-- > 0 && !UTF8_CODEPOINT(text[cursor])); | ||||
| 			break; | ||||
| 		} | ||||
| 		if(lines > 0) | ||||
| 		else if(lines > 0) | ||||
| 			return; | ||||
| 	case XK_Up: | ||||
| 		if(!sel || !sel->left) | ||||
|  | @ -394,15 +347,16 @@ keypress(XKeyEvent *e) { | |||
| 		calcoffsets(); | ||||
| 		break; | ||||
| 	case XK_Return: | ||||
| 		fprintf(stdout, "%s", ((e->state & ShiftMask) || sel) ? sel->text : text); | ||||
| 	case XK_KP_Enter: | ||||
| 		fputs(((e->state & ShiftMask) || sel) ? sel->text : text, stdout); | ||||
| 		fflush(stdout); | ||||
| 		exit(EXIT_SUCCESS); | ||||
| 	case XK_Right: | ||||
| 		if(cur < len) { | ||||
| 			while(cur++ < len && !IS_UTF8_1ST_CHAR(text[cur])); | ||||
| 		if(cursor < len) { | ||||
| 			while(cursor++ < len && !UTF8_CODEPOINT(text[cursor])); | ||||
| 			break; | ||||
| 		} | ||||
| 		if(lines > 0) | ||||
| 		else if(lines > 0) | ||||
| 			return; | ||||
| 	case XK_Down: | ||||
| 		if(!sel || !sel->right) | ||||
|  | @ -417,7 +371,7 @@ keypress(XKeyEvent *e) { | |||
| 		if(!sel) | ||||
| 			return; | ||||
| 		strncpy(text, sel->text, sizeof text); | ||||
| 		cur = strlen(text); | ||||
| 		cursor = strlen(text); | ||||
| 		match(); | ||||
| 		break; | ||||
| 	} | ||||
|  | @ -427,19 +381,19 @@ keypress(XKeyEvent *e) { | |||
| void | ||||
| match(void) { | ||||
| 	unsigned int len; | ||||
| 	Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; | ||||
| 	Item *item, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; | ||||
| 
 | ||||
| 	len = strlen(text); | ||||
| 	item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; | ||||
| 	for(i = allitems; i; i = i->next) | ||||
| 		if(!fstrncmp(text, i->text, len + 1)) | ||||
| 			appenditem(i, &lexact, &exactend); | ||||
| 		else if(!fstrncmp(text, i->text, len)) | ||||
| 			appenditem(i, &lprefix, &prefixend); | ||||
| 		else if(fstrstr(i->text, text)) | ||||
| 			appenditem(i, &lsubstr, &substrend); | ||||
| 	matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; | ||||
| 	for(item = allitems; item; item = item->next) | ||||
| 		if(!fstrncmp(text, item->text, len + 1)) | ||||
| 			appenditem(item, &lexact, &exactend); | ||||
| 		else if(!fstrncmp(text, item->text, len)) | ||||
| 			appenditem(item, &lprefix, &prefixend); | ||||
| 		else if(fstrstr(item->text, text)) | ||||
| 			appenditem(item, &lsubstr, &substrend); | ||||
| 	if(lexact) { | ||||
| 		item = lexact; | ||||
| 		matches = lexact; | ||||
| 		itemend = exactend; | ||||
| 	} | ||||
| 	if(lprefix) { | ||||
|  | @ -448,7 +402,7 @@ match(void) { | |||
| 			lprefix->left = itemend; | ||||
| 		} | ||||
| 		else | ||||
| 			item = lprefix; | ||||
| 			matches = lprefix; | ||||
| 		itemend = prefixend; | ||||
| 	} | ||||
| 	if(lsubstr) { | ||||
|  | @ -457,36 +411,48 @@ match(void) { | |||
| 			lsubstr->left = itemend; | ||||
| 		} | ||||
| 		else | ||||
| 			item = lsubstr; | ||||
| 			matches = lsubstr; | ||||
| 	} | ||||
| 	curr = prev = next = sel = item; | ||||
| 	curr = prev = next = sel = matches; | ||||
| 	calcoffsets(); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| readstdin(void) { | ||||
| 	char *p, buf[sizeof text]; | ||||
| 	unsigned int len = 0, max = 0; | ||||
| 	Item *i, *new; | ||||
| paste(Atom atom) | ||||
| { | ||||
| 	char *p, *q; | ||||
| 	int di; | ||||
| 	unsigned long dl; | ||||
| 	Atom da; | ||||
| 
 | ||||
| 	i = NULL; | ||||
| 	while(fgets(buf, sizeof buf, stdin)) { | ||||
| 	XGetWindowProperty(dc.dpy, win, atom, 0, sizeof text - cursor, True, | ||||
| 			utf8, &da, &di, &dl, &dl, (unsigned char **)&p); | ||||
| 	insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p)); | ||||
| 	XFree(p); | ||||
| 	drawmenu(); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| readstdin(void) { | ||||
| 	char buf[sizeof text]; | ||||
| 	size_t len; | ||||
| 	Item *item, *new; | ||||
| 
 | ||||
| 	allitems = NULL; | ||||
| 	for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) { | ||||
| 		len = strlen(buf); | ||||
| 		if(buf[len-1] == '\n') | ||||
| 			buf[--len] = '\0'; | ||||
| 		if(!(p = strdup(buf))) | ||||
| 			eprint("cannot strdup %u bytes\n", len); | ||||
| 		if((max = MAX(max, len)) == len) | ||||
| 			maxname = p; | ||||
| 		if(!(new = malloc(sizeof *new))) | ||||
| 			eprint("cannot malloc %u bytes\n", sizeof *new); | ||||
| 			eprintf("cannot malloc %u bytes\n", sizeof *new); | ||||
| 		if(!(new->text = strdup(buf))) | ||||
| 			eprintf("cannot strdup %u bytes\n", len); | ||||
| 		inputw = MAX(inputw, textw(&dc, new->text)); | ||||
| 		new->next = new->left = new->right = NULL; | ||||
| 		new->text = p; | ||||
| 		if(!i) | ||||
| 			allitems = new; | ||||
| 		if(item) | ||||
| 			item->next = new; | ||||
| 		else  | ||||
| 			i->next = new; | ||||
| 		i = new; | ||||
| 			allitems = new; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -494,8 +460,7 @@ void | |||
| run(void) { | ||||
| 	XEvent ev; | ||||
| 
 | ||||
| 	XSync(dpy, False); | ||||
| 	while(!XNextEvent(dpy, &ev)) | ||||
| 	while(!XNextEvent(dc.dpy, &ev)) | ||||
| 		switch(ev.type) { | ||||
| 		case Expose: | ||||
| 			if(ev.xexpose.count == 0) | ||||
|  | @ -504,62 +469,45 @@ run(void) { | |||
| 		case KeyPress: | ||||
| 			keypress(&ev.xkey); | ||||
| 			break; | ||||
| 		case SelectionNotify: | ||||
| 			if(ev.xselection.property != None) | ||||
| 				paste(ev.xselection.property); | ||||
| 			break; | ||||
| 		case VisibilityNotify: | ||||
| 			if(ev.xvisibility.state != VisibilityUnobscured) | ||||
| 				XRaiseWindow(dpy, win); | ||||
| 				XRaiseWindow(dc.dpy, win); | ||||
| 			break; | ||||
| 		} | ||||
| 	exit(EXIT_FAILURE); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| setup(void) { | ||||
| 	int i, j, x, y; | ||||
| #if XINERAMA | ||||
| 	int n; | ||||
| 	XineramaScreenInfo *info = NULL; | ||||
| 	int x, y; | ||||
| #ifdef XINERAMA | ||||
| 	int i, n; | ||||
| 	XineramaScreenInfo *info; | ||||
| #endif | ||||
| 	XModifierKeymap *modmap; | ||||
| 	XSetWindowAttributes wa; | ||||
| 
 | ||||
| 	/* init modifier map */ | ||||
| 	modmap = XGetModifierMapping(dpy); | ||||
| 	for(i = 0; i < 8; i++) | ||||
| 		for(j = 0; j < modmap->max_keypermod; j++) { | ||||
| 			if(modmap->modifiermap[i * modmap->max_keypermod + j] | ||||
| 			== XKeysymToKeycode(dpy, XK_Num_Lock)) | ||||
| 				numlockmask = (1 << i); | ||||
| 		} | ||||
| 	XFreeModifiermap(modmap); | ||||
| 
 | ||||
| 	dc.dpy = dpy; | ||||
| 	normcol[ColBG] = getcolor(&dc, normbgcolor); | ||||
| 	normcol[ColFG] = getcolor(&dc, normfgcolor); | ||||
| 	selcol[ColBG] = getcolor(&dc, selbgcolor); | ||||
| 	selcol[ColFG] = getcolor(&dc, selfgcolor); | ||||
| 	initfont(&dc, font); | ||||
| 
 | ||||
| 	/* input window */ | ||||
| 	wa.override_redirect = True; | ||||
| 	wa.background_pixmap = ParentRelative; | ||||
| 	wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | ||||
| 
 | ||||
| 	/* input window geometry */ | ||||
| 	mh = (dc.font.height + 2) * (lines + 1); | ||||
| #if XINERAMA | ||||
| 	if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) { | ||||
| 		i = 0; | ||||
| 		if(n > 1) { | ||||
| 			int di; | ||||
| 			unsigned int dui; | ||||
| 			Window dummy; | ||||
| 			if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui)) | ||||
| 				for(i = 0; i < n; i++) | ||||
| 					if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | ||||
| 						break; | ||||
| 		} | ||||
| #ifdef XINERAMA | ||||
| 	if((info = XineramaQueryScreens(dc.dpy, &n))) { | ||||
| 		int di; | ||||
| 		unsigned int du; | ||||
| 		Window dw; | ||||
| 
 | ||||
| 		XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du); | ||||
| 		for(i = 0; i < n; i++) | ||||
| 			if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height)) | ||||
| 				break; | ||||
| 		x = info[i].x_org; | ||||
| 		y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh; | ||||
| 		y = info[i].y_org + (topbar ? 0 : info[i].height - mh); | ||||
| 		mw = info[i].width; | ||||
| 		XFree(info); | ||||
| 	} | ||||
|  | @ -567,84 +515,86 @@ setup(void) { | |||
| #endif | ||||
| 	{ | ||||
| 		x = 0; | ||||
| 		y = topbar ? 0 : DisplayHeight(dpy, screen) - mh; | ||||
| 		mw = DisplayWidth(dpy, screen); | ||||
| 		y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh; | ||||
| 		mw = DisplayWidth(dc.dpy, screen); | ||||
| 	} | ||||
| 
 | ||||
| 	win = XCreateWindow(dpy, root, x, y, mw, mh, 0, | ||||
| 			DefaultDepth(dpy, screen), CopyFromParent, | ||||
| 			DefaultVisual(dpy, screen), | ||||
| 	/* input window */ | ||||
| 	wa.override_redirect = True; | ||||
| 	wa.background_pixmap = ParentRelative; | ||||
| 	wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; | ||||
| 	win = XCreateWindow(dc.dpy, root, x, y, mw, mh, 0, | ||||
| 			DefaultDepth(dc.dpy, screen), CopyFromParent, | ||||
| 			DefaultVisual(dc.dpy, screen), | ||||
| 			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); | ||||
| 
 | ||||
| 	match(); | ||||
| 	grabkeyboard(); | ||||
| 	setupdraw(&dc, win); | ||||
| 	if(prompt) | ||||
| 		promptw = MIN(textw(&dc, prompt), mw / 5); | ||||
| 	XMapRaised(dpy, win); | ||||
| 	inputw = MIN(inputw, mw / 3); | ||||
| 	utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False); | ||||
| 	XMapRaised(dc.dpy, win); | ||||
| } | ||||
| 
 | ||||
| void | ||||
| usage(void) { | ||||
| 	fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n" | ||||
| 		      "             [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr); | ||||
| 	exit(EXIT_FAILURE); | ||||
| } | ||||
| 
 | ||||
| int | ||||
| main(int argc, char *argv[]) { | ||||
| 	unsigned int i; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/* command line args */ | ||||
| 	progname = "dmenu"; | ||||
| 	for(i = 1; i < argc; i++) | ||||
| 		if(!strcmp(argv[i], "-i")) { | ||||
| 			fstrncmp = strncasecmp; | ||||
| 			fstrstr = cistrstr; | ||||
| 		/* 1-arg flags */ | ||||
| 		if(!strcmp(argv[i], "-v")) { | ||||
| 			fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout); | ||||
| 			exit(EXIT_SUCCESS); | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-b")) | ||||
| 			topbar = False; | ||||
| 		else if(!strcmp(argv[i], "-i")) { | ||||
| 			fstrncmp = strncasecmp; | ||||
| 			fstrstr = cistrstr; | ||||
| 		} | ||||
| 		else if(i == argc-1) | ||||
| 			usage(); | ||||
| 		/* 2-arg flags */ | ||||
| 		else if(!strcmp(argv[i], "-l")) { | ||||
| 			if(++i < argc) lines = atoi(argv[i]); | ||||
| 			if(lines > 0) | ||||
| 			if((lines = atoi(argv[++i])) > 0) | ||||
| 				calcoffsets = calcoffsetsv; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-fn")) { | ||||
| 			if(++i < argc) font = argv[i]; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-nb")) { | ||||
| 			if(++i < argc) normbgcolor = argv[i]; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-nf")) { | ||||
| 			if(++i < argc) normfgcolor = argv[i]; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-p")) { | ||||
| 			if(++i < argc) prompt = argv[i]; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-sb")) { | ||||
| 			if(++i < argc) selbgcolor = argv[i]; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-sf")) { | ||||
| 			if(++i < argc) selfgcolor = argv[i]; | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-v")) { | ||||
| 			printf("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n"); | ||||
| 			exit(EXIT_SUCCESS); | ||||
| 		} | ||||
| 		else { | ||||
| 			fputs("usage: dmenu [-i] [-b] [-l <lines>] [-fn <font>] [-nb <color>]\n" | ||||
| 			      "             [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n", stderr); | ||||
| 			exit(EXIT_FAILURE); | ||||
| 			prompt = argv[++i]; | ||||
| 			promptw = MIN(textw(&dc, prompt), mw/5); | ||||
| 		} | ||||
| 		else if(!strcmp(argv[i], "-fn")) | ||||
| 			font = argv[++i]; | ||||
| 		else if(!strcmp(argv[i], "-nb")) | ||||
| 			normbgcolor = argv[++i]; | ||||
| 		else if(!strcmp(argv[i], "-nf")) | ||||
| 			normfgcolor = argv[++i]; | ||||
| 		else if(!strcmp(argv[i], "-sb")) | ||||
| 			selbgcolor = argv[++i]; | ||||
| 		else if(!strcmp(argv[i], "-sf")) | ||||
| 			selfgcolor = argv[++i]; | ||||
| 		else | ||||
| 			usage(); | ||||
| 
 | ||||
| 	if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) | ||||
| 		fprintf(stderr, "dmenu: warning: no locale support\n"); | ||||
| 	if(!(dpy = XOpenDisplay(NULL))) | ||||
| 		eprint("cannot open display\n"); | ||||
| 	if(atexit(&cleanup) != 0) | ||||
| 		eprint("cannot register cleanup\n"); | ||||
| 	screen = DefaultScreen(dpy); | ||||
| 	root = RootWindow(dpy, screen); | ||||
| 	if(!(argp = malloc(sizeof *argp * (argc+2)))) | ||||
| 		eprint("cannot malloc %u bytes\n", sizeof *argp * (argc+2)); | ||||
| 	memcpy(argp + 2, argv + 1, sizeof *argp * argc); | ||||
| 		fputs("dmenu: warning: no locale support\n", stderr); | ||||
| 	if(!(dc.dpy = XOpenDisplay(NULL))) | ||||
| 		eprintf("cannot open display\n"); | ||||
| 	screen = DefaultScreen(dc.dpy); | ||||
| 	root = RootWindow(dc.dpy, screen); | ||||
| 	initfont(&dc, font); | ||||
| 
 | ||||
| 	readstdin(); | ||||
| 	grabkeyboard(); | ||||
| 	setup(); | ||||
| 	if(maxname) | ||||
| 		cmdw = MIN(textw(&dc, maxname), mw / 3); | ||||
| 	match(); | ||||
| 	run(); | ||||
| 	return 0; | ||||
| 
 | ||||
| 	return EXIT_FAILURE;  /* should not reach */ | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue