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