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