1/* 2 3Copyright 1988, 1998 The Open Group 4 5Permission to use, copy, modify, distribute, and sell this software and its 6documentation for any purpose is hereby granted without fee, provided that 7the above copyright notice appear in all copies and that both that 8copyright notice and this permission notice appear in supporting 9documentation. 10 11The above copyright notice and this permission notice shall be included 12in all copies or substantial portions of the Software. 13 14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR 18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20OTHER DEALINGS IN THE SOFTWARE. 21 22Except as contained in this notice, the name of The Open Group shall 23not be used in advertising or otherwise to promote the sale, use or 24other dealings in this Software without prior written authorization 25from The Open Group. 26 27*/ 28 29#ifdef HAVE_CONFIG_H 30#include "config.h" 31#endif 32 33#include <X11/Xos.h> 34#include <X11/Xlib.h> 35#include <stdio.h> 36#include <ctype.h> 37#include "xmodmap.h" 38#include "wq.h" 39#include <stdlib.h> 40#include <stdint.h> 41 42#ifdef HAVE_STRNCASECMP 43#include <strings.h> 44#endif 45 46static XModifierKeymap *map = NULL; 47 48 49/* 50 * The routines in this file manipulate a queue of intructions. Instead of 51 * executing each line as it is entered, we build up a list of actions to 52 * take and execute them all at the end. This allows us to find all errors 53 * at once, and to preserve the context in which we are looking up keysyms. 54 */ 55 56struct wq work_queue = {NULL, NULL}; 57 58 59/* 60 * common utility routines 61 */ 62 63/* 64 * This is a combination of reallocf() and reallocarray(). 65 * If the realloc fails, it frees the old pointer so it doesn't leak. 66 */ 67static void * 68reallocfarray(void *old, size_t num, size_t size) 69{ 70 static void *new; 71 72 if (size > 0 && num > (SIZE_MAX / size)) /* overflow would happen */ 73 new = NULL; 74 else 75 new = realloc(old, num * size); 76 77 if (new == NULL) 78 free(old); 79 80 return new; 81} 82 83static KeyCode * 84KeysymToKeycodes(Display *dpy, KeySym keysym, int *pnum_kcs) 85{ 86 KeyCode *kcs = NULL; 87 int i, j; 88 89 *pnum_kcs = 0; 90 for (i = min_keycode; i <= max_keycode; i++) { 91 for (j = 0; j < 8; j++) { 92 if (XKeycodeToKeysym(dpy, (KeyCode) i, j) == keysym) { 93 if (!kcs) 94 kcs = malloc(sizeof(KeyCode)); 95 else 96 kcs = reallocfarray(kcs, (*pnum_kcs + 1), sizeof(KeyCode)); 97 if (!kcs) { 98 fprintf(stderr, "attempt to allocate %ld byte keycode set", 99 (long) ((*pnum_kcs + 1) * sizeof (KeyCode))); 100 return NULL; 101 } 102 kcs[*pnum_kcs] = i; 103 *pnum_kcs += 1; 104 break; 105 } 106 } 107 } 108 return kcs; 109} 110 111static char * 112copy_to_scratch(const char *s, int len) 113{ 114 static char *buf = NULL; 115 static int buflen = 0; 116 117 if (len < 0) 118 len = 0; 119 120 if (len >= buflen) { 121 if (buf) free (buf); 122 buflen = (len < 40) ? 80 : (len * 2); 123 buf = malloc (buflen+1); 124 if (!buf) { 125 fprintf (stderr, "attempt to allocate %d byte scratch buffer\n", buflen + 1); 126 return NULL; 127 } 128 } 129 130 strncpy (buf, s, len); 131 buf[len] = '\0'; 132 133 return (buf); 134} 135 136static void 137badheader(void) 138{ 139 fprintf (stderr, "%s: %s:%d: bad ", ProgramName, inputFilename, lineno+1); 140 parse_errors++; 141} 142 143#define badmsg0(what) { badheader(); fprintf (stderr, what); \ 144 putc ('\n', stderr); } 145 146#define badmsg(what,arg) { badheader(); fprintf (stderr, what, arg); \ 147 putc ('\n', stderr); } 148 149#define badmsgn(what,s,len) badmsg (what, copy_to_scratch (s, len)) 150 151void 152initialize_map (void) 153{ 154 map = XGetModifierMapping (dpy); 155 return; 156} 157 158static void do_keycode ( char *line, int len ); 159static void do_keysym ( char *line, int len ); 160static void finish_keycodes ( const char *line, int len, KeyCode *keycodes, 161 int count ); 162static void do_add ( char *line, int len ); 163static void do_remove ( char *line, int len ); 164static void do_clear ( char *line, int len ); 165static void do_pointer ( char *line, int len ); 166static int get_keysym_list ( const char *line, int len, int *np, KeySym **kslistp ); 167 168static void print_opcode(union op *op); 169 170static int skip_word ( const char *s, int len ); 171static int skip_chars ( const char *s, int len ); 172static int skip_space ( const char *s, int len ); 173 174static struct dt { 175 const char *command; /* name of input command */ 176 int length; /* length of command */ 177 void (*proc)(char *, int); /* handler */ 178} dispatch_table[] = { 179 { "keycode", 7, do_keycode }, 180 { "keysym", 6, do_keysym }, 181 { "add", 3, do_add }, 182 { "remove", 6, do_remove }, 183 { "clear", 5, do_clear }, 184 { "pointer", 7, do_pointer }, 185 { NULL, 0, NULL }}; 186 187/* 188 * handle_line - this routine parses the input line (which has had all leading 189 * and trailing whitespace removed) and builds up the work queue. 190 */ 191 192void 193handle_line(char *line, /* string to parse */ 194 int len) /* length of line */ 195{ 196 int n; 197 struct dt *dtp; 198 199 n = skip_chars (line, len); 200 if (n < 0) { 201 badmsg ("input line '%s'", line); 202 return; 203 } 204 205 for (dtp = dispatch_table; dtp->command != NULL; dtp++) { 206 if (n == dtp->length && 207 strncmp (line, dtp->command, dtp->length) == 0) { 208 209 n += skip_space (line+n, len-n); 210 line += n, len -= n; 211 212 (*(dtp->proc)) (line, len); 213 return; 214 } 215 } 216 217 fprintf (stderr, "%s: unknown command on line %s:%d\n", 218 ProgramName, inputFilename, lineno+1); 219 parse_errors++; 220} 221 222/* 223 * the following routines are useful for parsing 224 */ 225 226static int 227skip_word (const char *s, int len) 228{ 229 register int n; 230 231 n = skip_chars (s, len); 232 return (n + skip_space (s+n, len-n)); 233} 234 235static int 236skip_chars(const char *s, int len) 237{ 238 register int i; 239 240 if (len <= 0 || !s || *s == '\0') return (0); 241 242 for (i = 0; i < len; i++) { 243 if (isspace(s[i])) break; 244 } 245 return (i); 246} 247 248static int 249skip_space(const char *s, int len) 250{ 251 register int i; 252 253 if (len <= 0 || !s || *s == '\0') return (0); 254 255 for (i = 0; i < len; i++) { 256 if (!s[i] || !isspace(s[i])) break; 257 } 258 return (i); 259} 260 261 262static int 263skip_until_char(const char *s, int len, char c) 264{ 265 register int i; 266 267 for (i = 0; i < len; i++) { 268 if (s[i] == c) break; 269 } 270 return (i); 271} 272 273 274/* 275 * The action routines. 276 * 277 * This is where the real work gets done. Each routine is responsible for 278 * parsing its input (note that the command keyword has been stripped off) 279 * and adding to the work queue. They are also in charge of outputting 280 * error messages and returning non-zero if there is a problem. 281 * 282 * The following global variables are available: 283 * dpy the display descriptor 284 * work_queue linked list of opcodes 285 * inputFilename name of the file being processed 286 * lineno line number of current line in input file 287 */ 288static void 289add_to_work_queue(union op *p) /* this can become a macro someday */ 290{ 291 if (work_queue.head == NULL) { /* nothing on the list */ 292 work_queue.head = work_queue.tail = p; /* head, tail, no prev */ 293 } else { 294 work_queue.tail->generic.next = p; /* head okay, prev */ 295 work_queue.tail = p; /* tail */ 296 } 297 p->generic.next = NULL; 298 299 if (verbose) { 300 print_opcode (p); 301 } 302 return; 303} 304 305static Bool 306parse_number(const char *str, unsigned long *val) 307{ 308 const char *fmt = "%ld"; 309 310 if (*str == '0') { 311 str++; 312 while (isspace(*str)) 313 str++; 314 fmt = "%lo"; 315 if (*str == '\0') { 316 *val = 0; 317 return 1; 318 } 319 if (*str == 'x' || *str == 'X') { 320 str++; 321 fmt = "%lx"; 322 } 323 } 324 return (sscanf (str, fmt, val) == 1); 325} 326 327static Bool 328parse_keysym(const char *line, int n, char **name, KeySym *keysym) 329{ 330 *name = copy_to_scratch (line, n); 331 if (!strcmp(*name, "NoSymbol")) { 332 *keysym = NoSymbol; 333 return (True); 334 } 335 *keysym = XStringToKeysym (*name); 336 if (*keysym == NoSymbol && '0' <= **name && **name <= '9') 337 return parse_number(*name, keysym); 338 return (*keysym != NoSymbol); 339} 340 341/* 342 * do_keycode - parse off lines of the form 343 * 344 * "keycode" number "=" [keysym ...] 345 * ^ 346 * 347 * where number is in decimal, hex, or octal. Any number of keysyms may be 348 * listed. 349 */ 350 351static void 352do_keycode(char *line, int len) 353{ 354 int dummy; 355 const char *fmt = "%d"; 356 KeyCode keycode; 357 358 if (len < 3 || !line || *line == '\0') { /* 5=a minimum */ 359 badmsg0 ("keycode input line"); 360 return; 361 } 362 363 /* 364 * We need not bother to advance line/len past the 365 * number (or the string 'any') as finish_keycodes() will 366 * first advance past the '='. 367 */ 368 if (!strncmp("any", line, 3)) { 369 keycode = 0; 370 } else { 371 if (*line == '0') line++, len--, fmt = "%o"; 372 if (*line == 'x' || *line == 'X') line++, len--, fmt = "%x"; 373 374 dummy = 0; 375 if (sscanf (line, fmt, &dummy) != 1 || dummy == 0) { 376 badmsg0 ("keycode value"); 377 return; 378 } 379 keycode = (KeyCode) dummy; 380 if ((int)keycode < min_keycode || (int)keycode > max_keycode) { 381 badmsg0 ("keycode value (out of range)"); 382 return; 383 } 384 } 385 386 finish_keycodes (line, len, &keycode, 1); 387} 388 389 390/* 391 * do_keysym - parse off lines of the form 392 * 393 * "keysym" keysym "=" [keysym ...] 394 * ^ 395 * 396 * The left keysyms has to be checked for validity and evaluated. 397 */ 398 399static void 400do_keysym(char *line, int len) 401{ 402 int n; 403 KeyCode *keycodes; 404 KeySym keysym; 405 char *tmpname; 406 407 if (len < 3 || !line || *line == '\0') { /* a=b minimum */ 408 badmsg0 ("keysym input line"); 409 return; 410 } 411 412 n = skip_chars (line, len); 413 if (n < 1) { 414 badmsg0 ("target keysym name"); 415 return; 416 } 417 if (!parse_keysym(line, n, &tmpname, &keysym)) { 418 badmsg ("keysym target key symbol '%s'", tmpname); 419 return; 420 } 421 422 keycodes = KeysymToKeycodes (dpy, keysym, &n); 423 if (n == 0) { 424 badmsg ("keysym target keysym '%s', no corresponding keycodes", 425 tmpname); 426 return; 427 } 428 if (verbose) { 429 int i; 430 printf ("! Keysym %s (0x%lx) corresponds to keycode(s)", 431 tmpname, (long) keysym); 432 for (i = 0; i < n; i++) 433 printf (" 0x%x", keycodes[i]); 434 printf("\n"); 435 } 436 437 finish_keycodes (line, len, keycodes, n); 438} 439 440static void 441finish_keycodes(const char *line, int len, KeyCode *keycodes, int count) 442{ 443 int n; 444 KeySym *kslist; 445 union op *uop; 446 struct op_keycode *opk; 447 448 n = skip_until_char (line, len, '='); 449 line += n, len -= n; 450 451 if (len < 1 || *line != '=') { /* = minimum */ 452 badmsg0 ("keycode command (missing keysym list),"); 453 return; 454 } 455 line++, len--; /* skip past the = */ 456 457 n = skip_space (line, len); 458 line += n, len -= n; 459 460 /* allow empty list */ 461 if (get_keysym_list (line, len, &n, &kslist) < 0) 462 return; 463 464 while (--count >= 0) { 465 uop = AllocStruct (union op); 466 if (!uop) { 467 badmsg ("attempt to allocate a %ld byte keycode opcode", 468 (long) sizeof (struct op_keycode)); 469 return; 470 } 471 opk = &uop->keycode; 472 473 opk->type = doKeycode; 474 opk->target_keycode = keycodes[count]; 475 opk->count = n; 476 opk->keysyms = kslist; 477 478 add_to_work_queue (uop); 479 } 480 481#ifdef later 482 /* make sure we handle any special keys */ 483 check_special_keys (keycode, n, kslist); 484#endif 485} 486 487 488/* 489 * parse_modifier - convert a modifier string name to its index 490 */ 491 492struct modtab modifier_table[] = { /* keep in order so it can be index */ 493 { "shift", 5, 0 }, 494 { "lock", 4, 1 }, 495 { "control", 7, 2 }, 496 { "mod1", 4, 3 }, 497 { "mod2", 4, 4 }, 498 { "mod3", 4, 5 }, 499 { "mod4", 4, 6 }, 500 { "mod5", 4, 7 }, 501 { "ctrl", 4, 2 }, 502 { NULL, 0, 0 }}; 503 504static int 505parse_modifier(char *line, int n) 506{ 507 register int i; 508 struct modtab *mt; 509 510 /* lowercase for comparison against table */ 511 for (i = 0; i < n; i++) { 512 if (isupper (line[i])) line[i] = tolower (line[i]); 513 } 514 515 for (mt = modifier_table; mt->name; mt++) { 516 if (n == mt->length && strncmp (line, mt->name, n) == 0) 517 return (mt->value); 518 } 519 return (-1); 520} 521 522 523/* 524 * do_add - parse off lines of the form 525 * 526 * add MODIFIER = keysym ... 527 * ^ 528 * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case 529 * is not important. There should also be an alias Ctrl for control. 530 */ 531 532static void 533do_add(char *line, int len) 534{ 535 int n; 536 int modifier; 537 KeySym *kslist; 538 union op *uop; 539 struct op_addmodifier *opam; 540 541 if (len < 6 || !line || *line == '\0') { /* Lock=a minimum */ 542 badmsg0 ("add modifier input line"); 543 return; 544 } 545 546 n = skip_chars (line, len); 547 if (n < 1) { 548 badmsg ("add modifier name %s", line); 549 return; 550 } 551 552 modifier = parse_modifier (line, n); 553 if (modifier < 0) { 554 badmsgn ("add modifier name '%s', not allowed", line, n); 555 return; 556 } 557 558 line += n, len -= n; 559 n = skip_until_char (line, len, '='); 560 if (n < 0) { 561 badmsg0 ("add modifier = keysym"); 562 return; 563 } 564 565 n++; /* skip = */ 566 n += skip_space (line+n, len-n); 567 line += n, len -= n; 568 569 if (get_keysym_list (line, len, &n, &kslist) < 0) 570 return; 571 if (n == 0) { 572 badmsg0 ("add modifier keysym list (empty)"); 573 return; 574 } 575 576 uop = AllocStruct (union op); 577 if (!uop) { 578 badmsg ("attempt to allocate %ld byte addmodifier opcode", 579 (long) sizeof (struct op_addmodifier)); 580 return; 581 } 582 opam = &uop->addmodifier; 583 584 opam->type = doAddModifier; 585 opam->modifier = modifier; 586 opam->count = n; 587 opam->keysyms = kslist; 588 589 add_to_work_queue (uop); 590} 591 592#ifdef AUTO_ADD_REMOVE 593/* 594 * make_add - stick a single add onto the queue 595 */ 596static void 597make_add(int modifier, KeySym keysym) 598{ 599 union op *uop; 600 struct op_addmodifier *opam; 601 602 uop = AllocStruct (union op); 603 if (!uop) { 604 badmsg ("attempt to allocate %ld byte addmodifier opcode", 605 (long) sizeof (struct op_addmodifier)); 606 return; 607 } 608 opam = &uop->addmodifier; 609 610 opam->type = doAddModifier; 611 opam->modifier = modifier; 612 opam->count = 1; 613 opam->keysyms = malloc (sizeof (KeySym)); 614 if (!opam->keysyms) { 615 badmsg ("attempt to allocate %ld byte KeySym", (long) sizeof (KeySym)); 616 free (opam); 617 return; 618 } 619 opam->keysyms[0] = keysym; 620 621 add_to_work_queue (uop); 622 return; 623} 624#endif /* AUTO_ADD_REMOVE */ 625 626 627/* 628 * do_remove - parse off lines of the form 629 * 630 * remove MODIFIER = oldkeysym ... 631 * ^ 632 * where the MODIFIER is one of Shift, Lock, Control, Mod[1-5] where case 633 * is not important. There should also be an alias Ctrl for control. 634 */ 635 636static void 637do_remove(char *line, int len) 638{ 639 int n; 640 int nc; 641 int i; 642 int tot; 643 int modifier; 644 KeySym *kslist; 645 KeyCode *kclist; 646 union op *uop; 647 struct op_removemodifier *oprm; 648 649 if (len < 6 || !line || *line == '\0') { /* Lock=a minimum */ 650 badmsg0 ("remove modifier input line"); 651 return; 652 } 653 654 n = skip_chars (line, len); 655 if (n < 1) { 656 badmsg ("remove modifier name %s", line); 657 return; 658 } 659 660 modifier = parse_modifier (line, n); 661 if (modifier < 0) { 662 badmsgn ("remove modifier name '%s', not allowed", line, n); 663 return; 664 } 665 666 line += n, len -= n; 667 n = skip_until_char (line, len, '='); 668 if (n < 0) { 669 badmsg0 ("remove modifier = keysym"); 670 return; 671 } 672 673 n++; 674 n += skip_space (line+n, len-n); 675 line += n, len -= n; 676 677 if (get_keysym_list (line, len, &n, &kslist) < 0) 678 return; 679 if (n == 0) { 680 badmsg0 ("remove modifier keysym list (empty)"); 681 return; 682 } 683 684 /* 685 * unlike the add command, we have to now evaluate the keysyms 686 */ 687 688 kclist = malloc (n * sizeof (KeyCode)); 689 if (!kclist) { 690 badmsg ("attempt to allocate %ld byte keycode list", 691 (long) (n * sizeof (KeyCode))); 692 free (kslist); 693 return; 694 } 695 696 tot = n; 697 nc = 0; 698 for (i = 0; i < n; i++) { 699 int num_kcs; 700 KeyCode *kcs; 701 kcs = KeysymToKeycodes (dpy, kslist[i], &num_kcs); 702 if (num_kcs == 0) { 703 char *tmpname = XKeysymToString (kslist[i]); 704 badmsg ("keysym in remove modifier list '%s', no corresponding keycodes", 705 tmpname ? tmpname : "?"); 706 continue; 707 } 708 if (verbose) { 709 int j; 710 char *tmpname = XKeysymToString (kslist[i]); 711 printf ("! Keysym %s (0x%lx) corresponds to keycode(s)", 712 tmpname ? tmpname : "?", (long) kslist[i]); 713 for (j = 0; j < num_kcs; j++) 714 printf(" 0x%x", kcs[j]); 715 printf("\n"); 716 } 717 if (nc + num_kcs > tot) { 718 tot = nc + num_kcs; 719 kclist = reallocfarray(kclist, tot, sizeof(KeyCode)); 720 if (!kclist) { 721 badmsg ("attempt to allocate %ld byte keycode list", 722 (long) (tot * sizeof (KeyCode))); 723 free (kslist); 724 return; 725 } 726 } 727 while (--num_kcs >= 0) 728 kclist[nc++] = *kcs++; /* okay, add it to list */ 729 } 730 731 free (kslist); /* all done with it */ 732 733 uop = AllocStruct (union op); 734 if (!uop) { 735 badmsg ("attempt to allocate %ld byte removemodifier opcode", 736 (long) sizeof (struct op_removemodifier)); 737 free(kclist); 738 return; 739 } 740 oprm = &uop->removemodifier; 741 742 oprm->type = doRemoveModifier; 743 oprm->modifier = modifier; 744 oprm->count = nc; 745 oprm->keycodes = kclist; 746 747 add_to_work_queue (uop); 748} 749 750#ifdef AUTO_ADD_REMOVE 751/* 752 * make_remove - stick a single remove onto the queue 753 */ 754static void 755make_remove(int modifier, KeyCode keycode) 756{ 757 union op *uop; 758 struct op_removemodifier *oprm; 759 760 uop = AllocStruct (union op); 761 if (!uop) { 762 badmsg ("attempt to allocate %ld byte removemodifier opcode", 763 (long) sizeof (struct op_removemodifier)); 764 return; 765 } 766 oprm = &uop->removemodifier; 767 768 oprm->type = doRemoveModifier; 769 oprm->modifier = modifier; 770 oprm->count = 1; 771 oprm->keycodes = malloc (sizeof (KeyCode)); 772 if (!oprm->keycodes) { 773 badmsg ("attempt to allocate %ld byte KeyCode", 774 (long) sizeof (KeyCode)); 775 free (oprm); 776 return; 777 } 778 oprm->keycodes[0] = keycode; 779 780 add_to_work_queue (uop); 781 return; 782} 783#endif /* AUTO_ADD_REMOVE */ 784 785 786/* 787 * do_clear - parse off lines of the form 788 * 789 * clear MODIFIER 790 * ^ 791 */ 792 793static void 794do_clear(char *line, int len) 795{ 796 int n; 797 int modifier; 798 union op *uop; 799 struct op_clearmodifier *opcm; 800 801 if (len < 4 || !line || *line == '\0') { /* Lock minimum */ 802 badmsg0 ("clear modifier input line"); 803 return; 804 } 805 806 n = skip_chars (line, len); 807 808 modifier = parse_modifier (line, n); 809 if (modifier < 0) { 810 badmsgn ("clear modifier name '%s'", line, n); 811 return; 812 } 813 n += skip_space (line+n, len-n); 814 if (n != len) { 815 badmsgn ("extra argument '%s' to clear modifier", line+n, len-n); 816 /* okay to continue */ 817 } 818 819 uop = AllocStruct (union op); 820 if (!uop) { 821 badmsg ("attempt to allocate %ld byte clearmodifier opcode", 822 (long) sizeof (struct op_clearmodifier)); 823 return; 824 } 825 opcm = &uop->clearmodifier; 826 827 opcm->type = doClearModifier; 828 opcm->modifier = modifier; 829 830 add_to_work_queue (uop); 831} 832 833#ifndef HAVE_STRNCASECMP 834static int 835strncasecmp(const char *a, const char *b, int n) 836{ 837 int i; 838 int a1, b1; 839 840 for (i = 0; i < n; i++, a++, b++) { 841 if (!*a) return -1; 842 if (!*b) return 1; 843 844 if (*a != *b) { 845 a1 = (isascii(*a) && isupper(*a)) ? tolower(*a) : *a; 846 b1 = (isascii(*b) && isupper(*b)) ? tolower(*b) : *b; 847 if (a1 != b1) return b1 - a1; 848 } 849 } 850 return 0; 851} 852#endif 853 854/* 855 * do_pointer = get list of numbers of the form 856 * 857 * buttons = NUMBER ... 858 * ^ 859 */ 860 861static void 862do_pointer(char *line, int len) 863{ 864 int n; 865 int i; 866 unsigned long val; 867 union op *uop; 868 struct op_pointer *opp; 869 unsigned char buttons[MAXBUTTONCODES]; 870 int nbuttons; 871 char *strval; 872 Bool ok; 873 874 if (len < 2 || !line || *line == '\0') { /* =1 minimum */ 875 badmsg0 ("buttons input line"); 876 return; 877 } 878 879 nbuttons = XGetPointerMapping (dpy, buttons, MAXBUTTONCODES); 880 881 n = skip_space (line, len); 882 line += n, len -= n; 883 884 if (line[0] != '=') { 885 badmsg0 ("buttons pointer code list, missing equal sign"); 886 return; 887 } 888 889 line++, len--; /* skip = */ 890 n = skip_space (line, len); 891 line += n, len -= n; 892 893 i = 0; 894 if (len < 7 || strncasecmp (line, "default", 7) != 0) { 895 while (len > 0) { 896 n = skip_space (line, len); 897 line += n, len -= n; 898 if (line[0] == '\0') break; 899 n = skip_word (line, len); 900 if (n < 1) { 901 badmsg ("skip of word in buttons line: %s", line); 902 return; 903 } 904 strval = copy_to_scratch(line, n); 905 if (strval == NULL) 906 /* copy_to_scratch already printed error message */ 907 return; 908 ok = parse_number (strval, &val); 909 if (!ok || val >= MAXBUTTONCODES) { 910 badmsg ("value %s given for buttons list", strval); 911 return; 912 } 913 buttons[i++] = (unsigned char) val; 914 line += n, len -= n; 915 } 916 } 917 918 if (i > 0 && i != nbuttons) { 919 if (i < nbuttons) { 920 fprintf (stderr, 921 "Warning: Only changing the first %d of %d buttons.\n", 922 i, nbuttons); 923 } 924 else { /* i > nbuttons */ 925 fprintf (stderr, 926 "Warning: Not changing %d extra buttons beyond %d.\n", 927 i - nbuttons, nbuttons); 928 } 929 i = nbuttons; 930 } 931 932 uop = AllocStruct (union op); 933 if (!uop) { 934 badmsg ("attempt to allocate a %ld byte pointer opcode", 935 (long) sizeof (struct op_pointer)); 936 return; 937 } 938 opp = &uop->pointer; 939 940 opp->type = doPointer; 941 opp->count = i; 942 for (i = 0; i < opp->count; i++) { 943 opp->button_codes[i] = buttons[i]; 944 } 945 946 add_to_work_queue (uop); 947} 948 949 950/* 951 * get_keysym_list - parses the rest of the line into a keysyms assumes 952 * that the = sign has been parsed off but there may be leading whitespace 953 * 954 * keysym ... 955 * ^ 956 * 957 * this involves getting the word containing the keysym, checking its range, 958 * and adding it to the list. 959 */ 960 961static int 962get_keysym_list(const char *line, int len, int *np, KeySym **kslistp) 963{ 964 int havesofar, maxcanhave; 965 KeySym *keysymlist; 966 967 *np = 0; 968 *kslistp = NULL; 969 970 if (len == 0) return (0); /* empty list */ 971 972 havesofar = 0; 973 maxcanhave = 4; /* most lists are small */ 974 keysymlist = malloc (maxcanhave * sizeof (KeySym)); 975 if (!keysymlist) { 976 badmsg ("attempt to allocate %ld byte initial keysymlist", 977 (long) (maxcanhave * sizeof (KeySym))); 978 return (-1); 979 } 980 981 while (len > 0) { 982 KeySym keysym; 983 int n; 984 char *tmpname; 985 Bool ok; 986 987 n = skip_space (line, len); 988 line += n, len -= n; 989 990 n = skip_chars (line, len); 991 if (n < 0) { 992 badmsg0 ("keysym name list"); 993 free(keysymlist); 994 return (-1); 995 } 996 997 ok = parse_keysym (line, n, &tmpname, &keysym); 998 line += n, len -= n; 999 if (!ok) { 1000 badmsg ("keysym name '%s' in keysym list", tmpname); 1001 /* do NOT return here, look for others */ 1002 continue; 1003 } 1004 1005 /* 1006 * Do NOT test to see if the keysym translates to a keycode or you 1007 * won't be able to assign new ones.... 1008 */ 1009 1010 /* grow the list bigger if necessary */ 1011 if (havesofar >= maxcanhave) { 1012 maxcanhave *= 2; 1013 keysymlist = reallocfarray(keysymlist, maxcanhave, sizeof(KeySym)); 1014 if (!keysymlist) { 1015 badmsg ("attempt to grow keysym list to %ld bytes", 1016 (long) (maxcanhave * sizeof (KeySym))); 1017 return (-1); 1018 } 1019 } 1020 1021 /* and add it to the list */ 1022 keysymlist[havesofar++] = keysym; 1023 } 1024 1025 *kslistp = keysymlist; 1026 *np = havesofar; 1027 return (0); 1028} 1029 1030 1031#ifdef later 1032/* 1033 * check_special_keys - run through list of keysyms and generate "add" or 1034 * "remove" commands for for any of the key syms that appear in the modifier 1035 * list. this involves running down the modifier map which is an array of 1036 * 8 by map->max_keypermod keycodes. 1037 */ 1038 1039static void 1040check_special_keys(KeyCode keycode, int n, KeySym *kslist) 1041{ 1042 int i; /* iterator variable */ 1043 KeyCode *kcp; /* keycode pointer */ 1044 1045 /* 1046 * walk the modifiermap array. since its dimensions are not known at 1047 * compile time, we have to walk it by hand instead of indexing. this 1048 * is why it is initialized outside the loop, but incremented inside the 1049 * second loop. 1050 */ 1051 1052 kcp = map->modifiermap; /* start at beginning and iterate */ 1053 for (i = 0; i < 8; i++) { /* there are 8 modifier keys */ 1054 int j; 1055 1056 for (j = 0; j < map->max_keypermod; j++, kcp++) { 1057 KeySym keysym; 1058 int k; 1059 1060 if (!*kcp) continue; /* only non-zero entries significant */ 1061 1062 /* 1063 * check to see if the target keycode is already a modifier; if so, 1064 * then we have to remove it 1065 */ 1066 if (keycode == *kcp) { 1067 make_remove (i, keycode); 1068 } 1069 1070 /* 1071 * now, check to see if any of the keysyms map to keycodes 1072 * that are in the modifier list 1073 */ 1074 for (k = 0; k < n; k++) { 1075 KeyCodes kc; 1076 1077 kc = XKeysymToKeycode (dpy, kslist[k]); 1078 if (kc == *kcp) { /* yup, found one */ 1079 /* 1080 * have to generate a remove of the CURRENT keycode 1081 * and then an add of the new KEYCODE 1082 */ 1083 make_remove (i, kc); /* modifier, keycode */ 1084 make_add (i, kslist[k]); /* modifier, keysym */ 1085 } 1086 } 1087 } 1088 } 1089 return; 1090} 1091#endif 1092 1093/* 1094 * print_work_queue - disassemble the work queue and print it on stdout 1095 */ 1096 1097void 1098print_work_queue(void) 1099{ 1100 union op *op; 1101 1102 printf ("! dump of work queue\n"); 1103 for (op = work_queue.head; op; op = op->generic.next) { 1104 print_opcode (op); 1105 } 1106 return; 1107} 1108 1109static void 1110print_opcode(union op *op) 1111{ 1112 int i; 1113 1114 printf (" "); 1115 switch (op->generic.type) { 1116 case doKeycode: 1117 if (op->keycode.target_keycode) 1118 printf ("keycode 0x%lx =", (long) op->keycode.target_keycode); 1119 else 1120 printf ("keycode any ="); 1121 for (i = 0; i < op->keycode.count; i++) { 1122 char *name = XKeysymToString (op->keycode.keysyms[i]); 1123 1124 printf (" %s", name ? name : "BADKEYSYM"); 1125 } 1126 printf ("\n"); 1127 break; 1128 case doAddModifier: 1129 printf ("add %s =", modifier_table[op->addmodifier.modifier].name); 1130 for (i = 0; i < op->addmodifier.count; i++) { 1131 char *name = XKeysymToString (op->addmodifier.keysyms[i]); 1132 printf (" %s", name ? name : "BADKEYSYM"); 1133 } 1134 printf ("\n"); 1135 break; 1136 case doRemoveModifier: 1137 printf ("remove %s = ", 1138 modifier_table[op->removemodifier.modifier].name); 1139 for (i = 0; i < op->removemodifier.count; i++) { 1140 printf (" 0x%lx", (long) op->removemodifier.keycodes[i]); 1141 } 1142 printf ("\n"); 1143 break; 1144 case doClearModifier: 1145 printf ("clear %s\n", modifier_table[op->clearmodifier.modifier].name); 1146 break; 1147 case doPointer: 1148 printf ("pointer = "); 1149 if (op->pointer.count == 0) 1150 printf(" default"); 1151 else for (i=0; i < op->pointer.count; i++) 1152 printf(" %d", op->pointer.button_codes[i]); 1153 printf ("\n"); 1154 break; 1155 default: 1156 printf ("! unknown opcode %d\n", op->generic.type); 1157 break; 1158 } /* end switch */ 1159 return; 1160} 1161 1162/* 1163 * execute_work_queue - do the real meat and potatoes now that we know what 1164 * we need to do and that all of the input is correct. 1165 */ 1166static int exec_keycode ( struct op_keycode *opk ); 1167static int exec_add ( struct op_addmodifier *opam ); 1168static int exec_remove ( struct op_removemodifier *oprm ); 1169static int exec_clear ( struct op_clearmodifier *opcm ); 1170static int exec_pointer ( struct op_pointer *opp ); 1171 1172 1173int 1174execute_work_queue (void) 1175{ 1176 union op *op; 1177 int errors; 1178 Bool update_map = False; 1179 int dosync; 1180 1181 if (verbose) { 1182 printf ("!\n"); 1183 printf ("! executing work queue\n"); 1184 printf ("!\n"); 1185 } 1186 1187 errors = 0; 1188 dosync = 0; 1189 1190 for (op = work_queue.head; op; op = op->generic.next) { 1191 if (verbose) print_opcode (op); 1192 1193 /* check to see if we have to update the keyboard mapping */ 1194 if (dosync && 1195 (dosync < 0 || 1196 op->generic.type != doKeycode || 1197 !op->keycode.target_keycode)) { 1198 XSync (dpy, 0); 1199 while (XEventsQueued (dpy, QueuedAlready) > 0) { 1200 XEvent event; 1201 XNextEvent (dpy, &event); 1202 if (event.type == MappingNotify) { 1203 /* read all MappingNotify events */ 1204 while (XCheckTypedEvent (dpy, MappingNotify, &event)) ; 1205 XRefreshKeyboardMapping (&event.xmapping); 1206 } else { 1207 fprintf (stderr, "%s: unknown event %ld\n", 1208 ProgramName, (long) event.type); 1209 } 1210 } 1211 } 1212 dosync = 0; 1213 switch (op->generic.type) { 1214 case doKeycode: 1215 if (exec_keycode (&op->keycode) < 0) errors++; 1216 if (op->keycode.target_keycode) 1217 dosync = 1; 1218 else 1219 dosync = -1; 1220 break; 1221 case doAddModifier: 1222 if (exec_add (&op->addmodifier) < 0) errors++; 1223 else update_map = True; 1224 break; 1225 case doRemoveModifier: 1226 if (exec_remove (&op->removemodifier) < 0) errors++; 1227 else update_map = True; 1228 break; 1229 case doClearModifier: 1230 if (exec_clear (&op->clearmodifier) < 0) errors++; 1231 else update_map = True; 1232 break; 1233 case doPointer: 1234 if (exec_pointer (&op->pointer) < 0) errors++; 1235 break; 1236 default: 1237 fprintf (stderr, "%s: unknown opcode %d\n", 1238 ProgramName, op->generic.type); 1239 break; 1240 } 1241 } 1242 1243 if (update_map) { 1244 if (UpdateModifierMapping (map) < 0) errors++; 1245 } 1246 1247 return (errors > 0 ? -1 : 0); 1248} 1249 1250static int 1251exec_keycode(struct op_keycode *opk) 1252{ 1253 if (!opk->target_keycode) { 1254 int i, j; 1255 KeyCode free; 1256 if (!opk->count) 1257 return (0); 1258 free = 0; 1259 for (i = min_keycode; i <= max_keycode; i++) { 1260 for (j = 0; j < opk->count; j++) { 1261 if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != opk->keysyms[j]) 1262 break; 1263 } 1264 if (j >= opk->count) 1265 return (0); 1266 if (free) 1267 continue; 1268 for (j = 0; j < 8; j++) { 1269 if (XKeycodeToKeysym(dpy, (KeyCode) i, j) != None) 1270 break; 1271 } 1272 if (j >= 8) 1273 free = i; 1274 } 1275 if (!free) { 1276 fprintf(stderr, "%s: no available keycode for assignment\n", 1277 ProgramName); 1278 return (-1); 1279 } 1280 XChangeKeyboardMapping (dpy, free, opk->count, opk->keysyms, 1); 1281 } else if (opk->count == 0) { 1282 KeySym dummy = NoSymbol; 1283 XChangeKeyboardMapping (dpy, opk->target_keycode, 1, 1284 &dummy, 1); 1285 } else { 1286 XChangeKeyboardMapping (dpy, opk->target_keycode, opk->count, 1287 opk->keysyms, 1); 1288 } 1289 return (0); 1290} 1291 1292static int 1293exec_add(struct op_addmodifier *opam) 1294{ 1295 int i; 1296 int status; 1297 1298 status = 0; 1299 for (i = 0; i < opam->count; i++) { 1300 int num_kcs; 1301 KeyCode *kcs; 1302 1303 kcs = KeysymToKeycodes (dpy, opam->keysyms[i], &num_kcs); 1304 if (num_kcs == 0) 1305 status = -1; 1306 while (--num_kcs >= 0) { 1307 if (AddModifier (&map, *kcs++, opam->modifier) < 0) 1308 status = -1; 1309 } 1310 } 1311 return (status); 1312} 1313 1314static int 1315exec_remove(struct op_removemodifier *oprm) 1316{ 1317 int i; 1318 int status; 1319 1320 status = 0; 1321 for (i = 0; i < oprm->count; i++) { 1322 if (RemoveModifier (&map, oprm->keycodes[i], oprm->modifier) < 0) 1323 status = -1; 1324 } 1325 return (status); 1326} 1327 1328static int 1329exec_clear(struct op_clearmodifier *opcm) 1330{ 1331 return (ClearModifier (&map, opcm->modifier)); 1332} 1333 1334 1335static int 1336exec_pointer(struct op_pointer *opp) 1337{ 1338 return (SetPointerMap (opp->button_codes, opp->count)); 1339} 1340 1341void 1342print_modifier_map(void) 1343{ 1344 PrintModifierMapping (map, stdout); 1345 return; 1346} 1347 1348void 1349print_key_table(Bool exprs) 1350{ 1351 PrintKeyTable (exprs, stdout); 1352 return; 1353} 1354 1355void 1356print_pointer_map(void) 1357{ 1358 PrintPointerMap (stdout); 1359 return; 1360} 1361 1362 1363