merge default -> lsx 4126b1e3
Connor Lane Smith · 2011-10-16 18:13 9 file(s) · +117 −118
.hgtags +1 −0
46 46
abb6579a324fffdf6a23c2fa4c32911277da594a 4.2.1
47 47
14c79f054bdf43ff3213af8e60a783192e92a018 4.3
48 48
34a2d77049a95b02f3332a0b88f9370965ebcfad 4.3.1
49 +
2b105eaae8315b076da93056da9ecd60de5a7ac9 4.4
Makefile +2 −2
30 30
31 31
clean:
32 32
	@echo cleaning
33 -
	@rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz
33 +
	@rm -f dmenu lsx ${OBJ} dmenu-${VERSION}.tar.gz
34 34
35 35
dist: clean
36 36
	@echo creating dist tarball
37 37
	@mkdir -p dmenu-${VERSION}
38 -
	@cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run ${SRC} dmenu-${VERSION}
38 +
	@cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run lsx.1 ${SRC} dmenu-${VERSION}
39 39
	@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
40 40
	@gzip dmenu-${VERSION}.tar
41 41
	@rm -rf dmenu-${VERSION}
config.mk +1 −1
1 1
# dmenu version
2 -
VERSION = 4.3.1
2 +
VERSION = hg
3 3
4 4
# paths
5 5
PREFIX = /usr/local
dmenu.1 +6 −9
25 25
.BR dmenu_run " ..."
26 26
.SH DESCRIPTION
27 27
.B dmenu
28 -
is a dynamic menu for X, originally designed for
29 -
.IR dwm (1).
30 -
It manages huge numbers of user\-defined menu items efficiently.
31 -
.P
32 -
dmenu reads a list of newline\-separated items from stdin and creates a menu.
33 -
When the user selects an item or enters any text and presses Return, their
34 -
choice is printed to stdout and dmenu terminates.
28 +
is a dynamic menu for X, which reads a list of newline\-separated items from
29 +
stdin.  When the user selects an item and presses Return, their choice is printed
30 +
to stdout and dmenu terminates.  Entering text will narrow the items to those
31 +
matching the tokens in the input.
35 32
.P
36 33
.B dmenu_run
37 34
is a dmenu script used by dwm which lists programs in the user's $PATH and
42 39
dmenu appears at the bottom of the screen.
43 40
.TP
44 41
.B \-f
45 -
dmenu grabs the keyboard before reading stdin.  This is faster, but may lock up
46 -
X if stdin is from a terminal.
42 +
dmenu grabs the keyboard before reading stdin.  This is faster, but will lock up
43 +
X until stdin reaches end\-of\-file.
47 44
.TP
48 45
.B \-i
49 46
dmenu matches menu items case insensitively.
dmenu.c +92 −92
25 25
26 26
static void appenditem(Item *item, Item **list, Item **last);
27 27
static void calcoffsets(void);
28 +
static char *cistrstr(const char *s, const char *sub);
28 29
static void drawmenu(void);
29 -
static char *fstrstr(const char *s, const char *sub);
30 30
static void grabkeyboard(void);
31 31
static void insert(const char *str, ssize_t n);
32 32
static void keypress(XKeyEvent *ev);
33 -
static void match(Bool sub);
33 +
static void match(void);
34 34
static size_t nextrune(int inc);
35 35
static void paste(void);
36 36
static void readstdin(void);
60 60
static Window win;
61 61
62 62
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
63 +
static char *(*fstrstr)(const char *, const char *) = strstr;
63 64
64 65
int
65 66
main(int argc, char *argv[]) {
76 77
			topbar = False;
77 78
		else if(!strcmp(argv[i], "-f"))
78 79
			fast = True;
79 -
		else if(!strcmp(argv[i], "-i"))
80 +
		else if(!strcmp(argv[i], "-i")) {
80 81
			fstrncmp = strncasecmp;
82 +
			fstrstr = cistrstr;
83 +
		}
81 84
		else if(i+1 == argc)
82 85
			usage();
83 86
		/* double flags */
112 115
	setup();
113 116
	run();
114 117
115 -
	return EXIT_FAILURE;  /* should not reach */
118 +
	return EXIT_FAILURE; /* unreachable */
116 119
}
117 120
118 121
void
119 122
appenditem(Item *item, Item **list, Item **last) {
120 -
	if(!*last)
121 -
		*list = item;
122 -
	else
123 +
	if(*last)
123 124
		(*last)->right = item;
125 +
	else
126 +
		*list = item;
127 +
124 128
	item->left = *last;
125 129
	item->right = NULL;
126 130
	*last = item;
143 147
			break;
144 148
}
145 149
150 +
char *
151 +
cistrstr(const char *s, const char *sub) {
152 +
	size_t len;
153 +
154 +
	for(len = strlen(sub); *s; s++)
155 +
		if(!strncasecmp(s, sub, len))
156 +
			return (char *)s;
157 +
	return NULL;
158 +
}
159 +
146 160
void
147 161
drawmenu(void) {
148 162
	int curpos;
188 202
	mapdc(dc, win, mw, mh);
189 203
}
190 204
191 -
char *
192 -
fstrstr(const char *s, const char *sub) {
193 -
	size_t len;
194 -
195 -
	for(len = strlen(sub); *s; s++)
196 -
		if(!fstrncmp(s, sub, len))
197 -
			return (char *)s;
198 -
	return NULL;
199 -
}
200 -
201 205
void
202 206
grabkeyboard(void) {
203 207
	int i;
219 223
	if(n > 0)
220 224
		memcpy(&text[cursor], str, n);
221 225
	cursor += n;
222 -
	match(n > 0 && text[cursor] == '\0');
226 +
	match();
223 227
}
224 228
225 229
void
233 237
234 238
		XConvertCase(ksym, &lower, &upper);
