mew.c (26858B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <errno.h> 3 #include <ctype.h> 4 #include <locale.h> 5 #include <poll.h> 6 #include <stdarg.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <strings.h> 11 #include <sys/timerfd.h> 12 #include <wayland-client.h> 13 #include <xkbcommon/xkbcommon.h> 14 15 #define MAX(A, B) ((A) > (B) ? (A) : (B)) 16 #define MIN(A, B) ((A) < (B) ? (A) : (B)) 17 #define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B)) 18 #define LENGTH(X) (sizeof (X) / sizeof (X)[0]) 19 20 #include "drwl.h" 21 #include "bufpool.h" 22 #include "xdg-activation-v1-protocol.h" 23 #include "wlr-layer-shell-unstable-v1-protocol.h" 24 25 #define TEXTW(X) (drwl_font_getwidth(drw, (X)) + lrpad) 26 27 enum { SchemeNorm, SchemeSel, SchemeOut }; /* color schemes */ 28 29 struct item { 30 char *text; 31 struct item *left, *right; 32 int out; 33 }; 34 35 static struct { 36 struct wl_keyboard *wl_keyboard; 37 struct xkb_context *xkb_context; 38 struct xkb_keymap *xkb_keymap; 39 struct xkb_state *xkb_state; 40 41 struct { 42 int delay; 43 int period; 44 int timer; 45 enum wl_keyboard_key_state key_state; 46 xkb_keysym_t sym; 47 } repeat; 48 49 int ctrl, shift, alt; 50 } kbd; 51 52 static char text[BUFSIZ] = ""; 53 static int bh, mw, mh; 54 static int inputw = 0, promptw, passwd = 0; 55 static int32_t scale = 1; 56 static int lrpad; /* sum of left and right padding */ 57 static size_t cursor; 58 static struct item *items = NULL; 59 static struct item *matches, *matchend; 60 static struct item *prev, *curr, *next, *sel; 61 static int running = 0; 62 63 static struct wl_display *display; 64 static struct wl_compositor *compositor; 65 static struct wl_seat *seat; 66 static struct wl_shm *shm; 67 static struct wl_data_device_manager *data_device_manager; 68 static struct wl_data_device *data_device; 69 static struct wl_data_offer *data_offer; 70 static struct zwlr_layer_shell_v1 *layer_shell; 71 static struct zwlr_layer_surface_v1 *layer_surface; 72 static struct xdg_activation_v1 *activation; 73 static struct wl_surface *surface; 74 static struct wl_registry *registry; 75 static Drwl *drw; 76 static BufPool pool; 77 static struct wl_callback *frame_callback; 78 /* default output supplied by compositor */ 79 static struct wl_output *output = NULL; 80 81 #include "config.h" 82 83 static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; 84 static char *(*fstrstr)(const char *, const char *) = strstr; 85 static int (*submit)(const char*) = puts; 86 87 static void 88 noop() 89 { 90 /* 91 * meow :3c 92 */ 93 } 94 95 static void 96 die(const char *fmt, ...) 97 { 98 va_list ap; 99 100 va_start(ap, fmt); 101 vfprintf(stderr, fmt, ap); 102 va_end(ap); 103 104 if (fmt[0] && fmt[strlen(fmt)-1] == ':') { 105 fputc(' ', stderr); 106 perror(NULL); 107 } else { 108 fputc('\n', stderr); 109 } 110 111 exit(EXIT_FAILURE); 112 } 113 114 static void 115 activation_token_handle_done(void *data, 116 struct xdg_activation_token_v1 *activation_token, const char *token) 117 { 118 char *argv[] = {"/bin/sh", "-c", (char*)data, NULL}; 119 120 xdg_activation_token_v1_destroy(activation_token); 121 122 switch (fork()) { 123 case -1: 124 die("fork:"); 125 case 0: 126 setenv("XDG_ACTIVATION_TOKEN", token, 1); 127 execv(argv[0], argv); 128 die("execvp:"); 129 } 130 131 running = 0; 132 } 133 134 static const struct xdg_activation_token_v1_listener activation_token_listener = { 135 .done = activation_token_handle_done, 136 }; 137 138 static int 139 exec_cmd(const char *text) 140 { 141 struct xdg_activation_token_v1 *activation_token; 142 143 activation_token = xdg_activation_v1_get_activation_token(activation); 144 xdg_activation_token_v1_set_surface(activation_token, surface); 145 xdg_activation_token_v1_add_listener(activation_token, 146 &activation_token_listener, (void *)text); 147 xdg_activation_token_v1_commit(activation_token); 148 149 return 0; 150 } 151 152 static void 153 parse_color(uint32_t *dest, const char *src) 154 { 155 int len; 156 157 if (src[0] == '#') 158 src++; 159 len = strlen(src); 160 if (len != 6 && len != 8) 161 die("bad color: %s", src); 162 163 *dest = strtoul(src, NULL, 16); 164 if (len == 6) 165 *dest = (*dest << 8) | 0xFF; 166 } 167 168 static void 169 loadfonts(void) 170 { 171 char fontattrs[12]; 172 173 drwl_font_destroy(drw->font); 174 snprintf(fontattrs, sizeof(fontattrs), "dpi=%d", 96 * scale); 175 if (!(drwl_font_create(drw, LENGTH(fonts), fonts, fontattrs))) 176 die("no fonts could be loaded"); 177 178 lrpad = drw->font->height; 179 bh = drw->font->height + 2; 180 lines = MAX(lines, 0); 181 mh = (lines + 1) * bh; 182 promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; 183 } 184 185 static unsigned int 186 textw_clamp(const char *str, unsigned int n) 187 { 188 unsigned int w = drwl_font_getwidth_clamp(drw, str, n) + lrpad; 189 return MIN(w, n); 190 } 191 192 static void 193 appenditem(struct item *item, struct item **list, struct item **last) 194 { 195 if (*last) 196 (*last)->right = item; 197 else 198 *list = item; 199 200 item->left = *last; 201 item->right = NULL; 202 *last = item; 203 } 204 205 static void 206 calcoffsets(void) 207 { 208 int i, n; 209 210 if (lines > 0) 211 n = lines * bh; 212 else 213 n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); 214 /* calculate which items will begin the next page and previous page */ 215 for (i = 0, next = curr; next; next = next->right) 216 if ((i += (lines > 0) ? bh : textw_clamp(next->text, n)) > n) 217 break; 218 for (i = 0, prev = curr; prev && prev->left; prev = prev->left) 219 if ((i += (lines > 0) ? bh : textw_clamp(prev->left->text, n)) > n) 220 break; 221 } 222 223 static void 224 cleanup(void) 225 { 226 size_t i; 227 228 for (i = 0; items && items[i].text; ++i) 229 free(items[i].text); 230 free(items); 231 232 bufpool_cleanup(&pool); 233 drwl_setimage(drw, NULL); 234 drwl_destroy(drw); 235 drwl_fini(); 236 237 wl_data_device_release(data_device); 238 zwlr_layer_shell_v1_destroy(layer_shell); 239 xdg_activation_v1_destroy(activation); 240 wl_shm_destroy(shm); 241 wl_compositor_destroy(compositor); 242 wl_registry_destroy(registry); 243 wl_display_disconnect(display); 244 } 245 246 static char * 247 cistrstr(const char *h, const char *n) 248 { 249 size_t i; 250 251 if (!n[0]) 252 return (char *)h; 253 254 for (; *h; ++h) { 255 for (i = 0; n[i] && tolower((unsigned char)n[i]) == 256 tolower((unsigned char)h[i]); ++i) 257 ; 258 if (n[i] == '\0') 259 return (char *)h; 260 } 261 return NULL; 262 } 263 264 static int 265 drawitem(struct item *item, int x, int y, int w) 266 { 267 if (item == sel) 268 drwl_setscheme(drw, colors[SchemeSel]); 269 else if (item->out) 270 drwl_setscheme(drw, colors[SchemeOut]); 271 else 272 drwl_setscheme(drw, colors[SchemeNorm]); 273 274 return drwl_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); 275 } 276 277 static void 278 drawmenu(void) 279 { 280 unsigned int curpos; 281 struct item *item; 282 int x = 0, y = 0, w; 283 char *censort; 284 DrwBuf *buf; 285 286 errno = 0; 287 if (!(buf = bufpool_getbuf(&pool, shm, mw, mh))) 288 die(errno ? "bufpool_getbuf:" : "no buffer available"); 289 drwl_setimage(drw, buf->image); 290 291 drwl_setscheme(drw, colors[SchemeNorm]); 292 drwl_rect(drw, 0, 0, mw, mh, 1, 1); 293 294 if (prompt && *prompt) { 295 drwl_setscheme(drw, colors[SchemeSel]); 296 x = drwl_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); 297 } 298 /* draw input field */ 299 w = (lines > 0 || !matches) ? mw - x : inputw; 300 drwl_setscheme(drw, colors[SchemeNorm]); 301 if (passwd) { 302 censort = calloc(1, sizeof(text)); 303 memset(censort, '*', strlen(text)); 304 drwl_text(drw, x, 0, w, bh, lrpad / 2, censort, 0); 305 free(censort); 306 } else drwl_text(drw, x, 0, w, bh, lrpad / 2, text, 0); 307 308 curpos = TEXTW(text) - TEXTW(&text[cursor]); 309 if ((curpos += lrpad / 2 - 1) < w) { 310 drwl_setscheme(drw, colors[SchemeNorm]); 311 drwl_rect(drw, x + curpos, (bh - drw->font->height) / 2 + 1, 312 2, drw->font->height - 2, 1, 0); 313 } 314 315 if (lines > 0) { 316 /* draw vertical list */ 317 for (item = curr; item != next; item = item->right) 318 drawitem(item, x, y += bh, mw - x); 319 } else if (matches) { 320 /* draw horizontal list */ 321 x += inputw; 322 w = TEXTW("<"); 323 if (curr->left) { 324 drwl_setscheme(drw, colors[SchemeNorm]); 325 drwl_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); 326 } 327 x += w; 328 for (item = curr; item != next; item = item->right) 329 x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">"))); 330 if (next) { 331 w = TEXTW(">"); 332 drwl_setscheme(drw, colors[SchemeNorm]); 333 drwl_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); 334 } 335 } 336 337 drwl_setimage(drw, NULL); 338 wl_surface_set_buffer_scale(surface, scale); 339 wl_surface_attach(surface, buf->wl_buf, 0, 0); 340 wl_surface_damage_buffer(surface, 0, 0, mw, mh); 341 wl_surface_commit(surface); 342 } 343 344 static void 345 frame_callback_handle_done(void *data, struct wl_callback *callback, 346 uint32_t time) 347 { 348 wl_callback_destroy(frame_callback); 349 frame_callback = NULL; 350 drawmenu(); 351 } 352 353 static const struct wl_callback_listener frame_callback_listener = { 354 .done = frame_callback_handle_done, 355 }; 356 357 static void 358 redraw() 359 { 360 if (frame_callback) 361 return; 362 frame_callback = wl_surface_frame(surface); 363 wl_callback_add_listener(frame_callback, &frame_callback_listener, NULL); 364 wl_surface_commit(surface); 365 } 366 367 static void 368 match(void) 369 { 370 static char **tokv = NULL; 371 static int tokn = 0; 372 373 char buf[sizeof text], *s; 374 int i, tokc = 0; 375 size_t len, textsize; 376 struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; 377 378 strcpy(buf, text); 379 /* separate input text into tokens to be matched individually */ 380 for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) 381 if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) 382 die("cannot realloc %zu bytes:", tokn * sizeof *tokv); 383 len = tokc ? strlen(tokv[0]) : 0; 384 385 matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; 386 textsize = strlen(text) + 1; 387 for (item = items; item && item->text; item++) { 388 for (i = 0; i < tokc; i++) 389 if (!fstrstr(item->text, tokv[i])) 390 break; 391 if (i != tokc) /* not all tokens match */ 392 continue; 393 /* exact matches go first, then prefixes, then substrings */ 394 if (!tokc || !fstrncmp(text, item->text, textsize)) 395 appenditem(item, &matches, &matchend); 396 else if (!fstrncmp(tokv[0], item->text, len)) 397 appenditem(item, &lprefix, &prefixend); 398 else 399 appenditem(item, &lsubstr, &substrend); 400 } 401 if (lprefix) { 402 if (matches) { 403 matchend->right = lprefix; 404 lprefix->left = matchend; 405 } else 406 matches = lprefix; 407 matchend = prefixend; 408 } 409 if (lsubstr) { 410 if (matches) { 411 matchend->right = lsubstr; 412 lsubstr->left = matchend; 413 } else 414 matches = lsubstr; 415 matchend = substrend; 416 } 417 curr = sel = matches; 418 calcoffsets(); 419 } 420 421 static void 422 insert(const char *str, ssize_t n) 423 { 424 if (strlen(text) + n > sizeof text - 1) 425 return; 426 /* move existing text out of the way, insert new text, and update cursor */ 427 memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); 428 if (str) 429 memcpy(&text[cursor], str, n); 430 cursor += n; 431 match(); 432 } 433 434 static size_t 435 nextrune(int inc) 436 { 437 ssize_t n; 438 439 /* return location of next utf8 rune in the given direction (+1 or -1) */ 440 for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) 441 ; 442 return n; 443 } 444 445 static void 446 movewordedge(int dir) 447 { 448 if (dir < 0) { /* move cursor to the start of the word*/ 449 while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 450 cursor = nextrune(-1); 451 while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 452 cursor = nextrune(-1); 453 } else { /* move cursor to the end of the word */ 454 while (text[cursor] && strchr(worddelimiters, text[cursor])) 455 cursor = nextrune(+1); 456 while (text[cursor] && !strchr(worddelimiters, text[cursor])) 457 cursor = nextrune(+1); 458 } 459 } 460 461 static void 462 paste(void) 463 { 464 int fds[2]; 465 ssize_t n; 466 char buf[1024]; 467 468 if (!data_offer) 469 return; 470 471 if (pipe(fds) < 0) 472 die("pipe"); 473 474 wl_data_offer_receive(data_offer, "text/plain", fds[1]); 475 close(fds[1]); 476 477 wl_display_roundtrip(display); 478 479 for (;;) { 480 n = read(fds[0], buf, sizeof(buf)); 481 if (n <= 0) 482 break; 483 insert(buf, n); 484 } 485 close(fds[0]); 486 487 wl_data_offer_destroy(data_offer); 488 } 489 490 static void 491 keyboard_keypress(enum wl_keyboard_key_state state, xkb_keysym_t sym) 492 { 493 char buf[8]; 494 495 if (state != WL_KEYBOARD_KEY_STATE_PRESSED) 496 return; 497 498 if (kbd.ctrl) { 499 switch (xkb_keysym_to_lower(sym)) { 500 case XKB_KEY_a: sym = XKB_KEY_Home; break; 501 case XKB_KEY_b: sym = XKB_KEY_Left; break; 502 case XKB_KEY_c: sym = XKB_KEY_Escape; break; 503 case XKB_KEY_d: sym = XKB_KEY_Delete; break; 504 case XKB_KEY_e: sym = XKB_KEY_End; break; 505 case XKB_KEY_f: sym = XKB_KEY_BackSpace; break; 506 case XKB_KEY_g: sym = XKB_KEY_Escape; break; 507 case XKB_KEY_h: sym = XKB_KEY_BackSpace; break; 508 case XKB_KEY_i: sym = XKB_KEY_Tab; break; 509 case XKB_KEY_j: /* fallthrough */ 510 case XKB_KEY_J: /* fallthrough */ 511 case XKB_KEY_m: /* fallthrough */ 512 case XKB_KEY_M: sym = XKB_KEY_Return; break; 513 case XKB_KEY_n: sym = XKB_KEY_Right; break; 514 case XKB_KEY_p: sym = XKB_KEY_Up; break; 515 516 case XKB_KEY_k: /* delete right */ 517 text[cursor] = '\0'; 518 match(); 519 goto draw; 520 case XKB_KEY_u: /* delete left */ 521 insert(NULL, 0 - cursor); 522 goto draw; 523 case XKB_KEY_w: /* delete word */ 524 while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) 525 insert(NULL, nextrune(-1) - cursor); 526 while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) 527 insert(NULL, nextrune(-1) - cursor); 528 goto draw; 529 case XKB_KEY_y: /* paste selection */ 530 case XKB_KEY_Y: 531 paste(); 532 goto draw; 533 case XKB_KEY_Left: 534 case XKB_KEY_KP_Left: 535 movewordedge(-1); 536 goto draw; 537 case XKB_KEY_Right: 538 case XKB_KEY_KP_Right: 539 movewordedge(+1); 540 goto draw; 541 case XKB_KEY_Return: 542 case XKB_KEY_KP_Enter: 543 break; 544 case XKB_KEY_bracketleft: 545 cleanup(); 546 exit(EXIT_FAILURE); 547 default: 548 return; 549 } 550 } else if (kbd.alt) { 551 switch(sym) { 552 case XKB_KEY_b: 553 movewordedge(-1); 554 goto draw; 555 case XKB_KEY_f: 556 movewordedge(+1); 557 goto draw; 558 case XKB_KEY_g: sym = XKB_KEY_Home; break; 559 case XKB_KEY_G: sym = XKB_KEY_End; break; 560 case XKB_KEY_h: sym = XKB_KEY_Up; break; 561 case XKB_KEY_j: sym = XKB_KEY_Next; break; 562 case XKB_KEY_k: sym = XKB_KEY_Prior; break; 563 case XKB_KEY_l: sym = XKB_KEY_Down; break; 564 default: 565 return; 566 } 567 } 568 569 switch (sym) { 570 case XKB_KEY_Delete: 571 case XKB_KEY_KP_Delete: 572 if (text[cursor] == '\0') 573 return; 574 cursor = nextrune(+1); 575 /* fallthrough */ 576 case XKB_KEY_BackSpace: 577 if (cursor == 0) 578 return; 579 insert(NULL, nextrune(-1) - cursor); 580 break; 581 case XKB_KEY_End: 582 case XKB_KEY_KP_End: 583 if (text[cursor] != '\0') { 584 cursor = strlen(text); 585 break; 586 } 587 if (next) { 588 /* jump to end of list and position items in reverse */ 589 curr = matchend; 590 calcoffsets(); 591 curr = prev; 592 calcoffsets(); 593 while (next && (curr = curr->right)) 594 calcoffsets(); 595 } 596 sel = matchend; 597 break; 598 case XKB_KEY_Escape: 599 cleanup(); 600 exit(EXIT_FAILURE); 601 case XKB_KEY_Home: 602 case XKB_KEY_KP_Home: 603 if (sel == matches) { 604 cursor = 0; 605 break; 606 } 607 sel = curr = matches; 608 calcoffsets(); 609 break; 610 case XKB_KEY_Left: 611 case XKB_KEY_KP_Left: 612 if (cursor > 0 && (!sel || !sel->left || lines > 0)) { 613 cursor = nextrune(-1); 614 break; 615 } 616 if (lines > 0) 617 return; 618 /* fallthrough */ 619 case XKB_KEY_Up: 620 case XKB_KEY_KP_Up: 621 if (sel && sel->left && (sel = sel->left)->right == curr) { 622 curr = prev; 623 calcoffsets(); 624 } 625 break; 626 case XKB_KEY_Next: 627 case XKB_KEY_KP_Next: 628 if (!next) 629 return; 630 sel = curr = next; 631 calcoffsets(); 632 break; 633 case XKB_KEY_Prior: 634 case XKB_KEY_KP_Prior: 635 if (!prev) 636 return; 637 sel = curr = prev; 638 calcoffsets(); 639 break; 640 case XKB_KEY_Return: 641 case XKB_KEY_KP_Enter: 642 submit((sel && !kbd.shift) ? sel->text : text); 643 if (!kbd.ctrl && submit != exec_cmd) { 644 running = 0; 645 return; 646 } 647 if (sel) 648 sel->out = 1; 649 break; 650 case XKB_KEY_Right: 651 case XKB_KEY_KP_Right: 652 if (text[cursor] != '\0') { 653 cursor = nextrune(+1); 654 break; 655 } 656 if (lines > 0) 657 return; 658 /* fallthrough */ 659 case XKB_KEY_Down: 660 case XKB_KEY_KP_Down: 661 if (sel && sel->right && (sel = sel->right) == next) { 662 curr = next; 663 calcoffsets(); 664 } 665 break; 666 case XKB_KEY_Tab: 667 if (!sel) 668 return; 669 cursor = strnlen(sel->text, sizeof text - 1); 670 memcpy(text, sel->text, cursor); 671 text[cursor] = '\0'; 672 match(); 673 break; 674 default: 675 if (xkb_keysym_to_utf8(sym, buf, 8)) 676 insert(buf, strnlen(buf, 8)); 677 } 678 draw: 679 redraw(); 680 } 681 682 static void 683 keyboard_handle_keymap(void *data, struct wl_keyboard *wl_keyboard, 684 uint32_t format, int32_t fd, uint32_t size) 685 { 686 char *map_shm; 687 688 if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) 689 die("unknown keymap"); 690 691 map_shm = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); 692 if (map_shm == MAP_FAILED) 693 die("mmap:"); 694 695 kbd.xkb_keymap = xkb_keymap_new_from_string(kbd.xkb_context, map_shm, 696 XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS); 697 munmap(map_shm, size); 698 close(fd); 699 700 kbd.xkb_state = xkb_state_new(kbd.xkb_keymap); 701 } 702 703 static void 704 keyboard_handle_leave(void *data, struct wl_keyboard *wl_keyboard, 705 uint32_t serial, struct wl_surface *surface) 706 { 707 /* 708 * In dmenu(1), if the keyboard cannot be grabbed, it will 709 * immediately exit. This is done before dmenu is initialized, 710 * but can't be the same for Wayland. If a new layer surface 711 * wants keyboard, it will get keyboard, set_exclusivity doesn't 712 * seem to work. 713 */ 714 die("lost keyboard"); 715 } 716 717 static void 718 keyboard_handle_key(void *data, struct wl_keyboard *wl_keyboard, 719 uint32_t serial, uint32_t time, uint32_t key, uint32_t _key_state) 720 { 721 struct itimerspec spec = { 0 }; 722 enum wl_keyboard_key_state key_state = _key_state; 723 xkb_keysym_t sym = xkb_state_key_get_one_sym(kbd.xkb_state, key + 8); 724 725 keyboard_keypress(key_state, sym); 726 727 if (key_state == WL_KEYBOARD_KEY_STATE_PRESSED && kbd.repeat.period >= 0) { 728 kbd.repeat.key_state = key_state; 729 kbd.repeat.sym = sym; 730 spec.it_value.tv_sec = kbd.repeat.delay / 1000; 731 spec.it_value.tv_nsec = (kbd.repeat.delay % 1000) * 1000000l; 732 } 733 timerfd_settime(kbd.repeat.timer, 0, &spec, NULL); 734 } 735 736 static void 737 keyboard_handle_modifiers(void *data, struct wl_keyboard *wl_keyboard, 738 uint32_t serial, uint32_t mods_depressed, 739 uint32_t mods_latched, uint32_t mods_locked, uint32_t group) 740 { 741 xkb_state_update_mask(kbd.xkb_state, 742 mods_depressed, mods_latched, mods_locked, 0, 0, group); 743 kbd.ctrl = xkb_state_mod_name_is_active(kbd.xkb_state, 744 XKB_MOD_NAME_CTRL, 745 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); 746 kbd.shift = xkb_state_mod_name_is_active(kbd.xkb_state, 747 XKB_MOD_NAME_SHIFT, 748 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); 749 kbd.alt = xkb_state_mod_name_is_active(kbd.xkb_state, 750 XKB_MOD_NAME_ALT, 751 XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED); 752 } 753 754 static void 755 keyboard_repeat(void) 756 { 757 struct itimerspec spec = { 0 }; 758 759 keyboard_keypress(kbd.repeat.key_state, kbd.repeat.sym); 760 761 spec.it_value.tv_sec = kbd.repeat.period / 1000; 762 spec.it_value.tv_nsec = (kbd.repeat.period % 1000) * 1000000l; 763 timerfd_settime(kbd.repeat.timer, 0, &spec, NULL); 764 } 765 766 static void 767 keyboard_handle_repeat_info(void *data, struct wl_keyboard *wl_keyboard, 768 int32_t rate, int32_t delay) 769 { 770 kbd.repeat.delay = delay; 771 kbd.repeat.period = rate >= 0 ? 1000 / rate : -1; 772 } 773 774 static const struct wl_keyboard_listener keyboard_listener = { 775 .keymap = keyboard_handle_keymap, 776 .enter = noop, 777 .leave = keyboard_handle_leave, 778 .key = keyboard_handle_key, 779 .modifiers = keyboard_handle_modifiers, 780 .repeat_info = keyboard_handle_repeat_info, 781 }; 782 783 static void 784 layer_surface_handle_configure(void *data, struct zwlr_layer_surface_v1 *layer_surface, 785 uint32_t serial, uint32_t width, uint32_t height) 786 { 787 if (mw / scale == width && mh / scale == height) 788 return; 789 790 mw = width * scale; 791 mh = height * scale; 792 inputw = mw / 3; /* input width: ~33% of output width */ 793 match(); 794 zwlr_layer_surface_v1_ack_configure(layer_surface, serial); 795 drawmenu(); 796 } 797 798 static void 799 layer_surface_handle_closed(void *data, struct zwlr_layer_surface_v1 *surface) 800 { 801 running = 0; 802 } 803 804 static const struct zwlr_layer_surface_v1_listener layer_surface_listener = { 805 .configure = layer_surface_handle_configure, 806 .closed = layer_surface_handle_closed, 807 }; 808 809 static void 810 surface_handle_preferred_scale(void *data, 811 struct wl_surface *wl_surface, int32_t factor) 812 { 813 if (scale == factor) 814 return; 815 scale = factor; 816 loadfonts(); 817 818 /* the scale of the surface is only known after an initial draw, not before :( */ 819 zwlr_layer_surface_v1_set_size(layer_surface, 0, mh / scale); 820 redraw(); 821 } 822 823 static const struct wl_surface_listener surface_listener = { 824 .enter = noop, 825 .leave = noop, 826 .preferred_buffer_scale = surface_handle_preferred_scale, 827 .preferred_buffer_transform = noop, 828 }; 829 830 static void 831 data_device_handle_selection(void *data, struct wl_data_device *data_device, 832 struct wl_data_offer *_data_offer) 833 { 834 data_offer = _data_offer; 835 } 836 837 static const struct wl_data_device_listener data_device_listener = { 838 .data_offer = noop, 839 .selection = data_device_handle_selection, 840 }; 841 842 static void 843 output_handle_name(void *data, struct wl_output *wl_output, const char *name) 844 { 845 if (output_name && !strcmp(name, output_name)) 846 output = wl_output; 847 else 848 wl_output_destroy(wl_output); 849 } 850 851 static const struct wl_output_listener output_listener = { 852 .geometry = noop, 853 .mode = noop, 854 .done = noop, 855 .scale = noop, 856 .name = output_handle_name, 857 .description = noop, 858 }; 859 860 static void 861 seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum wl_seat_capability caps) 862 { 863 if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD)) 864 return; 865 866 kbd.wl_keyboard = wl_seat_get_keyboard(seat); 867 if (!(kbd.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS))) 868 die("xkb_context_new failed"); 869 if ((kbd.repeat.timer = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) 870 die("timerfd_create:"); 871 wl_keyboard_add_listener(kbd.wl_keyboard, &keyboard_listener, NULL); 872 } 873 874 static const struct wl_seat_listener seat_listener = { 875 .capabilities = seat_handle_capabilities, 876 .name = noop, 877 }; 878 879 static void 880 registry_handle_global(void *data, struct wl_registry *registry, 881 uint32_t name, const char *interface, uint32_t version) 882 { 883 if (!strcmp(interface, wl_compositor_interface.name)) 884 compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 6); 885 else if (!strcmp(interface, wl_shm_interface.name)) 886 shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); 887 else if (!strcmp(interface, zwlr_layer_shell_v1_interface.name)) 888 layer_shell = wl_registry_bind(registry, name, 889 &zwlr_layer_shell_v1_interface, 1); 890 else if (!strcmp(interface, wl_data_device_manager_interface.name)) 891 data_device_manager = wl_registry_bind(registry, name, 892 &wl_data_device_manager_interface, 3); 893 else if (!strcmp(interface, xdg_activation_v1_interface.name)) 894 activation = wl_registry_bind(registry, name, &xdg_activation_v1_interface, 1); 895 else if (!strcmp(interface, wl_output_interface.name)) { 896 struct wl_output *output = wl_registry_bind(registry, name, 897 &wl_output_interface, 4); 898 wl_output_add_listener(output, &output_listener, NULL); 899 } else if (!strcmp(interface, wl_seat_interface.name)) { 900 seat = wl_registry_bind (registry, name, &wl_seat_interface, 4); 901 wl_seat_add_listener(seat, &seat_listener, NULL); 902 } 903 } 904 905 static const struct wl_registry_listener registry_listener = { 906 .global = registry_handle_global, 907 .global_remove = noop, 908 }; 909 910 static void 911 readstdin(void) 912 { 913 char *line = NULL; 914 size_t i, itemsiz = 0, linesiz = 0; 915 ssize_t len; 916 if(passwd){ 917 inputw = lines = 0; 918 return; 919 } 920 921 /* read each line from stdin and add it to the item list */ 922 for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) { 923 if (i + 1 >= itemsiz) { 924 itemsiz += 256; 925 if (!(items = realloc(items, itemsiz * sizeof(*items)))) 926 die("cannot realloc %zu bytes:", itemsiz * sizeof(*items)); 927 } 928 if (line[len - 1] == '\n') 929 line[len - 1] = '\0'; 930 if (!(items[i].text = strdup(line))) 931 die("strdup:"); 932 933 items[i].out = 0; 934 } 935 free(line); 936 if (items) 937 items[i].text = NULL; 938 lines = MIN(lines, i); 939 } 940 941 static void 942 run(void) 943 { 944 struct pollfd pfds[] = { 945 { wl_display_get_fd(display), POLLIN }, 946 { kbd.repeat.timer, POLLIN }, 947 }; 948 949 running = 1; 950 while (running) { 951 wl_display_flush(display); 952 953 if (poll(pfds, LENGTH(pfds), -1) < 0) 954 die("poll:"); 955 956 if (pfds[0].revents & POLLIN) 957 if (wl_display_dispatch(display) < 0) 958 die("display dispatch failed"); 959 960 if (pfds[1].revents & POLLIN) 961 keyboard_repeat(); 962 } 963 } 964 965 static void 966 setup(void) 967 { 968 if (!(display = wl_display_connect(NULL))) 969 die("failed to connect to wayland"); 970 971 registry = wl_display_get_registry(display); 972 wl_registry_add_listener(registry, ®istry_listener, NULL); 973 wl_display_roundtrip(display); 974 wl_display_roundtrip(display); /* output & seat listeners */ 975 976 if (!compositor) 977 die("wl_compositor not available"); 978 if (!shm) 979 die("wl_shm not available"); 980 if (!layer_shell) 981 die("layer_shell not available"); 982 if (!data_device_manager) 983 die("data_device_manager not available"); 984 985 if (output_name && !output) 986 die("output %s not found", output_name); 987 988 data_device = wl_data_device_manager_get_data_device( 989 data_device_manager, seat); 990 wl_data_device_add_listener(data_device, &data_device_listener, NULL); 991 992 drwl_init(); 993 if (!(drw = drwl_create())) 994 die("cannot create drwl drawing context"); 995 loadfonts(); 996 997 surface = wl_compositor_create_surface(compositor); 998 wl_surface_add_listener(surface, &surface_listener, NULL); 999 1000 layer_surface = zwlr_layer_shell_v1_get_layer_surface(layer_shell, 1001 surface, output, ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, "mew"); 1002 zwlr_layer_surface_v1_set_size(layer_surface, 0, mh); 1003 zwlr_layer_surface_v1_set_anchor(layer_surface, 1004 (top ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ) | 1005 ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT); 1006 zwlr_layer_surface_v1_set_exclusive_zone(layer_surface, -1); 1007 zwlr_layer_surface_v1_set_keyboard_interactivity(layer_surface, 1); 1008 zwlr_layer_surface_v1_add_listener(layer_surface, &layer_surface_listener, NULL); 1009 1010 wl_surface_commit(surface); 1011 } 1012 1013 static void 1014 usage(void) 1015 { 1016 die("usage: mew [-beivP] [-l lines] [-p prompt] [-f font] [-o output]\n" 1017 " [-nb color] [-nf color] [-sb color] [-sf color]"); 1018 } 1019 1020 int 1021 main(int argc, char *argv[]) 1022 { 1023 int i; 1024 1025 for (i = 1; i < argc; i++) { 1026 /* these options take no arguments */ 1027 if (!strcmp(argv[i], "-v")) { 1028 puts("mew-"VERSION); 1029 exit(0); 1030 } else if (!strcmp(argv[i], "-b")) 1031 top = 0; 1032 else if (!strcmp(argv[i], "-e")) 1033 submit = exec_cmd; 1034 else if (!strcmp(argv[i], "-i")) { 1035 fstrncmp = strncasecmp; 1036 fstrstr = cistrstr; 1037 } else if (!strcmp(argv[i], "-P")) /* is the input a password */ 1038 passwd = 1; 1039 else if (i + 1 == argc) 1040 usage(); 1041 else if (!strcmp(argv[i], "-l")) 1042 lines = atoi(argv[++i]); 1043 else if (!strcmp(argv[i], "-o")) 1044 output_name = argv[++i]; 1045 else if (!strcmp(argv[i], "-p")) 1046 prompt = argv[++i]; 1047 else if (!strcmp(argv[i], "-f")) 1048 fonts[0] = argv[++i]; 1049 else if (!strcmp(argv[i], "-nb")) 1050 parse_color(&colors[SchemeNorm][ColBg], argv[++i]); 1051 else if (!strcmp(argv[i], "-nf")) 1052 parse_color(&colors[SchemeNorm][ColFg], argv[++i]); 1053 else if (!strcmp(argv[i], "-sb")) 1054 parse_color(&colors[SchemeSel][ColBg], argv[++i]); 1055 else if (!strcmp(argv[i], "-sf")) 1056 parse_color(&colors[SchemeSel][ColFg], argv[++i]); 1057 else 1058 usage(); 1059 } 1060 1061 readstdin(); 1062 #ifdef __OpenBSD__ 1063 if (pledge("stdio rpath", NULL) == -1) 1064 die("pledge"); 1065 #endif 1066 setup(); 1067 run(); 1068 cleanup(); 1069 1070 return EXIT_SUCCESS; 1071 } 1072