dmenu.c (62886B)
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 "patches.h" 20 /* Patch incompatibility overrides */ 21 #if MULTI_SELECTION_PATCH 22 #undef NON_BLOCKING_STDIN_PATCH 23 #undef PIPEOUT_PATCH 24 #undef PRINTINPUTTEXT_PATCH 25 #endif // MULTI_SELECTION_PATCH 26 27 #include "drw.h" 28 #include "util.h" 29 #if GRIDNAV_PATCH 30 #include <stdbool.h> 31 #endif // GRIDNAV_PATCH 32 33 /* macros */ 34 #define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ 35 * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) 36 #if PANGO_PATCH 37 #define TEXTW(X) (drw_font_getwidth(drw, (X), False) + lrpad) 38 #define TEXTWM(X) (drw_font_getwidth(drw, (X), True) + lrpad) 39 #else 40 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) 41 #endif // PANGO_PATCH 42 #if ALPHA_PATCH 43 #define OPAQUE 0xffU 44 #define OPACITY "_NET_WM_WINDOW_OPACITY" 45 #endif // ALPHA_PATCH 46 47 /* enums */ 48 enum { 49 SchemeNorm, 50 SchemeSel, 51 SchemeOut, 52 #if BORDER_PATCH 53 SchemeBorder, 54 #endif // BORDER_PATCH 55 #if MORECOLOR_PATCH 56 SchemeMid, 57 #endif // MORECOLOR_PATCH 58 #if HIGHLIGHT_PATCH 59 SchemeNormHighlight, 60 SchemeSelHighlight, 61 #endif // HIGHLIGHT_PATCH 62 #if HIGHPRIORITY_PATCH 63 SchemeHp, 64 #endif // HIGHPRIORITY_PATCH 65 #if EMOJI_HIGHLIGHT_PATCH 66 SchemeHover, 67 SchemeGreen, 68 SchemeYellow, 69 SchemeBlue, 70 SchemePurple, 71 SchemeRed, 72 #endif // EMOJI_HIGHLIGHT_PATCH 73 #if VI_MODE_PATCH 74 SchemeCursor, 75 #endif // VI_MODE_PATCH 76 #if CARET_SCHEME_PATCH 77 SchemeCaret, 78 #endif // CARET_SCHEME_PATCH 79 SchemeLast, 80 }; /* color schemes */ 81 82 struct item { 83 char *text; 84 #if SEPARATOR_PATCH 85 char *text_output; 86 #elif TSV_PATCH 87 char *stext; 88 #endif // SEPARATOR_PATCH | TSV_PATCH 89 struct item *left, *right; 90 #if NON_BLOCKING_STDIN_PATCH 91 struct item *next; 92 #endif // NON_BLOCKING_STDIN_PATCH 93 #if MULTI_SELECTION_PATCH 94 int id; /* for multiselect */ 95 #else 96 int out; 97 #endif // MULTI_SELECTION_PATCH 98 #if HIGHPRIORITY_PATCH 99 int hp; 100 #endif // HIGHPRIORITY_PATCH 101 #if FUZZYMATCH_PATCH 102 double distance; 103 #endif // FUZZYMATCH_PATCH 104 #if PRINTINDEX_PATCH 105 int index; 106 #endif // PRINTINDEX_PATCH 107 }; 108 109 static char text[BUFSIZ] = ""; 110 #if PIPEOUT_PATCH 111 static char pipeout[8] = " | dmenu"; 112 #endif // PIPEOUT_PATCH 113 static char *embed; 114 #if SEPARATOR_PATCH 115 static char separator; 116 static char * (*sepchr)(const char *, int); 117 static int separator_reverse; 118 #endif // SEPARATOR_PATCH 119 static int bh, mw, mh; 120 #if XYW_PATCH 121 static int dmx = 0, dmy = 0; /* put dmenu at these x and y offsets */ 122 static unsigned int dmw = 0; /* make dmenu this wide */ 123 #endif // XYW_PATCH 124 static int inputw = 0, promptw; 125 #if PASSWORD_PATCH 126 static int passwd = 0; 127 #endif // PASSWORD_PATCH 128 static int lrpad; /* sum of left and right padding */ 129 #if BARPADDING_PATCH 130 static int vp; /* vertical padding for bar */ 131 static int sp; /* side padding for bar */ 132 #endif // BARPADDING_PATCH 133 #if REJECTNOMATCH_PATCH 134 static int reject_no_match = 0; 135 #endif // REJECTNOMATCH_PATCH 136 static size_t cursor; 137 static struct item *items = NULL; 138 static struct item *matches, *matchend; 139 static struct item *prev, *curr, *next, *sel; 140 static int mon = -1, screen; 141 #if PRINTINDEX_PATCH 142 static int print_index = 0; 143 #endif // PRINTINDEX_PATCH 144 #if MANAGED_PATCH 145 static int managed = 0; 146 #endif // MANAGED_PATCH 147 #if MULTI_SELECTION_PATCH 148 static int *selid = NULL; 149 static unsigned int selidsize = 0; 150 #endif // MULTI_SELECTION_PATCH 151 #if NO_SORT_PATCH 152 static unsigned int sortmatches = 1; 153 #endif // NO_SORT_PATCH 154 #if PRINTINPUTTEXT_PATCH 155 static int use_text_input = 0; 156 #endif // PRINTINPUTTEXT_PATCH 157 #if PRESELECT_PATCH 158 static unsigned int preselected = 0; 159 #endif // PRESELECT_PATCH 160 #if EMOJI_HIGHLIGHT_PATCH 161 static int commented = 0; 162 static int animated = 0; 163 #endif // EMOJI_HIGHLIGHT_PATCH 164 165 static Atom clip, utf8; 166 #if WMTYPE_PATCH 167 static Atom type, dock; 168 #endif // WMTYPE_PATCH 169 static Display *dpy; 170 static Window root, parentwin, win; 171 static XIC xic; 172 173 #if ALPHA_PATCH 174 static int useargb = 0; 175 static Visual *visual; 176 static int depth; 177 static Colormap cmap; 178 #endif // ALPHA_PATCH 179 180 static Drw *drw; 181 static Clr *scheme[SchemeLast]; 182 183 #include "patch/include.h" 184 185 #include "config.h" 186 187 static unsigned int 188 textw_clamp(const char *str, unsigned int n) 189 { 190 unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad; 191 return MIN(w, n); 192 } 193 194 static void appenditem(struct item *item, struct item **list, struct item **last); 195 static void calcoffsets(void); 196 static void cleanup(void); 197 static char * cistrstr(const char *s, const char *sub); 198 static int drawitem(struct item *item, int x, int y, int w); 199 static void drawmenu(void); 200 static void grabfocus(void); 201 static void grabkeyboard(void); 202 static void match(void); 203 static void insert(const char *str, ssize_t n); 204 static size_t nextrune(int inc); 205 static void movewordedge(int dir); 206 static void keypress(XKeyEvent *ev); 207 static void paste(void); 208 static void printitem(struct item *item); 209 static void printtext(char *text); 210 static void printcurrent(unsigned int state); 211 #if ALPHA_PATCH 212 static void xinitvisual(void); 213 #endif // ALPHA_PATCH 214 static void readstdin(void); 215 static void run(void); 216 static void setup(void); 217 static void usage(void); 218 219 #if CASEINSENSITIVE_PATCH 220 static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp; 221 static char *(*fstrstr)(const char *, const char *) = cistrstr; 222 #else 223 static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 224 static char *(*fstrstr)(const char *, const char *) = strstr; 225 #endif // CASEINSENSITIVE_PATCH 226 227 #include "patch/include.c" 228 229 static void 230 appenditem(struct item *item, struct item **list, struct item **last) 231 { 232 if (*last) 233 (*last)->right = item; 234 else 235 *list = item; 236 237 item->left = *last; 238 item->right = NULL; 239 *last = item; 240 } 241 242 static void 243 calcoffsets(void) 244 { 245 int i, n, rpad = 0; 246 247 if (lines > 0) { 248 #if GRID_PATCH 249 if (columns) 250 n = lines * columns * bh; 251 else 252 n = lines * bh; 253 #else 254 n = lines * bh; 255 #endif // GRID_PATCH 256 } else { 257 #if NUMBERS_PATCH 258 rpad = TEXTW(numbers); 259 #endif // NUMBERS_PATCH 260 #if SYMBOLS_PATCH 261 n = mw - (promptw + inputw + TEXTW(symbol_1) + TEXTW(symbol_2) + rpad); 262 #else 263 n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">") + rpad); 264 #endif // SYMBOLS_PATCH 265 } 266 /* calculate which items will begin the next page and previous page */ 267 for (i = 0, next = curr; next; next = next->right) 268 if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) 269 break; 270 for (i = 0, prev = curr; prev && prev->left; prev = prev->left) 271 if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) 272 break; 273 } 274 275 static void 276 cleanup(void) 277 { 278 size_t i; 279 280 XUngrabKeyboard(dpy, CurrentTime); 281 #if INPUTMETHOD_PATCH 282 XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); 283 #endif // INPUTMETHOD_PATCH 284 for (i = 0; i < SchemeLast; i++) 285 drw_scm_free(drw, scheme[i], 2); 286 287 #if NAVHISTORY_PATCH 288 savehistory(); 289 cleanhistory(); 290 restorebackupitems(); 291 #endif // NAVHISTORY_PATCH 292 293 for (i = 0; items && items[i].text; ++i) { 294 #if SEPARATOR_PATCH 295 free(separator_reverse ? items[i].text_output : items[i].text); 296 #else 297 free(items[i].text); 298 #endif // SEPARATOR_PATCH 299 } 300 free(items); 301 302 #if HIGHPRIORITY_PATCH 303 for (i = 0; i < hplength; ++i) 304 free(hpitems[i]); 305 free(hpitems); 306 #endif // HIGHPRIORITY_PATCH 307 drw_free(drw); 308 XSync(dpy, False); 309 XCloseDisplay(dpy); 310 #if MULTI_SELECTION_PATCH 311 free(selid); 312 #endif // MULTI_SELECTION_PATCH 313 } 314 315 static char * 316 cistrstr(const char *s, const char *sub) 317 { 318 size_t len; 319 320 for (len = strlen(sub); *s; s++) 321 if (!strncasecmp(s, sub, len)) 322 return (char *)s; 323 return NULL; 324 } 325 326 static int 327 drawitem(struct item *item, int x, int y, int w) 328 { 329 int r; 330 #if TSV_PATCH && !SEPARATOR_PATCH 331 char *text = item->stext; 332 #else 333 char *text = item->text; 334 #endif // TSV_PATCH 335 336 #if EMOJI_HIGHLIGHT_PATCH 337 int iscomment = 0; 338 if (text[0] == '>') { 339 if (text[1] == '>') { 340 iscomment = 3; 341 switch (text[2]) { 342 case 'r': 343 drw_setscheme(drw, scheme[SchemeRed]); 344 break; 345 case 'g': 346 drw_setscheme(drw, scheme[SchemeGreen]); 347 break; 348 case 'y': 349 drw_setscheme(drw, scheme[SchemeYellow]); 350 break; 351 case 'b': 352 drw_setscheme(drw, scheme[SchemeBlue]); 353 break; 354 case 'p': 355 drw_setscheme(drw, scheme[SchemePurple]); 356 break; 357 #if HIGHLIGHT_PATCH 358 case 'h': 359 drw_setscheme(drw, scheme[SchemeNormHighlight]); 360 break; 361 #endif // HIGHLIGHT_PATCH 362 case 's': 363 drw_setscheme(drw, scheme[SchemeSel]); 364 break; 365 default: 366 iscomment = 1; 367 drw_setscheme(drw, scheme[SchemeNorm]); 368 break; 369 } 370 } else { 371 drw_setscheme(drw, scheme[SchemeNorm]); 372 iscomment = 1; 373 } 374 } else if (text[0] == ':') { 375 iscomment = 2; 376 if (item == sel) { 377 switch (text[1]) { 378 case 'r': 379 drw_setscheme(drw, scheme[SchemeRed]); 380 break; 381 case 'g': 382 drw_setscheme(drw, scheme[SchemeGreen]); 383 break; 384 case 'y': 385 drw_setscheme(drw, scheme[SchemeYellow]); 386 break; 387 case 'b': 388 drw_setscheme(drw, scheme[SchemeBlue]); 389 break; 390 case 'p': 391 drw_setscheme(drw, scheme[SchemePurple]); 392 break; 393 #if HIGHLIGHT_PATCH 394 case 'h': 395 drw_setscheme(drw, scheme[SchemeNormHighlight]); 396 break; 397 #endif // HIGHLIGHT_PATCH 398 case 's': 399 drw_setscheme(drw, scheme[SchemeSel]); 400 break; 401 default: 402 drw_setscheme(drw, scheme[SchemeSel]); 403 iscomment = 0; 404 break; 405 } 406 } else { 407 drw_setscheme(drw, scheme[SchemeNorm]); 408 } 409 } 410 #endif // EMOJI_HIGHLIGHT_PATCH 411 412 #if EMOJI_HIGHLIGHT_PATCH 413 int temppadding = 0; 414 if (iscomment == 2) { 415 if (text[2] == ' ') { 416 #if PANGO_PATCH 417 temppadding = drw->font->h * 3; 418 #else 419 temppadding = drw->fonts->h * 3; 420 #endif // PANGO_PATCH 421 animated = 1; 422 char dest[1000]; 423 strcpy(dest, text); 424 dest[6] = '\0'; 425 drw_text(drw, x, y 426 , temppadding 427 #if LINE_HEIGHT_PATCH 428 , MAX(lineheight, bh) 429 #else 430 , bh 431 #endif // LINE_HEIGHT_PATCH 432 , temppadding / 2.6 433 , dest + 3 434 , 0 435 #if PANGO_PATCH 436 , True 437 #endif // PANGO_PATCH 438 ); 439 iscomment = 6; 440 drw_setscheme(drw, sel == item ? scheme[SchemeHover] : scheme[SchemeNorm]); 441 } 442 } 443 444 char *output; 445 if (commented) { 446 static char onestr[2]; 447 onestr[0] = text[0]; 448 onestr[1] = '\0'; 449 output = onestr; 450 } else { 451 output = text; 452 } 453 #endif // EMOJI_HIGHLIGHT_PATCH 454 455 if (item == sel) 456 drw_setscheme(drw, scheme[SchemeSel]); 457 #if HIGHPRIORITY_PATCH 458 else if (item->hp) 459 drw_setscheme(drw, scheme[SchemeHp]); 460 #endif // HIGHPRIORITY_PATCH 461 #if MORECOLOR_PATCH 462 else if (item->left == sel || item->right == sel) 463 drw_setscheme(drw, scheme[SchemeMid]); 464 #endif // MORECOLOR_PATCH 465 #if MULTI_SELECTION_PATCH 466 else if (issel(item->id)) 467 #else 468 else if (item->out) 469 #endif // MULTI_SELECTION_PATCH 470 drw_setscheme(drw, scheme[SchemeOut]); 471 else 472 drw_setscheme(drw, scheme[SchemeNorm]); 473 474 r = drw_text(drw 475 #if EMOJI_HIGHLIGHT_PATCH 476 , x + ((iscomment == 6) ? temppadding : 0) 477 #else 478 , x 479 #endif // EMOJI_HIGHLIGHT_PATCH 480 , y 481 , w 482 , bh 483 #if EMOJI_HIGHLIGHT_PATCH 484 , commented ? (bh - TEXTW(output) - lrpad) / 2 : lrpad / 2 485 #else 486 , lrpad / 2 487 #endif // EMOJI_HIGHLIGHT_PATCH 488 #if EMOJI_HIGHLIGHT_PATCH 489 , output + iscomment 490 #else 491 , text 492 #endif // EMOJI_HIGHLIGHT_PATCH 493 , 0 494 #if PANGO_PATCH 495 , True 496 #endif // PANGO_PATCH 497 ); 498 #if HIGHLIGHT_PATCH 499 #if EMOJI_HIGHLIGHT_PATCH 500 drawhighlights(item, output + iscomment, x + ((iscomment == 6) ? temppadding : 0), y, w); 501 #else 502 drawhighlights(item, x, y, w); 503 #endif // EMOJI_HIGHLIGHT_PATCH 504 #endif // HIGHLIGHT_PATCH 505 return r; 506 } 507 508 static void 509 drawmenu(void) 510 { 511 #if SCROLL_PATCH 512 static int curpos, oldcurlen; 513 int curlen, rcurlen; 514 #else 515 unsigned int curpos; 516 #endif // SCROLL_PATCH 517 struct item *item; 518 int x = 0, y = 0, w, rpad = 0, itw = 0, stw = 0; 519 #if LINE_HEIGHT_PATCH && PANGO_PATCH 520 int fh = drw->font->h; 521 #elif LINE_HEIGHT_PATCH 522 int fh = drw->fonts->h; 523 #endif // LINE_HEIGHT_PATCH 524 #if PASSWORD_PATCH 525 char *censort; 526 #endif // PASSWORD_PATCH 527 528 drw_setscheme(drw, scheme[SchemeNorm]); 529 drw_rect(drw, 0, 0, mw, mh, 1, 1); 530 531 if (prompt && *prompt) { 532 #if !PLAIN_PROMPT_PATCH 533 drw_setscheme(drw, scheme[SchemeSel]); 534 #endif // PLAIN_PROMPT_PATCH 535 x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0 536 #if PANGO_PATCH 537 , True 538 #endif // PANGO_PATCH 539 ); 540 } 541 /* draw input field */ 542 w = (lines > 0 || !matches) ? mw - x : inputw; 543 544 #if SCROLL_PATCH 545 w -= lrpad / 2; 546 x += lrpad / 2; 547 rcurlen = TEXTW(text + cursor) - lrpad; 548 curlen = TEXTW(text) - lrpad - rcurlen; 549 curpos += curlen - oldcurlen; 550 curpos = MIN(w, MAX(0, curpos)); 551 curpos = MAX(curpos, w - rcurlen); 552 curpos = MIN(curpos, curlen); 553 oldcurlen = curlen; 554 555 drw_setscheme(drw, scheme[SchemeNorm]); 556 #if PASSWORD_PATCH 557 if (passwd) { 558 censort = ecalloc(1, sizeof(text)); 559 memset(censort, '.', strlen(text)); 560 drw_text_align(drw, x, 0, curpos, bh, censort, cursor, AlignR); 561 drw_text_align(drw, x + curpos, 0, w - curpos, bh, censort + cursor, strlen(censort) - cursor, AlignL); 562 free(censort); 563 } else { 564 drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR); 565 drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL); 566 } 567 #else 568 drw_text_align(drw, x, 0, curpos, bh, text, cursor, AlignR); 569 drw_text_align(drw, x + curpos, 0, w - curpos, bh, text + cursor, strlen(text) - cursor, AlignL); 570 #endif // PASSWORD_PATCH 571 572 #if VI_MODE_PATCH 573 if (using_vi_mode && text[0] != '\0') { 574 drw_setscheme(drw, scheme[SchemeCursor]); 575 char vi_char[] = {text[cursor], '\0'}; 576 drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0 577 #if PANGO_PATCH 578 , False 579 #endif // PANGO_PATCH 580 ); 581 } else if (using_vi_mode) { 582 drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0); 583 } else 584 #endif // VI_MODE_PATCH 585 #if LINE_HEIGHT_PATCH 586 drw_rect(drw, x + curpos - 1, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); 587 #else 588 drw_rect(drw, x + curpos - 1, 2, 2, bh - 4, 1, 0); 589 #endif // LINE_HEIGHT_PATCH 590 #else // !SCROLL_PATCH 591 drw_setscheme(drw, scheme[SchemeNorm]); 592 #if PASSWORD_PATCH 593 if (passwd) { 594 censort = ecalloc(1, sizeof(text)); 595 memset(censort, '.', strlen(text)); 596 drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0 597 #if PANGO_PATCH 598 , False 599 #endif // PANGO_PATCH 600 ); 601 drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0 602 #if PANGO_PATCH 603 , False 604 #endif // PANGO_PATCH 605 ); 606 free(censort); 607 } else { 608 drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0 609 #if PANGO_PATCH 610 , False 611 #endif // PANGO_PATCH 612 ); 613 } 614 #else 615 drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0 616 #if PANGO_PATCH 617 , False 618 #endif // PANGO_PATCH 619 ); 620 #endif // PASSWORD_PATCH 621 622 curpos = TEXTW(text) - TEXTW(&text[cursor]); 623 curpos += lrpad / 2 - 1; 624 625 #if VI_MODE_PATCH 626 if (using_vi_mode && text[0] != '\0') { 627 drw_setscheme(drw, scheme[SchemeCursor]); 628 char vi_char[] = {text[cursor], '\0'}; 629 drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0 630 #if PANGO_PATCH 631 , False 632 #endif // PANGO_PATCH 633 ); 634 } else if (using_vi_mode) { 635 drw_setscheme(drw, scheme[SchemeNorm]); 636 drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0); 637 } else 638 #endif // VI_MODE_PATCH 639 if (curpos < w) { 640 #if CARET_SCHEME_PATCH 641 drw_setscheme(drw, scheme[SchemeCaret]); 642 #else 643 drw_setscheme(drw, scheme[SchemeNorm]); 644 #endif // CARET_SCHEME_PATCH 645 #if CARET_WIDTH_PATCH && LINE_HEIGHT_PATCH 646 drw_rect(drw, x + curpos, 2 + (bh-fh)/2, caret_width, fh - 4, 1, 0); 647 #elif CARET_WIDTH_PATCH 648 drw_rect(drw, x + curpos, 2, caret_width, bh - 4, 1, 0); 649 #elif LINE_HEIGHT_PATCH 650 drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0); 651 #else 652 drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); 653 #endif // LINE_HEIGHT_PATCH 654 } 655 #endif // SCROLL_PATCH 656 657 #if NUMBERS_PATCH 658 recalculatenumbers(); 659 rpad = TEXTW(numbers); 660 #if BARPADDING_PATCH 661 rpad += 2 * sp; 662 #endif // BARPADDING_PATCH 663 #if BORDER_PATCH 664 rpad += border_width; 665 #endif // BORDER_PATCH 666 #endif // NUMBERS_PATCH 667 668 #if QUIET_PATCH 669 if (quiet && strlen(text) == 0) { 670 #if DYNAMIC_HEIGHT_PATCH 671 if (lines > 0) 672 XResizeWindow(dpy, win, mw, bh); 673 #endif // DYNAMIC_HEIGHT_PATCH 674 goto skip_item_listing; 675 } 676 #endif // QUIET_PATCH 677 678 if (lines > 0) { 679 #if DYNAMIC_HEIGHT_PATCH || GRID_PATCH 680 int i = 0; 681 #endif // DYNAMIC_HEIGHT_PATCH | GRID_PATCH 682 683 #if GRID_PATCH 684 /* draw grid */ 685 for (item = curr; item != next; item = item->right, i++) { 686 if (columns) { 687 #if VERTFULL_PATCH 688 drawitem( 689 item, 690 0 + ((i / lines) * (mw / columns)), 691 y + (((i % lines) + 1) * bh), 692 mw / columns 693 ); 694 #else 695 drawitem( 696 item, 697 x + ((i / lines) * ((mw - x) / columns)), 698 y + (((i % lines) + 1) * bh), 699 (mw - x) / columns 700 ); 701 #endif // VERTFULL_PATCH 702 } else { 703 #if VERTFULL_PATCH 704 drawitem(item, 0, y += bh, mw); 705 #else 706 drawitem(item, x, y += bh, mw - x); 707 #endif // VERTFULL_PATCH 708 } 709 } 710 #if DYNAMIC_HEIGHT_PATCH 711 if (columns) { 712 XResizeWindow(dpy, win, mw, (MIN(i, lines) + 1) * bh); 713 } else { 714 XResizeWindow(dpy, win, mw, (i + 1) * bh); 715 } 716 #endif // DYNAMIC_HEIGHT_PATCH 717 718 #else 719 /* draw vertical list */ 720 for (item = curr; item != next; item = item->right) { 721 #if DYNAMIC_HEIGHT_PATCH 722 i++; 723 #endif // DYNAMIC_HEIGHT_PATCH 724 #if VERTFULL_PATCH 725 drawitem(item, 0, y += bh, mw); 726 #else 727 drawitem(item, x, y += bh, mw - x); 728 #endif // VERTFULL_PATCH 729 } 730 #if DYNAMIC_HEIGHT_PATCH 731 XResizeWindow(dpy, win, mw, (i + 1) * bh); 732 #endif // DYNAMIC_HEIGHT_PATCH 733 #endif // GRID_PATCH 734 } else if (matches) { 735 /* draw horizontal list */ 736 x += inputw; 737 #if SYMBOLS_PATCH 738 w = TEXTW(symbol_1); 739 #else 740 w = TEXTW("<"); 741 #endif // SYMBOLS_PATCH 742 if (curr->left) { 743 drw_setscheme(drw, scheme[SchemeNorm]); 744 #if SYMBOLS_PATCH 745 drw_text(drw, x, 0, w, bh, lrpad / 2, symbol_1, 0 746 #else 747 drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0 748 #endif // SYMBOLS_PATCH 749 #if PANGO_PATCH 750 , True 751 #endif // PANGO_PATCH 752 ); 753 } 754 x += w; 755 for (item = curr; item != next; item = item->right) { 756 #if SYMBOLS_PATCH 757 stw = TEXTW(symbol_2); 758 #else 759 stw = TEXTW(">"); 760 #endif // SYMBOLS_PATCH 761 #if TSV_PATCH && !SEPARATOR_PATCH 762 itw = textw_clamp(item->stext, mw - x - stw - rpad); 763 #else 764 itw = textw_clamp(item->text, mw - x - stw - rpad); 765 #endif // TSV_PATCH 766 x = drawitem(item, x, 0, itw); 767 } 768 if (next) { 769 #if SYMBOLS_PATCH 770 w = TEXTW(symbol_2); 771 #else 772 w = TEXTW(">"); 773 #endif // SYMBOLS_PATCH 774 drw_setscheme(drw, scheme[SchemeNorm]); 775 drw_text(drw, mw - w - rpad, 0, w, bh, lrpad / 2 776 #if SYMBOLS_PATCH 777 , symbol_2 778 #else 779 , ">" 780 #endif // SYMBOLS_PATCH 781 , 0 782 #if PANGO_PATCH 783 , True 784 #endif // PANGO_PATCH 785 ); 786 } 787 } 788 789 #if QUIET_PATCH 790 skip_item_listing: 791 #endif // QUIET_PATCH 792 793 #if NUMBERS_PATCH 794 drw_setscheme(drw, scheme[SchemeNorm]); 795 #if PANGO_PATCH 796 drw_text(drw, mw - rpad, 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0, False); 797 #else 798 drw_text(drw, mw - rpad, 0, TEXTW(numbers), bh, lrpad / 2, numbers, 0); 799 #endif // PANGO_PATCH 800 #endif // NUMBERS_PATCH 801 drw_map(drw, win, 0, 0, mw, mh); 802 #if NON_BLOCKING_STDIN_PATCH 803 XFlush(dpy); 804 #endif // NON_BLOCKING_STDIN_PATCH 805 } 806 807 static void 808 grabfocus(void) 809 { 810 struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; 811 Window focuswin; 812 int i, revertwin; 813 814 for (i = 0; i < 100; ++i) { 815 XGetInputFocus(dpy, &focuswin, &revertwin); 816 if (focuswin == win) 817 return; 818 #if !MANAGED_PATCH 819 XSetInputFocus(dpy, win, RevertToParent, CurrentTime); 820 #endif // MANAGED_PATCH 821 nanosleep(&ts, NULL); 822 } 823 die("cannot grab focus"); 824 } 825 826 static void 827 grabkeyboard(void) 828 { 829 struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; 830 int i; 831 832 #if MANAGED_PATCH 833 if (embed || managed) 834 #else 835 if (embed) 836 #endif // MANAGED_PATCH 837 return; 838 /* try to grab keyboard, we may have to wait for another process to ungrab */ 839 for (i = 0; i < 1000; i++) { 840 if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, 841 GrabModeAsync, CurrentTime) == GrabSuccess) { 842 #if MOUSE_SUPPORT_PATCH 843 /* one off attempt at grabbing the mouse pointer to avoid interactions 844 * with other windows while dmenu is active */ 845 XGrabPointer(dpy, DefaultRootWindow(dpy), True, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); 846 #endif // MOUSE_SUPPORT_PATCH 847 return; 848 } 849 nanosleep(&ts, NULL); 850 } 851 die("cannot grab keyboard"); 852 } 853 854 static void 855 match(void) 856 { 857 #if DYNAMIC_OPTIONS_PATCH 858 if (dynamic && *dynamic) 859 refreshoptions(); 860 #endif // DYNAMIC_OPTIONS_PATCH 861 862 #if FUZZYMATCH_PATCH 863 if (fuzzy) { 864 fuzzymatch(); 865 return; 866 } 867 #endif 868 static char **tokv = NULL; 869 static int tokn = 0; 870 871 char buf[sizeof text], *s; 872 int i, tokc = 0; 873 size_t len, textsize; 874 struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; 875 #if HIGHPRIORITY_PATCH 876 struct item *lhpprefix, *hpprefixend; 877 #endif // HIGHPRIORITY_PATCH 878 #if NON_BLOCKING_STDIN_PATCH 879 int preserve = 0; 880 #endif // NON_BLOCKING_STDIN_PATCH 881 882 strcpy(buf, text); 883 /* separate input text into tokens to be matched individually */ 884 for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) 885 if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) 886 die("cannot realloc %zu bytes:", tokn * sizeof *tokv); 887 len = tokc ? strlen(tokv[0]) : 0; 888 889 #if PREFIXCOMPLETION_PATCH 890 if (use_prefix) { 891 matches = lprefix = matchend = prefixend = NULL; 892 textsize = strlen(text); 893 } else { 894 matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; 895 textsize = strlen(text) + 1; 896 } 897 #else 898 matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; 899 textsize = strlen(text) + 1; 900 #endif // PREFIXCOMPLETION_PATCH 901 #if HIGHPRIORITY_PATCH 902 lhpprefix = hpprefixend = NULL; 903 #endif // HIGHPRIORITY_PATCH 904 #if NON_BLOCKING_STDIN_PATCH && DYNAMIC_OPTIONS_PATCH 905 for (item = items; item && (!(dynamic && *dynamic) || item->text); item = (dynamic && *dynamic) ? item + 1 : item->next) 906 #elif NON_BLOCKING_STDIN_PATCH 907 for (item = items; item; item = item->next) 908 #else 909 for (item = items; item && item->text; item++) 910 #endif 911 { 912 for (i = 0; i < tokc; i++) 913 if (!fstrstr(item->text, tokv[i])) 914 break; 915 #if DYNAMIC_OPTIONS_PATCH 916 if (i != tokc && !(dynamic && *dynamic)) /* not all tokens match */ 917 continue; 918 #else 919 if (i != tokc) /* not all tokens match */ 920 continue; 921 #endif // DYNAMIC_OPTIONS_PATCH 922 #if HIGHPRIORITY_PATCH 923 /* exact matches go first, then prefixes with high priority, then prefixes, then substrings */ 924 #else 925 /* exact matches go first, then prefixes, then substrings */ 926 #endif // HIGHPRIORITY_PATCH 927 #if NO_SORT_PATCH 928 if (!sortmatches) 929 appenditem(item, &matches, &matchend); 930 else 931 #endif // NO_SORT_PATCH 932 if (!tokc || !fstrncmp(text, item->text, textsize)) 933 appenditem(item, &matches, &matchend); 934 #if HIGHPRIORITY_PATCH 935 else if (item->hp && !fstrncmp(tokv[0], item->text, len)) 936 appenditem(item, &lhpprefix, &hpprefixend); 937 #endif // HIGHPRIORITY_PATCH 938 else if (!fstrncmp(tokv[0], item->text, len)) 939 appenditem(item, &lprefix, &prefixend); 940 #if PREFIXCOMPLETION_PATCH 941 else if (!use_prefix) 942 #else 943 else 944 #endif // PREFIXCOMPLETION_PATCH 945 appenditem(item, &lsubstr, &substrend); 946 #if NON_BLOCKING_STDIN_PATCH 947 if (sel == item) 948 preserve = 1; 949 #endif // NON_BLOCKING_STDIN_PATCH 950 } 951 #if HIGHPRIORITY_PATCH 952 if (lhpprefix) { 953 if (matches) { 954 matchend->right = lhpprefix; 955 lhpprefix->left = matchend; 956 } else 957 matches = lhpprefix; 958 matchend = hpprefixend; 959 } 960 #endif // HIGHPRIORITY_PATCH 961 if (lprefix) { 962 if (matches) { 963 matchend->right = lprefix; 964 lprefix->left = matchend; 965 } else 966 matches = lprefix; 967 matchend = prefixend; 968 } 969 #if PREFIXCOMPLETION_PATCH 970 if (!use_prefix && lsubstr) 971 #else 972 if (lsubstr) 973 #endif // PREFIXCOMPLETION_PATCH 974 { 975 if (matches) { 976 matchend->right = lsubstr; 977 lsubstr->left = matchend; 978 } else 979 matches = lsubstr; 980 matchend = substrend; 981 } 982 #if NON_BLOCKING_STDIN_PATCH 983 if (!preserve) 984 #endif // NON_BLOCKING_STDIN_PATCH 985 curr = sel = matches; 986 987 #if INSTANT_PATCH 988 if (instant && matches && matches==matchend && !lsubstr) { 989 printitem(matches); 990 cleanup(); 991 exit(0); 992 } 993 #endif // INSTANT_PATCH 994 995 calcoffsets(); 996 } 997 998 static void 999 insert(const char *str, ssize_t n) 1000 { 1001 if (strlen(text) + n > sizeof text - 1) 1002 return; 1003 1004 #if REJECTNOMATCH_PATCH 1005 static char last[BUFSIZ] = ""; 1006 if (reject_no_match) { 1007 /* store last text value in case we need to revert it */ 1008 memcpy(last, text, BUFSIZ); 1009 } 1010 #endif // REJECTNOMATCH_PATCH 1011 1012 /* move existing text out of the way, insert new text, and update cursor */ 1013 memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); 1014 if (n > 0) 1015 memcpy(&text[cursor], str, n); 1016 cursor += n; 1017 match(); 1018 1019 #if REJECTNOMATCH_PATCH 1020 if (!matches && reject_no_match) { 1021 /* revert to last text value if theres no match */ 1022 memcpy(text, last, BUFSIZ); 1023 cursor -= n; 1024 match(); 1025 } 1026 #endif // REJECTNOMATCH_PATCH 1027 } 1028 1029 static size_t 1030 nextrune(int inc) 1031 { 1032 ssize_t n; 1033 1034 /* return location of next utf8 rune in the given direction (+1 or -1) */ 1035 for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) 1036 ; 1037 return n; 1038 } 1039 1040 static void 1041 movewordedge(int dir) 1042 { 1043 if (dir < 0) { /* move cursor to the start of the word*/ 1044 while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 1045 cursor = nextrune(-1); 1046 while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 1047 cursor = nextrune(-1); 1048 } else { /* move cursor to the end of the word */ 1049 while (text[cursor] && strchr(worddelimiters, text[cursor])) 1050 cursor = nextrune(+1); 1051 while (text[cursor] && !strchr(worddelimiters, text[cursor])) 1052 cursor = nextrune(+1); 1053 } 1054 } 1055 1056 static void 1057 keypress(XKeyEvent *ev) 1058 { 1059 char buf[64]; 1060 int len; 1061 #if PREFIXCOMPLETION_PATCH 1062 struct item * item; 1063 #endif // PREFIXCOMPLETION_PATCH 1064 KeySym ksym = NoSymbol; 1065 Status status; 1066 #if GRID_PATCH && GRIDNAV_PATCH 1067 int i; 1068 struct item *tmpsel; 1069 bool offscreen = false; 1070 #endif // GRIDNAV_PATCH 1071 1072 len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); 1073 switch (status) { 1074 default: /* XLookupNone, XBufferOverflow */ 1075 return; 1076 case XLookupChars: /* composed string from input method */ 1077 goto insert; 1078 case XLookupKeySym: 1079 case XLookupBoth: /* a KeySym and a string are returned: use keysym */ 1080 break; 1081 } 1082 1083 #if VI_MODE_PATCH 1084 if (using_vi_mode) { 1085 vi_keypress(ksym, ev); 1086 return; 1087 } 1088 1089 if (vi_mode && 1090 (ksym == global_esc.ksym && 1091 (ev->state & global_esc.state) == global_esc.state)) { 1092 using_vi_mode = 1; 1093 if (cursor) 1094 cursor = nextrune(-1); 1095 goto draw; 1096 } 1097 #endif // VI_MODE_PATCH 1098 1099 if (ev->state & ControlMask) { 1100 switch(ksym) { 1101 #if FZFEXPECT_PATCH 1102 case XK_a: expect("ctrl-a", ev); ksym = XK_Home; break; 1103 case XK_b: expect("ctrl-b", ev); ksym = XK_Left; break; 1104 case XK_c: expect("ctrl-c", ev); ksym = XK_Escape; break; 1105 case XK_d: expect("ctrl-d", ev); ksym = XK_Delete; break; 1106 case XK_e: expect("ctrl-e", ev); ksym = XK_End; break; 1107 case XK_f: expect("ctrl-f", ev); ksym = XK_Right; break; 1108 case XK_g: expect("ctrl-g", ev); ksym = XK_Escape; break; 1109 case XK_h: expect("ctrl-h", ev); ksym = XK_BackSpace; break; 1110 case XK_i: expect("ctrl-i", ev); ksym = XK_Tab; break; 1111 case XK_j: expect("ctrl-j", ev); ksym = XK_Down; break; 1112 case XK_J:/* fallthrough */ 1113 case XK_l: expect("ctrl-l", ev); break; 1114 case XK_m: expect("ctrl-m", ev); /* fallthrough */ 1115 case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; 1116 case XK_n: expect("ctrl-n", ev); ksym = XK_Down; break; 1117 case XK_p: expect("ctrl-p", ev); ksym = XK_Up; break; 1118 case XK_o: expect("ctrl-o", ev); break; 1119 case XK_q: expect("ctrl-q", ev); break; 1120 #if !NAVHISTORY_PATCH 1121 case XK_r: expect("ctrl-r", ev); break; 1122 #endif // NAVHISTORY_PATCH 1123 case XK_s: expect("ctrl-s", ev); break; 1124 case XK_t: expect("ctrl-t", ev); break; 1125 case XK_k: expect("ctrl-k", ev); ksym = XK_Up; break; 1126 #else 1127 case XK_a: ksym = XK_Home; break; 1128 case XK_b: ksym = XK_Left; break; 1129 case XK_c: ksym = XK_Escape; break; 1130 case XK_d: ksym = XK_Delete; break; 1131 case XK_e: ksym = XK_End; break; 1132 case XK_f: ksym = XK_Right; break; 1133 case XK_g: ksym = XK_Escape; break; 1134 case XK_h: ksym = XK_BackSpace; break; 1135 case XK_i: ksym = XK_Tab; break; 1136 case XK_j: /* fallthrough */ 1137 case XK_J: /* fallthrough */ 1138 case XK_m: /* fallthrough */ 1139 case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; 1140 case XK_n: ksym = XK_Down; break; 1141 case XK_p: ksym = XK_Up; break; 1142 1143 case XK_k: /* delete right */ 1144 text[cursor] = '\0'; 1145 match(); 1146 break; 1147 #endif // FZFEXPECT_PATCH 1148 #if FZFEXPECT_PATCH 1149 case XK_u: expect("ctrl-u", ev); /* delete left */ 1150 #else 1151 case XK_u: /* delete left */ 1152 #endif // FZFEXPECT_PATCH 1153 insert(NULL, 0 - cursor); 1154 break; 1155 #if FZFEXPECT_PATCH 1156 case XK_w: expect("ctrl-w", ev); /* delete word */ 1157 #else 1158 case XK_w: /* delete word */ 1159 #endif // FZFEXPECT_PATCH 1160 while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 1161 insert(NULL, nextrune(-1) - cursor); 1162 while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 1163 insert(NULL, nextrune(-1) - cursor); 1164 break; 1165 #if FZFEXPECT_PATCH || CTRL_V_TO_PASTE_PATCH 1166 case XK_v: 1167 #if FZFEXPECT_PATCH 1168 expect("ctrl-v", ev); 1169 #endif // FZFEXPECT_PATCH 1170 case XK_V: 1171 XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 1172 utf8, utf8, win, CurrentTime); 1173 return; 1174 #endif // FZFEXPECT_PATCH | CTRL_V_TO_PASTE_PATCH 1175 #if NAVHISTORY_PATCH 1176 case XK_r: 1177 togglehistoryitems(); 1178 match(); 1179 goto draw; 1180 #endif // NAVHISTORY_PATCH 1181 #if FZFEXPECT_PATCH 1182 case XK_y: expect("ctrl-y", ev); /* paste selection */ 1183 #else 1184 case XK_y: /* paste selection */ 1185 #endif // FZFEXPECT_PATCH 1186 case XK_Y: 1187 XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, 1188 utf8, utf8, win, CurrentTime); 1189 return; 1190 #if FZFEXPECT_PATCH 1191 case XK_x: expect("ctrl-x", ev); break; 1192 case XK_z: expect("ctrl-z", ev); break; 1193 #endif // FZFEXPECT_PATCH 1194 case XK_Left: 1195 case XK_KP_Left: 1196 movewordedge(-1); 1197 goto draw; 1198 case XK_Right: 1199 case XK_KP_Right: 1200 movewordedge(+1); 1201 goto draw; 1202 case XK_Return: 1203 case XK_KP_Enter: 1204 #if RESTRICT_RETURN_PATCH 1205 if (restrict_return) 1206 break; 1207 #endif // RESTRICT_RETURN_PATCH 1208 #if MULTI_SELECTION_PATCH 1209 selsel(); 1210 #endif // MULTI_SELECTION_PATCH 1211 break; 1212 case XK_bracketleft: 1213 cleanup(); 1214 exit(1); 1215 default: 1216 return; 1217 } 1218 } else if (ev->state & Mod1Mask) { 1219 switch(ksym) { 1220 case XK_b: 1221 movewordedge(-1); 1222 goto draw; 1223 case XK_f: 1224 movewordedge(+1); 1225 goto draw; 1226 case XK_g: ksym = XK_Home; break; 1227 case XK_G: ksym = XK_End; break; 1228 case XK_h: ksym = XK_Up; break; 1229 case XK_j: ksym = XK_Next; break; 1230 case XK_k: ksym = XK_Prior; break; 1231 case XK_l: ksym = XK_Down; break; 1232 #if NAVHISTORY_PATCH 1233 case XK_p: 1234 navhistory(-1); 1235 buf[0]=0; 1236 break; 1237 case XK_n: 1238 navhistory(1); 1239 buf[0]=0; 1240 break; 1241 #endif // NAVHISTORY_PATCH 1242 default: 1243 return; 1244 } 1245 } 1246 1247 switch(ksym) { 1248 default: 1249 insert: 1250 if (!iscntrl((unsigned char)*buf)) 1251 insert(buf, len); 1252 break; 1253 case XK_Delete: 1254 case XK_KP_Delete: 1255 if (text[cursor] == '\0') 1256 return; 1257 cursor = nextrune(+1); 1258 /* fallthrough */ 1259 case XK_BackSpace: 1260 if (cursor == 0) 1261 return; 1262 insert(NULL, nextrune(-1) - cursor); 1263 break; 1264 case XK_End: 1265 case XK_KP_End: 1266 if (text[cursor] != '\0') { 1267 cursor = strlen(text); 1268 break; 1269 } 1270 if (next) { 1271 /* jump to end of list and position items in reverse */ 1272 curr = matchend; 1273 calcoffsets(); 1274 curr = prev; 1275 calcoffsets(); 1276 while (next && (curr = curr->right)) 1277 calcoffsets(); 1278 } 1279 sel = matchend; 1280 break; 1281 case XK_Escape: 1282 cleanup(); 1283 exit(1); 1284 case XK_Home: 1285 case XK_KP_Home: 1286 if (sel == matches) { 1287 cursor = 0; 1288 break; 1289 } 1290 sel = curr = matches; 1291 calcoffsets(); 1292 break; 1293 case XK_Left: 1294 case XK_KP_Left: 1295 #if GRID_PATCH && GRIDNAV_PATCH 1296 if (columns > 1) { 1297 if (!sel) 1298 return; 1299 tmpsel = sel; 1300 for (i = 0; i < lines; i++) { 1301 if (!tmpsel->left || tmpsel->left->right != tmpsel) 1302 return; 1303 if (tmpsel == curr) 1304 offscreen = true; 1305 tmpsel = tmpsel->left; 1306 } 1307 sel = tmpsel; 1308 if (offscreen) { 1309 curr = prev; 1310 calcoffsets(); 1311 } 1312 break; 1313 } 1314 #endif // GRIDNAV_PATCH 1315 if (cursor > 0 && (!sel || !sel->left || lines > 0)) { 1316 cursor = nextrune(-1); 1317 break; 1318 } 1319 if (lines > 0) 1320 return; 1321 /* fallthrough */ 1322 case XK_Up: 1323 case XK_KP_Up: 1324 if (sel && sel->left && (sel = sel->left)->right == curr) { 1325 curr = prev; 1326 calcoffsets(); 1327 } 1328 break; 1329 case XK_Next: 1330 case XK_KP_Next: 1331 if (!next) 1332 return; 1333 sel = curr = next; 1334 calcoffsets(); 1335 break; 1336 case XK_Prior: 1337 case XK_KP_Prior: 1338 if (!prev) 1339 return; 1340 sel = curr = prev; 1341 calcoffsets(); 1342 break; 1343 case XK_Return: 1344 case XK_KP_Enter: 1345 #if RESTRICT_RETURN_PATCH 1346 if (restrict_return && (!sel || ev->state & (ShiftMask | ControlMask))) 1347 break; 1348 #endif // RESTRICT_RETURN_PATCH 1349 #if !MULTI_SELECTION_PATCH 1350 printcurrent(ev->state); 1351 #endif // MULTI_SELECTION_PATCH 1352 if (!(ev->state & ControlMask)) { 1353 #if MULTI_SELECTION_PATCH 1354 printselected(ev->state); 1355 #endif // MULTI_SELECTION_PATCH 1356 cleanup(); 1357 exit(0); 1358 } 1359 #if !MULTI_SELECTION_PATCH 1360 if (sel) 1361 sel->out = 1; 1362 #endif // MULTI_SELECTION_PATCH 1363 break; 1364 case XK_Right: 1365 case XK_KP_Right: 1366 #if GRID_PATCH && GRIDNAV_PATCH 1367 if (columns > 1) { 1368 if (!sel) 1369 return; 1370 tmpsel = sel; 1371 for (i = 0; i < lines; i++) { 1372 if (!tmpsel->right || tmpsel->right->left != tmpsel) 1373 return; 1374 tmpsel = tmpsel->right; 1375 if (tmpsel == next) 1376 offscreen = true; 1377 } 1378 sel = tmpsel; 1379 if (offscreen) { 1380 curr = next; 1381 calcoffsets(); 1382 } 1383 break; 1384 } 1385 #endif // GRIDNAV_PATCH 1386 if (text[cursor] != '\0') { 1387 cursor = nextrune(+1); 1388 break; 1389 } 1390 if (lines > 0) 1391 return; 1392 /* fallthrough */ 1393 case XK_Down: 1394 case XK_KP_Down: 1395 if (sel && sel->right && (sel = sel->right) == next) { 1396 curr = next; 1397 calcoffsets(); 1398 } 1399 break; 1400 case XK_Tab: 1401 #if PREFIXCOMPLETION_PATCH 1402 if (!matches) 1403 break; /* cannot complete no matches */ 1404 #if FUZZYMATCH_PATCH 1405 /* only do tab completion if all matches start with prefix */ 1406 for (item = matches; item && item->text; item = item->right) 1407 if (item->text[0] != text[0]) 1408 goto draw; 1409 #endif // FUZZYMATCH_PATCH 1410 strncpy(text, matches->text, sizeof text - 1); 1411 text[sizeof text - 1] = '\0'; 1412 len = cursor = strlen(text); /* length of longest common prefix */ 1413 for (item = matches; item && item->text; item = item->right) { 1414 cursor = 0; 1415 while (cursor < len && text[cursor] == item->text[cursor]) 1416 cursor++; 1417 len = cursor; 1418 } 1419 memset(text + len, '\0', strlen(text) - len); 1420 #else 1421 if (!sel) 1422 return; 1423 cursor = strnlen(sel->text, sizeof text - 1); 1424 memcpy(text, sel->text, cursor); 1425 text[cursor] = '\0'; 1426 match(); 1427 #endif // PREFIXCOMPLETION_PATCH 1428 break; 1429 } 1430 1431 draw: 1432 #if INCREMENTAL_PATCH 1433 if (incremental) { 1434 puts(text); 1435 fflush(stdout); 1436 } 1437 #endif // INCREMENTAL_PATCH 1438 #if VI_MODE_PATCH 1439 if (using_vi_mode && text[cursor] == '\0') 1440 --cursor; 1441 #endif // VI_MODE_PATCH 1442 1443 drawmenu(); 1444 } 1445 1446 static void 1447 paste(void) 1448 { 1449 char *p, *q; 1450 int di; 1451 unsigned long dl; 1452 Atom da; 1453 1454 /* we have been given the current selection, now insert it into input */ 1455 if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, 1456 utf8, &da, &di, &dl, &dl, (unsigned char **)&p) 1457 == Success && p) { 1458 insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); 1459 XFree(p); 1460 } 1461 drawmenu(); 1462 } 1463 1464 static void 1465 printitem(struct item *item) 1466 { 1467 if (!item) 1468 return; 1469 1470 #if NAVHISTORY_PATCH 1471 addhistoryitem(item); 1472 #endif // NAVHISTORY_PATCH 1473 1474 #if PIPEOUT_PATCH 1475 if (item->text[0] == startpipe[0]) { 1476 strncpy(item->text + strlen(item->text),pipeout,8); 1477 puts(item->text+1); 1478 return; 1479 } 1480 #endif // PIPEOUT_PATCH 1481 1482 #if PRINTINDEX_PATCH 1483 if (print_index) { 1484 printf("%d\n", item->index); 1485 return; 1486 } 1487 #endif // PRINTINDEX_PATCH 1488 1489 #if SEPARATOR_PATCH 1490 puts(item->text_output); 1491 #else 1492 puts(item->text); 1493 #endif // SEPARATOR_PATCH 1494 } 1495 1496 static void 1497 printtext(char *text) 1498 { 1499 if (!text || !strlen(text)) 1500 return; 1501 1502 #if NAVHISTORY_PATCH 1503 addhistory(text); 1504 #endif // NAVHISTORY_PATCH 1505 1506 #if PIPEOUT_PATCH 1507 if (text[0] == startpipe[0]) { 1508 strncpy(text + strlen(text),pipeout,8); 1509 puts(text+1); 1510 return; 1511 } 1512 #endif // PIPEOUT_PATCH 1513 1514 puts(text); 1515 } 1516 1517 static void 1518 printcurrent(unsigned int state) 1519 { 1520 #if PRINTINPUTTEXT_PATCH 1521 if (sel && (use_text_input == !!(state & ShiftMask))) 1522 #else 1523 if (sel && !(state & ShiftMask)) 1524 #endif // PRINTINPUTTEXT_PATCH 1525 { 1526 printitem(sel); 1527 } else { 1528 printtext(text); 1529 } 1530 } 1531 1532 #if ALPHA_PATCH 1533 static void 1534 xinitvisual(void) 1535 { 1536 XVisualInfo *infos; 1537 XRenderPictFormat *fmt; 1538 int nitems; 1539 int i; 1540 1541 XVisualInfo tpl = { 1542 .screen = screen, 1543 .depth = 32, 1544 .class = TrueColor 1545 }; 1546 long masks = VisualScreenMask | VisualDepthMask | VisualClassMask; 1547 1548 infos = XGetVisualInfo(dpy, masks, &tpl, &nitems); 1549 visual = NULL; 1550 for(i = 0; i < nitems; i ++) { 1551 fmt = XRenderFindVisualFormat(dpy, infos[i].visual); 1552 if (fmt->type == PictTypeDirect && fmt->direct.alphaMask) { 1553 visual = infos[i].visual; 1554 depth = infos[i].depth; 1555 cmap = XCreateColormap(dpy, root, visual, AllocNone); 1556 useargb = 1; 1557 break; 1558 } 1559 } 1560 1561 XFree(infos); 1562 1563 if (!visual || !opacity) { 1564 visual = DefaultVisual(dpy, screen); 1565 depth = DefaultDepth(dpy, screen); 1566 cmap = DefaultColormap(dpy, screen); 1567 } 1568 } 1569 #endif // ALPHA_PATCH 1570 1571 #if !NON_BLOCKING_STDIN_PATCH 1572 static void 1573 readstdin(void) 1574 { 1575 char *line = NULL; 1576 #if SEPARATOR_PATCH 1577 char *p; 1578 #elif TSV_PATCH 1579 char *buf, *p; 1580 #endif // SEPARATOR_PATCH | TSV_PATCH 1581 1582 size_t i, linesiz, itemsiz = 0; 1583 ssize_t len; 1584 1585 #if PASSWORD_PATCH 1586 if (passwd) { 1587 inputw = lines = 0; 1588 return; 1589 } 1590 #endif // PASSWORD_PATCH 1591 1592 /* read each line from stdin and add it to the item list */ 1593 for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { 1594 if (i + 1 >= itemsiz) { 1595 itemsiz += 256; 1596 if (!(items = realloc(items, itemsiz * sizeof(*items)))) 1597 die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); 1598 } 1599 if (line[len - 1] == '\n') 1600 line[len - 1] = '\0'; 1601 1602 if (!(items[i].text = strdup(line))) 1603 die("strdup:"); 1604 #if SEPARATOR_PATCH 1605 if (separator && (p = sepchr(items[i].text, separator)) != NULL) { 1606 *p = '\0'; 1607 items[i].text_output = ++p; 1608 } else { 1609 items[i].text_output = items[i].text; 1610 } 1611 if (separator_reverse) { 1612 p = items[i].text; 1613 items[i].text = items[i].text_output; 1614 items[i].text_output = p; 1615 } 1616 #elif TSV_PATCH 1617 if (!(buf = strdup(line))) 1618 die("cannot strdup %u bytes:", strlen(line) + 1); 1619 if ((p = strchr(buf, '\t'))) 1620 *p = '\0'; 1621 items[i].stext = buf; 1622 #endif // SEPARATOR_PATCH | TSV_PATCH 1623 #if MULTI_SELECTION_PATCH 1624 items[i].id = i; /* for multiselect */ 1625 #if PRINTINDEX_PATCH 1626 items[i].index = i; 1627 #endif // PRINTINDEX_PATCH 1628 #elif PRINTINDEX_PATCH 1629 items[i].index = i; 1630 #else 1631 items[i].out = 0; 1632 #endif // MULTI_SELECTION_PATCH | PRINTINDEX_PATCH 1633 1634 #if HIGHPRIORITY_PATCH 1635 items[i].hp = arrayhas(hpitems, hplength, items[i].text); 1636 #endif // HIGHPRIORITY_PATCH 1637 } 1638 free(line); 1639 if (items) 1640 items[i].text = NULL; 1641 lines = MIN(lines, i); 1642 } 1643 #endif // NON_BLOCKING_STDIN_PATCH 1644 1645 static void 1646 #if NON_BLOCKING_STDIN_PATCH 1647 readevent(void) 1648 #else 1649 run(void) 1650 #endif // NON_BLOCKING_STDIN_PATCH 1651 { 1652 XEvent ev; 1653 #if PRESELECT_PATCH 1654 int i; 1655 #endif // PRESELECT_PATCH 1656 while (!XNextEvent(dpy, &ev)) { 1657 #if PRESELECT_PATCH 1658 if (preselected) { 1659 for (i = 0; i < preselected; i++) { 1660 if (sel && sel->right && (sel = sel->right) == next) { 1661 curr = next; 1662 calcoffsets(); 1663 } 1664 } 1665 drawmenu(); 1666 preselected = 0; 1667 } 1668 #endif // PRESELECT_PATCH 1669 #if INPUTMETHOD_PATCH 1670 if (XFilterEvent(&ev, None)) 1671 continue; 1672 if (composing) 1673 continue; 1674 #else 1675 if (XFilterEvent(&ev, win)) 1676 continue; 1677 #endif // INPUTMETHOD_PATCH 1678 switch(ev.type) { 1679 #if MOUSE_SUPPORT_PATCH 1680 case ButtonPress: 1681 buttonpress(&ev); 1682 break; 1683 #if MOTION_SUPPORT_PATCH 1684 case MotionNotify: 1685 motionevent(&ev.xbutton); 1686 break; 1687 #endif // MOTION_SUPPORT_PATCH 1688 #endif // MOUSE_SUPPORT_PATCH 1689 case DestroyNotify: 1690 if (ev.xdestroywindow.window != win) 1691 break; 1692 cleanup(); 1693 exit(1); 1694 case Expose: 1695 if (ev.xexpose.count == 0) 1696 drw_map(drw, win, 0, 0, mw, mh); 1697 break; 1698 case FocusIn: 1699 /* regrab focus from parent window */ 1700 if (ev.xfocus.window != win) 1701 grabfocus(); 1702 break; 1703 case KeyPress: 1704 keypress(&ev.xkey); 1705 break; 1706 case SelectionNotify: 1707 if (ev.xselection.property == utf8) 1708 paste(); 1709 break; 1710 case VisibilityNotify: 1711 if (ev.xvisibility.state != VisibilityUnobscured) 1712 XRaiseWindow(dpy, win); 1713 break; 1714 } 1715 } 1716 } 1717 1718 static void 1719 setup(void) 1720 { 1721 int x, y, i, j; 1722 unsigned int du; 1723 #if RELATIVE_INPUT_WIDTH_PATCH 1724 unsigned int tmp, minstrlen = 0, curstrlen = 0; 1725 int numwidthchecks = 100; 1726 struct item *item; 1727 #endif // RELATIVE_INPUT_WIDTH_PATCH 1728 XSetWindowAttributes swa; 1729 XIM xim; 1730 Window w, dw, *dws; 1731 XWindowAttributes wa; 1732 XClassHint ch = {"dmenu", "dmenu"}; 1733 #ifdef XINERAMA 1734 XineramaScreenInfo *info; 1735 Window pw; 1736 int a, di, n, area = 0; 1737 #endif 1738 /* init appearance */ 1739 #if XRESOURCES_PATCH 1740 for (j = 0; j < SchemeLast; j++) 1741 #if ALPHA_PATCH 1742 scheme[j] = drw_scm_create(drw, (const char**)colors[j], alphas[j], 2); 1743 #else 1744 scheme[j] = drw_scm_create(drw, (const char**)colors[j], 2); 1745 #endif // ALPHA_PATCH 1746 #else 1747 for (j = 0; j < SchemeLast; j++) 1748 #if ALPHA_PATCH 1749 scheme[j] = drw_scm_create(drw, colors[j], alphas[j], 2); 1750 #else 1751 scheme[j] = drw_scm_create(drw, colors[j], 2); 1752 #endif // ALPHA_PATCH 1753 #endif // XRESOURCES_PATCH 1754 1755 clip = XInternAtom(dpy, "CLIPBOARD", False); 1756 utf8 = XInternAtom(dpy, "UTF8_STRING", False); 1757 #if WMTYPE_PATCH 1758 type = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False); 1759 dock = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); 1760 #endif // WMTYPE_PATCH 1761 1762 /* calculate menu geometry */ 1763 #if PANGO_PATCH 1764 bh = drw->font->h + 2; 1765 #else 1766 bh = drw->fonts->h + 2; 1767 #endif // PANGO_PATCH 1768 #if LINE_HEIGHT_PATCH 1769 bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */ 1770 #endif // LINE_HEIGHT_PATCH 1771 lines = MAX(lines, 0); 1772 mh = (lines + 1) * bh; 1773 #if CENTER_PATCH && PANGO_PATCH 1774 promptw = (prompt && *prompt) ? TEXTWM(prompt) - lrpad / 4 : 0; 1775 #elif CENTER_PATCH 1776 promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 1777 #endif // CENTER_PATCH 1778 #ifdef XINERAMA 1779 i = 0; 1780 if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { 1781 XGetInputFocus(dpy, &w, &di); 1782 if (mon >= 0 && mon < n) 1783 i = mon; 1784 else if (w != root && w != PointerRoot && w != None) { 1785 /* find top-level window containing current input focus */ 1786 do { 1787 if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) 1788 XFree(dws); 1789 } while (w != root && w != pw); 1790 /* find xinerama screen with which the window intersects most */ 1791 if (XGetWindowAttributes(dpy, pw, &wa)) 1792 for (j = 0; j < n; j++) 1793 if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { 1794 area = a; 1795 i = j; 1796 } 1797 } 1798 /* no focused window is on screen, so use pointer location instead */ 1799 if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) 1800 for (i = 0; i < n; i++) 1801 if (INTERSECT(x, y, 1, 1, info[i]) != 0) 1802 break; 1803 1804 #if CENTER_PATCH 1805 if (center) { 1806 #if XYW_PATCH 1807 mw = (dmw>0 ? dmw : MIN(MAX(max_textw() + promptw, min_width), info[i].width)); 1808 #else 1809 mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); 1810 #endif // XYW_PATCH 1811 x = info[i].x_org + ((info[i].width - mw) / 2); 1812 y = info[i].y_org + ((info[i].height - mh) / 2); 1813 } else { 1814 #if XYW_PATCH 1815 x = info[i].x_org + dmx; 1816 y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy); 1817 mw = (dmw>0 ? dmw : info[i].width); 1818 #else 1819 x = info[i].x_org; 1820 y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 1821 mw = info[i].width; 1822 #endif // XYW_PATCH 1823 } 1824 #elif XYW_PATCH 1825 x = info[i].x_org + dmx; 1826 y = info[i].y_org + (topbar ? dmy : info[i].height - mh - dmy); 1827 mw = (dmw>0 ? dmw : info[i].width); 1828 #else 1829 x = info[i].x_org; 1830 y = info[i].y_org + (topbar ? 0 : info[i].height - mh); 1831 mw = info[i].width; 1832 #endif // CENTER_PATCH / XYW_PATCH 1833 XFree(info); 1834 } else 1835 #endif 1836 { 1837 if (!XGetWindowAttributes(dpy, parentwin, &wa)) 1838 die("could not get embedding window attributes: 0x%lx", 1839 parentwin); 1840 #if CENTER_PATCH 1841 if (center) { 1842 #if XYW_PATCH 1843 mw = (dmw>0 ? dmw : MIN(MAX(max_textw() + promptw, min_width), wa.width)); 1844 #else 1845 mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); 1846 #endif // XYW_PATCH 1847 x = (wa.width - mw) / 2; 1848 y = (wa.height - mh) / 2; 1849 } else { 1850 #if XYW_PATCH 1851 x = dmx; 1852 y = topbar ? dmy : wa.height - mh - dmy; 1853 mw = (dmw>0 ? dmw : wa.width); 1854 #else 1855 x = 0; 1856 y = topbar ? 0 : wa.height - mh; 1857 mw = wa.width; 1858 #endif // XYW_PATCH 1859 } 1860 #elif XYW_PATCH 1861 x = dmx; 1862 y = topbar ? dmy : wa.height - mh - dmy; 1863 mw = (dmw>0 ? dmw : wa.width); 1864 #else 1865 x = 0; 1866 y = topbar ? 0 : wa.height - mh; 1867 mw = wa.width; 1868 #endif // CENTER_PATCH / XYW_PATCH 1869 } 1870 #if !CENTER_PATCH 1871 #if PANGO_PATCH 1872 promptw = (prompt && *prompt) ? TEXTWM(prompt) - lrpad / 4 : 0; 1873 #else 1874 promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 1875 #endif // PANGO_PATCH 1876 #endif // CENTER_PATCH 1877 #if RELATIVE_INPUT_WIDTH_PATCH 1878 for (item = items; !lines && item && item->text; ++item) { 1879 curstrlen = strlen(item->text); 1880 if (numwidthchecks || minstrlen < curstrlen) { 1881 numwidthchecks = MAX(numwidthchecks - 1, 0); 1882 minstrlen = MAX(minstrlen, curstrlen); 1883 if ((tmp = textw_clamp(item->text, mw/3)) > inputw) { 1884 inputw = tmp; 1885 if (tmp == mw/3) 1886 break; 1887 } 1888 } 1889 } 1890 #else 1891 inputw = mw / 3; /* input width: ~33.33% of monitor width */ 1892 #endif // RELATIVE_INPUT_WIDTH_PATCH 1893 match(); 1894 1895 /* create menu window */ 1896 #if MANAGED_PATCH 1897 swa.override_redirect = managed ? False : True; 1898 #else 1899 swa.override_redirect = True; 1900 #endif // MANAGED_PATCH 1901 #if ALPHA_PATCH 1902 swa.background_pixel = 0; 1903 swa.colormap = cmap; 1904 #else 1905 swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 1906 #endif // ALPHA_PATCH 1907 swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask 1908 #if MOUSE_SUPPORT_PATCH 1909 | ButtonPressMask 1910 #if MOTION_SUPPORT_PATCH 1911 | PointerMotionMask 1912 #endif // MOTION_SUPPORT_PATCH 1913 #endif // MOUSE_SUPPORT_PATCH 1914 ; 1915 win = XCreateWindow( 1916 dpy, root, 1917 #if BARPADDING_PATCH && BORDER_PATCH 1918 x + sp, y + vp - (topbar ? 0 : border_width * 2), mw - 2 * sp - border_width * 2, mh, border_width, 1919 #elif BARPADDING_PATCH 1920 x + sp, y + vp, mw - 2 * sp, mh, 0, 1921 #elif BORDER_PATCH 1922 x, y - (topbar ? 0 : border_width * 2), mw - border_width * 2, mh, border_width, 1923 #else 1924 x, y, mw, mh, 0, 1925 #endif // BORDER_PATCH | BARPADDING_PATCH 1926 #if ALPHA_PATCH 1927 depth, InputOutput, visual, 1928 CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWColormap|CWEventMask, &swa 1929 #else 1930 CopyFromParent, CopyFromParent, CopyFromParent, 1931 CWOverrideRedirect | CWBackPixel | CWEventMask, &swa 1932 #endif // ALPHA_PATCH 1933 ); 1934 #if BORDER_PATCH 1935 if (border_width) 1936 XSetWindowBorder(dpy, win, scheme[SchemeBorder][ColBg].pixel); 1937 #endif // BORDER_PATCH 1938 XSetClassHint(dpy, win, &ch); 1939 #if WMTYPE_PATCH 1940 XChangeProperty(dpy, win, type, XA_ATOM, 32, PropModeReplace, 1941 (unsigned char *) &dock, 1); 1942 #endif // WMTYPE_PATCH 1943 1944 /* input methods */ 1945 if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) 1946 die("XOpenIM failed: could not open input device"); 1947 1948 #if INPUTMETHOD_PATCH 1949 init_input_method(xim); 1950 #else 1951 xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, 1952 XNClientWindow, win, XNFocusWindow, win, NULL); 1953 #endif // INPUTMETHOD_PATCH 1954 1955 #if MANAGED_PATCH 1956 if (managed) { 1957 XTextProperty prop; 1958 char *windowtitle = prompt != NULL ? prompt : "dmenu"; 1959 Xutf8TextListToTextProperty(dpy, &windowtitle, 1, XUTF8StringStyle, &prop); 1960 XSetWMName(dpy, win, &prop); 1961 XSetTextProperty(dpy, win, &prop, XInternAtom(dpy, "_NET_WM_NAME", False)); 1962 XFree(prop.value); 1963 } 1964 #endif // MANAGED_PATCH 1965 1966 XMapRaised(dpy, win); 1967 if (embed) { 1968 XReparentWindow(dpy, win, parentwin, x, y); 1969 XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); 1970 if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { 1971 for (i = 0; i < du && dws[i] != win; ++i) 1972 XSelectInput(dpy, dws[i], FocusChangeMask); 1973 XFree(dws); 1974 } 1975 #if !INPUTMETHOD_PATCH 1976 grabfocus(); 1977 #endif // INPUTMETHOD_PATCH 1978 } 1979 #if INPUTMETHOD_PATCH 1980 grabfocus(); 1981 #endif // INPUTMETHOD_PATCH 1982 drw_resize(drw, mw, mh); 1983 drawmenu(); 1984 } 1985 1986 static void 1987 usage(void) 1988 { 1989 die("usage: dmenu [-bv" 1990 #if CENTER_PATCH 1991 "c" 1992 #endif 1993 #if !NON_BLOCKING_STDIN_PATCH 1994 "f" 1995 #endif // NON_BLOCKING_STDIN_PATCH 1996 #if QUIET_PATCH 1997 "q" 1998 #endif // QUIET_PATCH 1999 #if INCREMENTAL_PATCH 2000 "r" 2001 #endif // INCREMENTAL_PATCH 2002 #if CASEINSENSITIVE_PATCH 2003 "s" 2004 #else 2005 "i" 2006 #endif // CASEINSENSITIVE_PATCH 2007 #if INSTANT_PATCH 2008 "n" 2009 #endif // INSTANT_PATCH 2010 #if PRINTINPUTTEXT_PATCH 2011 "t" 2012 #endif // PRINTINPUTTEXT_PATCH 2013 #if PREFIXCOMPLETION_PATCH 2014 "x" 2015 #endif // PREFIXCOMPLETION_PATCH 2016 #if FUZZYMATCH_PATCH 2017 "F" 2018 #endif // FUZZYMATCH_PATCH 2019 #if PASSWORD_PATCH 2020 "P" 2021 #endif // PASSWORD_PATCH 2022 #if NO_SORT_PATCH 2023 "S" 2024 #endif // NO_SORT_PATCH 2025 #if REJECTNOMATCH_PATCH 2026 "R" // (changed from r to R due to conflict with INCREMENTAL_PATCH) 2027 #endif // REJECTNOMATCH_PATCH 2028 #if RESTRICT_RETURN_PATCH 2029 "1" 2030 #endif // RESTRICT_RETURN_PATCH 2031 "] " 2032 #if CARET_WIDTH_PATCH 2033 "[-cw caret_width] " 2034 #endif // CARET_WIDTH_PATCH 2035 #if VI_MODE_PATCH 2036 "[-vi] " 2037 #endif // VI_MODE_PATCH 2038 #if MANAGED_PATCH 2039 "[-wm] " 2040 #endif // MANAGED_PATCH 2041 #if GRID_PATCH 2042 "[-g columns] " 2043 #endif // GRID_PATCH 2044 "[-l lines] [-p prompt] [-fn font] [-m monitor]" 2045 "\n [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]" 2046 #if DYNAMIC_OPTIONS_PATCH || FZFEXPECT_PATCH || ALPHA_PATCH || BORDER_PATCH || HIGHPRIORITY_PATCH 2047 "\n " 2048 #endif 2049 #if DYNAMIC_OPTIONS_PATCH 2050 " [-dy command]" 2051 #endif // DYNAMIC_OPTIONS_PATCH 2052 #if FZFEXPECT_PATCH 2053 " [-ex expectkey]" 2054 #endif // FZFEXPECT_PATCH 2055 #if ALPHA_PATCH 2056 " [-o opacity]" 2057 #endif // ALPHA_PATCH 2058 #if BORDER_PATCH 2059 " [-bw width]" 2060 #endif // BORDER_PATCH 2061 #if HIGHPRIORITY_PATCH 2062 " [-hb color] [-hf color] [-hp items]" 2063 #endif // HIGHPRIORITY_PATCH 2064 #if INITIALTEXT_PATCH || LINE_HEIGHT_PATCH || PRESELECT_PATCH || NAVHISTORY_PATCH || XYW_PATCH 2065 "\n " 2066 #endif 2067 #if INITIALTEXT_PATCH 2068 " [-it text]" 2069 #endif // INITIALTEXT_PATCH 2070 #if LINE_HEIGHT_PATCH 2071 " [-h height]" 2072 #endif // LINE_HEIGHT_PATCH 2073 #if PRESELECT_PATCH 2074 " [-ps index]" 2075 #endif // PRESELECT_PATCH 2076 #if NAVHISTORY_PATCH 2077 " [-H histfile]" 2078 #endif // NAVHISTORY_PATCH 2079 #if XYW_PATCH 2080 " [-X xoffset] [-Y yoffset] [-W width]" // (arguments made upper case due to conflicts) 2081 #endif // XYW_PATCH 2082 #if HIGHLIGHT_PATCH 2083 "\n [-nhb color] [-nhf color] [-shb color] [-shf color]" // highlight colors 2084 #endif // HIGHLIGHT_PATCH 2085 #if SEPARATOR_PATCH 2086 "\n [-d separator] [-D separator]" 2087 #endif // SEPARATOR_PATCH 2088 "\n"); 2089 } 2090 2091 int 2092 main(int argc, char *argv[]) 2093 { 2094 XWindowAttributes wa; 2095 int i; 2096 #if !NON_BLOCKING_STDIN_PATCH 2097 int fast = 0; 2098 #endif // NON_BLOCKING_STDIN_PATCH 2099 2100 #if XRESOURCES_PATCH 2101 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2102 fputs("warning: no locale support\n", stderr); 2103 #if INPUTMETHOD_PATCH 2104 if (!XSetLocaleModifiers("")) 2105 fputs("warning: could not set locale modifiers", stderr); 2106 #endif // INPUTMETHOD_PATCH 2107 if (!(dpy = XOpenDisplay(NULL))) 2108 die("cannot open display"); 2109 2110 /* These need to be checked before we init the visuals and read X resources. */ 2111 for (i = 1; i < argc; i++) { 2112 if (!strcmp(argv[i], "-v")) { /* prints version information */ 2113 puts("dmenu-"VERSION); 2114 exit(0); 2115 } else if (!strcmp(argv[i], "-w")) { 2116 argv[i][0] = '\0'; 2117 embed = strdup(argv[++i]); 2118 #if ALPHA_PATCH 2119 } else if (!strcmp(argv[i], "-o")) { /* opacity, pass -o 0 to disable alpha */ 2120 opacity = atoi(argv[++i]); 2121 #endif // ALPHA_PATCH 2122 } else { 2123 continue; 2124 } 2125 argv[i][0] = '\0'; // mark as used 2126 } 2127 2128 screen = DefaultScreen(dpy); 2129 root = RootWindow(dpy, screen); 2130 if (!embed || !(parentwin = strtol(embed, NULL, 0))) 2131 parentwin = root; 2132 if (!XGetWindowAttributes(dpy, parentwin, &wa)) 2133 die("could not get embedding window attributes: 0x%lx", 2134 parentwin); 2135 2136 #if ALPHA_PATCH 2137 xinitvisual(); 2138 drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); 2139 #else 2140 drw = drw_create(dpy, screen, root, wa.width, wa.height); 2141 #endif // ALPHA_PATCH 2142 readxresources(); 2143 #endif // XRESOURCES_PATCH 2144 2145 for (i = 1; i < argc; i++) { 2146 if (argv[i][0] == '\0') 2147 continue; 2148 2149 /* these options take no arguments */ 2150 if (!strcmp(argv[i], "-v")) { /* prints version information */ 2151 puts("dmenu-"VERSION); 2152 exit(0); 2153 } else if (!strcmp(argv[i], "-b")) { /* appears at the bottom of the screen */ 2154 topbar = 0; 2155 #if CENTER_PATCH 2156 } else if (!strcmp(argv[i], "-c")) { /* toggles centering of dmenu window on screen */ 2157 center = !center; 2158 #endif // CENTER_PATCH 2159 #if !NON_BLOCKING_STDIN_PATCH 2160 } else if (!strcmp(argv[i], "-f")) { /* grabs keyboard before reading stdin */ 2161 fast = 1; 2162 #endif // NON_BLOCKING_STDIN_PATCH 2163 #if INCREMENTAL_PATCH 2164 } else if (!strcmp(argv[i], "-r")) { /* incremental */ 2165 incremental = !incremental; 2166 #endif // INCREMENTAL_PATCH 2167 #if QUIET_PATCH 2168 } else if (!strcmp(argv[i], "-q")) { /* quiet, don't list items if search is empty */ 2169 quiet = !quiet; 2170 #endif // QUIET_PATCH 2171 #if CASEINSENSITIVE_PATCH 2172 } else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */ 2173 fstrncmp = strncmp; 2174 fstrstr = strstr; 2175 #else 2176 } else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ 2177 fstrncmp = strncasecmp; 2178 fstrstr = cistrstr; 2179 #endif // CASEINSENSITIVE_PATCH 2180 #if VI_MODE_PATCH 2181 } else if (!strcmp(argv[i], "-vi")) { 2182 if (i + 1 < argc) { 2183 if (!strcmp(argv[i+1], "0")) { 2184 start_mode = 0; 2185 i++; 2186 } else if (!strcmp(argv[i+1], "1")) { 2187 start_mode = 1; 2188 i++; 2189 } 2190 } 2191 vi_mode = 1; 2192 using_vi_mode = start_mode; 2193 global_esc.ksym = XK_Escape; 2194 global_esc.state = 0; 2195 #endif // VI_MODE_PATCH 2196 #if MANAGED_PATCH 2197 } else if (!strcmp(argv[i], "-wm")) { /* display as managed wm window */ 2198 managed = 1; 2199 #endif // MANAGED_PATCH 2200 #if INSTANT_PATCH 2201 } else if (!strcmp(argv[i], "-n")) { /* instant select only match */ 2202 instant = !instant; 2203 #endif // INSTANT_PATCH 2204 #if PRINTINPUTTEXT_PATCH 2205 } else if (!strcmp(argv[i], "-t")) { /* favors text input over selection */ 2206 use_text_input = 1; 2207 #endif // PRINTINPUTTEXT_PATCH 2208 #if PREFIXCOMPLETION_PATCH 2209 } else if (!strcmp(argv[i], "-x")) { /* invert use_prefix */ 2210 use_prefix = !use_prefix; 2211 #endif // PREFIXCOMPLETION_PATCH 2212 #if FUZZYMATCH_PATCH 2213 } else if (!strcmp(argv[i], "-F")) { /* disable/enable fuzzy matching, depends on default */ 2214 fuzzy = !fuzzy; 2215 #endif // FUZZYMATCH_PATCH 2216 #if PASSWORD_PATCH 2217 } else if (!strcmp(argv[i], "-P")) { /* is the input a password */ 2218 passwd = 1; 2219 #endif // PASSWORD_PATCH 2220 #if FZFEXPECT_PATCH 2221 } else if (!strcmp(argv[i], "-ex")) { /* expect key */ 2222 expected = argv[++i]; 2223 #endif // FZFEXPECT_PATCH 2224 #if REJECTNOMATCH_PATCH 2225 } else if (!strcmp(argv[i], "-R")) { /* reject input which results in no match */ 2226 reject_no_match = 1; 2227 #endif // REJECTNOMATCH_PATCH 2228 #if NO_SORT_PATCH 2229 } else if (!strcmp(argv[i], "-S")) { /* do not sort matches */ 2230 sortmatches = 0; 2231 #endif // NO_SORT_PATCH 2232 #if PRINTINDEX_PATCH 2233 } else if (!strcmp(argv[i], "-ix")) { /* adds ability to return index in list */ 2234 print_index = 1; 2235 #endif // PRINTINDEX_PATCH 2236 #if RESTRICT_RETURN_PATCH 2237 } else if (!strcmp(argv[i], "-1")) { 2238 restrict_return = 1; 2239 #endif // RESTRICT_RETURN_PATCH 2240 } else if (i + 1 == argc) 2241 usage(); 2242 /* these options take one argument */ 2243 #if NAVHISTORY_PATCH 2244 else if (!strcmp(argv[i], "-H")) 2245 histfile = argv[++i]; 2246 #endif // NAVHISTORY_PATCH 2247 #if GRID_PATCH 2248 else if (!strcmp(argv[i], "-g")) { /* number of columns in grid */ 2249 columns = atoi(argv[++i]); 2250 if (columns && lines == 0) 2251 lines = 1; 2252 } 2253 #endif // GRID_PATCH 2254 else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ 2255 lines = atoi(argv[++i]); 2256 #if XYW_PATCH 2257 else if (!strcmp(argv[i], "-X")) /* window x offset */ 2258 dmx = atoi(argv[++i]); 2259 else if (!strcmp(argv[i], "-Y")) /* window y offset (from bottom up if -b) */ 2260 dmy = atoi(argv[++i]); 2261 else if (!strcmp(argv[i], "-W")) /* make dmenu this wide */ 2262 dmw = atoi(argv[++i]); 2263 #endif // XYW_PATCH 2264 else if (!strcmp(argv[i], "-m")) 2265 mon = atoi(argv[++i]); 2266 #if ALPHA_PATCH && !XRESOURCES_PATCH 2267 else if (!strcmp(argv[i], "-o")) /* opacity, pass -o 0 to disable alpha */ 2268 opacity = atoi(argv[++i]); 2269 #endif // ALPHA_PATCH 2270 else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ 2271 prompt = argv[++i]; 2272 else if (!strcmp(argv[i], "-fn")) /* font or font set */ 2273 #if PANGO_PATCH 2274 font = argv[++i]; 2275 #else 2276 fonts[0] = argv[++i]; 2277 #endif // PANGO_PATCH 2278 #if LINE_HEIGHT_PATCH 2279 else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */ 2280 lineheight = atoi(argv[++i]); 2281 lineheight = MAX(lineheight, min_lineheight); /* reasonable default in case of value too small/negative */ 2282 } 2283 #endif // LINE_HEIGHT_PATCH 2284 else if (!strcmp(argv[i], "-nb")) /* normal background color */ 2285 colors[SchemeNorm][ColBg] = argv[++i]; 2286 else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ 2287 colors[SchemeNorm][ColFg] = argv[++i]; 2288 else if (!strcmp(argv[i], "-sb")) /* selected background color */ 2289 colors[SchemeSel][ColBg] = argv[++i]; 2290 else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ 2291 colors[SchemeSel][ColFg] = argv[++i]; 2292 else if (!strcmp(argv[i], "-ob")) /* outline background color */ 2293 colors[SchemeOut][ColBg] = argv[++i]; 2294 else if (!strcmp(argv[i], "-of")) /* outline foreground color */ 2295 colors[SchemeOut][ColFg] = argv[++i]; 2296 #if HIGHPRIORITY_PATCH 2297 else if (!strcmp(argv[i], "-hb")) /* high priority background color */ 2298 colors[SchemeHp][ColBg] = argv[++i]; 2299 else if (!strcmp(argv[i], "-hf")) /* low priority background color */ 2300 colors[SchemeHp][ColFg] = argv[++i]; 2301 else if (!strcmp(argv[i], "-hp")) 2302 hpitems = tokenize(argv[++i], ",", &hplength); 2303 #endif // HIGHPRIORITY_PATCH 2304 #if HIGHLIGHT_PATCH 2305 else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ 2306 colors[SchemeNormHighlight][ColBg] = argv[++i]; 2307 else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ 2308 colors[SchemeNormHighlight][ColFg] = argv[++i]; 2309 else if (!strcmp(argv[i], "-shb")) /* selected hi background color */ 2310 colors[SchemeSelHighlight][ColBg] = argv[++i]; 2311 else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ 2312 colors[SchemeSelHighlight][ColFg] = argv[++i]; 2313 #endif // HIGHLIGHT_PATCH 2314 #if CARET_WIDTH_PATCH 2315 else if (!strcmp(argv[i], "-cw")) /* sets caret witdth */ 2316 caret_width = atoi(argv[++i]); 2317 #endif // CARET_WIDTH_PATCH 2318 #if !XRESOURCES_PATCH 2319 else if (!strcmp(argv[i], "-w")) /* embedding window id */ 2320 embed = argv[++i]; 2321 #endif // XRESOURCES_PATCH 2322 #if SEPARATOR_PATCH 2323 else if (!strcmp(argv[i], "-d")) /* field separator */ 2324 { 2325 sepchr = strchr; 2326 separator = argv[++i][0]; 2327 separator_reverse = argv[i][1] == '|'; 2328 } 2329 else if (!strcmp(argv[i], "-D")) /* greedy field separator */ 2330 { 2331 sepchr = strrchr; 2332 separator = argv[++i][0]; 2333 separator_reverse = argv[i][1] == '|'; 2334 } 2335 #endif // SEPARATOR_PATCH 2336 #if PRESELECT_PATCH 2337 else if (!strcmp(argv[i], "-ps")) /* preselected item */ 2338 preselected = atoi(argv[++i]); 2339 #endif // PRESELECT_PATCH 2340 #if DYNAMIC_OPTIONS_PATCH 2341 else if (!strcmp(argv[i], "-dy")) /* dynamic command to run */ 2342 dynamic = argv[++i]; 2343 #endif // DYNAMIC_OPTIONS_PATCH 2344 #if BORDER_PATCH 2345 else if (!strcmp(argv[i], "-bw")) /* border width around dmenu */ 2346 border_width = atoi(argv[++i]); 2347 #endif // BORDER_PATCH 2348 #if INITIALTEXT_PATCH 2349 else if (!strcmp(argv[i], "-it")) { /* adds initial text */ 2350 const char * text = argv[++i]; 2351 insert(text, strlen(text)); 2352 } 2353 #endif // INITIALTEXT_PATCH 2354 else { 2355 usage(); 2356 } 2357 } 2358 2359 #if XRESOURCES_PATCH 2360 #if PANGO_PATCH 2361 if (!drw_font_create(drw, font)) 2362 die("no fonts could be loaded."); 2363 #else 2364 if (!drw_fontset_create(drw, (const char**)fonts, LENGTH(fonts))) 2365 die("no fonts could be loaded."); 2366 #endif // PANGO_PATCH 2367 #else // !XRESOURCES_PATCH 2368 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 2369 fputs("warning: no locale support\n", stderr); 2370 #if INPUTMETHOD_PATCH 2371 if (!XSetLocaleModifiers("")) 2372 fputs("warning: could not set locale modifiers", stderr); 2373 #endif // INPUTMETHOD_PATCH 2374 if (!(dpy = XOpenDisplay(NULL))) 2375 die("cannot open display"); 2376 screen = DefaultScreen(dpy); 2377 root = RootWindow(dpy, screen); 2378 if (!embed || !(parentwin = strtol(embed, NULL, 0))) 2379 parentwin = root; 2380 if (!XGetWindowAttributes(dpy, parentwin, &wa)) 2381 die("could not get embedding window attributes: 0x%lx", 2382 parentwin); 2383 2384 #if ALPHA_PATCH 2385 xinitvisual(); 2386 drw = drw_create(dpy, screen, root, wa.width, wa.height, visual, depth, cmap); 2387 #else 2388 drw = drw_create(dpy, screen, root, wa.width, wa.height); 2389 #endif // ALPHA_PATCH 2390 2391 #if PANGO_PATCH 2392 if (!drw_font_create(drw, font)) 2393 die("no fonts could be loaded."); 2394 #else 2395 if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) 2396 die("no fonts could be loaded."); 2397 #endif // PANGO_PATCH 2398 #endif // XRESOURCES_PATCH 2399 2400 #if PANGO_PATCH 2401 lrpad = drw->font->h; 2402 #else 2403 lrpad = drw->fonts->h; 2404 #endif // PANGO_PATCH 2405 2406 #if BARPADDING_PATCH 2407 sp = sidepad; 2408 vp = (topbar ? vertpad : - vertpad); 2409 #endif // BARPADDING_PATCH 2410 2411 #if LINE_HEIGHT_PATCH 2412 if (lineheight == -1) 2413 #if PANGO_PATCH 2414 lineheight = drw->font->h * 2.5; 2415 #else 2416 lineheight = drw->fonts->h * 2.5; 2417 #endif // PANGO_PATCH 2418 #endif // LINE_HEIGHT_PATCH 2419 2420 #ifdef __OpenBSD__ 2421 if (pledge("stdio rpath", NULL) == -1) 2422 die("pledge"); 2423 #endif 2424 #if NAVHISTORY_PATCH 2425 loadhistory(); 2426 #endif // NAVHISTORY_PATCH 2427 2428 #if NON_BLOCKING_STDIN_PATCH 2429 grabkeyboard(); 2430 #else 2431 if (fast && !isatty(0)) { 2432 grabkeyboard(); 2433 #if DYNAMIC_OPTIONS_PATCH 2434 if (!(dynamic && *dynamic)) 2435 readstdin(); 2436 #else 2437 readstdin(); 2438 #endif // DYNAMIC_OPTIONS_PATCH 2439 } else { 2440 #if DYNAMIC_OPTIONS_PATCH 2441 if (!(dynamic && *dynamic)) 2442 readstdin(); 2443 #else 2444 readstdin(); 2445 #endif // DYNAMIC_OPTIONS_PATCH 2446 grabkeyboard(); 2447 } 2448 #endif // NON_BLOCKING_STDIN_PATCH 2449 setup(); 2450 run(); 2451 2452 return 1; /* unreachable */ 2453 }