235 239
		switch(lower) {
236 -
		default:
237 -
			return;
238 -
		case XK_a:
239 -
			ksym = XK_Home;
240 -
			break;
241 -
		case XK_b:
242 -
			ksym = XK_Left;
243 -
			break;
244 -
		case XK_c:
245 -
			ksym = XK_Escape;
246 -
			break;
247 -
		case XK_d:
248 -
			ksym = XK_Delete;
249 -
			break;
250 -
		case XK_e:
251 -
			ksym = XK_End;
252 -
			break;
253 -
		case XK_f:
254 -
			ksym = XK_Right;
255 -
			break;
256 -
		case XK_h:
257 -
			ksym = XK_BackSpace;
258 -
			break;
259 -
		case XK_i:
260 -
			ksym = XK_Tab;
261 -
			break;
262 -
		case XK_j:
263 -
		case XK_m:
264 -
			ksym = XK_Return;
265 -
			break;
266 -
		case XK_k:  /* delete right */
240 +
		case XK_a: ksym = XK_Home;      break;
241 +
		case XK_b: ksym = XK_Left;      break;
242 +
		case XK_c: ksym = XK_Escape;    break;
243 +
		case XK_d: ksym = XK_Delete;    break;
244 +
		case XK_e: ksym = XK_End;       break;
245 +
		case XK_f: ksym = XK_Right;     break;
246 +
		case XK_h: ksym = XK_BackSpace; break;
247 +
		case XK_i: ksym = XK_Tab;       break;
248 +
		case XK_j: ksym = XK_Return;    break;
249 +
		case XK_m: ksym = XK_Return;    break;
250 +
		case XK_n: ksym = XK_Up;        break;
251 +
		case XK_p: ksym = XK_Down;      break;
252 +
253 +
		case XK_k: /* delete right */
267 254
			text[cursor] = '\0';
268 -
			match(False);
269 -
			break;
270 -
		case XK_n:
271 -
			ksym = XK_Down;
272 -
			break;
273 -
		case XK_p:
274 -
			ksym = XK_Up;
255 +
			match();
275 256
			break;
276 -
		case XK_u:  /* delete left */
257 +
		case XK_u: /* delete left */
277 258
			insert(NULL, 0 - cursor);
278 259
			break;
279 -
		case XK_w:  /* delete word */
260 +
		case XK_w: /* delete word */
280 261
			while(cursor > 0 && text[nextrune(-1)] == ' ')
281 262
				insert(NULL, nextrune(-1) - cursor);
282 263
			while(cursor > 0 && text[nextrune(-1)] != ' ')
283 264
				insert(NULL, nextrune(-1) - cursor);
284 265
			break;
285 -
		case XK_y:  /* paste selection */
266 +
		case XK_y: /* paste selection */
286 267
			XConvertSelection(dc->dpy, XA_PRIMARY, utf8, utf8, win, CurrentTime);
268 +
			return;
269 +
		default:
287 270
			return;
288 271
		}
289 272
	}
296 279
		if(text[cursor] == '\0')
297 280
			return;
298 281
		cursor = nextrune(+1);
282 +
		/* fallthrough */
299 283
	case XK_BackSpace:
300 -
		if(cursor > 0)
301 -
			insert(NULL, nextrune(-1) - cursor);
284 +
		if(cursor == 0)
285 +
			return;
286 +
		insert(NULL, nextrune(-1) - cursor);
302 287
		break;
303 288
	case XK_End:
304 289
		if(text[cursor] != '\0') {
330 315
			cursor = nextrune(-1);
331 316
			break;
332 317
		}
333 -
		else if(lines > 0)
334 -
			return;
318 +
		/* fallthrough */
335 319
	case XK_Up:
