dmenu

fork of dmenu
git clone git://popovic.xyz/dmenu.git
Log | Files | Refs | README | LICENSE

dmenu.c (22772B)


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