input.c revision 4e40088c
1/* $XTermId: input.c,v 1.303 2009/01/08 23:28:36 tom Exp $ */ 2 3/* 4 * Copyright 1999-2008,2009 by Thomas E. Dickey 5 * 6 * All Rights Reserved 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sublicense, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY 23 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * Except as contained in this notice, the name(s) of the above copyright 28 * holders shall not be used in advertising or otherwise to promote the 29 * sale, use or other dealings in this Software without prior written 30 * authorization. 31 * 32 * 33 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. 34 * 35 * All Rights Reserved 36 * 37 * Permission to use, copy, modify, and distribute this software and its 38 * documentation for any purpose and without fee is hereby granted, 39 * provided that the above copyright notice appear in all copies and that 40 * both that copyright notice and this permission notice appear in 41 * supporting documentation, and that the name of Digital Equipment 42 * Corporation not be used in advertising or publicity pertaining to 43 * distribution of the software without specific, written prior permission. 44 * 45 * 46 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 47 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL 48 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 49 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 50 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 51 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 52 * SOFTWARE. 53 */ 54 55/* input.c */ 56 57#include <xterm.h> 58 59#include <X11/keysym.h> 60 61#ifdef VMS 62#include <X11/keysymdef.h> 63#endif 64 65#if HAVE_X11_DECKEYSYM_H 66#include <X11/DECkeysym.h> 67#endif 68 69#if HAVE_X11_SUNKEYSYM_H 70#include <X11/Sunkeysym.h> 71#endif 72 73#if HAVE_X11_XF86KEYSYM_H 74#include <X11/XF86keysym.h> 75#endif 76 77#include <X11/Xutil.h> 78#include <stdio.h> 79#include <ctype.h> 80 81#include <xutf8.h> 82 83#include <data.h> 84#include <fontutils.h> 85#include <xstrings.h> 86#include <xtermcap.h> 87 88/* 89 * Xutil.h has no macro to check for the complete set of function- and 90 * modifier-keys that might be returned. Fake it. 91 */ 92#ifdef XK_ISO_Lock 93#define IsPredefinedKey(n) ((n) >= XK_ISO_Lock && (n) <= XK_Delete) 94#else 95#define IsPredefinedKey(n) ((n) >= XK_BackSpace && (n) <= XK_Delete) 96#endif 97 98#ifdef XK_ISO_Left_Tab 99#define IsTabKey(n) ((n) == XK_Tab || (n) == XK_ISO_Left_Tab) 100#else 101#define IsTabKey(n) ((n) == XK_Tab) 102#endif 103 104#ifndef IsPrivateKeypadKey 105#define IsPrivateKeypadKey(k) (0) 106#endif 107 108#define IsBackarrowToggle(keyboard, keysym, state) \ 109 ((((keyboard->flags & MODE_DECBKM) == 0) \ 110 ^ ((state & ControlMask) != 0)) \ 111 && (keysym == XK_BackSpace)) 112 113#define MAP(from, to) case from: result = to; break 114#define Masked(value,mask) ((value) & (unsigned) (~(mask))) 115 116#define KEYSYM_FMT "0x%04lX" /* simplify matching <X11/keysymdef.h> */ 117 118#define TEK4014_GIN(tw) (tw != 0 && tw->screen.TekGIN) 119 120typedef struct { 121 KeySym keysym; 122 Bool is_fkey; 123 int nbytes; 124#define STRBUFSIZE 500 125 char strbuf[STRBUFSIZE]; 126} KEY_DATA; 127 128/* 0123456789 abc def0123456789abcdef0123456789abcdef0123456789abcd */ 129static char *kypd_num = " XXXXXXXX\tXXX\rXXXxxxxXXXXXXXXXXXXXXXXXXXXX*+,-./0123456789XXX="; 130 131/* 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd */ 132static char *kypd_apl = " ABCDEFGHIJKLMNOPQRSTUVWXYZ??????abcdefghijklmnopqrstuvwxyzXXX"; 133 134static char *curfinal = "HDACB FE"; 135 136static int decfuncvalue(KEY_DATA *); 137static void sunfuncvalue(ANSI *, KEY_DATA *); 138static void hpfuncvalue(ANSI *, KEY_DATA *); 139static void scofuncvalue(ANSI *, KEY_DATA *); 140 141#if OPT_TRACE 142static char * 143ModifierName(unsigned modifier) 144{ 145 char *s = ""; 146 if (modifier & ShiftMask) 147 s = " Shift"; 148 else if (modifier & LockMask) 149 s = " Lock"; 150 else if (modifier & ControlMask) 151 s = " Control"; 152 else if (modifier & Mod1Mask) 153 s = " Mod1"; 154 else if (modifier & Mod2Mask) 155 s = " Mod2"; 156 else if (modifier & Mod3Mask) 157 s = " Mod3"; 158 else if (modifier & Mod4Mask) 159 s = " Mod4"; 160 else if (modifier & Mod5Mask) 161 s = " Mod5"; 162 return s; 163} 164 165#define FMT_MODIFIER_NAMES "%s%s%s%s%s%s%s%s" 166#define ARG_MODIFIER_NAMES(state) \ 167 ModifierName(state & ShiftMask), \ 168 ModifierName(state & LockMask), \ 169 ModifierName(state & ControlMask), \ 170 ModifierName(state & Mod1Mask), \ 171 ModifierName(state & Mod2Mask), \ 172 ModifierName(state & Mod3Mask), \ 173 ModifierName(state & Mod4Mask), \ 174 ModifierName(state & Mod5Mask) 175#endif 176 177static void 178AdjustAfterInput(XtermWidget xw) 179{ 180 TScreen *screen = &(xw->screen); 181 182 if (screen->scrollkey && screen->topline != 0) 183 WindowScroll(xw, 0); 184 if (screen->marginbell) { 185 int col = screen->max_col - screen->nmarginbell; 186 if (screen->bellarmed >= 0) { 187 if (screen->bellarmed == screen->cur_row) { 188 if (screen->cur_col >= col) { 189 Bell(XkbBI_MarginBell, 0); 190 screen->bellarmed = -1; 191 } 192 } else 193 screen->bellarmed = 194 screen->cur_col < col ? screen->cur_row : -1; 195 } else if (screen->cur_col < col) 196 screen->bellarmed = screen->cur_row; 197 } 198} 199 200/* 201 * Return true if the key is on the editing keypad. This overlaps with 202 * IsCursorKey() and IsKeypadKey() and must be tested before those macro to 203 * distinguish it from them. 204 */ 205static Bool 206IsEditFunctionKey(KeySym keysym) 207{ 208 switch (keysym) { 209 case XK_Prior: /* editing keypad */ 210 case XK_Next: /* editing keypad */ 211 case XK_Insert: /* editing keypad */ 212 case XK_Find: /* editing keypad */ 213 case XK_Select: /* editing keypad */ 214#ifdef DXK_Remove 215 case DXK_Remove: /* editing keypad */ 216#endif 217#ifdef XK_KP_Delete 218 case XK_KP_Delete: /* editing key on numeric keypad */ 219 case XK_KP_Insert: /* editing key on numeric keypad */ 220#endif 221#ifdef XK_ISO_Left_Tab 222 case XK_ISO_Left_Tab: 223#endif 224 return True; 225 default: 226 return False; 227 } 228} 229 230#if OPT_MOD_FKEYS 231#define IS_CTRL(n) ((n) < ANSI_SPA || ((n) >= 0x7f && (n) <= 0x9f)) 232 233/* 234 * Return true if the keysym corresponds to one of the control characters, 235 * or one of the common ASCII characters that is combined with control to 236 * make a control character. 237 */ 238static Bool 239IsControlInput(KEY_DATA * kd) 240{ 241 return ((kd->keysym) >= 0x40 && (kd->keysym) <= 0x7f); 242} 243 244static Bool 245IsControlOutput(KEY_DATA * kd) 246{ 247 return IS_CTRL(kd->keysym); 248} 249 250/* 251 * X "normally" has some built-in translations, which the user may want to 252 * suppress when processing the modifyOtherKeys resource. In particular, the 253 * control modifier applied to some of the keyboard digits gives results for 254 * control characters. 255 * 256 * control 2 0 NUL 257 * control SPC 0 NUL 258 * control @ 0 NUL 259 * control ` 0 NUL 260 * control 3 0x1b ESC 261 * control 4 0x1c FS 262 * control \ 0x1c FS 263 * control 5 0x1d GS 264 * control 6 0x1e RS 265 * control ^ 0x1e RS 266 * control ~ 0x1e RS 267 * control 7 0x1f US 268 * control / 0x1f US 269 * control _ 0x1f US 270 * control 8 0x7f DEL 271 * 272 * It is possible that some other keyboards do not work for these combinations, 273 * but they do work with modifyOtherKeys=2 for the US keyboard: 274 * 275 * control ` 0 NUL 276 * control [ 0x1b ESC 277 * control \ 0x1c FS 278 * control ] 0x1d GS 279 * control ? 0x7f DEL 280 */ 281static Bool 282IsControlAlias(KEY_DATA * kd) 283{ 284 Bool result = False; 285 286 if (kd->nbytes == 1) { 287 result = IS_CTRL(CharOf(kd->strbuf[0])); 288 } 289 return result; 290} 291 292/* 293 * If we are in the non-VT220/VT52 keyboard state, allow modifiers to add a 294 * parameter to the function-key control sequences. 295 * 296 * Note that we generally cannot capture the Shift-modifier for the numeric 297 * keypad since this is commonly used to act as a type of NumLock, e.g., 298 * making the keypad send "7" (actually XK_KP_7) where the unshifted code 299 * would be Home (XK_KP_Home). The other modifiers work, subject to the 300 * usual window-manager assignments. 301 */ 302static Bool 303allowModifierParm(XtermWidget xw, KEY_DATA * kd) 304{ 305 TKeyboard *keyboard = &(xw->keyboard); 306 TScreen *screen = &(xw->screen); 307 int keypad_mode = ((keyboard->flags & MODE_DECKPAM) != 0); 308 309 Bool result = False; 310 311 (void) screen; 312 if (!(IsKeypadKey(kd->keysym) && keypad_mode) 313#if OPT_SUNPC_KBD 314 && keyboard->type != keyboardIsVT220 315#endif 316#if OPT_VT52_MODE 317 && screen->vtXX_level != 0 318#endif 319 ) { 320 result = True; 321 } 322 return result; 323} 324 325/* 326* Modifier codes: 327* None 1 328* Shift 2 = 1(None)+1(Shift) 329* Alt 3 = 1(None)+2(Alt) 330* Alt+Shift 4 = 1(None)+1(Shift)+2(Alt) 331* Ctrl 5 = 1(None)+4(Ctrl) 332* Ctrl+Shift 6 = 1(None)+1(Shift)+4(Ctrl) 333* Ctrl+Alt 7 = 1(None)+2(Alt)+4(Ctrl) 334* Ctrl+Alt+Shift 8 = 1(None)+1(Shift)+2(Alt)+4(Ctrl) 335* Meta 9 = 1(None)+8(Meta) 336* Meta+Shift 10 = 1(None)+8(Meta)+1(Shift) 337* Meta+Alt 11 = 1(None)+8(Meta)+2(Alt) 338* Meta+Alt+Shift 12 = 1(None)+8(Meta)+1(Shift)+2(Alt) 339* Meta+Ctrl 13 = 1(None)+8(Meta)+4(Ctrl) 340* Meta+Ctrl+Shift 14 = 1(None)+8(Meta)+1(Shift)+4(Ctrl) 341* Meta+Ctrl+Alt 15 = 1(None)+8(Meta)+2(Alt)+4(Ctrl) 342* Meta+Ctrl+Alt+Shift 16 = 1(None)+8(Meta)+1(Shift)+2(Alt)+4(Ctrl) 343*/ 344 345#undef CTRL 346 347/* FIXME - make these used in xtermcap.c */ 348#define UNMOD 1 349#define SHIFT 1 350#define ALT 2 351#define CTRL 4 352#define META 8 353 354#define MODIFIER_NAME(parm, name) \ 355 (((parm > UNMOD) && ((parm - UNMOD) & name)) ? " "#name : "") 356 357int 358xtermParamToState(XtermWidget xw, unsigned param) 359{ 360 int result = 0; 361#if OPT_NUM_LOCK 362 if (param > UNMOD 363 && ((ShiftMask 364 | ControlMask 365 | xw->misc.alt_mods 366 | xw->misc.meta_mods) & xw->misc.other_mods) == 0) { 367 if ((param - UNMOD) & SHIFT) 368 result |= ShiftMask; 369 if ((param - UNMOD) & CTRL) 370 result |= ControlMask; 371 if ((param - UNMOD) & ALT) 372 result |= xw->misc.alt_mods; 373 if ((param - UNMOD) & META) 374 result |= xw->misc.meta_mods; 375 } 376#else 377 (void) xw; 378 (void) param; 379#endif 380 TRACE(("xtermParamToState(%d) %s%s%s%s -> %#x\n", param, 381 MODIFIER_NAME(param, SHIFT), 382 MODIFIER_NAME(param, ALT), 383 MODIFIER_NAME(param, CTRL), 384 MODIFIER_NAME(param, META), 385 result)); 386 return result; 387} 388 389int 390xtermStateToParam(XtermWidget xw, unsigned state) 391{ 392 int modify_parm = UNMOD; 393 394#if OPT_NUM_LOCK 395 if ((state & xw->misc.other_mods) == 0) { 396 if (state & ShiftMask) { 397 modify_parm += SHIFT; 398 state &= ~ShiftMask; 399 } 400 if (state & ControlMask) { 401 modify_parm += CTRL; 402 state &= ~ControlMask; 403 } 404 if ((state & xw->misc.alt_mods) != 0) { 405 modify_parm += ALT; 406 state &= ~xw->misc.alt_mods; 407 } 408 if ((state & xw->misc.meta_mods) != 0) { 409 modify_parm += META; 410 state &= ~xw->misc.meta_mods; 411 } 412 } 413#else 414 (void) xw; 415 (void) state; 416#endif 417 TRACE(("...xtermStateToParam %d%s%s%s%s\n", modify_parm, 418 MODIFIER_NAME(modify_parm, SHIFT), 419 MODIFIER_NAME(modify_parm, ALT), 420 MODIFIER_NAME(modify_parm, CTRL), 421 MODIFIER_NAME(modify_parm, META))); 422 return modify_parm; 423} 424 425#define computeMaskedModifier(xw, state, mask) \ 426 xtermStateToParam(xw, Masked(state, mask)) 427 428#if OPT_NUM_LOCK 429static unsigned 430filterAltMeta(unsigned result, unsigned mask, Bool enable, KEY_DATA * kd) 431{ 432 if ((result & mask) != 0) { 433 /* 434 * metaSendsEscape makes the meta key independent of 435 * modifyOtherKeys. 436 */ 437 if (enable) { 438 result &= ~mask; 439 } 440 /* 441 * A bare meta-modifier is independent of modifyOtherKeys. If it 442 * is combined with other modifiers, make it depend. 443 */ 444 if ((result & ~(mask)) == 0) { 445 result &= ~mask; 446 } 447 /* 448 * Check for special cases of control+meta which are used by some 449 * applications, e.g., emacs. 450 */ 451 if ((IsControlInput(kd) 452 || IsControlOutput(kd)) 453 && (result & ControlMask) != 0) { 454 result &= ~(mask | ControlMask); 455 } 456 if (kd->keysym == XK_Return || kd->keysym == XK_Tab) { 457 result &= ~(mask | ControlMask); 458 } 459 } 460 return result; 461} 462#endif /* OPT_NUM_LOCK */ 463 464/* 465 * Single characters (not function-keys) are allowed fewer modifiers when 466 * interpreting modifyOtherKeys due to pre-existing associations with some 467 * modifiers. 468 */ 469static unsigned 470allowedCharModifiers(XtermWidget xw, unsigned state, KEY_DATA * kd) 471{ 472#if OPT_NUM_LOCK 473 unsigned a_or_m = (state & (xw->misc.meta_mods | xw->misc.alt_mods)); 474#else 475 unsigned a_or_m = 0; 476#endif 477 /* 478 * Start by limiting the result to the modifiers we might want to use. 479 */ 480 unsigned result = (state & (ControlMask 481 | ShiftMask 482 | a_or_m)); 483 484 /* 485 * If modifyOtherKeys is off or medium (0 or 1), moderate its effects by 486 * excluding the common cases for modifiers. 487 */ 488 if (xw->keyboard.modify_now.other_keys <= 1) { 489 if (IsControlInput(kd) 490 && Masked(result, ControlMask) == 0) { 491 /* These keys are already associated with the control-key */ 492 if (xw->keyboard.modify_now.other_keys == 0) { 493 result &= ~ControlMask; 494 } 495 } else if (kd->keysym == XK_Tab || kd->keysym == XK_Return) { 496 ; 497 } else if (IsControlAlias(kd)) { 498 /* Things like "^_" work here... */ 499 if (Masked(result, (ControlMask | ShiftMask)) == 0) { 500 result = 0; 501 } 502 } else if (!IsControlOutput(kd) && !IsPredefinedKey(kd->keysym)) { 503 /* Printable keys are already associated with the shift-key */ 504 if (!(result & ControlMask)) { 505 result &= ~ShiftMask; 506 } 507 } 508#if OPT_NUM_LOCK 509 result = filterAltMeta(result, 510 xw->misc.meta_mods, 511 xw->screen.meta_sends_esc, kd); 512 if (xw->screen.alt_is_not_meta) { 513 result = filterAltMeta(result, 514 xw->misc.alt_mods, 515 xw->screen.alt_sends_esc, kd); 516 } 517#endif 518 } 519 TRACE(("...allowedCharModifiers(state=%u" FMT_MODIFIER_NAMES 520 ", ch=" KEYSYM_FMT ") ->" 521 "%u" FMT_MODIFIER_NAMES "\n", 522 state, ARG_MODIFIER_NAMES(state), kd->keysym, 523 result, ARG_MODIFIER_NAMES(result))); 524 return result; 525} 526 527/* 528 * Decide if we should generate a special escape sequence for "other" keys 529 * than cursor-, function-keys, etc., as per the modifyOtherKeys resource. 530 */ 531static Bool 532ModifyOtherKeys(XtermWidget xw, 533 unsigned state, 534 KEY_DATA * kd, 535 int modify_parm) 536{ 537 TKeyboard *keyboard = &(xw->keyboard); 538 Bool result = False; 539 540 /* 541 * Exclude the keys already covered by a modifier. 542 */ 543 if (kd->is_fkey 544 || IsEditFunctionKey(kd->keysym) 545 || IsKeypadKey(kd->keysym) 546 || IsCursorKey(kd->keysym) 547 || IsPFKey(kd->keysym) 548 || IsMiscFunctionKey(kd->keysym) 549 || IsPrivateKeypadKey(kd->keysym) 550#if OPT_NUM_LOCK 551 || (state & xw->misc.other_mods) != 0 552#endif 553 ) { 554 result = False; 555 } else if (modify_parm != 0) { 556 if (IsBackarrowToggle(keyboard, kd->keysym, state)) { 557 kd->keysym = XK_Delete; 558 state &= ~ControlMask; 559 } 560 if (!IsPredefinedKey(kd->keysym)) { 561 state = allowedCharModifiers(xw, state, kd); 562 } 563 if (state != 0) { 564 switch (keyboard->modify_now.other_keys) { 565 default: 566 break; 567 case 1: 568 switch (kd->keysym) { 569 case XK_BackSpace: 570 case XK_Delete: 571 result = False; 572 break; 573#ifdef XK_ISO_Left_Tab 574 case XK_ISO_Left_Tab: 575 if (computeMaskedModifier(xw, state, ShiftMask) > 1) 576 result = True; 577 break; 578#endif 579 case XK_Return: 580 case XK_Tab: 581 result = (modify_parm > 1); 582 break; 583 default: 584 if (IsControlInput(kd)) { 585 if (state == ControlMask || state == ShiftMask) { 586 result = False; 587 } else { 588 result = (modify_parm > 1); 589 } 590 } else if (IsControlAlias(kd)) { 591 if (state == ShiftMask) 592 result = False; 593 else if (computeMaskedModifier(xw, state, ControlMask) 594 > 1) { 595 result = True; 596 } 597 } else { 598 result = True; 599 } 600 break; 601 } 602 break; 603 case 2: 604 switch (kd->keysym) { 605 case XK_BackSpace: 606 /* strip ControlMask as per IsBackarrowToggle() */ 607 if (computeMaskedModifier(xw, state, ControlMask) > 1) 608 result = True; 609 break; 610 case XK_Delete: 611 result = (xtermStateToParam(xw, state) > 1); 612 break; 613#ifdef XK_ISO_Left_Tab 614 case XK_ISO_Left_Tab: 615 if (computeMaskedModifier(xw, state, ShiftMask) > 1) 616 result = True; 617 break; 618#endif 619 case XK_Return: 620 case XK_Tab: 621 result = (modify_parm > 1); 622 break; 623 default: 624 if (IsControlInput(kd)) { 625 result = True; 626 } else if (state == ShiftMask) { 627 result = (kd->keysym == ' ' || kd->keysym == XK_Return); 628 } else if (computeMaskedModifier(xw, state, ShiftMask) > 1) { 629 result = True; 630 } 631 break; 632 } 633 break; 634 } 635 } 636 } 637 TRACE(("...ModifyOtherKeys(%d,%d) %s\n", 638 keyboard->modify_now.other_keys, 639 modify_parm, 640 BtoS(result))); 641 return result; 642} 643 644#define APPEND_PARM(number) \ 645 reply->a_param[(int) reply->a_nparam] = number, \ 646 reply->a_nparam += 1 647 648/* 649 * Function-key code 27 happens to not be used in the vt220-style encoding. 650 * xterm uses this to represent modified non-function-keys such as control/+ in 651 * the Sun/PC keyboard layout. See the modifyOtherKeys resource in the manpage 652 * for more information. 653 */ 654static Bool 655modifyOtherKey(ANSI * reply, int input_char, int modify_parm, int format_keys) 656{ 657 Bool result = False; 658 659 if (input_char >= 0) { 660 reply->a_type = ANSI_CSI; 661 if (format_keys) { 662 APPEND_PARM(input_char); 663 APPEND_PARM(modify_parm); 664 reply->a_final = 'u'; 665 } else { 666 APPEND_PARM(27); 667 APPEND_PARM(modify_parm); 668 APPEND_PARM(input_char); 669 reply->a_final = '~'; 670 } 671 672 result = True; 673 } 674 return result; 675} 676 677static void 678modifyCursorKey(ANSI * reply, int modify, int *modify_parm) 679{ 680 if (*modify_parm > 1) { 681 if (modify < 0) { 682 *modify_parm = 0; 683 } 684 if (modify > 0) { 685 reply->a_type = ANSI_CSI; /* SS3 should not have params */ 686 } 687 if (modify > 1 && reply->a_nparam == 0) { 688 APPEND_PARM(1); /* force modifier to 2nd param */ 689 } 690 if (modify > 2) { 691 reply->a_pintro = '>'; /* mark this as "private" */ 692 } 693 } 694} 695#else 696#define modifyCursorKey(reply, modify, parm) /* nothing */ 697#endif /* OPT_MOD_FKEYS */ 698 699#if OPT_SUNPC_KBD 700/* 701 * If we have told xterm that our keyboard is really a Sun/PC keyboard, this is 702 * enough to make a reasonable approximation to DEC vt220 numeric and editing 703 * keypads. 704 */ 705static KeySym 706TranslateFromSUNPC(KeySym keysym) 707{ 708 /* *INDENT-OFF* */ 709 static struct { 710 KeySym before, after; 711 } table[] = { 712#ifdef DXK_Remove 713 { XK_Delete, DXK_Remove }, 714#endif 715 { XK_Home, XK_Find }, 716 { XK_End, XK_Select }, 717#ifdef XK_KP_Home 718 { XK_Delete, XK_KP_Decimal }, 719 { XK_KP_Delete, XK_KP_Decimal }, 720 { XK_KP_Insert, XK_KP_0 }, 721 { XK_KP_End, XK_KP_1 }, 722 { XK_KP_Down, XK_KP_2 }, 723 { XK_KP_Next, XK_KP_3 }, 724 { XK_KP_Left, XK_KP_4 }, 725 { XK_KP_Begin, XK_KP_5 }, 726 { XK_KP_Right, XK_KP_6 }, 727 { XK_KP_Home, XK_KP_7 }, 728 { XK_KP_Up, XK_KP_8 }, 729 { XK_KP_Prior, XK_KP_9 }, 730#endif 731 }; 732 /* *INDENT-ON* */ 733 734 unsigned n; 735 736 for (n = 0; n < sizeof(table) / sizeof(table[0]); n++) { 737 if (table[n].before == keysym) { 738 TRACE(("...Input keypad before was " KEYSYM_FMT "\n", keysym)); 739 keysym = table[n].after; 740 TRACE(("...Input keypad changed to " KEYSYM_FMT "\n", keysym)); 741 break; 742 } 743 } 744 return keysym; 745} 746#endif /* OPT_SUNPC_KBD */ 747 748#define VT52_KEYPAD \ 749 if_OPT_VT52_MODE(screen,{ \ 750 reply.a_type = ANSI_ESC; \ 751 reply.a_pintro = '?'; \ 752 }) 753 754#define VT52_CURSOR_KEYS \ 755 if_OPT_VT52_MODE(screen,{ \ 756 reply.a_type = ANSI_ESC; \ 757 }) 758 759#undef APPEND_PARM 760#define APPEND_PARM(number) \ 761 reply.a_param[(int) reply.a_nparam] = number, \ 762 reply.a_nparam += 1 763 764#if OPT_MOD_FKEYS 765#define MODIFIER_PARM \ 766 if (modify_parm > 1) APPEND_PARM(modify_parm) 767#else 768#define MODIFIER_PARM /*nothing */ 769#endif 770 771/* 772 * Determine if we use the \E[3~ sequence for Delete, or the legacy ^?. We 773 * maintain the delete_is_del value as 3 states: unspecified(2), true and 774 * false. If unspecified, it is handled differently according to whether the 775 * legacy keyboard support is enabled, or if xterm emulates a VT220. 776 * 777 * Once the user (or application) has specified delete_is_del via resource 778 * setting, popup menu or escape sequence, it overrides the keyboard type 779 * rather than the reverse. 780 */ 781Bool 782xtermDeleteIsDEL(XtermWidget xw) 783{ 784 Bool result = True; 785 786 if (xw->keyboard.type == keyboardIsDefault 787 || xw->keyboard.type == keyboardIsVT220) 788 result = (xw->screen.delete_is_del == True); 789 790 if (xw->keyboard.type == keyboardIsLegacy) 791 result = (xw->screen.delete_is_del != False); 792 793 TRACE(("xtermDeleteIsDEL(%d/%d) = %d\n", 794 xw->keyboard.type, 795 xw->screen.delete_is_del, 796 result)); 797 798 return result; 799} 800 801void 802Input(XtermWidget xw, 803 XKeyEvent * event, 804 Bool eightbit) 805{ 806 Char *string; 807 808 TKeyboard *keyboard = &(xw->keyboard); 809 TScreen *screen = &(xw->screen); 810 811 int j; 812 int key = False; 813 ANSI reply; 814 int dec_code; 815 int modify_parm = 0; 816 int keypad_mode = ((keyboard->flags & MODE_DECKPAM) != 0); 817 unsigned evt_state = event->state; 818 unsigned mod_state; 819 KEY_DATA kd; 820 821 /* Ignore characters typed at the keyboard */ 822 if (keyboard->flags & MODE_KAM) 823 return; 824 825 kd.keysym = 0; 826 kd.is_fkey = False; 827#if OPT_TCAP_QUERY 828 if (screen->tc_query_code >= 0) { 829 kd.keysym = (KeySym) screen->tc_query_code; 830 kd.is_fkey = screen->tc_query_fkey; 831 if (kd.keysym != XK_BackSpace) { 832 kd.nbytes = 0; 833 kd.strbuf[0] = 0; 834 } else { 835 kd.nbytes = 1; 836 kd.strbuf[0] = 8; 837 } 838 } else 839#endif 840 { 841#if OPT_I18N_SUPPORT 842 if (screen->xic) { 843 Status status_return; 844#if OPT_WIDE_CHARS 845 if (screen->utf8_mode) { 846 kd.nbytes = Xutf8LookupString(screen->xic, event, 847 kd.strbuf, sizeof(kd.strbuf), 848 &kd.keysym, &status_return); 849 } else 850#endif 851 { 852 kd.nbytes = XmbLookupString(screen->xic, event, 853 kd.strbuf, sizeof(kd.strbuf), 854 &kd.keysym, &status_return); 855 } 856#if OPT_MOD_FKEYS 857 /* 858 * Fill-in some code useful with IsControlAlias(): 859 */ 860 if (status_return == XLookupBoth 861 && kd.nbytes <= 1 862 && !IsPredefinedKey(kd.keysym) 863 && (keyboard->modify_now.other_keys > 1) 864 && !IsControlInput(&kd)) { 865 kd.nbytes = 1; 866 kd.strbuf[0] = (char) kd.keysym; 867 } 868#endif /* OPT_MOD_FKEYS */ 869 } else 870#endif /* OPT_I18N_SUPPORT */ 871 { 872 static XComposeStatus compose_status = 873 {NULL, 0}; 874 kd.nbytes = XLookupString(event, kd.strbuf, sizeof(kd.strbuf), 875 &kd.keysym, &compose_status); 876 } 877 kd.is_fkey = IsFunctionKey(kd.keysym); 878 } 879 880 memset(&reply, 0, sizeof(reply)); 881 882 TRACE(("Input keysym " 883 KEYSYM_FMT 884 ", %d:'%s'%s" FMT_MODIFIER_NAMES "%s%s%s%s%s%s\n", 885 kd.keysym, 886 kd.nbytes, 887 visibleChars(PAIRED_CHARS((Char *) kd.strbuf, 0), 888 ((kd.nbytes > 0) 889 ? (unsigned) kd.nbytes 890 : 0)), 891 ARG_MODIFIER_NAMES(evt_state), 892 eightbit ? " 8bit" : " 7bit", 893 IsKeypadKey(kd.keysym) ? " KeypadKey" : "", 894 IsCursorKey(kd.keysym) ? " CursorKey" : "", 895 IsPFKey(kd.keysym) ? " PFKey" : "", 896 kd.is_fkey ? " FKey" : "", 897 IsMiscFunctionKey(kd.keysym) ? " MiscFKey" : "", 898 IsEditFunctionKey(kd.keysym) ? " EditFkey" : "")); 899 900#if OPT_SUNPC_KBD 901 /* 902 * DEC keyboards don't have keypad(+), but do have keypad(,) instead. 903 * Other (Sun, PC) keyboards commonly have keypad(+), but no keypad(,) 904 * - it's a pain for users to work around. 905 */ 906 if (keyboard->type == keyboardIsVT220 907 && (evt_state & ShiftMask) == 0) { 908 if (kd.keysym == XK_KP_Add) { 909 kd.keysym = XK_KP_Separator; 910 evt_state &= ~ShiftMask; 911 TRACE(("...Input keypad(+), change keysym to " 912 KEYSYM_FMT 913 "\n", 914 kd.keysym)); 915 } 916 if ((evt_state & ControlMask) != 0 917 && kd.keysym == XK_KP_Separator) { 918 kd.keysym = XK_KP_Subtract; 919 evt_state &= ~ControlMask; 920 TRACE(("...Input control/keypad(,), change keysym to " 921 KEYSYM_FMT 922 "\n", 923 kd.keysym)); 924 } 925 } 926#endif 927 928 /* 929 * The keyboard tables may give us different keypad codes according to 930 * whether NumLock is pressed. Use this check to simplify the process 931 * of determining whether we generate an escape sequence for a keypad 932 * key, or force it to the value kypd_num[]. There is no fixed 933 * modifier for this feature, so we assume that it is the one assigned 934 * to the NumLock key. 935 * 936 * This check used to try to return the contents of strbuf, but that 937 * does not work properly when a control modifier is given (trash is 938 * returned in the buffer in some cases -- perhaps an X bug). 939 */ 940#if OPT_NUM_LOCK 941 if (kd.nbytes == 1 942 && IsKeypadKey(kd.keysym) 943 && xw->misc.real_NumLock 944 && (xw->misc.num_lock & evt_state) != 0) { 945 keypad_mode = 0; 946 TRACE(("...Input num_lock, force keypad_mode off\n")); 947 } 948#endif 949 950#if OPT_MOD_FKEYS 951 if (evt_state != 0 952 && allowModifierParm(xw, &kd)) { 953 modify_parm = xtermStateToParam(xw, evt_state); 954 } 955 956 /* 957 * Shift-tab is often mapped to XK_ISO_Left_Tab which is classified as 958 * IsEditFunctionKey(), and the conversion does not produce any bytes. 959 * Check for this special case so we have data when handling the 960 * modifyOtherKeys resource. 961 */ 962 if (keyboard->modify_now.other_keys > 1) { 963 if (IsTabKey(kd.keysym) && kd.nbytes == 0) { 964 kd.nbytes = 1; 965 kd.strbuf[0] = '\t'; 966 } 967 } 968#endif /* OPT_MOD_FKEYS */ 969 970 /* VT300 & up: backarrow toggle */ 971 if ((kd.nbytes == 1) 972 && IsBackarrowToggle(keyboard, kd.keysym, evt_state)) { 973 kd.strbuf[0] = ANSI_DEL; 974 TRACE(("...Input backarrow changed to %d\n", kd.strbuf[0])); 975 } 976#if OPT_SUNPC_KBD 977 /* make an DEC editing-keypad from a Sun or PC editing-keypad */ 978 if (keyboard->type == keyboardIsVT220 979 && (kd.keysym != XK_Delete || !xtermDeleteIsDEL(xw))) 980 kd.keysym = TranslateFromSUNPC(kd.keysym); 981 else 982#endif 983 { 984#ifdef XK_KP_Home 985 if (kd.keysym >= XK_KP_Home && kd.keysym <= XK_KP_Begin) { 986 TRACE(("...Input keypad before was " KEYSYM_FMT "\n", kd.keysym)); 987 kd.keysym += (KeySym) (XK_Home - XK_KP_Home); 988 TRACE(("...Input keypad changed to " KEYSYM_FMT "\n", kd.keysym)); 989 } 990#endif 991 } 992 993 /* 994 * Map the Sun afterthought-keys in as F36 and F37. 995 */ 996#ifdef SunXK_F36 997 if (!kd.is_fkey) { 998 if (kd.keysym == SunXK_F36) { 999 kd.keysym = XK_Fn(36); 1000 kd.is_fkey = True; 1001 } 1002 if (kd.keysym == SunXK_F37) { 1003 kd.keysym = XK_Fn(37); 1004 kd.is_fkey = True; 1005 } 1006 } 1007#endif 1008 1009 /* 1010 * Use the control- and shift-modifiers to obtain more function keys than 1011 * the keyboard provides. We can do this if there is no conflicting use of 1012 * those modifiers: 1013 * 1014 * a) for VT220 keyboard, we use only the control-modifier. The keyboard 1015 * uses shift-modifier for UDK's. 1016 * 1017 * b) for non-VT220 keyboards, we only have to check if the 1018 * modifyFunctionKeys resource is inactive. 1019 * 1020 * Thereafter, we note when we have a function-key and keep that 1021 * distinction when testing for "function-key" values. 1022 */ 1023 if ((evt_state & (ControlMask | ShiftMask)) != 0 1024 && kd.is_fkey) { 1025 1026 /* VT220 keyboard uses shift for UDK */ 1027 if (keyboard->type == keyboardIsVT220 1028 || keyboard->type == keyboardIsLegacy) { 1029 1030 TRACE(("...map XK_F%ld", kd.keysym - XK_Fn(1) + 1)); 1031 if (evt_state & ControlMask) { 1032 kd.keysym += (KeySym) xw->misc.ctrl_fkeys; 1033 evt_state &= ~ControlMask; 1034 } 1035 TRACE((" to XK_F%ld\n", kd.keysym - XK_Fn(1) + 1)); 1036 1037 } 1038#if OPT_MOD_FKEYS 1039 else if (keyboard->modify_now.function_keys < 0) { 1040 1041 TRACE(("...map XK_F%ld", kd.keysym - XK_Fn(1) + 1)); 1042 if (evt_state & ShiftMask) { 1043 kd.keysym += (KeySym) (xw->misc.ctrl_fkeys * 1); 1044 evt_state &= ~ShiftMask; 1045 } 1046 if (evt_state & ControlMask) { 1047 kd.keysym += (KeySym) (xw->misc.ctrl_fkeys * 2); 1048 evt_state &= ~ControlMask; 1049 } 1050 TRACE((" to XK_F%ld\n", kd.keysym - XK_Fn(1) + 1)); 1051 1052 } 1053 /* 1054 * Reevaluate the modifier parameter, stripping off the modifiers 1055 * that we just used. 1056 */ 1057 if (modify_parm) 1058 modify_parm = xtermStateToParam(xw, evt_state); 1059#endif /* OPT_MOD_FKEYS */ 1060 } 1061 1062 /* 1063 * Test for one of the keyboard variants. 1064 */ 1065 switch (keyboard->type) { 1066 case keyboardIsHP: 1067 hpfuncvalue(&reply, &kd); 1068 break; 1069 case keyboardIsSCO: 1070 scofuncvalue(&reply, &kd); 1071 break; 1072 case keyboardIsSun: 1073 sunfuncvalue(&reply, &kd); 1074 break; 1075 case keyboardIsTermcap: 1076#if OPT_TCAP_FKEYS 1077 if (xtermcapString(xw, (int) kd.keysym, evt_state)) 1078 return; 1079#endif 1080 break; 1081 case keyboardIsDefault: 1082 case keyboardIsLegacy: 1083 case keyboardIsVT220: 1084 break; 1085 } 1086 1087 if (reply.a_final) { 1088 /* 1089 * The key symbol matches one of the variants. Most of those are 1090 * function-keys, though some cursor- and editing-keys are mixed in. 1091 */ 1092 modifyCursorKey(&reply, 1093 ((kd.is_fkey 1094 || IsMiscFunctionKey(kd.keysym) 1095 || IsEditFunctionKey(kd.keysym)) 1096 ? keyboard->modify_now.function_keys 1097 : keyboard->modify_now.cursor_keys), 1098 &modify_parm); 1099 MODIFIER_PARM; 1100 unparseseq(xw, &reply); 1101 } else if (((kd.is_fkey 1102 || IsMiscFunctionKey(kd.keysym) 1103 || IsEditFunctionKey(kd.keysym)) 1104#if OPT_MOD_FKEYS 1105 && !ModifyOtherKeys(xw, evt_state, &kd, modify_parm) 1106#endif 1107 ) || (kd.keysym == XK_Delete 1108 && ((modify_parm > 1) 1109 || !xtermDeleteIsDEL(xw)))) { 1110 dec_code = decfuncvalue(&kd); 1111 if ((evt_state & ShiftMask) 1112#if OPT_SUNPC_KBD 1113 && keyboard->type == keyboardIsVT220 1114#endif 1115 && ((string = (Char *) udk_lookup(dec_code, &kd.nbytes)) != 0)) { 1116 evt_state &= ~ShiftMask; 1117 while (kd.nbytes-- > 0) 1118 unparseputc(xw, CharOf(*string++)); 1119 } 1120#if OPT_VT52_MODE 1121 /* 1122 * Interpret F1-F4 as PF1-PF4 for VT52, VT100 1123 */ 1124 else if (keyboard->type != keyboardIsLegacy 1125 && (dec_code >= 11 && dec_code <= 14)) { 1126 reply.a_type = ANSI_SS3; 1127 VT52_CURSOR_KEYS; 1128 reply.a_final = (Char) A2E(dec_code - 11 + E2A('P')); 1129 modifyCursorKey(&reply, 1130 keyboard->modify_now.function_keys, 1131 &modify_parm); 1132 MODIFIER_PARM; 1133 unparseseq(xw, &reply); 1134 } 1135#endif 1136 else { 1137 reply.a_type = ANSI_CSI; 1138 reply.a_final = 0; 1139 1140#ifdef XK_ISO_Left_Tab 1141 if (kd.keysym == XK_ISO_Left_Tab) { 1142 reply.a_nparam = 0; 1143 reply.a_final = 'Z'; 1144#if OPT_MOD_FKEYS 1145 if (keyboard->modify_now.other_keys > 1 1146 && computeMaskedModifier(xw, evt_state, ShiftMask) > 1) 1147 modifyOtherKey(&reply, '\t', modify_parm, keyboard->format_keys); 1148#endif 1149 } else 1150#endif /* XK_ISO_Left_Tab */ 1151 { 1152 reply.a_nparam = 1; 1153#if OPT_MOD_FKEYS 1154 if (kd.is_fkey) { 1155 modifyCursorKey(&reply, 1156 keyboard->modify_now.function_keys, 1157 &modify_parm); 1158 } 1159 MODIFIER_PARM; 1160#endif 1161 reply.a_param[0] = dec_code; 1162 reply.a_final = '~'; 1163 } 1164 if (reply.a_final != 0 1165 && (reply.a_nparam == 0 || reply.a_param[0] >= 0)) 1166 unparseseq(xw, &reply); 1167 } 1168 key = True; 1169 } else if (IsPFKey(kd.keysym)) { 1170 reply.a_type = ANSI_SS3; 1171 reply.a_final = (Char) ((kd.keysym - XK_KP_F1) + 'P'); 1172 VT52_CURSOR_KEYS; 1173 MODIFIER_PARM; 1174 unparseseq(xw, &reply); 1175 key = True; 1176 } else if (IsKeypadKey(kd.keysym)) { 1177 if (keypad_mode) { 1178 reply.a_type = ANSI_SS3; 1179 reply.a_final = (Char) (kypd_apl[kd.keysym - XK_KP_Space]); 1180 VT52_KEYPAD; 1181 MODIFIER_PARM; 1182 unparseseq(xw, &reply); 1183 } else { 1184 unparseputc(xw, kypd_num[kd.keysym - XK_KP_Space]); 1185 } 1186 key = True; 1187 } else if (IsCursorKey(kd.keysym)) { 1188 if (keyboard->flags & MODE_DECCKM) { 1189 reply.a_type = ANSI_SS3; 1190 } else { 1191 reply.a_type = ANSI_CSI; 1192 } 1193 modifyCursorKey(&reply, keyboard->modify_now.cursor_keys, &modify_parm); 1194 reply.a_final = (Char) (curfinal[kd.keysym - XK_Home]); 1195 VT52_CURSOR_KEYS; 1196 MODIFIER_PARM; 1197 unparseseq(xw, &reply); 1198 key = True; 1199 } else if (kd.nbytes > 0) { 1200 int prefix = 0; 1201 1202#if OPT_TEK4014 1203 if (TEK4014_GIN(tekWidget)) { 1204 TekEnqMouse(tekWidget, kd.strbuf[0]); 1205 TekGINoff(tekWidget); 1206 kd.nbytes--; 1207 for (j = 0; j < kd.nbytes; ++j) { 1208 kd.strbuf[j] = kd.strbuf[j + 1]; 1209 } 1210 } 1211#endif 1212#if OPT_MOD_FKEYS 1213 if ((keyboard->modify_now.other_keys > 0) 1214 && ModifyOtherKeys(xw, evt_state, &kd, modify_parm) 1215 && (mod_state = allowedCharModifiers(xw, evt_state, &kd)) != 0) { 1216 int input_char; 1217 1218 evt_state = mod_state; 1219 1220 modify_parm = xtermStateToParam(xw, evt_state); 1221 1222 /* 1223 * We want to show a keycode that corresponds to the 8-bit value 1224 * of the key. If the keysym is less than 256, that is good 1225 * enough. Special keys such as Tab may result in a value that 1226 * is usable as well. For the latter (special cases), try to use 1227 * the result from the X library lookup. 1228 */ 1229 input_char = ((kd.keysym < 256) 1230 ? (int) kd.keysym 1231 : ((kd.nbytes == 1) 1232 ? CharOf(kd.strbuf[0]) 1233 : -1)); 1234 1235 TRACE(("...modifyOtherKeys %d;%d\n", modify_parm, input_char)); 1236 if (modifyOtherKey(&reply, input_char, modify_parm, keyboard->format_keys)) { 1237 unparseseq(xw, &reply); 1238 } else { 1239 Bell(XkbBI_MinorError, 0); 1240 } 1241 } else 1242#endif /* OPT_MOD_FKEYS */ 1243 { 1244#if OPT_NUM_LOCK 1245 /* 1246 * Send ESC if we have a META modifier and metaSendsEcape is true. 1247 * Like eightBitInput, except that it is not associated with 1248 * terminal settings. 1249 */ 1250 if (kd.nbytes != 0) { 1251 if (screen->meta_sends_esc 1252 && (evt_state & xw->misc.meta_mods) != 0) { 1253 TRACE(("...input-char is modified by META\n")); 1254 evt_state &= ~xw->misc.meta_mods; 1255 eightbit = False; 1256 prefix = ANSI_ESC; 1257 } else if (eightbit) { 1258 /* it might be overridden, but this helps for debugging */ 1259 TRACE(("...input-char is shifted by META\n")); 1260 } 1261 if (screen->alt_is_not_meta 1262 && (evt_state & xw->misc.alt_mods) != 0) { 1263 evt_state &= ~xw->misc.alt_mods; 1264 if (screen->alt_sends_esc) { 1265 TRACE(("...input-char is modified by ALT\n")); 1266 eightbit = False; 1267 prefix = ANSI_ESC; 1268 } else if (!eightbit) { 1269 TRACE(("...input-char is shifted by ALT\n")); 1270 eightbit = True; 1271 } 1272 } 1273 } 1274#endif 1275 /* 1276 * If metaSendsEscape is false, fall through to this chunk, which 1277 * implements the eightBitInput resource. 1278 * 1279 * It is normally executed when the user presses Meta plus a 1280 * printable key, e.g., Meta+space. The presence of the Meta 1281 * modifier is not guaranteed since what really happens is the 1282 * "insert-eight-bit" or "insert-seven-bit" action, which we 1283 * distinguish by the eightbit parameter to this function. So the 1284 * eightBitInput resource really means that we use this shifting 1285 * logic in the "insert-eight-bit" action. 1286 */ 1287 if (eightbit && (kd.nbytes == 1) && screen->input_eight_bits) { 1288 IChar ch = CharOf(kd.strbuf[0]); 1289 if (ch < 128) { 1290 kd.strbuf[0] |= (char) 0x80; 1291 TRACE(("...input shift from %d to %d (%#x to %#x)\n", 1292 ch, CharOf(kd.strbuf[0]), 1293 ch, CharOf(kd.strbuf[0]))); 1294#if OPT_WIDE_CHARS 1295 if (screen->utf8_mode) { 1296 /* 1297 * We could interpret the incoming code as "in the 1298 * current locale", but it's simpler to treat it as 1299 * a Unicode value to translate to UTF-8. 1300 */ 1301 ch = CharOf(kd.strbuf[0]); 1302 kd.nbytes = 2; 1303 kd.strbuf[0] = (char) (0xc0 | ((ch >> 6) & 0x3)); 1304 kd.strbuf[1] = (char) (0x80 | (ch & 0x3f)); 1305 TRACE(("...encoded %#x in UTF-8 as %#x,%#x\n", 1306 ch, CharOf(kd.strbuf[0]), CharOf(kd.strbuf[1]))); 1307 } 1308#endif 1309 } 1310 eightbit = False; 1311 } 1312#if OPT_WIDE_CHARS 1313 if (kd.nbytes == 1) /* cannot do NRC on UTF-8, for instance */ 1314#endif 1315 { 1316 /* VT220 & up: National Replacement Characters */ 1317 if ((xw->flags & NATIONAL) != 0) { 1318 unsigned cmp = xtermCharSetIn(CharOf(kd.strbuf[0]), 1319 screen->keyboard_dialect[0]); 1320 TRACE(("...input NRC %d, %s %d\n", 1321 CharOf(kd.strbuf[0]), 1322 (CharOf(kd.strbuf[0]) == cmp) 1323 ? "unchanged" 1324 : "changed to", 1325 CharOf(cmp))); 1326 kd.strbuf[0] = (char) cmp; 1327 } else if (eightbit) { 1328 prefix = ANSI_ESC; 1329 } else if (kd.strbuf[0] == '?' 1330 && (evt_state & ControlMask) != 0) { 1331 kd.strbuf[0] = ANSI_DEL; 1332 evt_state &= ~ControlMask; 1333 } 1334 } 1335 if (prefix != 0) 1336 unparseputc(xw, prefix); /* escape */ 1337 for (j = 0; j < kd.nbytes; ++j) 1338 unparseputc(xw, CharOf(kd.strbuf[j])); 1339 } 1340 key = True; 1341 } 1342 unparse_end(xw); 1343 1344 if (key && !TEK4014_ACTIVE(xw)) 1345 AdjustAfterInput(xw); 1346 1347 xtermShowPointer(xw, False); 1348 return; 1349} 1350 1351void 1352StringInput(XtermWidget xw, Char * string, size_t nbytes) 1353{ 1354 TRACE(("InputString (%s,%d)\n", 1355 visibleChars(PAIRED_CHARS(string, 0), nbytes), 1356 nbytes)); 1357#if OPT_TEK4014 1358 if (nbytes && TEK4014_GIN(tekWidget)) { 1359 TekEnqMouse(tekWidget, *string++); 1360 TekGINoff(tekWidget); 1361 nbytes--; 1362 } 1363#endif 1364 while (nbytes-- != 0) 1365 unparseputc(xw, *string++); 1366 if (!TEK4014_ACTIVE(xw)) 1367 AdjustAfterInput(xw); 1368 unparse_end(xw); 1369} 1370 1371/* These definitions are DEC-style (e.g., vt320) */ 1372static int 1373decfuncvalue(KEY_DATA * kd) 1374{ 1375 int result; 1376 1377 if (kd->is_fkey) { 1378 switch (kd->keysym) { 1379 MAP(XK_Fn(1), 11); 1380 MAP(XK_Fn(2), 12); 1381 MAP(XK_Fn(3), 13); 1382 MAP(XK_Fn(4), 14); 1383 MAP(XK_Fn(5), 15); 1384 MAP(XK_Fn(6), 17); 1385 MAP(XK_Fn(7), 18); 1386 MAP(XK_Fn(8), 19); 1387 MAP(XK_Fn(9), 20); 1388 MAP(XK_Fn(10), 21); 1389 MAP(XK_Fn(11), 23); 1390 MAP(XK_Fn(12), 24); 1391 MAP(XK_Fn(13), 25); 1392 MAP(XK_Fn(14), 26); 1393 MAP(XK_Fn(15), 28); 1394 MAP(XK_Fn(16), 29); 1395 MAP(XK_Fn(17), 31); 1396 MAP(XK_Fn(18), 32); 1397 MAP(XK_Fn(19), 33); 1398 MAP(XK_Fn(20), 34); 1399 default: 1400 /* after F20 the codes are made up and do not correspond to any 1401 * real terminal. So they are simply numbered sequentially. 1402 */ 1403 result = 42 + (int) (kd->keysym - XK_Fn(21)); 1404 break; 1405 } 1406 } else { 1407 switch (kd->keysym) { 1408 MAP(XK_Find, 1); 1409 MAP(XK_Insert, 2); 1410 MAP(XK_Delete, 3); 1411#ifdef XK_KP_Insert 1412 MAP(XK_KP_Insert, 2); 1413 MAP(XK_KP_Delete, 3); 1414#endif 1415#ifdef DXK_Remove 1416 MAP(DXK_Remove, 3); 1417#endif 1418 MAP(XK_Select, 4); 1419 MAP(XK_Prior, 5); 1420 MAP(XK_Next, 6); 1421#ifdef XK_ISO_Left_Tab 1422 MAP(XK_ISO_Left_Tab, 'Z'); 1423#endif 1424 MAP(XK_Help, 28); 1425 MAP(XK_Menu, 29); 1426 default: 1427 result = -1; 1428 break; 1429 } 1430 } 1431 return result; 1432} 1433 1434static void 1435hpfuncvalue(ANSI * reply, KEY_DATA * kd) 1436{ 1437#if OPT_HP_FUNC_KEYS 1438 int result; 1439 1440 if (kd->is_fkey) { 1441 switch (kd->keysym) { 1442 MAP(XK_Fn(1), 'p'); 1443 MAP(XK_Fn(2), 'q'); 1444 MAP(XK_Fn(3), 'r'); 1445 MAP(XK_Fn(4), 's'); 1446 MAP(XK_Fn(5), 't'); 1447 MAP(XK_Fn(6), 'u'); 1448 MAP(XK_Fn(7), 'v'); 1449 MAP(XK_Fn(8), 'w'); 1450 default: 1451 result = -1; 1452 break; 1453 } 1454 } else { 1455 switch (kd->keysym) { 1456 MAP(XK_Up, 'A'); 1457 MAP(XK_Down, 'B'); 1458 MAP(XK_Right, 'C'); 1459 MAP(XK_Left, 'D'); 1460 MAP(XK_End, 'F'); 1461 MAP(XK_Clear, 'J'); 1462 MAP(XK_Delete, 'P'); 1463 MAP(XK_Insert, 'Q'); 1464 MAP(XK_Next, 'S'); 1465 MAP(XK_Prior, 'T'); 1466 MAP(XK_Home, 'h'); 1467#ifdef XK_KP_Insert 1468 MAP(XK_KP_Delete, 'P'); 1469 MAP(XK_KP_Insert, 'Q'); 1470#endif 1471#ifdef DXK_Remove 1472 MAP(DXK_Remove, 'P'); 1473#endif 1474 MAP(XK_Select, 'F'); 1475 MAP(XK_Find, 'h'); 1476 default: 1477 result = -1; 1478 break; 1479 } 1480 } 1481 if (result > 0) { 1482 reply->a_type = ANSI_ESC; 1483 reply->a_final = (Char) result; 1484 } 1485#else 1486 (void) reply; 1487 (void) kd; 1488#endif /* OPT_HP_FUNC_KEYS */ 1489} 1490 1491static void 1492scofuncvalue(ANSI * reply, KEY_DATA * kd) 1493{ 1494#if OPT_SCO_FUNC_KEYS 1495 int result; 1496 1497 if (kd->is_fkey) { 1498 switch (kd->keysym) { 1499 MAP(XK_Fn(1), 'M'); 1500 MAP(XK_Fn(2), 'N'); 1501 MAP(XK_Fn(3), 'O'); 1502 MAP(XK_Fn(4), 'P'); 1503 MAP(XK_Fn(5), 'Q'); 1504 MAP(XK_Fn(6), 'R'); 1505 MAP(XK_Fn(7), 'S'); 1506 MAP(XK_Fn(8), 'T'); 1507 MAP(XK_Fn(9), 'U'); 1508 MAP(XK_Fn(10), 'V'); 1509 MAP(XK_Fn(11), 'W'); 1510 MAP(XK_Fn(12), 'X'); 1511 MAP(XK_Fn(13), 'Y'); 1512 MAP(XK_Fn(14), 'Z'); 1513 MAP(XK_Fn(15), 'a'); 1514 MAP(XK_Fn(16), 'b'); 1515 MAP(XK_Fn(17), 'c'); 1516 MAP(XK_Fn(18), 'd'); 1517 MAP(XK_Fn(19), 'e'); 1518 MAP(XK_Fn(20), 'f'); 1519 MAP(XK_Fn(21), 'g'); 1520 MAP(XK_Fn(22), 'h'); 1521 MAP(XK_Fn(23), 'i'); 1522 MAP(XK_Fn(24), 'j'); 1523 MAP(XK_Fn(25), 'k'); 1524 MAP(XK_Fn(26), 'l'); 1525 MAP(XK_Fn(27), 'm'); 1526 MAP(XK_Fn(28), 'n'); 1527 MAP(XK_Fn(29), 'o'); 1528 MAP(XK_Fn(30), 'p'); 1529 MAP(XK_Fn(31), 'q'); 1530 MAP(XK_Fn(32), 'r'); 1531 MAP(XK_Fn(33), 's'); 1532 MAP(XK_Fn(34), 't'); 1533 MAP(XK_Fn(35), 'u'); 1534 MAP(XK_Fn(36), 'v'); 1535 MAP(XK_Fn(37), 'w'); 1536 MAP(XK_Fn(38), 'x'); 1537 MAP(XK_Fn(39), 'y'); 1538 MAP(XK_Fn(40), 'z'); 1539 MAP(XK_Fn(41), '@'); 1540 MAP(XK_Fn(42), '['); 1541 MAP(XK_Fn(43), '\\'); 1542 MAP(XK_Fn(44), ']'); 1543 MAP(XK_Fn(45), '^'); 1544 MAP(XK_Fn(46), '_'); 1545 MAP(XK_Fn(47), '`'); 1546 MAP(XK_Fn(48), '{'); /* no matching '}' */ 1547 default: 1548 result = -1; 1549 break; 1550 } 1551 } else { 1552 switch (kd->keysym) { 1553 MAP(XK_Up, 'A'); 1554 MAP(XK_Down, 'B'); 1555 MAP(XK_Right, 'C'); 1556 MAP(XK_Left, 'D'); 1557 MAP(XK_Begin, 'E'); 1558 MAP(XK_End, 'F'); 1559 MAP(XK_Insert, 'L'); 1560 MAP(XK_Next, 'G'); 1561 MAP(XK_Prior, 'I'); 1562 MAP(XK_Home, 'H'); 1563#ifdef XK_KP_Insert 1564 MAP(XK_KP_Insert, 'L'); 1565#endif 1566 default: 1567 result = -1; 1568 break; 1569 } 1570 } 1571 if (result > 0) { 1572 reply->a_type = ANSI_CSI; 1573 reply->a_final = (Char) result; 1574 } 1575#else 1576 (void) reply; 1577 (void) kd; 1578#endif /* OPT_SCO_FUNC_KEYS */ 1579} 1580 1581static void 1582sunfuncvalue(ANSI * reply, KEY_DATA * kd) 1583{ 1584#if OPT_SUN_FUNC_KEYS 1585 int result; 1586 1587 if (kd->is_fkey) { 1588 switch (kd->keysym) { 1589 /* kf1-kf20 are numbered sequentially */ 1590 MAP(XK_Fn(1), 224); 1591 MAP(XK_Fn(2), 225); 1592 MAP(XK_Fn(3), 226); 1593 MAP(XK_Fn(4), 227); 1594 MAP(XK_Fn(5), 228); 1595 MAP(XK_Fn(6), 229); 1596 MAP(XK_Fn(7), 230); 1597 MAP(XK_Fn(8), 231); 1598 MAP(XK_Fn(9), 232); 1599 MAP(XK_Fn(10), 233); 1600 MAP(XK_Fn(11), 192); 1601 MAP(XK_Fn(12), 193); 1602 MAP(XK_Fn(13), 194); 1603 MAP(XK_Fn(14), 195); /* kund */ 1604 MAP(XK_Fn(15), 196); 1605 MAP(XK_Fn(16), 197); /* kcpy */ 1606 MAP(XK_Fn(17), 198); 1607 MAP(XK_Fn(18), 199); 1608 MAP(XK_Fn(19), 200); /* kfnd */ 1609 MAP(XK_Fn(20), 201); 1610 1611 /* kf31-kf36 are numbered sequentially */ 1612 MAP(XK_Fn(21), 208); /* kf31 */ 1613 MAP(XK_Fn(22), 209); 1614 MAP(XK_Fn(23), 210); 1615 MAP(XK_Fn(24), 211); 1616 MAP(XK_Fn(25), 212); 1617 MAP(XK_Fn(26), 213); /* kf36 */ 1618 1619 /* kf37-kf47 are interspersed with keypad keys */ 1620 MAP(XK_Fn(27), 214); /* khome */ 1621 MAP(XK_Fn(28), 215); /* kf38 */ 1622 MAP(XK_Fn(29), 216); /* kpp */ 1623 MAP(XK_Fn(30), 217); /* kf40 */ 1624 MAP(XK_Fn(31), 218); /* kb2 */ 1625 MAP(XK_Fn(32), 219); /* kf42 */ 1626 MAP(XK_Fn(33), 220); /* kend */ 1627 MAP(XK_Fn(34), 221); /* kf44 */ 1628 MAP(XK_Fn(35), 222); /* knp */ 1629 MAP(XK_Fn(36), 234); /* kf46 */ 1630 MAP(XK_Fn(37), 235); /* kf47 */ 1631 default: 1632 result = -1; 1633 break; 1634 } 1635 } else { 1636 switch (kd->keysym) { 1637 MAP(XK_Help, 196); /* khlp */ 1638 MAP(XK_Menu, 197); 1639 1640 MAP(XK_Find, 1); 1641 MAP(XK_Insert, 2); /* kich1 */ 1642 MAP(XK_Delete, 3); 1643#ifdef XK_KP_Insert 1644 MAP(XK_KP_Insert, 2); 1645 MAP(XK_KP_Delete, 3); 1646#endif 1647#ifdef DXK_Remove 1648 MAP(DXK_Remove, 3); 1649#endif 1650 MAP(XK_Select, 4); 1651 1652 MAP(XK_Prior, 216); 1653 MAP(XK_Next, 222); 1654 MAP(XK_Home, 214); 1655 MAP(XK_End, 220); 1656 MAP(XK_Begin, 218); /* kf41=kb2 */ 1657 1658 default: 1659 result = -1; 1660 break; 1661 } 1662 } 1663 if (result > 0) { 1664 reply->a_type = ANSI_CSI; 1665 reply->a_nparam = 1; 1666 reply->a_param[0] = result; 1667 reply->a_final = 'z'; 1668 } else if (IsCursorKey(kd->keysym)) { 1669 reply->a_type = ANSI_SS3; 1670 reply->a_final = (Char) curfinal[kd->keysym - XK_Home]; 1671 } 1672#else 1673 (void) reply; 1674 (void) kd; 1675#endif /* OPT_SUN_FUNC_KEYS */ 1676} 1677 1678#if OPT_NUM_LOCK 1679#define isName(c) ((c) == '_' || isalnum(CharOf(c))) 1680 1681/* 1682 * Strip unneeded whitespace from a translations resource, mono-casing and 1683 * returning a malloc'd copy of the result. 1684 */ 1685static char * 1686stripTranslations(const char *s) 1687{ 1688 char *dst = 0; 1689 1690 if (s != 0) { 1691 dst = TypeMallocN(char, strlen(s) + 1); 1692 1693 if (dst != 0) { 1694 int state = 0; 1695 int ch = 0; 1696 int prv = 0; 1697 char *d = dst; 1698 1699 TRACE(("stripping:\n%s\n", s)); 1700 while (*s != '\0') { 1701 ch = *s++; 1702 if (ch == '\n') { 1703 if (d != dst) 1704 *d++ = (char) ch; 1705 state = 0; 1706 } else if (strchr(":!#", ch) != 0) { 1707 while (d != dst && isspace(CharOf(d[-1]))) 1708 --d; 1709 state = -1; 1710 } else if (state >= 0) { 1711 if (isspace(CharOf(ch))) { 1712 if (state == 0 || strchr("<>~ \t", prv)) 1713 continue; 1714 } else if (strchr("<>~", ch)) { 1715 while (d != dst && isspace(CharOf(d[-1]))) 1716 --d; 1717 } 1718 *d++ = x_toupper(ch); 1719 ++state; 1720 } 1721 prv = ch; 1722 } 1723 *d = '\0'; 1724 TRACE(("...result:\n%s\n", dst)); 1725 } 1726 } 1727 return dst; 1728} 1729 1730/* 1731 * Make a simple check to see if a given translations keyword appears in 1732 * xterm's translations resource. It does not attempt to parse the strings, 1733 * just makes a case-independent check and ensures that the ends of the match 1734 * are on token-boundaries. 1735 * 1736 * That this can only retrieve translations that are given as resource values; 1737 * the default translations in charproc.c for example are not retrievable by 1738 * any interface to X. 1739 * 1740 * Also: We can retrieve only the most-specified translation resource. For 1741 * example, if the resource file specifies both "*translations" and 1742 * "XTerm*translations", we see only the latter. 1743 */ 1744static Bool 1745TranslationsUseKeyword(Widget w, char **cache, const char *keyword) 1746{ 1747 static String data; 1748 static XtResource key_resources[] = 1749 { 1750 {XtNtranslations, XtCTranslations, XtRString, 1751 sizeof(data), 0, XtRString, (XtPointer) NULL} 1752 }; 1753 Bool result = False; 1754 char *copy; 1755 char *test; 1756 1757 if ((test = stripTranslations(keyword)) != 0) { 1758 if (*cache == 0) { 1759 XtGetSubresources(w, 1760 (XtPointer) &data, 1761 "vt100", 1762 "VT100", 1763 key_resources, 1764 XtNumber(key_resources), 1765 NULL, 1766 (Cardinal) 0); 1767 if (data != 0 && (copy = stripTranslations(data)) != 0) { 1768 *cache = copy; 1769 } 1770 } 1771 1772 if (*cache != 0) { 1773 char *p = *cache; 1774 int state = 0; 1775 int now = ' ', prv; 1776 1777 while (*p != 0) { 1778 prv = now; 1779 now = *p++; 1780 if (now == ':' 1781 || now == '!') { 1782 state = -1; 1783 } else if (now == '\n') { 1784 state = 0; 1785 } else if (state >= 0) { 1786 if (now == test[state]) { 1787 if ((state != 0 1788 || !isName(prv)) 1789 && ((test[++state] == 0) 1790 && !isName(*p))) { 1791 result = True; 1792 break; 1793 } 1794 } else { 1795 state = 0; 1796 } 1797 } 1798 } 1799 } 1800 free(test); 1801 } 1802 TRACE(("TranslationsUseKeyword(%p, %s) = %d\n", w, keyword, result)); 1803 return result; 1804} 1805 1806static Bool 1807xtermHasTranslation(XtermWidget xw, const char *keyword) 1808{ 1809 return (TranslationsUseKeyword(SHELL_OF(xw), 1810 &(xw->keyboard.shell_translations), 1811 keyword) 1812 || TranslationsUseKeyword((Widget) xw, 1813 &(xw->keyboard.xterm_translations), 1814 keyword)); 1815} 1816 1817#if OPT_EXTRA_PASTE 1818static void 1819addTranslation(XtermWidget xw, char *fromString, char *toString) 1820{ 1821 unsigned have = (xw->keyboard.extra_translations 1822 ? strlen(xw->keyboard.extra_translations) 1823 : 0); 1824 unsigned need = (((have != 0) ? (have + 4) : 0) 1825 + strlen(fromString) 1826 + strlen(toString) 1827 + 6); 1828 1829 if (!xtermHasTranslation(xw, fromString)) { 1830 xw->keyboard.extra_translations 1831 = TypeRealloc(char, need, xw->keyboard.extra_translations); 1832 if ((xw->keyboard.extra_translations) != 0) { 1833 TRACE(("adding %s: %s\n", fromString, toString)); 1834 if (have) 1835 strcat(xw->keyboard.extra_translations, " \\n\\"); 1836 sprintf(xw->keyboard.extra_translations, "%s: %s", 1837 fromString, toString); 1838 TRACE(("...{%s}\n", xw->keyboard.extra_translations)); 1839 } 1840 } 1841} 1842#endif 1843 1844#define SaveMask(name) xw->misc.name |= mask;\ 1845 TRACE(("SaveMask(%s) %#lx (%#lx is%s modifier)\n", \ 1846 #name, \ 1847 xw->misc.name, mask, \ 1848 ModifierName(mask))); 1849/* 1850 * Determine which modifier mask (if any) applies to the Num_Lock keysym. 1851 * 1852 * Also, determine which modifiers are associated with the ALT keys, so we can 1853 * send that information as a parameter for special keys in Sun/PC keyboard 1854 * mode. However, if the ALT modifier is used in translations, we do not want 1855 * to confuse things by sending the parameter. 1856 */ 1857void 1858VTInitModifiers(XtermWidget xw) 1859{ 1860 Display *dpy = XtDisplay(xw); 1861 XModifierKeymap *keymap = XGetModifierMapping(dpy); 1862 int i, j, k, l; 1863 KeySym keysym; 1864 unsigned long mask; 1865 int min_keycode, max_keycode, keysyms_per_keycode = 0; 1866 1867 if (keymap != 0) { 1868 KeySym *theMap; 1869 int keycode_count; 1870 1871 TRACE(("VTInitModifiers\n")); 1872 1873 XDisplayKeycodes(dpy, &min_keycode, &max_keycode); 1874 keycode_count = (max_keycode - min_keycode + 1); 1875 theMap = XGetKeyboardMapping(dpy, 1876 min_keycode, 1877 keycode_count, 1878 &keysyms_per_keycode); 1879 1880 if (theMap != 0) { 1881 1882#if OPT_EXTRA_PASTE 1883 /* 1884 * Assume that if we can find the paste keysym in the X keyboard 1885 * mapping that the server allows the corresponding translations 1886 * resource. 1887 */ 1888 int limit = (max_keycode - min_keycode) * keysyms_per_keycode; 1889 for (i = 0; i < limit; ++i) { 1890#ifdef XF86XK_Paste 1891 if (theMap[i] == XF86XK_Paste) { 1892 TRACE(("keyboard has XF86XK_Paste\n")); 1893 addTranslation(xw, 1894 "<KeyPress> XF86Paste", 1895 "insert-selection(SELECT, CUT_BUFFER0)"); 1896 } 1897#endif 1898#ifdef SunXK_Paste 1899 if (theMap[i] == SunXK_Paste) { 1900 TRACE(("keyboard has SunXK_Paste\n")); 1901 addTranslation(xw, 1902 "<KeyPress> SunPaste", 1903 "insert-selection(SELECT, CUT_BUFFER0)"); 1904 } 1905#endif 1906 } 1907#endif /* OPT_EXTRA_PASTE */ 1908 1909 for (i = k = 0, mask = 1; i < 8; i++, mask <<= 1) { 1910 for (j = 0; j < keymap->max_keypermod; j++) { 1911 KeyCode code = keymap->modifiermap[k++]; 1912 if (code == 0) 1913 continue; 1914 1915 for (l = 0; l < keysyms_per_keycode; ++l) { 1916 keysym = XKeycodeToKeysym(dpy, code, l); 1917 if (keysym == NoSymbol) { 1918 ; 1919 } else if (keysym == XK_Num_Lock) { 1920 SaveMask(num_lock); 1921 } else if (keysym == XK_Alt_L || keysym == XK_Alt_R) { 1922 SaveMask(alt_mods); 1923 } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) { 1924 SaveMask(meta_mods); 1925 } else if (mask == ShiftMask 1926 && (keysym == XK_Shift_L 1927 || keysym == XK_Shift_R)) { 1928 ; /* ignore */ 1929 } else if (mask == ControlMask 1930 && (keysym == XK_Control_L 1931 || keysym == XK_Control_R)) { 1932 ; /* ignore */ 1933 } else if (mask == LockMask 1934 && (keysym == XK_Caps_Lock)) { 1935 ; /* ignore */ 1936 } else if (keysym == XK_Mode_switch 1937#ifdef XK_ISO_Level3_Shift 1938 || keysym == XK_ISO_Level3_Shift 1939#endif 1940 ) { 1941 SaveMask(other_mods); 1942 } 1943 } 1944 } 1945 } 1946 XFree(theMap); 1947 } 1948 1949 /* Don't disable any mods if "alwaysUseMods" is true. */ 1950 if (!xw->misc.alwaysUseMods) { 1951 /* 1952 * If the Alt modifier is used in translations, we would rather not 1953 * use it to modify function-keys when NumLock is active. 1954 */ 1955 if ((xw->misc.alt_mods != 0) 1956 && xtermHasTranslation(xw, "alt")) { 1957 TRACE(("ALT is used as a modifier in translations (ignore mask)\n")); 1958 xw->misc.alt_mods = 0; 1959 } 1960 1961 /* 1962 * If the Meta modifier is used in translations, we would rather not 1963 * use it to modify function-keys. 1964 */ 1965 if ((xw->misc.meta_mods != 0) 1966 && xtermHasTranslation(xw, "meta")) { 1967 TRACE(("META is used as a modifier in translations\n")); 1968 xw->misc.meta_mods = 0; 1969 } 1970 } 1971 1972 XFreeModifiermap(keymap); 1973 } 1974} 1975#endif /* OPT_NUM_LOCK */ 1976