svkbd.c (31225B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <sys/select.h> 3 #include <sys/time.h> 4 5 #include <ctype.h> 6 #include <locale.h> 7 #include <signal.h> 8 #include <stdarg.h> 9 #include <stdio.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <time.h> 13 #include <unistd.h> 14 15 #include <X11/keysym.h> 16 #include <X11/keysymdef.h> 17 #include <X11/XF86keysym.h> 18 #include <X11/Xatom.h> 19 #include <X11/Xlib.h> 20 #include <X11/Xutil.h> 21 #include <X11/Xproto.h> 22 #include <X11/extensions/XTest.h> 23 #include <X11/Xft/Xft.h> 24 #include <X11/Xresource.h> 25 #ifdef XINERAMA 26 #include <X11/extensions/Xinerama.h> 27 #endif 28 29 #include "drw.h" 30 #include "util.h" 31 32 /* macros */ 33 #define LENGTH(x) (sizeof x / sizeof x[0]) 34 #define STRINGTOKEYSYM(X) (XStringToKeySym(X)) 35 #define TEXTW(X) (drw_fontset_getwidth(drw, (X))) 36 37 /* enums */ 38 enum { 39 SchemeNorm, SchemeNormABC, SchemeNormABCShift, SchemeNormShift, SchemePress, 40 SchemePressShift, SchemeHighlight, SchemeHighlightShift, SchemeOverlay, 41 SchemeOverlayShift, SchemeWindow, SchemeLast 42 }; 43 enum { NetWMWindowType, NetLast }; 44 45 /* typedefs */ 46 typedef struct { 47 char *label; 48 char *label2; 49 KeySym keysym; 50 double width; 51 KeySym modifier; 52 int x, y, w, h; 53 Bool pressed; 54 Bool highlighted; 55 Bool isoverlay; 56 } Key; 57 58 typedef struct { 59 KeySym mod; 60 unsigned int button; 61 } Buttonmod; 62 63 /* function declarations */ 64 static void printdbg(const char *fmt, ...); 65 static void motionnotify(XEvent *e); 66 static void buttonpress(XEvent *e); 67 static void buttonrelease(XEvent *e); 68 static void cleanup(void); 69 static void configurenotify(XEvent *e); 70 static void countrows(); 71 static int countkeys(Key *layer); 72 static void drawkeyboard(void); 73 static void drawkey(Key *k, Bool map); 74 static void expose(XEvent *e); 75 static Key *findkey(int x, int y); 76 static void leavenotify(XEvent *e); 77 static void press(Key *k, KeySym buttonmod); 78 static double get_press_duration(); 79 static void run(void); 80 static void setup(void); 81 static void simulate_keypress(KeySym keysym); 82 static void simulate_keyrelease(KeySym keysym); 83 static void showoverlay(int idx); 84 static void hideoverlay(); 85 static void cyclelayer(); 86 static void setlayer(); 87 static void togglelayer(); 88 static void unpress(Key *k, KeySym buttonmod); 89 static void updatekeys(); 90 static void printkey(Key *k, KeySym mod); 91 92 /* variables */ 93 static int screen; 94 static void (*handler[LASTEvent]) (XEvent *) = { 95 [ButtonPress] = buttonpress, 96 [ButtonRelease] = buttonrelease, 97 [ConfigureNotify] = configurenotify, 98 [Expose] = expose, 99 [LeaveNotify] = leavenotify, 100 [MotionNotify] = motionnotify 101 }; 102 static Atom netatom[NetLast]; 103 static Display *dpy; 104 static Drw *drw; 105 static Window root, win; 106 static Clr* scheme[SchemeLast]; 107 static Bool running = True, isdock = False; 108 static struct timeval pressbegin; 109 static int currentlayer = 0; 110 static int enableoverlays = 1; 111 static int currentoverlay = -1; /* -1 = no overlay */ 112 static int pressonrelease = 1; 113 static KeySym overlaykeysym = 0; /* keysym for which the overlay is presented */ 114 static int releaseprotect = 0; /* set to 1 after overlay is shown, protecting against immediate release */ 115 static int tmp_keycode = 1; 116 static int rows = 0, ww = 0, wh = 0, wx = 0, wy = 0; 117 static int simulateoutput = 1; /* simulate key presses for X */ 118 static int printoutput = 0; /* print key pressed to stdout */ 119 static char *name = "svkbd"; 120 static int debug = 0; 121 static int numlayers = 0; 122 static int numkeys = 0; 123 124 static char *colors[11][2]; /* 11 schemes, 2 colors each */ 125 static char *fonts[] = { 0 }; 126 127 static KeySym ispressingkeysym; 128 129 Bool ispressing = False; 130 Bool sigtermd = False; 131 132 /* configuration, allows nested code to access above variables */ 133 #include "config.h" 134 #ifndef LAYOUT 135 #error "make sure to define LAYOUT" 136 #endif 137 #include LAYOUT 138 139 static Key keys[KEYS]; 140 static Key *layers[LAYERS]; 141 142 void 143 motionnotify(XEvent *e) 144 { 145 XPointerMovedEvent *ev = &e->xmotion; 146 int i; 147 int lostfocus = -1; 148 int gainedfocus = -1; 149 150 for (i = 0; i < numkeys; i++) { 151 if (keys[i].keysym && ev->x > keys[i].x 152 && ev->x < keys[i].x + keys[i].w 153 && ev->y > keys[i].y 154 && ev->y < keys[i].y + keys[i].h) { 155 if (keys[i].highlighted != True) { 156 if (ispressing) { 157 gainedfocus = i; 158 } else { 159 keys[i].highlighted = True; 160 } 161 drawkey(&keys[i], True); 162 } 163 continue; 164 } else if (keys[i].highlighted == True) { 165 keys[i].highlighted = False; 166 drawkey(&keys[i], True); 167 } 168 } 169 170 for (i = 0; i < numkeys; i++) { 171 if (!IsModifierKey(keys[i].keysym) && keys[i].pressed == True && 172 lostfocus != gainedfocus) { 173 printdbg("Pressed key lost focus: %ld\n", keys[i].keysym); 174 lostfocus = i; 175 ispressingkeysym = 0; 176 keys[i].pressed = 0; 177 drawkey(&keys[i], True); 178 } 179 } 180 181 if ((lostfocus != -1) && (gainedfocus != -1) && (lostfocus != gainedfocus)) { 182 printdbg("Clicking new key that gained focus\n"); 183 press(&keys[gainedfocus], 0); 184 keys[gainedfocus].pressed = True; 185 keys[gainedfocus].highlighted = True; 186 } 187 } 188 189 void 190 buttonpress(XEvent *e) 191 { 192 XButtonPressedEvent *ev = &e->xbutton; 193 Key *k; 194 KeySym mod = 0; 195 int i; 196 197 ispressing = True; 198 199 if (!(k = findkey(ev->x, ev->y))) 200 return; 201 202 for (i = 0; i < LENGTH(buttonmods); i++) { 203 if (ev->button == buttonmods[i].button) { 204 mod = buttonmods[i].mod; 205 break; 206 } 207 } 208 209 if (k->modifier) { 210 if (mod == k->modifier) 211 mod = 0; 212 else 213 mod = k->modifier; 214 } 215 press(k, mod); 216 } 217 218 void 219 buttonrelease(XEvent *e) 220 { 221 XButtonPressedEvent *ev = &e->xbutton; 222 Key *k; 223 KeySym mod = 0; 224 int i; 225 226 ispressing = False; 227 228 for (i = 0; i < LENGTH(buttonmods); i++) { 229 if (ev->button == buttonmods[i].button) { 230 mod = buttonmods[i].mod; 231 break; 232 } 233 } 234 235 if (ev->x < 0 || ev->y < 0) { 236 unpress(NULL, mod); 237 } else if ((k = findkey(ev->x, ev->y))) { 238 if (k->modifier == mod) 239 unpress(k, 0); 240 else if (k->modifier) 241 unpress(k, k->modifier); 242 else 243 unpress(k, mod); 244 } 245 } 246 247 void 248 cleanup(void) 249 { 250 int i; 251 252 for (i = 0; i < SchemeLast; i++) 253 free(scheme[i]); 254 drw_sync(drw); 255 drw_free(drw); 256 XSync(dpy, False); 257 XDestroyWindow(dpy, win); 258 XSync(dpy, False); 259 XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); 260 } 261 262 void 263 configurenotify(XEvent *e) 264 { 265 XConfigureEvent *ev = &e->xconfigure; 266 267 if (ev->window == win && (ev->width != ww || ev->height != wh)) { 268 ww = ev->width; 269 wh = ev->height; 270 drw_resize(drw, ww, wh); 271 updatekeys(); 272 } 273 } 274 275 void 276 countrows(void) 277 { 278 int i; 279 280 for (i = 0, rows = 1; i < numkeys; i++) { 281 if (keys[i].keysym == 0) { 282 rows++; 283 if ((i > 0) && (keys[i-1].keysym == 0)) { 284 rows--; 285 break; 286 } 287 } 288 } 289 } 290 291 int 292 countkeys(Key *layer) 293 { 294 int i, nkeys = 0; 295 296 for (i = 0; i < KEYS; i++) { 297 if (i > 0 && layer[i].keysym == 0 && layer[i - 1].keysym == 0) { 298 nkeys--; 299 break; 300 } 301 nkeys++; 302 } 303 304 return nkeys; 305 } 306 307 void 308 drawkeyboard(void) 309 { 310 int i; 311 312 drw_setscheme(drw, scheme[SchemeWindow]); 313 drw_rect(drw, 0, 0, ww, wh, 1, 1); 314 for (i = 0; i < numkeys; i++) { 315 if (keys[i].keysym != 0) 316 drawkey(&keys[i], False); 317 } 318 drw_map(drw, win, 0, 0, ww, wh); 319 } 320 321 void 322 drawkey(Key *k, Bool map) 323 { 324 int x, y, w, h; 325 const char *l; 326 327 int use_scheme = SchemeNorm; 328 329 if (k->pressed) 330 use_scheme = SchemePress; 331 else if (k->highlighted) 332 use_scheme = SchemeHighlight; 333 else if (k->isoverlay) 334 use_scheme = SchemeOverlay; 335 else if ((k->keysym == XK_Return) || 336 ((k->keysym >= XK_a) && (k->keysym <= XK_z)) || 337 ((k->keysym >= XK_Cyrillic_io) && (k->keysym <= XK_Cyrillic_hardsign))) 338 use_scheme = SchemeNormABC; 339 else 340 use_scheme = SchemeNorm; 341 342 drw_setscheme(drw, scheme[use_scheme]); 343 drw_rect(drw, k->x, k->y, k->w, k->h, 1, 1); 344 345 if (k->keysym == XK_KP_Insert) { 346 if (enableoverlays) { 347 l = "≅"; 348 } else { 349 l = "≇"; 350 } 351 } else if (k->label) { 352 l = k->label; 353 } else { 354 l = XKeysymToString(k->keysym); 355 } 356 h = fontsize * 2; 357 y = k->y + (k->h / 2) - (h / 2); 358 w = TEXTW(l); 359 x = k->x + (k->w / 2) - (w / 2); 360 drw_text(drw, x, y, w, h, 0, l, 0); 361 if (k->label2) { 362 if (use_scheme == SchemeNorm) 363 use_scheme = SchemeNormShift; 364 else if (use_scheme == SchemeNormABC) 365 use_scheme = SchemeNormABCShift; 366 else if (use_scheme == SchemePress) 367 use_scheme = SchemePressShift; 368 else if (use_scheme == SchemeHighlight) 369 use_scheme = SchemeHighlightShift; 370 else if (use_scheme == SchemeOverlay) 371 use_scheme = SchemeOverlayShift; 372 drw_setscheme(drw, scheme[use_scheme]); 373 x += w; 374 y -= 15; 375 l = k->label2; 376 w = TEXTW(l); 377 drw_text(drw, x, y, w, h, 0, l, 0); 378 } 379 if (map) 380 drw_map(drw, win, k->x, k->y, k->w, k->h); 381 } 382 383 void 384 expose(XEvent *e) 385 { 386 XExposeEvent *ev = &e->xexpose; 387 388 if (ev->count == 0 && (ev->window == win)) 389 drawkeyboard(); 390 } 391 392 Key * 393 findkey(int x, int y) { 394 int i; 395 396 for (i = 0; i < numkeys; i++) { 397 if (keys[i].keysym && x > keys[i].x && 398 x < keys[i].x + keys[i].w && 399 y > keys[i].y && y < keys[i].y + keys[i].h) { 400 return &keys[i]; 401 } 402 } 403 return NULL; 404 } 405 406 int 407 hasoverlay(KeySym keysym) 408 { 409 int begin, i; 410 411 begin = 0; 412 for (i = 0; i < OVERLAYS; i++) { 413 if (overlay[i].keysym == XK_Cancel) { 414 begin = i + 1; 415 } else if (overlay[i].keysym == keysym) { 416 return begin + 1; 417 } 418 } 419 return -1; 420 } 421 422 void 423 leavenotify(XEvent *e) 424 { 425 if (currentoverlay != -1) 426 hideoverlay(); 427 ispressingkeysym = 0; 428 unpress(NULL, 0); 429 } 430 431 void 432 record_press_begin(KeySym ks) 433 { 434 /* record the begin of the press, don't simulate the actual keypress yet */ 435 gettimeofday(&pressbegin, NULL); 436 ispressingkeysym = ks; 437 } 438 439 void 440 press(Key *k, KeySym buttonmod) 441 { 442 int i; 443 int overlayidx = -1; 444 445 k->pressed = !k->pressed; 446 447 printdbg("Begin click: %ld\n", k->keysym); 448 pressbegin.tv_sec = 0; 449 pressbegin.tv_usec = 0; 450 ispressingkeysym = 0; 451 452 if (!IsModifierKey(k->keysym)) { 453 if (enableoverlays && currentoverlay == -1) 454 overlayidx = hasoverlay(k->keysym); 455 if ((pressonrelease) || (enableoverlays && overlayidx != -1)) { 456 /* record the begin of the press, don't simulate the actual keypress yet */ 457 record_press_begin(k->keysym); 458 } else { 459 printdbg("Simulating press: %ld (mod %ld)\n", k->keysym, buttonmod); 460 for (i = 0; i < numkeys; i++) { 461 if (keys[i].pressed && IsModifierKey(keys[i].keysym)) { 462 simulate_keypress(keys[i].keysym); 463 } 464 } 465 if (buttonmod) 466 simulate_keypress(buttonmod); 467 simulate_keypress(k->keysym); 468 if (printoutput) 469 printkey(k, buttonmod); 470 471 for (i = 0; i < numkeys; i++) { 472 if (keys[i].pressed && IsModifierKey(keys[i].keysym)) { 473 simulate_keyrelease(keys[i].keysym); 474 } 475 } 476 } 477 } 478 drawkey(k, True); 479 } 480 481 int 482 tmp_remap(KeySym keysym) 483 { 484 XChangeKeyboardMapping(dpy, tmp_keycode, 1, &keysym, 1); 485 XSync(dpy, False); 486 487 return tmp_keycode; 488 } 489 490 void 491 printkey(Key *k, KeySym mod) 492 { 493 int i, shift; 494 495 shift = (mod == XK_Shift_L) || (mod == XK_Shift_R) || (mod == XK_Shift_Lock); 496 if (!shift) { 497 for (i = 0; i < numkeys; i++) { 498 if ((keys[i].pressed) && ((keys[i].keysym == XK_Shift_L) || 499 (keys[i].keysym == XK_Shift_R) || (keys[i].keysym == XK_Shift_Lock))) { 500 shift = True; 501 break; 502 } 503 } 504 } 505 printdbg("Printing key %ld (shift=%d)\n", k->keysym, shift); 506 if (k->keysym == XK_Cancel) 507 return; 508 509 KeySym *keysym = &(k->keysym); 510 XIM xim = XOpenIM(dpy, 0, 0, 0); 511 XIC xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, NULL); 512 513 XKeyPressedEvent event; 514 event.type = KeyPress; 515 event.display = dpy; 516 event.state = shift ? ShiftMask : 0; 517 event.keycode = XKeysymToKeycode(dpy, *keysym); 518 if (event.keycode == 0) 519 event.keycode = tmp_remap(*keysym); 520 521 char buffer[32]; 522 KeySym ignore; 523 Status return_status; 524 int l = Xutf8LookupString(xic, &event, buffer, sizeof(buffer), &ignore, &return_status); 525 buffer[l] = '\0'; 526 printdbg("Print buffer: [%s] (length=%d)\n", &buffer, l); 527 printf("%s", buffer); 528 529 XDestroyIC(xic); 530 XCloseIM(xim); 531 } 532 533 void 534 simulate_keypress(KeySym keysym) 535 { 536 KeyCode code; 537 538 if (!simulateoutput) 539 return; 540 541 code = XKeysymToKeycode(dpy, keysym); 542 if (code == 0) 543 code = tmp_remap(keysym); 544 XTestFakeKeyEvent(dpy, code, True, 0); 545 } 546 547 void 548 simulate_keyrelease(KeySym keysym) 549 { 550 KeyCode code; 551 552 if (!simulateoutput) 553 return; 554 555 code = XKeysymToKeycode(dpy, keysym); 556 if (code == 0) 557 code = tmp_remap(keysym); 558 XTestFakeKeyEvent(dpy, code, False, 0); 559 } 560 561 double 562 get_press_duration(void) 563 { 564 struct timeval now; 565 566 gettimeofday(&now, NULL); 567 568 return (double) ((now.tv_sec * 1000000L + now.tv_usec) - 569 (pressbegin.tv_sec * 1000000L + pressbegin.tv_usec)) / 570 (double) 1000000L; 571 } 572 573 void 574 unpress(Key *k, KeySym buttonmod) 575 { 576 int i; 577 Bool neutralizebuttonmod = False; 578 579 if (k) { 580 switch(k->keysym) { 581 case XK_Cancel: 582 cyclelayer(); 583 break; 584 case XK_script_switch: 585 togglelayer(); 586 break; 587 case XK_KP_Insert: 588 enableoverlays = !enableoverlays; 589 break; 590 case XK_Break: 591 running = False; 592 break; 593 default: 594 break; 595 } 596 } 597 598 if ((pressbegin.tv_sec || pressbegin.tv_usec) && (enableoverlays || pressonrelease) && k && k->keysym == ispressingkeysym) { 599 printdbg("Delayed simulation of press after release: %ld\n", k->keysym); 600 /* simulate the press event, as we postponed it earlier in press() */ 601 for (i = 0; i < numkeys; i++) { 602 if (keys[i].pressed && IsModifierKey(keys[i].keysym)) { 603 if (keys[i].keysym == buttonmod) 604 neutralizebuttonmod = True; 605 else 606 simulate_keypress(keys[i].keysym); 607 } 608 } 609 if (buttonmod && !neutralizebuttonmod) { 610 simulate_keypress(buttonmod); 611 } 612 simulate_keypress(k->keysym); 613 pressbegin.tv_sec = 0; 614 pressbegin.tv_usec = 0; 615 } 616 617 if (k) 618 printdbg("Simulation of release: %ld\n", k->keysym); 619 else 620 printdbg("Simulation of release (all keys)\n"); 621 622 for (i = 0; i < numkeys; i++) { 623 if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) { 624 simulate_keyrelease(keys[i].keysym); 625 if (printoutput) 626 printkey(&keys[i], buttonmod); 627 keys[i].pressed = 0; 628 drawkey(&keys[i], True); 629 } 630 } 631 632 if (buttonmod && !neutralizebuttonmod) { 633 simulate_keyrelease(buttonmod); 634 } 635 636 if (k == NULL || !IsModifierKey(k->keysym)) { 637 for (i = 0; i < numkeys; i++) { 638 if (keys[i].pressed && IsModifierKey(keys[i].keysym)) { 639 if (!(keys[i].keysym == buttonmod && neutralizebuttonmod)) 640 simulate_keyrelease(keys[i].keysym); 641 keys[i].pressed = 0; 642 drawkey(&keys[i], True); 643 } 644 } 645 } 646 647 if (enableoverlays && currentoverlay != -1 && 648 (k == NULL || !IsModifierKey(k->keysym))) { 649 if (releaseprotect) { 650 releaseprotect = 0; 651 } else { 652 hideoverlay(); 653 } 654 } 655 } 656 657 void 658 run(void) 659 { 660 XEvent ev; 661 int xfd; 662 fd_set fds; 663 struct timeval tv; 664 double duration; 665 int overlayidx; 666 int i, r; 667 668 xfd = ConnectionNumber(dpy); 669 tv.tv_sec = 0; 670 tv.tv_usec = scan_rate; 671 672 XFlush(dpy); 673 674 while (running) { 675 usleep(100000L); /* 100ms */ 676 FD_ZERO(&fds); 677 FD_SET(xfd, &fds); 678 r = select(xfd + 1, &fds, NULL, NULL, &tv); 679 if (r) { 680 while (XPending(dpy)) { 681 XNextEvent(dpy, &ev); 682 if (handler[ev.type]) { 683 (handler[ev.type])(&ev); /* call handler */ 684 } 685 } 686 } else { 687 /* time-out expired without anything interesting happening, check for long-presses */ 688 if (ispressing && ispressingkeysym) { 689 duration = get_press_duration(); 690 if (debug >= 2) 691 printdbg("%f\n", duration); 692 overlayidx = hasoverlay(ispressingkeysym); 693 duration = get_press_duration(); 694 if (overlayidx != -1 && duration >= overlay_delay) { 695 printdbg("press duration %f, activating overlay\n", duration); 696 showoverlay(overlayidx); 697 pressbegin.tv_sec = 0; 698 pressbegin.tv_usec = 0; 699 ispressingkeysym = 0; 700 } else if ((overlayidx == -1) && (duration >= repeat_delay)) { 701 printdbg("press duration %f, activating repeat\n", duration); 702 simulate_keyrelease(ispressingkeysym); 703 simulate_keypress(ispressingkeysym); 704 XSync(dpy, False); 705 } 706 } 707 } 708 if (r == -1 || sigtermd) { 709 /* an error occurred or we received a signal */ 710 /* E.g. Generally in scripts we want to call SIGTERM on svkbd in which case 711 if the user is holding for example the enter key (to execute 712 the kill or script that does the kill), that causes an issue 713 since then X doesn't know the keyup is never coming.. (since 714 process will be dead before finger lifts - in that case we 715 just trigger out fake up presses for all keys */ 716 printdbg("signal received, releasing all keys"); 717 for (i = 0; i < numkeys; i++) { 718 XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, keys[i].keysym), False, 0); 719 } 720 running = False; 721 } 722 } 723 } 724 725 void 726 readxresources(void) 727 { 728 XrmDatabase xdb; 729 XrmValue xval; 730 char *type, *xrm; 731 732 XrmInitialize(); 733 734 if ((xrm = XResourceManagerString(drw->dpy))) { 735 xdb = XrmGetStringDatabase(xrm); 736 737 if (XrmGetResource(xdb, "svkbd.font", "*", &type, &xval) && !fonts[0]) 738 fonts[0] = estrdup(xval.addr); 739 740 if (XrmGetResource(xdb, "svkbd.background", "*", &type, &xval) && !colors[SchemeNorm][ColBg]) 741 colors[SchemeNorm][ColBg] = estrdup(xval.addr); 742 if (XrmGetResource(xdb, "svkbd.foreground", "*", &type, &xval) && !colors[SchemeNorm][ColFg]) 743 colors[SchemeNorm][ColFg] = estrdup(xval.addr); 744 745 if (XrmGetResource(xdb, "svkbd.shiftforeground", "*", &type, &xval) && !colors[SchemeNormShift][ColFg]) 746 colors[SchemeNormShift][ColFg] = estrdup(xval.addr); 747 if (XrmGetResource(xdb, "svkbd.shiftbackground", "*", &type, &xval) && !colors[SchemeNormShift][ColBg]) 748 colors[SchemeNormShift][ColBg] = estrdup(xval.addr); 749 750 if (XrmGetResource(xdb, "svkbd.ABCforeground", "*", &type, &xval) && !colors[SchemeNormABC][ColFg]) 751 colors[SchemeNormABC][ColFg] = estrdup(xval.addr); 752 if (XrmGetResource(xdb, "svkbd.ABCbackground", "*", &type, &xval) && !colors[SchemeNormABC][ColBg]) 753 colors[SchemeNormABC][ColBg] = estrdup(xval.addr); 754 755 if (XrmGetResource(xdb, "svkbd.ABCshiftforeground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColFg]) 756 colors[SchemeNormABCShift][ColFg] = estrdup(xval.addr); 757 if (XrmGetResource(xdb, "svkbd.ABCshiftbackground", "*", &type, &xval) && !colors[SchemeNormABCShift][ColBg]) 758 colors[SchemeNormABCShift][ColBg] = estrdup(xval.addr); 759 760 if (XrmGetResource(xdb, "svkbd.pressbackground", "*", &type, &xval) && !colors[SchemePress][ColBg]) 761 colors[SchemePress][ColBg] = estrdup(xval.addr); 762 if (XrmGetResource(xdb, "svkbd.pressforeground", "*", &type, &xval) && !colors[SchemePress][ColFg]) 763 colors[SchemePress][ColFg] = estrdup(xval.addr); 764 765 if (XrmGetResource(xdb, "svkbd.pressshiftbackground", "*", &type, &xval) && !colors[SchemePressShift][ColBg]) 766 colors[SchemePressShift][ColBg] = estrdup(xval.addr); 767 if (XrmGetResource(xdb, "svkbd.pressshiftforeground", "*", &type, &xval) && !colors[SchemePressShift][ColFg]) 768 colors[SchemePressShift][ColFg] = estrdup(xval.addr); 769 770 if (XrmGetResource(xdb, "svkbd.highlightbackground", "*", &type, &xval) && !colors[SchemeHighlight][ColBg]) 771 colors[SchemeHighlight][ColBg] = estrdup(xval.addr); 772 if (XrmGetResource(xdb, "svkbd.highlightforeground", "*", &type, &xval) && !colors[SchemeHighlight][ColFg]) 773 colors[SchemeHighlight][ColFg] = estrdup(xval.addr); 774 775 if (XrmGetResource(xdb, "svkbd.highlightshiftbackground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColBg]) 776 colors[SchemeHighlightShift][ColBg] = estrdup(xval.addr); 777 if (XrmGetResource(xdb, "svkbd.highlightshiftforeground", "*", &type, &xval) && !colors[SchemeHighlightShift][ColFg]) 778 colors[SchemeHighlightShift][ColFg] = estrdup(xval.addr); 779 780 if (XrmGetResource(xdb, "svkbd.overlaybackground", "*", &type, &xval) && !colors[SchemeOverlay][ColBg]) 781 colors[SchemeOverlay][ColBg] = estrdup(xval.addr); 782 if (XrmGetResource(xdb, "svkbd.overlayforeground", "*", &type, &xval) && !colors[SchemeOverlay][ColFg]) 783 colors[SchemeOverlay][ColFg] = estrdup(xval.addr); 784 785 if (XrmGetResource(xdb, "svkbd.overlayshiftbackground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColBg]) 786 colors[SchemeOverlayShift][ColBg] = estrdup(xval.addr); 787 if (XrmGetResource(xdb, "svkbd.overlayshiftforeground", "*", &type, &xval) && !colors[SchemeOverlayShift][ColFg]) 788 colors[SchemeOverlayShift][ColFg] = estrdup(xval.addr); 789 790 if (XrmGetResource(xdb, "svkbd.windowbackground", "*", &type, &xval) && !colors[SchemeWindow][ColBg]) 791 colors[SchemeWindow][ColBg] = estrdup(xval.addr); 792 if (XrmGetResource(xdb, "svkbd.windowforeground", "*", &type, &xval) && !colors[SchemeWindow][ColFg]) 793 colors[SchemeWindow][ColFg] = estrdup(xval.addr); 794 795 XrmDestroyDatabase(xdb); 796 } 797 } 798 799 void 800 setup(void) 801 { 802 XSetWindowAttributes wa; 803 XTextProperty str; 804 XSizeHints *sizeh = NULL; 805 XClassHint *ch; 806 XWMHints *wmh; 807 Atom atype = -1; 808 int i, j, sh, sw; 809 810 #ifdef XINERAMA 811 XineramaScreenInfo *info = NULL; 812 #endif 813 814 /* init screen */ 815 screen = DefaultScreen(dpy); 816 root = RootWindow(dpy, screen); 817 #ifdef XINERAMA 818 if (XineramaIsActive(dpy)) { 819 info = XineramaQueryScreens(dpy, &i); 820 sw = info[0].width; 821 sh = info[0].height; 822 XFree(info); 823 } else 824 #endif 825 { 826 sw = DisplayWidth(dpy, screen); 827 sh = DisplayHeight(dpy, screen); 828 } 829 drw = drw_create(dpy, screen, root, sw, sh); 830 831 readxresources(); 832 833 /* Apply defaults to font and colors*/ 834 if (!fonts[0]) 835 fonts[0] = estrdup(defaultfonts[0]); 836 for (i = 0; i < SchemeLast; ++i) { 837 for (j = 0; j < 2; ++j) { 838 if (!colors[i][j]) 839 colors[i][j] = estrdup(defaultcolors[i][j]); 840 } 841 } 842 843 if (!drw_fontset_create(drw, (const char **) fonts, LENGTH(fonts))) 844 die("no fonts could be loaded"); 845 free(fonts[0]); 846 847 drw_setscheme(drw, scheme[SchemeNorm]); 848 849 /* find an unused keycode to use as a temporary keycode (derived from source: 850 https://stackoverflow.com/questions/44313966/c-xtest-emitting-key-presses-for-every-unicode-character) */ 851 KeySym *keysyms; 852 int keysyms_per_keycode = 0; 853 int keycode_low, keycode_high; 854 Bool key_is_empty; 855 int symindex; 856 857 XDisplayKeycodes(dpy, &keycode_low, &keycode_high); 858 keysyms = XGetKeyboardMapping(dpy, keycode_low, keycode_high - keycode_low, &keysyms_per_keycode); 859 for (i = keycode_low; i <= keycode_high; i++) { 860 key_is_empty = True; 861 for (j = 0; j < keysyms_per_keycode; j++) { 862 symindex = (i - keycode_low) * keysyms_per_keycode + j; 863 if (keysyms[symindex] != 0) { 864 key_is_empty = False; 865 } else { 866 break; 867 } 868 } 869 if (key_is_empty) { 870 tmp_keycode = i; 871 break; 872 } 873 } 874 875 /* init appearance */ 876 for (j = 0; j < SchemeLast; j++) 877 scheme[j] = drw_scm_create(drw, (const char **) colors[j], 2); 878 879 for (j = 0; j < SchemeLast; ++j) { 880 free(colors[j][ColFg]); 881 free(colors[j][ColBg]); 882 } 883 884 /* init atoms */ 885 if (isdock) { 886 netatom[NetWMWindowType] = XInternAtom(dpy, 887 "_NET_WM_WINDOW_TYPE", False); 888 atype = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DOCK", False); 889 } 890 891 /* init appearance */ 892 countrows(); 893 if (!ww) 894 ww = sw; 895 if (!wh) 896 wh = sh * rows / heightfactor; 897 898 if (!wx) 899 wx = 0; 900 if (wx < 0) 901 wx = sw + wx - ww; 902 if (!wy) 903 wy = sh - wh; 904 if (wy < 0) 905 wy = sh + wy - wh; 906 907 for (i = 0; i < numkeys; i++) 908 keys[i].pressed = 0; 909 910 wa.override_redirect = !wmborder; 911 wa.border_pixel = scheme[SchemeNorm][ColFg].pixel; 912 wa.background_pixel = scheme[SchemeNorm][ColBg].pixel; 913 win = XCreateWindow(dpy, root, wx, wy, ww, wh, 0, 914 CopyFromParent, CopyFromParent, CopyFromParent, 915 CWOverrideRedirect | CWBorderPixel | 916 CWBackingPixel, &wa); 917 XSelectInput(dpy, win, StructureNotifyMask|ButtonReleaseMask| 918 ButtonPressMask|ExposureMask|LeaveWindowMask| 919 PointerMotionMask); 920 921 wmh = XAllocWMHints(); 922 wmh->input = False; 923 wmh->flags = InputHint; 924 if (!isdock) { 925 sizeh = XAllocSizeHints(); 926 sizeh->flags = PMaxSize | PMinSize; 927 sizeh->min_width = sizeh->max_width = ww; 928 sizeh->min_height = sizeh->max_height = wh; 929 } 930 XStringListToTextProperty(&name, 1, &str); 931 ch = XAllocClassHint(); 932 ch->res_class = name; 933 ch->res_name = name; 934 935 XSetWMProperties(dpy, win, &str, &str, NULL, 0, sizeh, wmh, ch); 936 937 XFree(keysyms); 938 XFree(ch); 939 XFree(wmh); 940 XFree(str.value); 941 if (sizeh != NULL) 942 XFree(sizeh); 943 944 if (isdock) { 945 XChangeProperty(dpy, win, netatom[NetWMWindowType], XA_ATOM, 946 32, PropModeReplace, 947 (unsigned char *)&atype, 1); 948 } 949 950 XMapRaised(dpy, win); 951 drw_resize(drw, ww, wh); 952 updatekeys(); 953 drawkeyboard(); 954 } 955 956 void 957 updatekeys(void) 958 { 959 int i, j; 960 double base; 961 int x, y = 0, h, r = rows; 962 963 h = (wh - 1) / rows; 964 for (i = 0; i < numkeys; i++, r--) { 965 for (j = i, base = 0; j < numkeys && keys[j].keysym != 0; j++) 966 base += keys[j].width; 967 for (x = 0; i < numkeys && keys[i].keysym != 0; i++) { 968 keys[i].x = x + xspacing; 969 keys[i].y = y + yspacing; 970 keys[i].w = keys[i].width * ww / base; 971 keys[i].h = r == 1 ? wh - y - 1 : h; 972 x += keys[i].w; 973 keys[i].w = keys[i].w - (xspacing * 2); 974 keys[i].h = keys[i].h - (yspacing * 2); 975 } 976 if (base != 0) 977 keys[i - 1].w = ww - 1 - keys[i - 1].x; 978 y += h; 979 } 980 } 981 982 void 983 usage(char *argv0) 984 { 985 fprintf(stderr, "usage: %s [-hdnovDOR] [-g geometry] [-fn font] [-l layers] [-s initial_layer]\n", argv0); 986 fprintf(stderr, "Options:\n"); 987 fprintf(stderr, " -d - Set Dock Window Type\n"); 988 fprintf(stderr, " -D - Enable debug\n"); 989 fprintf(stderr, " -O - Disable overlays\n"); 990 fprintf(stderr, " -R - Disable press-on-release\n"); 991 fprintf(stderr, " -n - Do not simulate key presses for X\n"); 992 fprintf(stderr, " -o - Print to standard output\n"); 993 fprintf(stderr, " -l - Comma separated list of layers to enable\n"); 994 fprintf(stderr, " -s - Layer to select on program start\n"); 995 fprintf(stderr, " -H [int] - Height fraction, one key row takes 1/x of the screen height\n"); 996 fprintf(stderr, " -fn [font] - Set font (Xft, e.g: DejaVu Sans:bold:size=20)\n"); 997 fprintf(stderr, " -g - Set the window position or size using the X geometry format\n"); 998 exit(1); 999 } 1000 1001 void 1002 setlayer(void) 1003 { 1004 numkeys = countkeys(layers[currentlayer]); 1005 memcpy(&keys, layers[currentlayer], sizeof(Key) * numkeys); 1006 countrows(); 1007 } 1008 1009 void 1010 cyclelayer(void) 1011 { 1012 currentlayer++; 1013 if (currentlayer >= numlayers) 1014 currentlayer = 0; 1015 printdbg("Cycling to layer %d\n", currentlayer); 1016 setlayer(); 1017 updatekeys(); 1018 drawkeyboard(); 1019 } 1020 1021 void 1022 togglelayer(void) 1023 { 1024 if (currentlayer > 0) { 1025 currentlayer = 0; 1026 } else if (numlayers > 1) { 1027 currentlayer = 1; 1028 } 1029 printdbg("Toggling layer %d\n", currentlayer); 1030 setlayer(); 1031 updatekeys(); 1032 drawkeyboard(); 1033 } 1034 1035 void 1036 showoverlay(int idx) 1037 { 1038 int i, j; 1039 1040 printdbg("Showing overlay %d\n", idx); 1041 1042 /* unpress existing key (visually only) */ 1043 for (i = 0; i < numkeys; i++) { 1044 if (keys[i].pressed && !IsModifierKey(keys[i].keysym)) { 1045 keys[i].pressed = 0; 1046 drawkey(&keys[i], True); 1047 break; 1048 } 1049 } 1050 1051 for (i = idx, j = 0; i < OVERLAYS; i++, j++) { 1052 if (overlay[i].keysym == XK_Cancel) { 1053 break; 1054 } 1055 /* certain modifier keys and basic keys are excluded from being overlayed: */ 1056 while (keys[j].keysym == 0 || keys[j].keysym == XK_Shift_L || 1057 keys[j].keysym == XK_Shift_R || keys[j].keysym == XK_Control_L || 1058 keys[j].keysym == XK_Control_R || keys[j].keysym == XK_Alt_L || 1059 keys[j].keysym == XK_Alt_R || keys[j].keysym == XK_BackSpace || 1060 keys[j].keysym == XK_Return || keys[j].keysym == XK_space || 1061 keys[j].keysym == XK_Cancel) 1062 j++; 1063 if (overlay[i].width > 1) 1064 j += overlay[i].width - 1; 1065 if (j >= numkeys) 1066 break; 1067 keys[j].label = overlay[i].label; 1068 keys[j].label2 = overlay[i].label2; 1069 keys[j].keysym = overlay[i].keysym; 1070 keys[j].modifier = overlay[i].modifier; 1071 keys[j].isoverlay = True; 1072 } 1073 currentoverlay = idx; 1074 overlaykeysym = ispressingkeysym; 1075 releaseprotect = 1; 1076 updatekeys(); 1077 drawkeyboard(); 1078 XSync(dpy, False); 1079 } 1080 1081 void 1082 hideoverlay(void) 1083 { 1084 printdbg("Hiding overlay, overlay was #%d\n", currentoverlay); 1085 currentoverlay = -1; 1086 overlaykeysym = 0; 1087 currentlayer--; 1088 cyclelayer(); 1089 } 1090 1091 void 1092 sigterm(int signo) 1093 { 1094 running = False; 1095 sigtermd = True; 1096 printdbg("SIGTERM received\n"); 1097 } 1098 1099 void 1100 init_layers(char *layer_names_list, const char *initial_layer_name) 1101 { 1102 char *s; 1103 int j, found; 1104 1105 if (layer_names_list == NULL) { 1106 numlayers = LAYERS; 1107 memcpy(&layers, &available_layers, sizeof(available_layers)); 1108 if (initial_layer_name != NULL) { 1109 for (j = 0; j < LAYERS; j++) { 1110 if (strcmp(layer_names[j], initial_layer_name) == 0) { 1111 currentlayer = j; 1112 break; 1113 } 1114 } 1115 } 1116 } else { 1117 s = strtok(layer_names_list, ","); 1118 while (s != NULL) { 1119 if (numlayers + 1 > LAYERS) 1120 die("too many layers specified"); 1121 found = 0; 1122 for (j = 0; j < LAYERS; j++) { 1123 if (strcmp(layer_names[j], s) == 0) { 1124 fprintf(stderr, "Adding layer %s\n", s); 1125 layers[numlayers] = available_layers[j]; 1126 if (initial_layer_name != NULL && strcmp(layer_names[j], initial_layer_name) == 0) { 1127 currentlayer = numlayers; 1128 } 1129 found = 1; 1130 break; 1131 } 1132 } 1133 if (!found) { 1134 fprintf(stderr, "Undefined layer: %s\n", s); 1135 exit(3); 1136 } 1137 numlayers++; 1138 s = strtok(NULL,","); 1139 } 1140 } 1141 setlayer(); 1142 } 1143 1144 void 1145 printdbg(const char *fmt, ...) 1146 { 1147 if (!debug) 1148 return; 1149 1150 va_list ap; 1151 va_start(ap, fmt); 1152 vfprintf(stderr, fmt, ap); 1153 va_end(ap); 1154 fflush(stderr); 1155 } 1156 1157 int 1158 main(int argc, char *argv[]) 1159 { 1160 char *initial_layer_name = NULL; 1161 char *layer_names_list = NULL; 1162 char *tmp; 1163 int i, xr, yr, bitm; 1164 unsigned int wr, hr; 1165 1166 signal(SIGTERM, sigterm); 1167 1168 if (OVERLAYS <= 1) { 1169 enableoverlays = 0; 1170 } else { 1171 if ((tmp = getenv("SVKBD_ENABLEOVERLAYS"))) 1172 enableoverlays = atoi(tmp); 1173 } 1174 if ((tmp = getenv("SVKBD_LAYERS"))) 1175 layer_names_list = estrdup(tmp); 1176 1177 if ((tmp = getenv("SVKBD_HEIGHTFACTOR"))) 1178 heightfactor = atoi(tmp); 1179 1180 if ((tmp = getenv("SVKBD_PRESSONRELEASE"))) /* defaults to 1 */ 1181 pressonrelease = atoi(tmp); 1182 1183 /* parse command line arguments */ 1184 for (i = 1; argv[i]; i++) { 1185 if (!strcmp(argv[i], "-v")) { 1186 die("svkbd-"VERSION); 1187 } else if (!strcmp(argv[i], "-d")) { 1188 isdock = True; 1189 } else if (!strncmp(argv[i], "-g", 2)) { 1190 if (i >= argc - 1) 1191 usage(argv[0]); 1192 1193 bitm = XParseGeometry(argv[++i], &xr, &yr, &wr, &hr); 1194 if (bitm & XValue) 1195 wx = xr; 1196 if (bitm & YValue) 1197 wy = yr; 1198 if (bitm & WidthValue) 1199 ww = (int)wr; 1200 if (bitm & HeightValue) 1201 wh = (int)hr; 1202 if (bitm & XNegative && wx == 0) 1203 wx = -1; 1204 if (bitm & YNegative && wy == 0) 1205 wy = -1; 1206 } else if (!strcmp(argv[i], "-fn")) { /* font or font set */ 1207 if (i >= argc - 1) 1208 usage(argv[0]); 1209 fonts[0] = estrdup(argv[++i]); 1210 } else if (!strcmp(argv[i], "-D")) { 1211 debug = 1; 1212 } else if (!strcmp(argv[i], "-h")) { 1213 usage(argv[0]); 1214 } else if (!strcmp(argv[i], "-O")) { 1215 enableoverlays = 0; 1216 } else if (!strcmp(argv[i], "-o")) { 1217 printoutput = 1; 1218 } else if (!strcmp(argv[i], "-n")) { 1219 simulateoutput = 0; 1220 } else if (!strcmp(argv[i], "-R")) { 1221 pressonrelease = 0; 1222 } else if (!strcmp(argv[i], "-l")) { 1223 if (i >= argc - 1) 1224 usage(argv[0]); 1225 free(layer_names_list); 1226 layer_names_list = estrdup(argv[++i]); 1227 } else if (!strcmp(argv[i], "-s")) { 1228 if (i >= argc - 1) 1229 usage(argv[0]); 1230 initial_layer_name = argv[++i]; 1231 } else if (!strcmp(argv[i], "-H")) { 1232 if (i >= argc - 1) 1233 usage(argv[0]); 1234 heightfactor = atoi(argv[++i]); 1235 } else { 1236 fprintf(stderr, "Invalid argument: %s\n", argv[i]); 1237 usage(argv[0]); 1238 } 1239 } 1240 1241 if (printoutput) 1242 setbuf(stdout, NULL); /* unbuffered output */ 1243 1244 if (heightfactor <= 0) 1245 die("height factor must be a positive integer"); 1246 1247 init_layers(layer_names_list, initial_layer_name); 1248 1249 if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) 1250 fprintf(stderr, "warning: no locale support"); 1251 if (!(dpy = XOpenDisplay(0))) 1252 die("cannot open display"); 1253 setup(); 1254 run(); 1255 cleanup(); 1256 XCloseDisplay(dpy); 1257 free(layer_names_list); 1258 1259 return 0; 1260 }