dmenu

Mahdi's build of dmenu
git clone git://mahdi.pw/dmenu.git
Log | Files | Refs | README | LICENSE

dmenu.c (25703B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <locale.h>
      4 #include <stdio.h>
      5 #include <stdlib.h>
      6 #include <string.h>
      7 #include <strings.h>
      8 #include <time.h>
      9 #include <unistd.h>
     10 
     11 #include <X11/Xlib.h>
     12 #include <X11/Xatom.h>
     13 #include <X11/Xproto.h>
     14 #include <X11/Xutil.h>
     15 #include <X11/Xresource.h>
     16 #ifdef XINERAMA
     17 #include <X11/extensions/Xinerama.h>
     18 #endif
     19 #include <X11/Xft/Xft.h>
     20 
     21 #include <fribidi.h>
     22 
     23 #include "drw.h"
     24 #include "util.h"
     25 
     26 /* macros */
     27 #define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
     28                              * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
     29 #define LENGTH(X)             (sizeof X / sizeof X[0])
     30 #define TEXTW(X)              (drw_fontset_getwidth(drw, (X)) + lrpad)
     31 #define NUMBERSMAXDIGITS      100
     32 #define NUMBERSBUFSIZE        (NUMBERSMAXDIGITS * 2) + 1
     33 
     34 #define OPAQUE                0xffU
     35 
     36 /* enums */
     37 enum { SchemeNorm, SchemeSel, SchemeOut, SchemeNormHighlight, SchemeSelHighlight, SchemeOutHighlight, SchemeLast }; /* color schemes */
     38 struct item {
     39 	char *text;
     40 	struct item *left, *right;
     41 	int out;
     42 };
     43 
     44 static char numbers[NUMBERSBUFSIZE] = "";
     45 static char text[BUFSIZ] = "";
     46 static char fribidi_text[BUFSIZ] = "";
     47 static char *embed;
     48 static int bh, mw, mh;
     49 static int inputw = 0, promptw, passwd = 0;
     50 static int lrpad; /* sum of left and right padding */
     51 static size_t cursor;
     52 static struct item *items = NULL;
     53 static struct item *matches, *matchend;
     54 static struct item *prev, *curr, *next, *sel;
     55 static int mon = -1, screen;
     56 
     57 static Atom clip, utf8;
     58 static Display *dpy;
     59 static Window root, parentwin, win;
     60 static XIC xic;
     61 
     62 static Drw *drw;
     63 static Clr *scheme[SchemeLast];
     64 
     65 /* Xresources preferences */
     66 enum resource_type {
     67 	STRING = 0,
     68 	INTEGER = 1,
     69 	FLOAT = 2
     70 };
     71 typedef struct {
     72 	char *name;
     73 	enum resource_type type;
     74 	void *dst;
     75 } ResourcePref;
     76 
     77 static void load_xresources(void);
     78 static void resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst);
     79 
     80 static int useargb = 0;
     81 static Visual *visual;
     82 static int depth;
     83 static Colormap cmap;
     84 
     85 #include "config.h"
     86 
     87 static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
     88 static char *(*fstrstr)(const char *, const char *) = strstr;
     89 static void xinitvisual();
     90 
     91 static unsigned int
     92 textw_clamp(const char *str, unsigned int n)
     93 {
     94 	unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
     95 	return MIN(w, n);
     96 }
     97 
     98 static void
     99 appenditem(struct item *item, struct item **list, struct item **last)
    100 {
    101 	if (*last)
    102 		(*last)->right = item;
    103 	else
    104 		*list = item;
    105 
    106 	item->left = *last;
    107 	item->right = NULL;
    108 	*last = item;
    109 }
    110 
    111 static void
    112 calcoffsets(void)
    113 {
    114 	int i, n;
    115 
    116 	if (lines > 0)
    117 		n = lines * bh;
    118 	else
    119 		n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">") + TEXTW(numbers));
    120 	/* calculate which items will begin the next page and previous page */
    121 	for (i = 0, next = curr; next; next = next->right)
    122 		if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n)
    123 			break;
    124 	for (i = 0, prev = curr; prev && prev->left; prev = prev->left)
    125 		if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n)
    126 			break;
    127 }
    128 
    129 static void
    130 cleanup(void)
    131 {
    132 	size_t i;
    133 
    134 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    135 	for (i = 0; i < SchemeLast; i++)
    136 		free(scheme[i]);
    137 	for (i = 0; items && items[i].text; ++i)
    138 		free(items[i].text);
    139 	free(items);
    140 	drw_free(drw);
    141 	XSync(dpy, False);
    142 	XCloseDisplay(dpy);
    143 }
    144 
    145 static char *
    146 cistrstr(const char *h, const char *n)
    147 {
    148 	size_t i;
    149 
    150 	if (!n[0])
    151 		return (char *)h;
    152 
    153 	for (; *h; ++h) {
    154 		for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
    155 		            tolower((unsigned char)h[i]); ++i)
    156 			;
    157 		if (n[i] == '\0')
    158 			return (char *)h;
    159 	}
    160 	return NULL;
    161 }
    162 
    163 static void
    164 drawhighlights(struct item *item, int x, int y, int maxw)
    165 {
    166 	char restorechar, tokens[sizeof text], *highlight,  *token;
    167 	int indentx, highlightlen;
    168 
    169 	drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : item->out ? SchemeOutHighlight : SchemeNormHighlight]);
    170 	strcpy(tokens, text);
    171 	for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) {
    172 		highlight = fstrstr(item->text, token);
    173 		while (highlight) {
    174 			// Move item str end, calc width for highlight indent, & restore
    175 			highlightlen = highlight - item->text;
    176 			restorechar = *highlight;
    177 			item->text[highlightlen] = '\0';
    178 			indentx = TEXTW(item->text);
    179 			item->text[highlightlen] = restorechar;
    180 
    181 			// Move highlight str end, draw highlight, & restore
    182 			restorechar = highlight[strlen(token)];
    183 			highlight[strlen(token)] = '\0';
    184 			if (indentx - (lrpad / 2) - 1 < maxw)
    185 				drw_text(
    186 					drw,
    187 					x + indentx - (lrpad / 2) - 1,
    188 					y,
    189 					MIN(maxw - indentx, TEXTW(highlight) - lrpad),
    190 					bh, 0, highlight, 0
    191 				);
    192 			highlight[strlen(token)] = restorechar;
    193 
    194 			if (strlen(highlight) - strlen(token) < strlen(token)) break;
    195 			highlight = fstrstr(highlight + strlen(token), token);
    196 		}
    197 	}
    198 }
    199 
    200 static void
    201 apply_fribidi(char *str)
    202 {
    203 	FriBidiStrIndex len = strlen(str);
    204 	FriBidiChar logical[BUFSIZ];
    205 	FriBidiChar visual[BUFSIZ];
    206 	FriBidiParType base = FRIBIDI_PAR_ON;
    207 	FriBidiCharSet charset;
    208 	fribidi_boolean result;
    209 
    210 	fribidi_text[0] = 0;
    211 	if (len > 0) {
    212 		charset = fribidi_parse_charset("UTF-8");
    213 		len = fribidi_charset_to_unicode(charset, str, len, logical);
    214 		result = fribidi_log2vis(logical, len, &base, visual, NULL, NULL, NULL);
    215 		len = fribidi_unicode_to_charset(charset, visual, len, fribidi_text);
    216 	}
    217 }
    218 
    219 static int
    220 drawitem(struct item *item, int x, int y, int w)
    221 {
    222 	if (item == sel)
    223 		drw_setscheme(drw, scheme[SchemeSel]);
    224 	else if (item->out)
    225 		drw_setscheme(drw, scheme[SchemeOut]);
    226 	else
    227 		drw_setscheme(drw, scheme[SchemeNorm]);
    228 
    229 	apply_fribidi(item->text);
    230 	int r = drw_text(drw, x, y, w, bh, lrpad / 2, fribidi_text, 0);
    231 	drawhighlights(item, x, y, w);
    232 	return r;
    233 }
    234 
    235 static void
    236 recalculatenumbers()
    237 {
    238 	unsigned int numer = 0, denom = 0;
    239 	struct item *item;
    240 	if (matchend) {
    241 		numer++;
    242 		for (item = matchend; item && item->left; item = item->left)
    243 			numer++;
    244 	}
    245 	for (item = items; item && item->text; item++)
    246 		denom++;
    247 	snprintf(numbers, NUMBERSBUFSIZE, "%d/%d", numer, denom);
    248 }
    249 
    250 static void
    251 drawmenu(void)
    252 {
    253 	unsigned int curpos;
    254 	struct item *item;
    255 	int x = 0, y = 0, w;
    256 	char *censort;
    257 
    258 	drw_setscheme(drw, scheme[SchemeNorm]);
    259 	drw_rect(drw, 0, 0, mw, mh, 1, 1);
    260 
    261 	if (prompt && *prompt) {
    262 		drw_setscheme(drw, scheme[SchemeSel]);
    263 		x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
    264 	}
    265 	/* draw input field */
    266 	w = (lines > 0 || !matches) ? mw - x : inputw;
    267 	drw_setscheme(drw, scheme[SchemeNorm]);
    268 	if (passwd) {
    269 	        censort = ecalloc(1, sizeof(text));
    270 		memset(censort, '.', strlen(text));
    271 		drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0);
    272 		free(censort);
    273 	} else {
    274 		apply_fribidi(text);
    275 		drw_text(drw, x, 0, w, bh, lrpad / 2, fribidi_text, 0);
    276 	}
    277 
    278 	curpos = TEXTW(text) - TEXTW(&text[cursor]);
    279 	if ((curpos += lrpad / 2 - 1) < w) {
    280 		drw_setscheme(drw, scheme[SchemeNorm]);
    281 		drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
    282 	}
    283 
    284 	recalculatenumbers();
    285 	if (lines > 0) {
    286 		/* draw vertical list */
    287 		for (item = curr; item != next; item = item->right)
    288 			drawitem(item, x - promptw, y += bh, mw);
    289 	} else if (matches) {
    290 		/* draw horizontal list */
    291 		x += inputw;
    292 		w = TEXTW("<");
    293 		if (curr->left) {
    294 			drw_setscheme(drw, scheme[SchemeNorm]);
    295 			drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0);
    296 		}
    297 		x += w;
    298 		for (item = curr; item != next; item = item->right)
    299 			x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">") - TEXTW(numbers)));
    300 		if (next) {
    301 			w = TEXTW(">");
    302 			drw_setscheme(drw, scheme[SchemeNorm]);
    303 			drw_text(drw, mw - w - TEXTW(numbers), 0, w, bh, lrpad / 2, ">", 0);
    304 		}
    305 	}
    306 	drw_setscheme(drw, scheme[SchemeNorm]);
    307 	drw_text(drw, mw - TEXTW(numbers), 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0);
    308 	drw_map(drw, win, 0, 0, mw, mh);
    309 }
    310 
    311 static void
    312 grabfocus(void)
    313 {
    314 	struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000  };
    315 	Window focuswin;
    316 	int i, revertwin;
    317 
    318 	for (i = 0; i < 100; ++i) {
    319 		XGetInputFocus(dpy, &focuswin, &revertwin);
    320 		if (focuswin == win)
    321 			return;
    322 		XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
    323 		nanosleep(&ts, NULL);
    324 	}
    325 	die("cannot grab focus");
    326 }
    327 
    328 static void
    329 grabkeyboard(void)
    330 {
    331 	struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000  };
    332 	int i;
    333 
    334 	if (embed)
    335 		return;
    336 	/* try to grab keyboard, we may have to wait for another process to ungrab */
    337 	for (i = 0; i < 1000; i++) {
    338 		if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
    339 		                  GrabModeAsync, CurrentTime) == GrabSuccess)
    340 			return;
    341 		nanosleep(&ts, NULL);
    342 	}
    343 	die("cannot grab keyboard");
    344 }
    345 
    346 static void
    347 match(void)
    348 {
    349 	static char **tokv = NULL;
    350 	static int tokn = 0;
    351 
    352 	char buf[sizeof text], *s;
    353 	int i, tokc = 0;
    354 	size_t len, textsize;
    355 	struct item *item, *lprefix, *lsubstr, *prefixend, *substrend;
    356 
    357 	strcpy(buf, text);
    358 	/* separate input text into tokens to be matched individually */
    359 	for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " "))
    360 		if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv)))
    361 			die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
    362 	len = tokc ? strlen(tokv[0]) : 0;
    363 
    364 	matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
    365 	textsize = strlen(text) + 1;
    366 	for (item = items; item && item->text; item++) {
    367 		for (i = 0; i < tokc; i++)
    368 			if (!fstrstr(item->text, tokv[i]))
    369 				break;
    370 		if (i != tokc) /* not all tokens match */
    371 			continue;
    372 		/* exact matches go first, then prefixes, then substrings */
    373 		if (!tokc || !fstrncmp(text, item->text, textsize))
    374 			appenditem(item, &matches, &matchend);
    375 		else if (!fstrncmp(tokv[0], item->text, len))
    376 			appenditem(item, &lprefix, &prefixend);
    377 		else
    378 			appenditem(item, &lsubstr, &substrend);
    379 	}
    380 	if (lprefix) {
    381 		if (matches) {
    382 			matchend->right = lprefix;
    383 			lprefix->left = matchend;
    384 		} else
    385 			matches = lprefix;
    386 		matchend = prefixend;
    387 	}
    388 	if (lsubstr) {
    389 		if (matches) {
    390 			matchend->right = lsubstr;
    391 			lsubstr->left = matchend;
    392 		} else
    393 			matches = lsubstr;
    394 		matchend = substrend;
    395 	}
    396 	curr = sel = matches;
    397 	calcoffsets();
    398 }
    399 
    400 static void
    401 insert(const char *str, ssize_t n)
    402 {
    403 	if (strlen(text) + n > sizeof text - 1)
    404 		return;
    405 	/* move existing text out of the way, insert new text, and update cursor */
    406 	memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0));
    407 	if (n > 0)
    408 		memcpy(&text[cursor], str, n);
    409 	cursor += n;
    410 	match();
    411 }
    412 
    413 static size_t
    414 nextrune(int inc)
    415 {
    416 	ssize_t n;
    417 
    418 	/* return location of next utf8 rune in the given direction (+1 or -1) */
    419 	for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc)
    420 		;
    421 	return n;
    422 }
    423 
    424 static void
    425 movewordedge(int dir)
    426 {
    427 	if (dir < 0) { /* move cursor to the start of the word*/
    428 		while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
    429 			cursor = nextrune(-1);
    430 		while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
    431 			cursor = nextrune(-1);
    432 	} else { /* move cursor to the end of the word */
    433 		while (text[cursor] && strchr(worddelimiters, text[cursor]))
    434 			cursor = nextrune(+1);
    435 		while (text[cursor] && !strchr(worddelimiters, text[cursor]))
    436 			cursor = nextrune(+1);
    437 	}
    438 }
    439 
    440 static void
    441 keypress(XKeyEvent *ev)
    442 {
    443 	char buf[64];
    444 	int len;
    445 	KeySym ksym = NoSymbol;
    446 	Status status;
    447 
    448 	len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
    449 	switch (status) {
    450 	default: /* XLookupNone, XBufferOverflow */
    451 		return;
    452 	case XLookupChars: /* composed string from input method */
    453 		goto insert;
    454 	case XLookupKeySym:
    455 	case XLookupBoth: /* a KeySym and a string are returned: use keysym */
    456 		break;
    457 	}
    458 
    459 	if (ev->state & ControlMask) {
    460 		switch(ksym) {
    461 		case XK_a: ksym = XK_Home;      break;
    462 		case XK_b: ksym = XK_Left;      break;
    463 		case XK_c: ksym = XK_Escape;    break;
    464 		case XK_d: ksym = XK_Delete;    break;
    465 		case XK_e: ksym = XK_End;       break;
    466 		case XK_f: ksym = XK_Right;     break;
    467 		case XK_g: ksym = XK_Escape;    break;
    468 		case XK_h: ksym = XK_BackSpace; break;
    469 		case XK_i: ksym = XK_Tab;       break;
    470 		case XK_j: /* fallthrough */
    471 		case XK_J: /* fallthrough */
    472 		case XK_m: /* fallthrough */
    473 		case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
    474 		case XK_n: ksym = XK_Down;      break;
    475 		case XK_p: ksym = XK_Up;        break;
    476 
    477 		case XK_k: /* delete right */
    478 			text[cursor] = '\0';
    479 			match();
    480 			break;
    481 		case XK_u: /* delete left */
    482 			insert(NULL, 0 - cursor);
    483 			break;
    484 		case XK_w: /* delete word */
    485 			while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)]))
    486 				insert(NULL, nextrune(-1) - cursor);
    487 			while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)]))
    488 				insert(NULL, nextrune(-1) - cursor);
    489 			break;
    490 		case XK_y: /* paste selection */
    491 			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
    492 					    utf8, utf8, win, CurrentTime);
    493 			return;
    494 		case XK_Y: /* paste selection from clipboard */
    495 			XConvertSelection(dpy, clip, utf8, utf8, win, CurrentTime);
    496 			return;
    497 		case XK_Left:
    498 		case XK_KP_Left:
    499 			movewordedge(-1);
    500 			goto draw;
    501 		case XK_Right:
    502 		case XK_KP_Right:
    503 			movewordedge(+1);
    504 			goto draw;
    505 		case XK_Return:
    506 		case XK_KP_Enter:
    507 			break;
    508 		case XK_bracketleft:
    509 			cleanup();
    510 			exit(1);
    511 		default:
    512 			return;
    513 		}
    514 	} else if (ev->state & Mod1Mask) {
    515 		switch(ksym) {
    516 		case XK_b:
    517 			movewordedge(-1);
    518 			goto draw;
    519 		case XK_f:
    520 			movewordedge(+1);
    521 			goto draw;
    522 		case XK_g: ksym = XK_Home;  break;
    523 		case XK_G: ksym = XK_End;   break;
    524 		case XK_h: ksym = XK_Up;    break;
    525 		case XK_j: ksym = XK_Next;  break;
    526 		case XK_k: ksym = XK_Prior; break;
    527 		case XK_l: ksym = XK_Down;  break;
    528 		default:
    529 			return;
    530 		}
    531 	}
    532 
    533 	switch(ksym) {
    534 	default:
    535 	insert:
    536 		if (!iscntrl((unsigned char)*buf))
    537 			insert(buf, len);
    538 		break;
    539 	case XK_Delete:
    540 	case XK_KP_Delete:
    541 		if (text[cursor] == '\0')
    542 			return;
    543 		cursor = nextrune(+1);
    544 		/* fallthrough */
    545 	case XK_BackSpace:
    546 		if (cursor == 0)
    547 			return;
    548 		insert(NULL, nextrune(-1) - cursor);
    549 		break;
    550 	case XK_End:
    551 	case XK_KP_End:
    552 		if (text[cursor] != '\0') {
    553 			cursor = strlen(text);
    554 			break;
    555 		}
    556 		if (next) {
    557 			/* jump to end of list and position items in reverse */
    558 			curr = matchend;
    559 			calcoffsets();
    560 			curr = prev;
    561 			calcoffsets();
    562 			while (next && (curr = curr->right))
    563 				calcoffsets();
    564 		}
    565 		sel = matchend;
    566 		break;
    567 	case XK_Escape:
    568 		cleanup();
    569 		exit(1);
    570 	case XK_Home:
    571 	case XK_KP_Home:
    572 		if (sel == matches) {
    573 			cursor = 0;
    574 			break;
    575 		}
    576 		sel = curr = matches;
    577 		calcoffsets();
    578 		break;
    579 	case XK_Left:
    580 	case XK_KP_Left:
    581 		if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
    582 			cursor = nextrune(-1);
    583 			break;
    584 		}
    585 		if (lines > 0)
    586 			return;
    587 		/* fallthrough */
    588 	case XK_Up:
    589 	case XK_KP_Up:
    590 		if (sel && sel->left && (sel = sel->left)->right == curr) {
    591 			curr = prev;
    592 			calcoffsets();
    593 		}
    594 		break;
    595 	case XK_Next:
    596 	case XK_KP_Next:
    597 		if (!next)
    598 			return;
    599 		sel = curr = next;
    600 		calcoffsets();
    601 		break;
    602 	case XK_Prior:
    603 	case XK_KP_Prior:
    604 		if (!prev)
    605 			return;
    606 		sel = curr = prev;
    607 		calcoffsets();
    608 		break;
    609 	case XK_Return:
    610 	case XK_KP_Enter:
    611 		puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
    612 		if (!(ev->state & ControlMask)) {
    613 			cleanup();
    614 			exit(0);
    615 		}
    616 		if (sel)
    617 			sel->out = 1;
    618 		break;
    619 	case XK_Right:
    620 	case XK_KP_Right:
    621 		if (text[cursor] != '\0') {
    622 			cursor = nextrune(+1);
    623 			break;
    624 		}
    625 		if (lines > 0)
    626 			return;
    627 		/* fallthrough */
    628 	case XK_Down:
    629 	case XK_KP_Down:
    630 		if (sel && sel->right && (sel = sel->right) == next) {
    631 			curr = next;
    632 			calcoffsets();
    633 		}
    634 		break;
    635 	case XK_Tab:
    636 		if (!sel)
    637 			return;
    638 		cursor = strnlen(sel->text, sizeof text - 1);
    639 		memcpy(text, sel->text, cursor);
    640 		text[cursor] = '\0';
    641 		match();
    642 		break;
    643 	}
    644 
    645 draw:
    646 	drawmenu();
    647 }
    648 
    649 static void
    650 paste(void)
    651 {
    652 	char *p, *q;
    653 	int di;
    654 	unsigned long dl;
    655 	Atom da;
    656 
    657 	/* we have been given the current selection, now insert it into input */
    658 	if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False,
    659 	                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
    660 	    == Success && p) {
    661 		insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
    662 		XFree(p);
    663 	}
    664 	drawmenu();
    665 }
    666 
    667 static void
    668 readstdin(void)
    669 {
    670 	char *line = NULL;
    671 	size_t i, junk, size = 0;
    672 	ssize_t len;
    673 	if (passwd) {
    674 		inputw = lines = 0;
    675 		return;
    676 	}
    677 
    678 	/* read each line from stdin and add it to the item list */
    679 	for (i = 0; (len = getline(&line, &junk, stdin)) != -1; i++, line = NULL) {
    680 		if (i + 1 >= size / sizeof *items)
    681 			if (!(items = realloc(items, (size += BUFSIZ))))
    682 				die("cannot realloc %zu bytes:", size);
    683 		if (line[len - 1] == '\n')
    684 			line[len - 1] = '\0';
    685 		items[i].text = line;
    686 		items[i].out = 0;
    687 	}
    688 	if (items)
    689 		items[i].text = NULL;
    690 	lines = MIN(lines, i);
    691 }
    692 
    693 void
    694 resource_load(XrmDatabase db, char *name, enum resource_type rtype, void *dst)
    695 {
    696 	char *sdst = NULL;
    697 	int *idst = NULL;
    698 	float *fdst = NULL;
    699 	sdst = dst;
    700 	idst = dst;
    701 	fdst = dst;
    702 	char fullname[256];
    703 	char *type;
    704 	XrmValue ret;
    705 	snprintf(fullname, sizeof(fullname), "%s.%s", "dmenu", name);
    706 	fullname[sizeof(fullname) - 1] = '\0';
    707 	XrmGetResource(db, fullname, "*", &type, &ret);
    708 	if (!(ret.addr == NULL || strncmp("String", type, 64)))
    709 	{
    710 		switch (rtype) {
    711 		case STRING:
    712 			strcpy(sdst, ret.addr);
    713 			break;
    714 		case INTEGER:
    715 			*idst = strtoul(ret.addr, NULL, 10);
    716 			break;
    717 		case FLOAT:
    718 			*fdst = strtof(ret.addr, NULL);
    719 			break;
    720 		}
    721 	}
    722 }
    723 
    724 void
    725 load_xresources(void)
    726 {
    727 	Display *display;
    728 	char *resm;
    729 	XrmDatabase db;
    730 	ResourcePref *p;
    731 	display = XOpenDisplay(NULL);
    732 	resm = XResourceManagerString(display);
    733 	if (!resm)
    734 		return;
    735 	db = XrmGetStringDatabase(resm);
    736 	for (p = resources; p < resources + LENGTH(resources); p++)
    737 		resource_load(db, p->name, p->type, p->dst);
    738 	XCloseDisplay(display);
    739 }
    740 
    741 static void
    742 run(void)
    743 {
    744 	XEvent ev;
    745 	int i;
    746 
    747 	while (!XNextEvent(dpy, &ev)) {
    748 		if (preselected) {
    749 			for (i = 0; i < preselected; i++) {
    750 				if (sel && sel->right && (sel = sel->right) == next) {
    751 					curr = next;
    752 					calcoffsets();
    753 				}
    754 			}
    755 			drawmenu();
    756 			preselected = 0;
    757 		}
    758 		if (XFilterEvent(&ev, win))
    759 			continue;
    760 		switch(ev.type) {
    761 		case DestroyNotify:
    762 			if (ev.xdestroywindow.window != win)
    763 				break;
    764 			cleanup();
    765 			exit(1);
    766 		case Expose:
    767 			if (ev.xexpose.count == 0)
    768 				drw_map(drw, win, 0, 0, mw, mh);
    769 			break;
    770 		case FocusIn:
    771 			/* regrab focus from parent window */
    772 			if (ev.xfocus.window != win)
    773 				grabfocus();
    774 			break;
    775 		case KeyPress:
    776 			keypress(&ev.xkey);
    777 			break;
    778 		case SelectionNotify:
    779 			if (ev.xselection.property == utf8)
    780 				paste();
    781 			break;
    782 		case VisibilityNotify:
    783 			if (ev.xvisibility.state != VisibilityUnobscured)
    784 				XRaiseWindow(dpy, win);
    785 			break;
    786 		}
    787 	}
    788 }
    789 
    790 static void
    791 setup(void)
    792 {
    793 	int x, y, i, j;
    794 	unsigned int du;
    795 	XSetWindowAttributes swa;
    796 	XIM xim;
    797 	Window w, dw, *dws;
    798 	XWindowAttributes wa;
    799 	XClassHint ch = {"dmenu", "dmenu"};
    800 	struct item *item;
    801 #ifdef XINERAMA
    802 	XineramaScreenInfo *info;
    803 	Window pw;
    804 	int a, di, n, area = 0;
    805 #endif
    806 	/* init appearance */
    807 	for (j = 0; j < SchemeLast; j++)
    808 		scheme[j] = drw_scm_create(drw, colors[j], alphas[j], 2);
    809 
    810 	clip = XInternAtom(dpy, "CLIPBOARD",   False);
    811 	utf8 = XInternAtom(dpy, "UTF8_STRING", False);
    812 
    813 	/* calculate menu geometry */
    814 	bh = drw->fonts->h + 2;
    815 	lines = MAX(lines, 0);
    816 	mh = (lines + 1) * bh;
    817 #ifdef XINERAMA
    818 	i = 0;
    819 	if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
    820 		XGetInputFocus(dpy, &w, &di);
    821 		if (mon >= 0 && mon < n)
    822 			i = mon;
    823 		else if (w != root && w != PointerRoot && w != None) {
    824 			/* find top-level window containing current input focus */
    825 			do {
    826 				if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
    827 					XFree(dws);
    828 			} while (w != root && w != pw);
    829 			/* find xinerama screen with which the window intersects most */
    830 			if (XGetWindowAttributes(dpy, pw, &wa))
    831 				for (j = 0; j < n; j++)
    832 					if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
    833 						area = a;
    834 						i = j;
    835 					}
    836 		}
    837 		/* no focused window is on screen, so use pointer location instead */
    838 		if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
    839 			for (i = 0; i < n; i++)
    840 				if (INTERSECT(x, y, 1, 1, info[i]) != 0)
    841 					break;
    842 
    843 		x = info[i].x_org;
    844 		y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
    845 		mw = info[i].width;
    846 
    847 		XFree(info);
    848 	} else
    849 #endif
    850 	{
    851 		if (!XGetWindowAttributes(dpy, parentwin, &wa))
    852 			die("could not get embedding window attributes: 0x%lx",
    853 			    parentwin);
    854 		x = 0;
    855 		y = topbar ? 0 : wa.height - mh;
    856 		mw = wa.width;
    857 	}
    858 	promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
    859 	inputw = mw / 4; /* input width: 25% of monitor width */
    860 	match();
    861 
    862 	/* create menu window */
    863 	swa.override_redirect = True;
    864 	swa.background_pixel = 0;
    865 	swa.border_pixel = 0;
    866 	swa.colormap = cmap;
    867 	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
    868 	win = XCreateWindow(dpy, parentwin, x, y, mw - border_width * 2, mh, border_width,
    869 	                    depth, CopyFromParent, visual,
    870 	                    CWOverrideRedirect | CWBackPixel | CWBorderPixel | CWColormap | CWEventMask, &swa);
    871 	if (border_width)
    872 		XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
    873 	XSetClassHint(dpy, win, &ch);
    874 
    875 
    876 	/* input methods */
    877 	if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
    878 		die("XOpenIM failed: could not open input device");
    879 
    880 	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
    881 	                XNClientWindow, win, XNFocusWindow, win, NULL);
    882 
    883 	XMapRaised(dpy, win);
    884 	if (embed) {
    885 		XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
    886 		if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
    887 			for (i = 0; i < du && dws[i] != win; ++i)
    888 				XSelectInput(dpy, dws[i], FocusChangeMask);
    889 			XFree(dws);
    890 		}
    891 		grabfocus();
    892 	}
    893 	drw_resize(drw, mw, mh);
    894 	drawmenu();
    895 }
    896 
    897 static void
    898 usage(void)
    899 {
    900 	die("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
    901 	      "             [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid] [-n number] [-bw number]");
    902 }
    903 
    904 int
    905 main(int argc, char *argv[])
    906 {
    907 	XWindowAttributes wa;
    908 	int i, fast = 0;
    909 
    910 	XrmInitialize();
    911 	load_xresources();
    912 
    913 	for (i = 1; i < argc; i++)
    914 		/* these options take no arguments */
    915 		if (!strcmp(argv[i], "-v")) {      /* prints version information */
    916 			puts("dmenu-"VERSION);
    917 			exit(0);
    918 		} else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
    919 			topbar = 0;
    920 		else if (!strcmp(argv[i], "-f"))   /* grabs keyboard before reading stdin */
    921 			fast = 1;
    922 		else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
    923 			fstrncmp = strncasecmp;
    924 			fstrstr = cistrstr;
    925 		} else if (!strcmp(argv[i], "-P"))   /* is the input a password */
    926 			passwd = 1;
    927 		else if (i + 1 == argc)
    928 			usage();
    929 		/* these options take one argument */
    930 		else if (!strcmp(argv[i], "-l"))   /* number of lines in vertical list */
    931 			lines = atoi(argv[++i]);
    932 		else if (!strcmp(argv[i], "-m"))
    933 			mon = atoi(argv[++i]);
    934 		else if (!strcmp(argv[i], "-p"))   /* adds prompt to left of input field */
    935 			prompt = argv[++i];
    936 		else if (!strcmp(argv[i], "-fn"))  /* font or font set */
    937 			fonts[0] = argv[++i];
    938 		else if (!strcmp(argv[i], "-nb"))  /* normal background color */
    939 			colors[SchemeNorm][ColBg] = argv[++i];
    940 		else if (!strcmp(argv[i], "-nf"))  /* normal foreground color */
    941 			colors[SchemeNorm][ColFg] = argv[++i];
    942 		else if (!strcmp(argv[i], "-sb"))  /* selected background color */
    943 			colors[SchemeSel][ColBg] = argv[++i];
    944 		else if (!strcmp(argv[i], "-sf"))  /* selected foreground color */
    945 			colors[SchemeSel][ColFg] = argv[++i];
    946 		else if (!strcmp(argv[i], "-w"))   /* embedding window id */
    947 			embed = argv[++i];
    948 		else if (!strcmp(argv[i], "-n"))   /* preselected item */
    949 			preselected = atoi(argv[++i]);
    950 		else if (!strcmp(argv[i], "-bw"))
    951 			border_width = atoi(argv[++i]); /* border width */
    952 		else
    953 			usage();
    954 
    955 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
    956 		fputs("warning: no locale support\n", stderr);
    957 	if (!(dpy = XOpenDisplay(NULL)))
    958 		die("cannot open display");
    959 	screen = DefaultScreen(dpy);
    960 	root = RootWindow(dpy, screen);
    961 	if (!embed || !(parentwin = strtol(embed, NULL, 0)))
    962 		parentwin = root;
    963 	if (!XGetWindowAttributes(dpy, parentwin, &wa))
    964 		die("could not get embedding window attributes: 0x%lx",
    965 		    parentwin);
    966 	xinitvisual();
    967 	drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap);
    968 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
    969 		die("no fonts could be loaded.");
    970 	lrpad = drw->fonts->h;
    971 
    972 #ifdef __OpenBSD__
    973 	if (pledge("stdio rpath", NULL) == -1)
    974 		die("pledge");
    975 #endif
    976 
    977 	if (fast && !isatty(0)) {
    978 		grabkeyboard();
    979 		readstdin();
    980 	} else {
    981 		readstdin();
    982 		grabkeyboard();
    983 	}
    984 	setup();
    985 	run();
    986 
    987 	return 1; /* unreachable */
    988 }
    989 
    990 void
    991 xinitvisual()
    992 {
    993        XVisualInfo *infos;
    994        XRenderPictFormat *fmt;
    995        int nitems;
    996        int i;
    997 
    998        XVisualInfo tpl = {
    999                .screen = screen,
   1000                .depth = 32,
   1001                .class = TrueColor
   1002        };
   1003        long masks = VisualScreenMask | VisualDepthMask | VisualClassMask;
   1004 
   1005        infos = XGetVisualInfo(dpy, masks, &tpl, &nitems);
   1006        visual = NULL;
   1007        for(i = 0; i < nitems; i ++) {
   1008                fmt = XRenderFindVisualFormat(dpy, infos[i].visual);
   1009                if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) {
   1010                        visual = infos[i].visual;
   1011                        depth = infos[i].depth;
   1012                        cmap = XCreateColormap(dpy, root, visual, AllocNone);
   1013                        useargb = 1;
   1014                        break;
   1015                }
   1016        }
   1017 
   1018        XFree(infos);
   1019 
   1020        if (! visual) {
   1021                visual = DefaultVisual(dpy, screen);
   1022                depth = DefaultDepth(dpy, screen);
   1023                cmap = DefaultColormap(dpy, screen);
   1024        }
   1025 }
   1026