updated manpage, added paste, cleaned up, new libdraw a3606ecb
Connor Lane Smith · 2010-07-31 14:56 4 file(s) · +271 −319
Makefile +1 −1
6 6
SRC = dmenu.c
7 7
OBJ = ${SRC:.c=.o}
8 8
9 -
all: options dinput dmenu
9 +
all: options dmenu
10 10
11 11
options:
12 12
	@echo dmenu build options:
README +2 −4
8 8
In order to build dmenu you need the Xlib header files.
9 9
You also need libdraw, available from http://hg.suckless.org/libdraw
10 10
11 -
Pasting the X selection to dmenu requires the sselp utility at runtime.
12 -
13 11
14 12
Installation
15 13
------------
16 -
Edit config.mk to match your local setup (dmenu is installed into
17 -
the /usr/local namespace by default).
14 +
Edit config.mk to match your local setup (dmenu is installed into the
15 +
/usr/local namespace by default).
18 16
19 17
Afterwards enter the following command to build and install dmenu (if
20 18
necessary as root):
dmenu.1 +49 −45
3 3
dmenu \- dynamic menu
4 4
.SH SYNOPSIS
5 5
.B dmenu
6 -
.RB [ \-i ]
7 6
.RB [ \-b ]
8 -
.RB [ \-e " <xid>]"
7 +
.RB [ \-i ]
9 8
.RB [ \-l " <lines>]"
9 +
.RB [ \-p " <prompt>]"
10 10
.RB [ \-fn " <font>]"
11 11
.RB [ \-nb " <color>]"
12 12
.RB [ \-nf " <color>]"
13 -
.RB [ \-p " <prompt>]"
14 13
.RB [ \-sb " <color>]"
15 14
.RB [ \-sf " <color>]"
16 15
.RB [ \-v ]
17 16
18 17
.B dmenu_run
19 -
[<options...>]
18 +
.RB [ \-b ]
19 +
.RB [ \-i ]
20 +
.RB [ \-l " <lines>]"
21 +
.RB [ \-p " <prompt>]"
22 +
.RB [ \-fn " <font>]"
23 +
.RB [ \-nb " <color>]"
24 +
.RB [ \-nf " <color>]"
25 +
.RB [ \-sb " <color>]"
26 +
.RB [ \-sf " <color>]"
27 +
.RB [ \-v ]
20 28
21 29
.B dmenu_path
22 30
.SH DESCRIPTION
23 31
.SS Overview
24 -
dmenu is a generic menu for X, originally designed for
32 +
.B dmenu
33 +
is a generic menu for X, originally designed for
25 34
.BR dwm (1).
26 -
It manages huge amounts (up to 10.000 and more) of user defined menu items
27 -
efficiently.
28 -
29 -
dmenu_run is a dmenu script used by dwm which lists executables in the user's PATH
30 -
and executes the selected item.
31 -
32 -
dmenu_path is a script used by dmenu_run to find and cache a list of executables.
35 +
It manages huge amounts (10000 and more) of user defined menu items efficiently.
36 +
.P
37 +
.B dmenu_run
38 +
is a dmenu script which lists programs in the user's PATH and executes
39 +
the selected item.
40 +
.P
41 +
.B dmenu_path
42 +
is a script used by
43 +
.I dmenu_run
44 +
to find and cache a list of programs.
33 45
.SS Options
34 46
.TP
35 -
.B \-i
36 -
makes dmenu match menu entries case insensitively.
37 -
.TP
38 47
.B \-b
39 -
defines that dmenu appears at the bottom.
48 +
dmenu appears at the bottom of the screen.
40 49
.TP
41 -
.B \-e <xid>
42 -
reparents dmenu to the window specified by xid.
50 +
.B \-i
51 +
dmenu matches menu entries case insensitively.
43 52
.TP
44 53
.B \-l <lines>
45 -
activates vertical list mode.
46 -
The given number of lines will be displayed. Window height will be adjusted.
54 +
dmenu lists items vertically, with the given number of lines.
55 +
.TP
56 +
.B \-p <prompt>
57 +
sets the prompt to be displayed to the left of the input area.
47 58
.TP
48 59
.B \-fn <font>
49 -
defines the font.
60 +
sets the font.
50 61
.TP
51 62
.B \-nb <color>
52 -
defines the normal background color (#RGB, #RRGGBB, and color names are supported).
63 +
sets the background color (#RGB, #RRGGBB, and color names are supported).
53 64
.TP
54 65
.B \-nf <color>
55 -
defines the normal foreground color (#RGB, #RRGGBB, and color names are supported).
56 -
.TP
57 -
.B \-p <prompt>
58 -
defines a prompt to be displayed before the input area.
66 +
sets the foreground color (#RGB, #RRGGBB, and color names are supported).
59 67
.TP
60 68
.B \-sb <color>
61 -
defines the selected background color (#RGB, #RRGGBB, and color names are supported).
69 +
sets the background color of selected items (#RGB, #RRGGBB, and color names are
70 +
supported).
62 71
.TP
63 72
.B \-sf <color>
64 -
defines the selected foreground color (#RGB, #RRGGBB, and color names are supported).
73 +
sets the foreground color of selected items (#RGB, #RRGGBB, and color names are
74 +
supported).
65 75
.TP
66 76
.B \-v
67 77
prints version information to standard output, then exits.
68 78
.SH USAGE
69 79
dmenu reads a list of newline-separated items from standard input and creates a
70 -
menu.  When the user selects an item or enters any text and presses Return, his/her
71 -
choice is printed to standard output and dmenu terminates.
80 +
menu.  When the user selects an item or enters any text and presses Return,
81 +
their choice is printed to standard output and dmenu terminates.
72 82
.P
73 -
dmenu is completely controlled by the keyboard. Besides standard Unix line editing,
74 -
and item selection (Up/Down or Left/Right, PageUp/PageDown, Home/End), the following
75 -
keys are recognized:
83 +
dmenu is completely controlled by the keyboard.  Besides standard Unix line
84 +
editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the
85 +
following keys are recognized:
76 86
.TP
77 87
.B Tab (Control\-i)
78 88
Copy the selected item to the input field.
79 89
.TP
80 90
.B Return (Control\-j)
81 -
Confirm selection and quit (print the selected item to standard output). Returns
82 -
.B 0
83 -
on termination.
91 +
Confirm selection.  Prints the selected item to standard output and exits,
92 +
returning success.
84 93
.TP
85 94
.B Shift\-Return (Control\-Shift\-j)
86 -
Confirm input and quit (print the text in the input field to standard output).
87 -
Returns
88 -
.B 0
89 -
on termination.
95 +
Confirm input.  Prints the input text to standard output and exits, returning
96 +
success.
90 97
.TP
91 98
.B Escape (Control\-c)
92 -
Quit without selecting an item. Returns
93 -
.B 1
94 -
on termination.
99 +
Quit without selecting an item, returning failure.
95 100
.TP
96 101
.B Control\-y
97 -
Pastes the X selection into the input field. This requires
98 -
.BR sselp (1).
102 +
Paste the current X selection into the input field.
99 103
.SH SEE ALSO
100 104
.BR dwm (1),
101 105
.BR wmii (1).
dmenu.c +219 −269
6 6
#include <string.h>
7 7
#include <unistd.h>
8 8
#include <X11/keysym.h>
9 +
#include <X11/Xatom.h>
9 10
#include <X11/Xlib.h>
10 11
#include <X11/Xutil.h>
11 12
#ifdef XINERAMA
14 15
#include <draw.h>
15 16
#include "config.h"
16 17
17 -
#define INRECT(x,y,rx,ry,rw,rh) ((rx) < (x) && (x) < (rx)+(rw) && (ry) < (y) && (y) < (ry)+(rh))
18 +
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
18 19
#define MIN(a,b)                ((a) < (b) ? (a) : (b))
19 20
#define MAX(a,b)                ((a) > (b) ? (a) : (b))
20 -
#define IS_UTF8_1ST_CHAR(c)     (((c) & 0xc0) == 0xc0 || ((c) & 0x80) == 0x00)
21 +
#define UTF8_CODEPOINT(c)       (((c) & 0xc0) != 0x80)
21 22
22 23
typedef struct Item Item;
23 24
struct Item {
24 25
	char *text;
25 -
	Item *next;         /* traverses all items */
26 -
	Item *left, *right; /* traverses items matching current search pattern */
26 +
	Item *next;          /* traverses all items */
27 +
	Item *left, *right;  /* traverses matching items */
27 28
};
28 29
29 -
static void appenditem(Item *i, Item **list, Item **last);
30 +
static void appenditem(Item *item, Item **list, Item **last);
30 31
static void calcoffsetsh(void);
31 32
static void calcoffsetsv(void);
32 33
static char *cistrstr(const char *s, const char *sub);
33 -
static void cleanup(void);
34 -
static void drawitem(const char *s, unsigned long col[ColLast]);
35 34
static void drawmenu(void);
36 35
static void drawmenuh(void);
37 36
static void drawmenuv(void);
38 37
static void grabkeyboard(void);
38 +
static void insert(const char *s, ssize_t n);
39 39
static void keypress(XKeyEvent *e);
40 40
static void match(void);
41 +
static void paste(Atom atom);
41 42
static void readstdin(void);
42 43
static void run(void);
43 44
static void setup(void);
45 +
static void usage(void);
44 46
45 -
static char **argp = NULL;
46 -
static char *maxname = NULL;
47 47
static char *prompt;
48 48
static char text[4096];
49 -
static int promptw;
50 49
static int screen;
51 -
static size_t cur = 0;
52 -
static unsigned int cmdw = 0;
50 +
static size_t cursor = 0;
51 +
static unsigned int inputw = 0;
53 52
static unsigned int lines = 0;
54 -
static unsigned int numlockmask;
55 53
static unsigned int mw, mh;
54 +
static unsigned int promptw = 0;
56 55
static unsigned long normcol[ColLast];
57 56
static unsigned long selcol[ColLast];
57 +
static Atom utf8;
58 58
static Bool topbar = True;
59 59
static DC dc;
60 -
static Display *dpy;
61 -
static Item *allitems = NULL;  /* first of all items */
62 -
static Item *item = NULL;      /* first of pattern matching items */
63 -
static Item *sel = NULL;
64 -
static Item *next = NULL;
65 -
static Item *prev = NULL;
66 -
static Item *curr = NULL;
67 -
static Window win, root;
60 +
static Item *allitems, *matches;
61 +
static Item *curr, *prev, *next, *sel;
62 +
static Window root, win;
68 63
69 64
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
70 65
static char *(*fstrstr)(const char *, const char *) = strstr;
71 66
static void (*calcoffsets)(void) = calcoffsetsh;
72 67
73 68
void
74 -
appenditem(Item *i, Item **list, Item **last) {
69 +
appenditem(Item *item, Item **list, Item **last) {
75 70
	if(!(*last))
76 -
		*list = i;
71 +
		*list = item;
77 72
	else
78 -
		(*last)->right = i;
79 -
	i->left = *last;
80 -
	i->right = NULL;
81 -
	*last = i;
73 +
		(*last)->right = item;
74 +
	item->left = *last;
75 +
	item->right = NULL;
76 +
	*last = item;
82 77
}
83 78
84 79
void
85 80
calcoffsetsh(void) {
86 81
	unsigned int w, x;
87 82
88 -
	w = promptw + cmdw + textw(&dc, "<") + textw(&dc, ">");
83 +
	w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">");
89 84
	for(x = w, next = curr; next; next = next->right)
90 85
		if((x += MIN(textw(&dc, next->text), mw / 3)) > mw)
91 86
			break;
129 124
}
130 125
131 126
void
132 -
cleanup(void) {
133 -
	Item *itm;
134 -
135 -
	while(allitems) {
136 -
		itm = allitems->next;
137 -
		free(allitems->text);
138 -
		free(allitems);
139 -
		allitems = itm;
140 -
	}
141 -
	cleanupdraw(&dc);
142 -
	XDestroyWindow(dpy, win);
143 -
	XUngrabKeyboard(dpy, CurrentTime);
144 -
	XCloseDisplay(dpy);
145 -
}
146 -
147 -
void
148 -
drawitem(const char *s, unsigned long col[ColLast]) {
149 -
	const char *p;
150 -
	unsigned int w = textnw(&dc, text, strlen(text));
151 -
152 -
	drawbox(&dc, col);
153 -
	drawtext(&dc, s, col);
154 -
	for(p = fstrstr(s, text); *text && (p = fstrstr(p, text)); p++)
155 -
		drawline(&dc, textnw(&dc, s, p-s) + dc.h/2 - 1, dc.h-2, w, 1, col);
156 -
}
157 -
158 -
void
159 127
drawmenu(void) {
160 128
	dc.x = 0;
161 129
	dc.y = 0;
172 140
		dc.x += dc.w;
173 141
	}
174 142
	dc.w = mw - dc.x;
175 -
	/* print command */
176 -
	if(cmdw && item && lines == 0)
177 -
		dc.w = cmdw;
143 +
	/* print input area */
144 +
	if(matches && lines == 0 && textw(&dc, text) <= inputw)
145 +
		dc.w = inputw;
178 146
	drawtext(&dc, text, normcol);
179 -
	drawline(&dc, textnw(&dc, text, cur) + dc.h/2 - 2, 2, 1, dc.h-4, normcol);
147 +
	drawline(&dc, textnw(&dc, text, cursor) + dc.h/2 - 2, 2, 1, dc.h-4, normcol);
180 148
	if(lines > 0)
181 149
		drawmenuv();
182 -
	else if(curr)
150 +
	else if(curr && (dc.w == inputw || curr->next))
183 151
		drawmenuh();
184 152
	commitdraw(&dc, win);
185 153
}
186 154
187 155
void
188 156
drawmenuh(void) {
189 -
	Item *i;
157 +
	Item *item;
190 158
191 -
	dc.x += cmdw;
159 +
	dc.x += inputw;
192 160
	dc.w = textw(&dc, "<");
193 -
	drawtext(&dc, curr->left ? "<" : NULL, normcol);
161 +
	if(curr->left)
162 +
		drawtext(&dc, "<", normcol);
194 163
	dc.x += dc.w;
195 -
	for(i = curr; i != next; i = i->right) {
196 -
		dc.w = MIN(textw(&dc, i->text), mw / 3);
197 -
		drawitem(i->text, (sel == i) ? selcol : normcol);
164 +
	for(item = curr; item != next; item = item->right) {
165 +
		dc.w = MIN(textw(&dc, item->text), mw / 3);
166 +
		if(item == sel)
167 +
			drawbox(&dc, selcol);
168 +
		drawtext(&dc, item->text, (item == sel) ? selcol : normcol);
198 169
		dc.x += dc.w;
199 170
	}
200 171
	dc.w = textw(&dc, ">");
201 172
	dc.x = mw - dc.w;
202 -
	drawtext(&dc, next ? ">" : NULL, normcol);
173 +
	if(next)
174 +
		drawtext(&dc, ">", normcol);
203 175
}
204 176
205 177
void
206 178
drawmenuv(void) {
207 -
	Item *i;
179 +
	Item *item;
208 180
	XWindowAttributes wa;
209 181
210 182
	dc.y = topbar ? dc.h : 0;
211 183
	dc.w = mw - dc.x;
212 -
	for(i = curr; i != next; i = i->right) {
213 -
		drawitem(i->text, (sel == i) ? selcol : normcol);
184 +
	for(item = curr; item != next; item = item->right) {
185 +
		if(item == sel)
186 +
			drawbox(&dc, selcol);
187 +
		drawtext(&dc, item->text, (item == sel) ? selcol : normcol);
214 188
		dc.y += dc.h;
215 189
	}
216 -
	if(!XGetWindowAttributes(dpy, win, &wa))
217 -
		eprint("cannot get window attributes");
218 -
	XMoveResizeWindow(dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh);
190 +
	if(!XGetWindowAttributes(dc.dpy, win, &wa))
191 +
		eprintf("cannot get window attributes\n");
192 +
	if(wa.height != mh)
193 +
		XMoveResizeWindow(dc.dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh);
219 194
}
220 195
221 196
void
222 197
grabkeyboard(void) {
223 -
	unsigned int n;
198 +
	int i;
224 199
225 -
	for(n = 0; n < 1000; n++) {
226 -
		if(!XGrabKeyboard(dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime))
200 +
	for(i = 0; i < 1000; i++) {
201 +
		if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime))
227 202
			return;
228 203
		usleep(1000);
229 204
	}
230 -
	exit(EXIT_FAILURE);
205 +
	eprintf("cannot grab keyboard\n");
206 +
}
207 +
208 +
void
209 +
insert(const char *s, ssize_t n) {
210 +
	memmove(text + cursor + n, text + cursor, sizeof text - cursor - n);
211 +
	if(n > 0)
212 +
		memcpy(text + cursor, s, n);
213 +
	cursor += n;
214 +
	match();
231 215
}
232 216
233 217
void
234 218
keypress(XKeyEvent *e) {
235 219
	char buf[sizeof text];
236 -
	int num;
237 -
	unsigned int i, len;
220 +
	int n;
221 +
	size_t len;
238 222
	KeySym ksym;
239 223
240 224
	len = strlen(text);
241 -
	num = XLookupString(e, buf, sizeof buf, &ksym, NULL);
242 -
	if(ksym == XK_KP_Enter)
243 -
		ksym = XK_Return;
244 -
	else if(ksym >= XK_KP_0 && ksym <= XK_KP_9)
245 -
		ksym = (ksym - XK_KP_0) + XK_0;
246 -
	else if(IsFunctionKey(ksym) || IsKeypadKey(ksym)
247 -
	|| IsMiscFunctionKey(ksym) || IsPFKey(ksym)
248 -
	|| IsPrivateKeypadKey(ksym))
249 -
		return;
250 -
	/* first check if a control mask is omitted */
225 +
	XLookupString(e, buf, sizeof buf, &ksym, NULL);
251 226
	if(e->state & ControlMask) {
252 227
		switch(tolower(ksym)) {
253 228
		default:
277 252
		case XK_m:
278 253
			ksym = XK_Return;
279 254
			break;
280 -
		case XK_k:
281 -
			text[cur] = '\0';
255 +
		case XK_k:  /* delete right */
256 +
			text[cursor] = '\0';
282 257
			break;
283 258
		case XK_n:
284 259
			ksym = XK_Down;
286 261
		case XK_p:
287 262
			ksym = XK_Up;
288 263
			break;
289 -
		case XK_u:
290 -
			memmove(text, text + cur, sizeof text - cur + 1);
291 -
			cur = 0;
292 -
			match();
264 +
		case XK_u:  /* delete left */
265 +
			insert(NULL, -cursor);
293 266
			break;
294 -
		case XK_w:
295 -
			if(cur == 0)
267 +
		case XK_w:  /* delete word */
268 +
			if(cursor == 0)
296 269
				return;
297 -
			i = cur;
298 -
			while(i-- > 0 && text[i] == ' ');
299 -
			while(i-- > 0 && text[i] != ' ');
300 -
			memmove(text + i + 1, text + cur, sizeof text - cur + 1);
301 -
			cur = i + 1;
302 -
			match();
303 -
			break;
304 -
		case XK_y:
305 -
			{
306 -
				FILE *fp;
307 -
				char *s;
308 -
				if(!(fp = fopen("sselp", "r")))
309 -
					eprint("cannot popen sselp\n");
310 -
				s = fgets(buf, sizeof buf, fp);
311 -
				fclose(fp);
312 -
				if(!s)
313 -
					return;
314 -
			}
315 -
			num = strlen(buf);
316 -
			if(num && buf[num-1] == '\n')
317 -
				buf[--num] = '\0';
270 +
			n = 0;
271 +
			while(cursor - n++ > 0 && text[cursor - n] == ' ');
272 +
			while(cursor - n++ > 0 && text[cursor - n] != ' ');
273 +
			insert(NULL, -(--n));
318 274
			break;
275 +
		case XK_y:  /* paste selection */
276 +
			XConvertSelection(dc.dpy, XA_PRIMARY, utf8, None, win, CurrentTime);
277 +
			/* causes SelectionNotify event */
278 +
			return;
319 279
		}
320 280
	}
321 281
	switch(ksym) {
322 282
	default:
323 -
		num = MIN(num, sizeof text);
324 -
		if(num && !iscntrl((int) buf[0])) {
325 -
			memmove(text + cur + num, text + cur, sizeof text - cur - num);
326 -
			memcpy(text + cur, buf, num);
327 -
			cur += num;
328 -
			match();
329 -
		}
283 +
		if(!iscntrl((int)*buf))
284 +
			insert(buf, MIN(strlen(buf), sizeof text - cursor));
330 285
		break;
331 286
	case XK_BackSpace:
332 -
		if(cur == 0)
287 +
		if(cursor == 0)
333 288
			return;
334 -
		for(i = 1; len - i > 0 && !IS_UTF8_1ST_CHAR(text[cur - i]); i++);
335 -
		memmove(text + cur - i, text + cur, sizeof text - cur + i);
336 -
		cur -= i;
337 -
		match();
289 +
		for(n = 1; cursor - n > 0 && !UTF8_CODEPOINT(text[cursor - n]); n++);
290 +
		insert(NULL, -n);
338 291
		break;
339 292
	case XK_Delete:
340 -
		if(cur == len)
293 +
		if(cursor == len)
341 294
			return;
342 -
		for(i = 1; cur + i < len && !IS_UTF8_1ST_CHAR(text[cur + i]); i++);
343 -
		memmove(text + cur, text + cur + i, sizeof text - cur);
344 -
		match();
295 +
		for(n = 1; cursor + n < len && !UTF8_CODEPOINT(text[cursor + n]); n++);
296 +
		cursor += n;
297 +
		insert(NULL, -n);
345 298
		break;
346 299
	case XK_End:
347 -
		if(cur < len) {
348 -
			cur = len;
300 +
		if(cursor < len) {
301 +
			cursor = len;
349 302
			break;
350 303
		}
351 304
		while(next) {
358 311
	case XK_Escape:
359 312
		exit(EXIT_FAILURE);
360 313
	case XK_Home:
361 -
		if(sel == item) {
362 -
			cur = 0;
314 +
		if(sel == matches) {
315 +
			cursor = 0;
363 316
			break;
364 317
		}
365 -
		sel = curr = item;
318 +
		sel = curr = matches;
366 319
		calcoffsets();
367 320
		break;
368 321
	case XK_Left:
369 -
		if(cur > 0 && (!sel || !sel->left || lines > 0)) {
370 -
			while(cur-- > 0 && !IS_UTF8_1ST_CHAR(text[cur]));
322 +
		if(cursor > 0 && (!sel || !sel->left || lines > 0)) {
323 +
			while(cursor-- > 0 && !UTF8_CODEPOINT(text[cursor]));
371 324
			break;
372 325
		}
373 -
		if(lines > 0)
326 +
		else if(lines > 0)
374 327
			return;
375 328
	case XK_Up:
376 329
		if(!sel || !sel->left)
394 347
		calcoffsets();
395 348
		break;
396 349
	case XK_Return:
397 -
		fprintf(stdout, "%s", ((e->state & ShiftMask) || sel) ? sel->text : text);
350 +
	case XK_KP_Enter:
351 +
		fputs(((e->state & ShiftMask) || sel) ? sel->text : text, stdout);
398 352
		fflush(stdout);
399 353
		exit(EXIT_SUCCESS);
400 354
	case XK_Right:
401 -
		if(cur < len) {
402 -
			while(cur++ < len && !IS_UTF8_1ST_CHAR(text[cur]));
355 +
		if(cursor < len) {
356 +
			while(cursor++ < len && !UTF8_CODEPOINT(text[cursor]));
403 357
			break;
404 358
		}
405 -
		if(lines > 0)
359 +
		else if(lines > 0)
406 360
			return;
407 361
	case XK_Down:
408 362
		if(!sel || !sel->right)
417 371
		if(!sel)
418 372
			return;
419 373
		strncpy(text, sel->text, sizeof text);
420 -
		cur = strlen(text);
374 +
		cursor = strlen(text);
421 375
		match();
422 376
		break;
423 377
	}
427 381
void
428 382
match(void) {
429 383
	unsigned int len;
430 -
	Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
384 +
	Item *item, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
431 385
432 386
	len = strlen(text);
433 -
	item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
434 -
	for(i = allitems; i; i = i->next)
435 -
		if(!fstrncmp(text, i->text, len + 1))
436 -
			appenditem(i, &lexact, &exactend);
437 -
		else if(!fstrncmp(text, i->text, len))
438 -
			appenditem(i, &lprefix, &prefixend);
439 -
		else if(fstrstr(i->text, text))
440 -
			appenditem(i, &lsubstr, &substrend);
387 +
	matches = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL;
388 +
	for(item = allitems; item; item = item->next)
389 +
		if(!fstrncmp(text, item->text, len + 1))
390 +
			appenditem(item, &lexact, &exactend);
391 +
		else if(!fstrncmp(text, item->text, len))
392 +
			appenditem(item, &lprefix, &prefixend);
393 +
		else if(fstrstr(item->text, text))
394 +
			appenditem(item, &lsubstr, &substrend);
441 395
	if(lexact) {
442 -
		item = lexact;
396 +
		matches = lexact;
443 397
		itemend = exactend;
444 398
	}
445 399
	if(lprefix) {
448 402
			lprefix->left = itemend;
449 403
		}
450 404
		else
451 -
			item = lprefix;
405 +
			matches = lprefix;
452 406
		itemend = prefixend;
453 407
	}
454 408
	if(lsubstr) {
457 411
			lsubstr->left = itemend;
458 412
		}
459 413
		else
460 -
			item = lsubstr;
414 +
			matches = lsubstr;
461 415
	}
462 -
	curr = prev = next = sel = item;
416 +
	curr = prev = next = sel = matches;
463 417
	calcoffsets();
464 418
}
465 419
466 420
void
421 +
paste(Atom atom)
422 +
{
423 +
	char *p, *q;
424 +
	int di;
425 +
	unsigned long dl;
426 +
	Atom da;
427 +
428 +
	XGetWindowProperty(dc.dpy, win, atom, 0, sizeof text - cursor, True,
429 +
			utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
430 +
	insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p));
431 +
	XFree(p);
432 +
	drawmenu();
433 +
}
434 +
435 +
void
467 436
readstdin(void) {
468 -
	char *p, buf[sizeof text];
469 -
	unsigned int len = 0, max = 0;
470 -
	Item *i, *new;
437 +
	char buf[sizeof text];
438 +
	size_t len;
439 +
	Item *item, *new;
471 440
472 -
	i = NULL;
473 -
	while(fgets(buf, sizeof buf, stdin)) {
441 +
	allitems = NULL;
442 +
	for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) {
474 443
		len = strlen(buf);
475 444
		if(buf[len-1] == '\n')
476 445
			buf[--len] = '\0';
477 -
		if(!(p = strdup(buf)))
478 -
			eprint("cannot strdup %u bytes\n", len);
479 -
		if((max = MAX(max, len)) == len)
480 -
			maxname = p;
481 446
		if(!(new = malloc(sizeof *new)))
482 -
			eprint("cannot malloc %u bytes\n", sizeof *new);
447 +
			eprintf("cannot malloc %u bytes\n", sizeof *new);
448 +
		if(!(new->text = strdup(buf)))
449 +
			eprintf("cannot strdup %u bytes\n", len);
450 +
		inputw = MAX(inputw, textw(&dc, new->text));
483 451
		new->next = new->left = new->right = NULL;
484 -
		new->text = p;
485 -
		if(!i)
486 -
			allitems = new;
452 +
		if(item)
453 +
			item->next = new;
487 454
		else 
488 -
			i->next = new;
489 -
		i = new;
455 +
			allitems = new;
490 456
	}
491 457
}
492 458
494 460
run(void) {
495 461
	XEvent ev;
496 462
497 -
	XSync(dpy, False);
498 -
	while(!XNextEvent(dpy, &ev))
463 +
	while(!XNextEvent(dc.dpy, &ev))
499 464
		switch(ev.type) {
500 465
		case Expose:
501 466
			if(ev.xexpose.count == 0)
504 469
		case KeyPress:
505 470
			keypress(&ev.xkey);
506 471
			break;
472 +
		case SelectionNotify:
473 +
			if(ev.xselection.property != None)
474 +
				paste(ev.xselection.property);
475 +
			break;
507 476
		case VisibilityNotify:
508 477
			if(ev.xvisibility.state != VisibilityUnobscured)
509 -
				XRaiseWindow(dpy, win);
478 +
				XRaiseWindow(dc.dpy, win);
510 479
			break;
511 480
		}
512 -
	exit(EXIT_FAILURE);
513 481
}
514 482
515 483
void
516 484
setup(void) {
517 -
	int i, j, x, y;
518 -
#if XINERAMA
519 -
	int n;
520 -
	XineramaScreenInfo *info = NULL;
485 +
	int x, y;
486 +
#ifdef XINERAMA
487 +
	int i, n;
488 +
	XineramaScreenInfo *info;
521 489
#endif
522 -
	XModifierKeymap *modmap;
523 490
	XSetWindowAttributes wa;
524 491
525 -
	/* init modifier map */
526 -
	modmap = XGetModifierMapping(dpy);
527 -
	for(i = 0; i < 8; i++)
528 -
		for(j = 0; j < modmap->max_keypermod; j++) {
529 -
			if(modmap->modifiermap[i * modmap->max_keypermod + j]
530 -
			== XKeysymToKeycode(dpy, XK_Num_Lock))
531 -
				numlockmask = (1 << i);
532 -
		}
533 -
	XFreeModifiermap(modmap);
534 -
535 -
	dc.dpy = dpy;
536 492
	normcol[ColBG] = getcolor(&dc, normbgcolor);
537 493
	normcol[ColFG] = getcolor(&dc, normfgcolor);
538 494
	selcol[ColBG] = getcolor(&dc, selbgcolor);
539 495
	selcol[ColFG] = getcolor(&dc, selfgcolor);
540 -
	initfont(&dc, font);
541 -
542 -
	/* input window */
543 -
	wa.override_redirect = True;
544 -
	wa.background_pixmap = ParentRelative;
545 -
	wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
546 496
547 497
	/* input window geometry */
548 498
	mh = (dc.font.height + 2) * (lines + 1);
549 -
#if XINERAMA
550 -
	if(XineramaIsActive(dpy) && (info = XineramaQueryScreens(dpy, &n))) {
551 -
		i = 0;
552 -
		if(n > 1) {
553 -
			int di;
554 -
			unsigned int dui;
555 -
			Window dummy;
556 -
			if(XQueryPointer(dpy, root, &dummy, &dummy, &x, &y, &di, &di, &dui))
557 -
				for(i = 0; i < n; i++)
558 -
					if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
559 -
						break;
560 -
		}
499 +
#ifdef XINERAMA
500 +
	if((info = XineramaQueryScreens(dc.dpy, &n))) {
501 +
		int di;
502 +
		unsigned int du;
503 +
		Window dw;
504 +
505 +
		XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
506 +
		for(i = 0; i < n; i++)
507 +
			if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
508 +
				break;
561 509
		x = info[i].x_org;
562 -
		y = topbar ? info[i].y_org : info[i].y_org + info[i].height - mh;
510 +
		y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
563 511
		mw = info[i].width;
564 512
		XFree(info);
565 513
	}
567 515
#endif
568 516
	{
569 517
		x = 0;
570 -
		y = topbar ? 0 : DisplayHeight(dpy, screen) - mh;
571 -
		mw = DisplayWidth(dpy, screen);
518 +
		y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh;
519 +
		mw = DisplayWidth(dc.dpy, screen);
572 520
	}
573 521
574 -
	win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
575 -
			DefaultDepth(dpy, screen), CopyFromParent,
576 -
			DefaultVisual(dpy, screen),
522 +
	/* input window */
523 +
	wa.override_redirect = True;
524 +
	wa.background_pixmap = ParentRelative;
525 +
	wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
526 +
	win = XCreateWindow(dc.dpy, root, x, y, mw, mh, 0,
527 +
			DefaultDepth(dc.dpy, screen), CopyFromParent,
528 +
			DefaultVisual(dc.dpy, screen),
577 529
			CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
578 530
531 +
	match();
532 +
	grabkeyboard();
579 533
	setupdraw(&dc, win);
580 -
	if(prompt)
581 -
		promptw = MIN(textw(&dc, prompt), mw / 5);
582 -
	XMapRaised(dpy, win);
534 +
	inputw = MIN(inputw, mw / 3);
535 +
	utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False);
536 +
	XMapRaised(dc.dpy, win);
537 +
}
538 +
539 +
void
540 +
usage(void) {
541 +
	fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n"
542 +
		      "             [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
543 +
	exit(EXIT_FAILURE);
583 544
}
584 545
585 546
int
586 547
main(int argc, char *argv[]) {
587 -
	unsigned int i;
548 +
	int i;
588 549
589 -
	/* command line args */
590 550
	progname = "dmenu";
591 551
	for(i = 1; i < argc; i++)
592 -
		if(!strcmp(argv[i], "-i")) {
593 -
			fstrncmp = strncasecmp;
594 -
			fstrstr = cistrstr;
552 +
		/* 1-arg flags */
553 +
		if(!strcmp(argv[i], "-v")) {
554 +
			fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout);
555 +
			exit(EXIT_SUCCESS);
595 556
		}
596 557
		else if(!strcmp(argv[i], "-b"))
597 558
			topbar = False;
559 +
		else if(!strcmp(argv[i], "-i")) {
560 +
			fstrncmp = strncasecmp;
561 +
			fstrstr = cistrstr;
562 +
		}
563 +
		else if(i == argc-1)
564 +
			usage();
565 +
		/* 2-arg flags */
598 566
		else if(!strcmp(argv[i], "-l")) {
599 -
			if(++i < argc) lines = atoi(argv[i]);
600 -
			if(lines > 0)
567 +
			if((lines = atoi(argv[++i])) > 0)
601 568
				calcoffsets = calcoffsetsv;
602 569
		}
603 -
		else if(!strcmp(argv[i], "-fn")) {
604 -
			if(++i < argc) font = argv[i];
605 -
		}
606 -
		else if(!strcmp(argv[i], "-nb")) {
607 -
			if(++i < argc) normbgcolor = argv[i];
608 -
		}
609 -
		else if(!strcmp(argv[i], "-nf")) {
610 -
			if(++i < argc) normfgcolor = argv[i];
611 -
		}
612 570
		else if(!strcmp(argv[i], "-p")) {
613 -
			if(++i < argc) prompt = argv[i];
571 +
			prompt = argv[++i];
572 +
			promptw = MIN(textw(&dc, prompt), mw/5);
614 573
		}
615 -
		else if(!strcmp(argv[i], "-sb")) {
616 -
			if(++i < argc) selbgcolor = argv[i];
617 -
		}
618 -
		else if(!strcmp(argv[i], "-sf")) {
619 -
			if(++i < argc) selfgcolor = argv[i];
620 -
		}
621 -
		else if(!strcmp(argv[i], "-v")) {
622 -
			printf("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n");
623 -
			exit(EXIT_SUCCESS);
624 -
		}
625 -
		else {
626 -
			fputs("usage: dmenu [-i] [-b] [-l <lines>] [-fn <font>] [-nb <color>]\n"
627 -
			      "             [-nf <color>] [-p <prompt>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
628 -
			exit(EXIT_FAILURE);
629 -
		}
574 +
		else if(!strcmp(argv[i], "-fn"))
575 +
			font = argv[++i];
576 +
		else if(!strcmp(argv[i], "-nb"))
577 +
			normbgcolor = argv[++i];
578 +
		else if(!strcmp(argv[i], "-nf"))
579 +
			normfgcolor = argv[++i];
580 +
		else if(!strcmp(argv[i], "-sb"))
581 +
			selbgcolor = argv[++i];
582 +
		else if(!strcmp(argv[i], "-sf"))
583 +
			selfgcolor = argv[++i];
584 +
		else
585 +
			usage();
586 +
630 587
	if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
631 -
		fprintf(stderr, "dmenu: warning: no locale support\n");
632 -
	if(!(dpy = XOpenDisplay(NULL)))
633 -
		eprint("cannot open display\n");
634 -
	if(atexit(&cleanup) != 0)
635 -
		eprint("cannot register cleanup\n");
636 -
	screen = DefaultScreen(dpy);
637 -
	root = RootWindow(dpy, screen);
638 -
	if(!(argp = malloc(sizeof *argp * (argc+2))))
639 -
		eprint("cannot malloc %u bytes\n", sizeof *argp * (argc+2));
640 -
	memcpy(argp + 2, argv + 1, sizeof *argp * argc);
588 +
		fputs("dmenu: warning: no locale support\n", stderr);
589 +
	if(!(dc.dpy = XOpenDisplay(NULL)))
590 +
		eprintf("cannot open display\n");
591 +
	screen = DefaultScreen(dc.dpy);
592 +
	root = RootWindow(dc.dpy, screen);
593 +
	initfont(&dc, font);
641 594
642 595
	readstdin();
643 -
	grabkeyboard();
644 596
	setup();
645 -
	if(maxname)
646 -
		cmdw = MIN(textw(&dc, maxname), mw / 3);
647 -
	match();
648 597
	run();
649 -
	return 0;
598 +
599 +
	return EXIT_FAILURE;  /* should not reach */
650 600
}