336 320
		if(sel && sel->left && (sel = sel->left)->right == curr) {
337 321
			curr = prev;
352 336
		break;
353 337
	case XK_Return:
354 338
	case XK_KP_Enter:
355 -
		fputs((sel && !(ev->state & ShiftMask)) ? sel->text : text, stdout);
339 +
		puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
356 340
		exit(EXIT_SUCCESS);
357 341
	case XK_Right:
358 342
		if(text[cursor] != '\0') {
359 343
			cursor = nextrune(+1);
360 344
			break;
361 345
		}
362 -
		else if(lines > 0)
363 -
			return;
346 +
		/* fallthrough */
364 347
	case XK_Down:
365 348
		if(sel && sel->right && (sel = sel->right) == next) {
366 349
			curr = next;
372 355
			return;
373 356
		strncpy(text, sel->text, sizeof text);
374 357
		cursor = strlen(text);
375 -
		match(True);
358 +
		match();
376 359
		break;
377 360
	}
378 361
	drawmenu();
379 362
}
380 363
381 364
void
382 -
match(Bool sub) {
383 -
	size_t len = strlen(text);
384 -
	Item *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend;
385 -
	Item *item, *lnext;
365 +
match(void) {
366 +
	static char **tokv = NULL;
367 +
	static int tokn = 0;
368 +
369 +
	char buf[sizeof text], *s;
370 +
	int i, tokc = 0;
371 +
	size_t len;
372 +
	Item *item, *lprefix, *lsubstr, *prefixend, *substrend;
386 373
387 -
	lexact = lprefix = lsubstr = exactend = prefixend = substrend = NULL;
388 -
	for(item = sub ? matches : items; item && item->text; item = lnext) {
389 -
		lnext = sub ? item->right : item + 1;
390 -
		if(!fstrncmp(text, item->text, len + 1))
391 -
			appenditem(item, &lexact, &exactend);
392 -
		else if(!fstrncmp(text, item->text, len))
374 +
	strcpy(buf, text);
375 +
	for(s = strtok(buf, " "); s; tokv[tokc-1] = s, s = strtok(NULL, " "))
376 +
		if(++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
377 +
			eprintf("cannot realloc %u bytes\n", tokn * sizeof *tokv);
378 +
	len = tokc ? strlen(tokv[0]) : 0;
379 +
380 +
	matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
381 +
	for(item = items; item && item->text; item++) {
382 +
		for(i = 0; i < tokc; i++)
383 +
			if(!fstrstr(item->text, tokv[i]))
384 +
				break;
385 +
		if(i != tokc)
386 +
			continue;
387 +
		if(!tokc || !fstrncmp(tokv[0], item->text, len+1))
388 +
			appenditem(item, &matches, &matchend);
389 +
		else if(!fstrncmp(tokv[0], item->text, len))
393 390
			appenditem(item, &lprefix, &prefixend);
394 -
		else if(fstrstr(item->text, text))
391 +
		else
395 392
			appenditem(item, &lsubstr, &substrend);
396 393
	}
397 -
	matches = lexact;
398 -
	matchend = exactend;
399 -
400 394
	if(lprefix) {
401 -
		if(matchend) {
395 +
		if(matches) {
402 396
			matchend->right = lprefix;
403 397
			lprefix->left = matchend;
404 398
		}
407 401
		matchend = prefixend;
408 402
	}
409 403
	if(lsubstr) {
410 -
		if(matchend) {
404 +
		if(matches) {
411 405
			matchend->right = lsubstr;
412 406
			lsubstr->left = matchend;
413 407
		}
460 454
	if(items)
461 455
		items[i].text = NULL;
462 456
	inputw = maxstr ? textw(dc, maxstr) : 0;
457 +
	lines = MIN(lines, i);
463 458
}
464 459
465 460
void
470 465
		switch(ev.type) {
471 466
		case Expose:
472 467
			if(ev.xexpose.count == 0)
473 -
				drawmenu();
468 +
				mapdc(dc, win, mw, mh);
474 469
			break;
475 470
		case KeyPress:
476 471
			keypress(&ev.xkey);
490 485
setup(void) {
491 486
	int x, y, screen = DefaultScreen(dc->dpy);
492 487
	Window root = RootWindow(dc->dpy, screen);
493 -
	XSetWindowAttributes wa;
488 +
	XSetWindowAttributes swa;
494 489
#ifdef XINERAMA
495 490
	int n;
496 491
	XineramaScreenInfo *info;
511 506
	if((info = XineramaQueryScreens(dc->dpy, &n))) {
512 507
		int i, di;
513 508
		unsigned int du;
514 -
		Window dw;
509 +
		Window w, dw;
510 +
		XWindowAttributes wa;
515 511
516 -
		XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
512 +
		XGetInputFocus(dc->dpy, &w, &di);
513 +
		if(w != root && w != PointerRoot && w != None && XGetWindowAttributes(dc->dpy, w, &wa))
514 +
			XTranslateCoordinates(dc->dpy, w, root, wa.x, wa.y, &x, &y, &dw);
515 +
		else
516 +
			XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
517 517
		for(i = 0; i < n-1; i++)
518 518
			if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
519 519
				break;
531 531
	}
532 532
	promptw = prompt ? textw(dc, prompt) : 0;
533 533
	inputw = MIN(inputw, mw/3);
534 -
	match(False);
534 +
	match();
535 535
536 536
	/* menu window */
537 -
	wa.override_redirect = True;
538 -
	wa.background_pixmap = ParentRelative;
539 -
	wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
537 +
	swa.override_redirect = True;
538 +
	swa.background_pixmap = ParentRelative;
539 +
	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
540 540
	win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0,
541 541
	                    DefaultDepth(dc->dpy, screen), CopyFromParent,
542 542
	                    DefaultVisual(dc->dpy, screen),
543 -
	                    CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
543 +
	                    CWOverrideRedirect | CWBackPixmap | CWEventMask, &swa);
544 544
545 545
	XMapRaised(dc->dpy, win);
546 546
	resizedc(dc, mw, mh);
dmenu_run +1 −1
6 6
		mkdir -p "`dirname "$CACHE"`" && lsx $PATH | sort -u > "$CACHE"
7 7
	fi
8 8
)
9 -
cmd=`dmenu "$@" < "$CACHE"` && exec $cmd
9 +
cmd=`dmenu "$@" < "$CACHE"` && exec sh -c "$cmd"
draw.c +11 −11
96 96
	DC *dc;
97 97
98 98
	if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
99 -
		fprintf(stderr, "no locale support\n");
99 +
		fputs("no locale support\n", stderr);
100 100
	if(!(dc = calloc(1, sizeof *dc)))
101 101
		eprintf("cannot malloc %u bytes:", sizeof *dc);
102 102
	if(!(dc->dpy = XOpenDisplay(NULL)))
120 120
121 121
Bool
122 122
loadfont(DC *dc, const char *fontstr) {
123 -
	char *def, **missing;
123 +
	char *def, **missing, **names;
124 124
	int i, n;
125 +
	XFontStruct **xfonts;
125 126
126 127
	if(!*fontstr)
127 128
		return False;
128 129
	if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
129 -
		char **names;
130 -
		XFontStruct **xfonts;
131 -
132 130
		n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
133 -
		for(i = dc->font.ascent = dc->font.descent = 0; i < n; i++) {
134 -
			dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent);
131 +
		for(i = 0; i < n; i++) {
132 +
			dc->font.ascent  = MAX(dc->font.ascent,  xfonts[i]->ascent);
135 133
			dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
134 +
			dc->font.width   = MAX(dc->font.width,   xfonts[i]->max_bounds.width);
136 135
		}
137 136
	}
138 137
	else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
139 -
		dc->font.ascent = dc->font.xfont->ascent;
138 +
		dc->font.ascent  = dc->font.xfont->ascent;
140 139
		dc->font.descent = dc->font.xfont->descent;
140 +
		dc->font.width   = dc->font.xfont->max_bounds.width;
141 141
	}
142 142
	if(missing)
143 143
		XFreeStringList(missing);
144 -
	return (dc->font.set || dc->font.xfont);
144 +
	return dc->font.set || dc->font.xfont;
145 145
}
146 146
147 147
void
154 154
	if(dc->canvas)
155 155
		XFreePixmap(dc->dpy, dc->canvas);
156 156
157 +
	dc->w = w;
158 +
	dc->h = h;
157 159
	dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
158 160
	                           DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)));
159 -
	dc->w = w;
160 -
	dc->h = h;
161 161
}
162 162
163 163
int
draw.h +1 −0
15 15
		int ascent;
16 16
		int descent;
17 17
		int height;
18 +
		int width;
18 19
		XFontSet set;
19 20
		XFontStruct *xfont;
20 21
	} font;
lsx.c +2 −2
1 1
/* See LICENSE file for copyright and license details. */
2 2
#include <dirent.h>
3 +
#include <limits.h>
3 4
#include <stdio.h>
4 5
#include <stdlib.h>
5 -
#include <string.h>
6 6
#include <unistd.h>
7 7
#include <sys/stat.h>
8 8
31 31
		return;
32 32
	}
33 33
	while((d = readdir(dp)))
34 -
		if(snprintf(buf, sizeof buf, "%s/%s", dir, d->d_name) < sizeof buf
34 +
		if(snprintf(buf, sizeof buf, "%s/%s", dir, d->d_name) < (int)sizeof buf
35 35
		&& !stat(buf, &st) && S_ISREG(st.st_mode) && access(buf, X_OK) == 0)
36 36
			puts(d->d_name);
37 37
	closedir(dp);