/* $XTermId: input.c,v 1.348 2013/11/26 00:12:10 tom Exp $ */ /* * Copyright 1999-2012,2013 by Thomas E. Dickey * * All Rights Reserved * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name(s) of the above copyright * holders shall not be used in advertising or otherwise to promote the * sale, use or other dealings in this Software without prior written * authorization. * * * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. * * All Rights Reserved * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation, and that the name of Digital Equipment * Corporation not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior permission. * * * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. */ /* input.c */ #include #include #ifdef VMS #include #endif #if HAVE_X11_DECKEYSYM_H #include #endif #if HAVE_X11_SUNKEYSYM_H #include #endif #if HAVE_X11_XF86KEYSYM_H #include #endif #ifdef HAVE_XKBKEYCODETOKEYSYM #include #endif #include #include #include #include #include #include #include #include /* * Xutil.h has no macro to check for the complete set of function- and * modifier-keys that might be returned. Fake it. */ #ifdef XK_ISO_Lock #define IsPredefinedKey(n) ((n) >= XK_ISO_Lock && (n) <= XK_Delete) #else #define IsPredefinedKey(n) ((n) >= XK_BackSpace && (n) <= XK_Delete) #endif #ifdef XK_ISO_Left_Tab #define IsTabKey(n) ((n) == XK_Tab || (n) == XK_ISO_Left_Tab) #else #define IsTabKey(n) ((n) == XK_Tab) #endif #ifndef IsPrivateKeypadKey #define IsPrivateKeypadKey(k) (0) #endif #define IsBackarrowToggle(keyboard, keysym, state) \ ((((keyboard->flags & MODE_DECBKM) == 0) \ ^ ((state & ControlMask) != 0)) \ && (keysym == XK_BackSpace)) #define MAP(from, to) case from: result = to; break #define Masked(value,mask) ((value) & (unsigned) (~(mask))) #define KEYSYM_FMT "0x%04lX" /* simplify matching */ #define TEK4014_GIN(tw) (tw != 0 && TekScreenOf(tw)->TekGIN) typedef struct { KeySym keysym; Bool is_fkey; int nbytes; #define STRBUFSIZE 500 char strbuf[STRBUFSIZE]; } KEY_DATA; static const char *kypd_num = " XXXXXXXX\tXXX\rXXXxxxxXXXXXXXXXXXXXXXXXXXXX*+,-./0123456789XXX="; /* 0123456789 abc def0123456789abcdef0123456789abcdef0123456789abcd */ static const char *kypd_apl = " ABCDEFGHIJKLMNOPQRSTUVWXYZ??????abcdefghijklmnopqrstuvwxyzXXX"; /* 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd */ static const char *curfinal = "HDACB FE"; static int decfuncvalue(KEY_DATA *); static void sunfuncvalue(ANSI *, KEY_DATA *); static void hpfuncvalue(ANSI *, KEY_DATA *); static void scofuncvalue(ANSI *, KEY_DATA *); static void AdjustAfterInput(XtermWidget xw) { TScreen *screen = TScreenOf(xw); if (screen->scrollkey && screen->topline != 0) WindowScroll(xw, 0, False); if (screen->marginbell) { int col = screen->max_col - screen->nmarginbell; if (screen->bellArmed >= 0) { if (screen->bellArmed == screen->cur_row) { if (screen->cur_col >= col) { Bell(xw, XkbBI_MarginBell, 0); screen->bellArmed = -1; } } else { screen->bellArmed = screen->cur_col < col ? screen->cur_row : -1; } } else if (screen->cur_col < col) screen->bellArmed = screen->cur_row; } } /* * Return true if the key is on the editing keypad. This overlaps with * IsCursorKey() and IsKeypadKey() and must be tested before those macros to * distinguish it from them. * * VT220 emulation uses the VT100 numeric keypad as well as a 6-key * editing keypad. Here's a picture of the VT220 editing keypad: * +--------+--------+--------+ * | Find | Insert | Remove | * +--------+--------+--------+ * | Select | Prev | Next | * +--------+--------+--------+ * * and the similar Sun and PC keypads: * +--------+--------+--------+ * | Insert | Home | PageUp | * +--------+--------+--------+ * | Delete | End | PageDn | * +--------+--------+--------+ */ static Bool IsEditKeypad(XtermWidget xw, KeySym keysym) { Bool result; switch (keysym) { case XK_Delete: result = !xtermDeleteIsDEL(xw); break; case XK_Prior: case XK_Next: case XK_Insert: case XK_Find: case XK_Select: #ifdef DXK_Remove case DXK_Remove: #endif result = True; break; default: result = False; break; } return result; } /* * Editing-keypad, plus other editing keys which are not included in the * other macros. */ static Bool IsEditFunctionKey(XtermWidget xw, KeySym keysym) { Bool result; switch (keysym) { #ifdef XK_KP_Delete case XK_KP_Delete: /* editing key on numeric keypad */ case XK_KP_Insert: /* editing key on numeric keypad */ #endif #ifdef XK_ISO_Left_Tab case XK_ISO_Left_Tab: #endif result = True; break; default: result = IsEditKeypad(xw, keysym); break; } return result; } #if OPT_MOD_FKEYS #define IS_CTRL(n) ((n) < ANSI_SPA || ((n) >= 0x7f && (n) <= 0x9f)) /* * Return true if the keysym corresponds to one of the control characters, * or one of the common ASCII characters that is combined with control to * make a control character. */ static Bool IsControlInput(KEY_DATA * kd) { return ((kd->keysym) >= 0x40 && (kd->keysym) <= 0x7f); } static Bool IsControlOutput(KEY_DATA * kd) { return IS_CTRL(kd->keysym); } /* * X "normally" has some built-in translations, which the user may want to * suppress when processing the modifyOtherKeys resource. In particular, the * control modifier applied to some of the keyboard digits gives results for * control characters. * * control 2 0 NUL * control SPC 0 NUL * control @ 0 NUL * control ` 0 NUL * control 3 0x1b ESC * control 4 0x1c FS * control \ 0x1c FS * control 5 0x1d GS * control 6 0x1e RS * control ^ 0x1e RS * control ~ 0x1e RS * control 7 0x1f US * control / 0x1f US * control _ 0x1f US * control 8 0x7f DEL * * It is possible that some other keyboards do not work for these combinations, * but they do work with modifyOtherKeys=2 for the US keyboard: * * control ` 0 NUL * control [ 0x1b ESC * control \ 0x1c FS * control ] 0x1d GS * control ? 0x7f DEL */ static Bool IsControlAlias(KEY_DATA * kd) { Bool result = False; if (kd->nbytes == 1) { result = IS_CTRL(CharOf(kd->strbuf[0])); } return result; } /* * If we are in the non-VT220/VT52 keyboard state, allow modifiers to add a * parameter to the function-key control sequences. * * Note that we generally cannot capture the Shift-modifier for the numeric * keypad since this is commonly used to act as a type of NumLock, e.g., * making the keypad send "7" (actually XK_KP_7) where the unshifted code * would be Home (XK_KP_Home). The other modifiers work, subject to the * usual window-manager assignments. */ #if OPT_SUNPC_KBD #define LegacyAllows(code) (!is_legacy || (code & xw->keyboard.modify_now.allow_keys) != 0) #else #define LegacyAllows(code) True #endif static Bool allowModifierParm(XtermWidget xw, KEY_DATA * kd) { TKeyboard *keyboard = &(xw->keyboard); int is_legacy = (keyboard->type == keyboardIsLegacy); Bool result = False; #if OPT_SUNPC_KBD if (keyboard->type == keyboardIsVT220) is_legacy = True; #endif #if OPT_VT52_MODE if (TScreenOf(xw)->vtXX_level != 0) #endif { if (IsCursorKey(kd->keysym) || IsEditFunctionKey(xw, kd->keysym)) { result = LegacyAllows(2); } else if (IsKeypadKey(kd->keysym)) { result = LegacyAllows(1); } else if (IsFunctionKey(kd->keysym)) { result = LegacyAllows(4); } else if (IsMiscFunctionKey(kd->keysym)) { result = LegacyAllows(8); } } if (xw->keyboard.modify_now.other_keys != 0) { result = True; } return result; } /* * Modifier codes: * None 1 * Shift 2 = 1(None)+1(Shift) * Alt 3 = 1(None)+2(Alt) * Alt+Shift 4 = 1(None)+1(Shift)+2(Alt) * Ctrl 5 = 1(None)+4(Ctrl) * Ctrl+Shift 6 = 1(None)+1(Shift)+4(Ctrl) * Ctrl+Alt 7 = 1(None)+2(Alt)+4(Ctrl) * Ctrl+Alt+Shift 8 = 1(None)+1(Shift)+2(Alt)+4(Ctrl) * Meta 9 = 1(None)+8(Meta) * Meta+Shift 10 = 1(None)+8(Meta)+1(Shift) * Meta+Alt 11 = 1(None)+8(Meta)+2(Alt) * Meta+Alt+Shift 12 = 1(None)+8(Meta)+1(Shift)+2(Alt) * Meta+Ctrl 13 = 1(None)+8(Meta)+4(Ctrl) * Meta+Ctrl+Shift 14 = 1(None)+8(Meta)+1(Shift)+4(Ctrl) * Meta+Ctrl+Alt 15 = 1(None)+8(Meta)+2(Alt)+4(Ctrl) * Meta+Ctrl+Alt+Shift 16 = 1(None)+8(Meta)+1(Shift)+2(Alt)+4(Ctrl) */ unsigned xtermParamToState(XtermWidget xw, unsigned param) { unsigned result = 0; #if OPT_NUM_LOCK if (param > MOD_NONE) { if ((param - MOD_NONE) & MOD_SHIFT) UIntSet(result, ShiftMask); if ((param - MOD_NONE) & MOD_CTRL) UIntSet(result, ControlMask); if ((param - MOD_NONE) & MOD_ALT) UIntSet(result, xw->work.alt_mods); if ((param - MOD_NONE) & MOD_META) UIntSet(result, xw->work.meta_mods); } #else (void) xw; (void) param; #endif TRACE(("xtermParamToState(%d) %s%s%s%s -> %#x\n", param, MODIFIER_NAME(param, MOD_SHIFT), MODIFIER_NAME(param, MOD_ALT), MODIFIER_NAME(param, MOD_CTRL), MODIFIER_NAME(param, MOD_META), result)); return result; } unsigned xtermStateToParam(XtermWidget xw, unsigned state) { unsigned modify_parm = MOD_NONE; TRACE(("xtermStateToParam %#x\n", state)); #if OPT_NUM_LOCK if (state & ShiftMask) { modify_parm += MOD_SHIFT; UIntClr(state, ShiftMask); } if (state & ControlMask) { modify_parm += MOD_CTRL; UIntClr(state, ControlMask); } if ((state & xw->work.alt_mods) != 0) { modify_parm += MOD_ALT; UIntClr(state, xw->work.alt_mods); } if ((state & xw->work.meta_mods) != 0) { modify_parm += MOD_META; UIntClr(state, xw->work.meta_mods); } if (modify_parm == MOD_NONE) modify_parm = 0; #else (void) xw; (void) state; #endif TRACE(("...xtermStateToParam %d%s%s%s%s\n", modify_parm, MODIFIER_NAME(modify_parm, MOD_SHIFT), MODIFIER_NAME(modify_parm, MOD_ALT), MODIFIER_NAME(modify_parm, MOD_CTRL), MODIFIER_NAME(modify_parm, MOD_META))); return modify_parm; } #define computeMaskedModifier(xw, state, mask) \ xtermStateToParam(xw, Masked(state, mask)) #if OPT_NUM_LOCK static unsigned filterAltMeta(unsigned result, unsigned mask, Bool enable, KEY_DATA * kd) { if ((result & mask) != 0) { /* * metaSendsEscape makes the meta key independent of * modifyOtherKeys. */ if (enable) { result &= ~mask; } /* * A bare meta-modifier is independent of modifyOtherKeys. If it * is combined with other modifiers, make it depend. */ if ((result & ~(mask)) == 0) { result &= ~mask; } /* * Check for special cases of control+meta which are used by some * applications, e.g., emacs. */ if ((IsControlInput(kd) || IsControlOutput(kd)) && (result & ControlMask) != 0) { result &= ~(mask | ControlMask); } if (kd->keysym == XK_Return || kd->keysym == XK_Tab) { result &= ~(mask | ControlMask); } } return result; } #endif /* OPT_NUM_LOCK */ /* * Single characters (not function-keys) are allowed fewer modifiers when * interpreting modifyOtherKeys due to pre-existing associations with some * modifiers. */ static unsigned allowedCharModifiers(XtermWidget xw, unsigned state, KEY_DATA * kd) { #if OPT_NUM_LOCK unsigned a_or_m = (state & (xw->work.meta_mods | xw->work.alt_mods)); #else unsigned a_or_m = 0; #endif /* * Start by limiting the result to the modifiers we might want to use. */ unsigned result = (state & (ControlMask | ShiftMask | a_or_m)); /* * If modifyOtherKeys is off or medium (0 or 1), moderate its effects by * excluding the common cases for modifiers. */ if (xw->keyboard.modify_now.other_keys <= 1) { if (IsControlInput(kd) && Masked(result, ControlMask) == 0) { /* These keys are already associated with the control-key */ if (xw->keyboard.modify_now.other_keys == 0) { UIntClr(result, ControlMask); } } else if (kd->keysym == XK_Tab || kd->keysym == XK_Return) { /* EMPTY */ ; } else if (IsControlAlias(kd)) { /* Things like "^_" work here... */ if (Masked(result, (ControlMask | ShiftMask)) == 0) { result = 0; } } else if (!IsControlOutput(kd) && !IsPredefinedKey(kd->keysym)) { /* Printable keys are already associated with the shift-key */ if (!(result & ControlMask)) { UIntClr(result, ShiftMask); } } #if OPT_NUM_LOCK result = filterAltMeta(result, xw->work.meta_mods, TScreenOf(xw)->meta_sends_esc, kd); if (TScreenOf(xw)->alt_is_not_meta) { result = filterAltMeta(result, xw->work.alt_mods, TScreenOf(xw)->alt_sends_esc, kd); } #endif } TRACE(("...allowedCharModifiers(state=%u" FMT_MODIFIER_NAMES ", ch=" KEYSYM_FMT ") ->" "%u" FMT_MODIFIER_NAMES "\n", state, ARG_MODIFIER_NAMES(state), kd->keysym, result, ARG_MODIFIER_NAMES(result))); return result; } /* * Decide if we should generate a special escape sequence for "other" keys * than cursor-, function-keys, etc., as per the modifyOtherKeys resource. */ static Bool ModifyOtherKeys(XtermWidget xw, unsigned state, KEY_DATA * kd, unsigned modify_parm) { TKeyboard *keyboard = &(xw->keyboard); Bool result = False; /* * Exclude the keys already covered by a modifier. */ if (kd->is_fkey || IsEditFunctionKey(xw, kd->keysym) || IsKeypadKey(kd->keysym) || IsCursorKey(kd->keysym) || IsPFKey(kd->keysym) || IsMiscFunctionKey(kd->keysym) || IsPrivateKeypadKey(kd->keysym)) { result = False; } else if (modify_parm != 0) { if (IsBackarrowToggle(keyboard, kd->keysym, state)) { kd->keysym = XK_Delete; UIntClr(state, ControlMask); } if (!IsPredefinedKey(kd->keysym)) { state = allowedCharModifiers(xw, state, kd); } if (state != 0) { switch (keyboard->modify_now.other_keys) { default: break; case 1: switch (kd->keysym) { case XK_BackSpace: case XK_Delete: result = False; break; #ifdef XK_ISO_Left_Tab case XK_ISO_Left_Tab: if (computeMaskedModifier(xw, state, ShiftMask)) result = True; break; #endif case XK_Return: case XK_Tab: result = (modify_parm != 0); break; default: if (IsControlInput(kd)) { if (state == ControlMask || state == ShiftMask) { result = False; } else { result = (modify_parm != 0); } } else if (IsControlAlias(kd)) { if (state == ShiftMask) result = False; else if (computeMaskedModifier(xw, state, ControlMask)) { result = True; } } else { result = True; } break; } break; case 2: switch (kd->keysym) { case XK_BackSpace: /* strip ControlMask as per IsBackarrowToggle() */ if (computeMaskedModifier(xw, state, ControlMask)) result = True; break; case XK_Delete: result = (xtermStateToParam(xw, state) != 0); break; #ifdef XK_ISO_Left_Tab case XK_ISO_Left_Tab: if (computeMaskedModifier(xw, state, ShiftMask)) result = True; break; #endif case XK_Return: case XK_Tab: result = (modify_parm != 0); break; default: if (IsControlInput(kd)) { result = True; } else if (state == ShiftMask) { result = (kd->keysym == ' ' || kd->keysym == XK_Return); } else if (computeMaskedModifier(xw, state, ShiftMask)) { result = True; } break; } break; } } } TRACE(("...ModifyOtherKeys(%d,%d) %s\n", keyboard->modify_now.other_keys, modify_parm, BtoS(result))); return result; } #define APPEND_PARM(number) \ reply->a_param[reply->a_nparam] = (ParmType) number; \ reply->a_nparam++ /* * Function-key code 27 happens to not be used in the vt220-style encoding. * xterm uses this to represent modified non-function-keys such as control/+ in * the Sun/PC keyboard layout. See the modifyOtherKeys resource in the manpage * for more information. */ static Bool modifyOtherKey(ANSI *reply, int input_char, unsigned modify_parm, int format_keys) { Bool result = False; if (input_char >= 0) { reply->a_type = ANSI_CSI; if (format_keys) { APPEND_PARM(input_char); APPEND_PARM(modify_parm); reply->a_final = 'u'; } else { APPEND_PARM(27); APPEND_PARM(modify_parm); APPEND_PARM(input_char); reply->a_final = '~'; } result = True; } return result; } static void modifyCursorKey(ANSI *reply, int modify, unsigned *modify_parm) { if (*modify_parm != 0) { if (modify < 0) { *modify_parm = 0; } if (modify > 0) { reply->a_type = ANSI_CSI; /* SS3 should not have params */ } if (modify > 1 && reply->a_nparam == 0) { APPEND_PARM(1); /* force modifier to 2nd param */ } if (modify > 2) { reply->a_pintro = '>'; /* mark this as "private" */ } } } #else #define modifyCursorKey(reply, modify, parm) /* nothing */ #endif /* OPT_MOD_FKEYS */ #if OPT_SUNPC_KBD /* * If we have told xterm that our keyboard is really a Sun/PC keyboard, this is * enough to make a reasonable approximation to DEC vt220 numeric and editing * keypads. */ static KeySym TranslateFromSUNPC(KeySym keysym) { /* *INDENT-OFF* */ static struct { KeySym before, after; } table[] = { #ifdef DXK_Remove { XK_Delete, DXK_Remove }, #endif { XK_Home, XK_Find }, { XK_End, XK_Select }, #ifdef XK_KP_Home { XK_Delete, XK_KP_Decimal }, { XK_KP_Delete, XK_KP_Decimal }, { XK_KP_Insert, XK_KP_0 }, { XK_KP_End, XK_KP_1 }, { XK_KP_Down, XK_KP_2 }, { XK_KP_Next, XK_KP_3 }, { XK_KP_Left, XK_KP_4 }, { XK_KP_Begin, XK_KP_5 }, { XK_KP_Right, XK_KP_6 }, { XK_KP_Home, XK_KP_7 }, { XK_KP_Up, XK_KP_8 }, { XK_KP_Prior, XK_KP_9 }, #endif }; /* *INDENT-ON* */ unsigned n; for (n = 0; n < sizeof(table) / sizeof(table[0]); n++) { if (table[n].before == keysym) { TRACE(("...Input keypad before was " KEYSYM_FMT "\n", keysym)); keysym = table[n].after; TRACE(("...Input keypad changed to " KEYSYM_FMT "\n", keysym)); break; } } return keysym; } #endif /* OPT_SUNPC_KBD */ #define VT52_KEYPAD \ if_OPT_VT52_MODE(screen,{ \ reply.a_type = ANSI_ESC; \ reply.a_pintro = '?'; \ }) #define VT52_CURSOR_KEYS \ if_OPT_VT52_MODE(screen,{ \ reply.a_type = ANSI_ESC; \ }) #undef APPEND_PARM #define APPEND_PARM(number) \ reply.a_param[reply.a_nparam] = (ParmType) number, \ reply.a_nparam++ #if OPT_MOD_FKEYS #define MODIFIER_PARM \ if (modify_parm != 0) APPEND_PARM(modify_parm) #else #define MODIFIER_PARM /*nothing */ #endif /* * Determine if we use the \E[3~ sequence for Delete, or the legacy ^?. We * maintain the delete_is_del value as 3 states: unspecified(2), true and * false. If unspecified, it is handled differently according to whether the * legacy keyboard support is enabled, or if xterm emulates a VT220. * * Once the user (or application) has specified delete_is_del via resource * setting, popup menu or escape sequence, it overrides the keyboard type * rather than the reverse. */ Bool xtermDeleteIsDEL(XtermWidget xw) { Bool result = True; if (xw->keyboard.type == keyboardIsDefault || xw->keyboard.type == keyboardIsVT220) result = (TScreenOf(xw)->delete_is_del == True); if (xw->keyboard.type == keyboardIsLegacy) result = (TScreenOf(xw)->delete_is_del != False); TRACE(("xtermDeleteIsDEL(%d/%d) = %d\n", xw->keyboard.type, TScreenOf(xw)->delete_is_del, result)); return result; } static Boolean lookupKeyData(KEY_DATA * kd, XtermWidget xw, XKeyEvent * event) { TScreen *screen = TScreenOf(xw); TKeyboard *keyboard = &(xw->keyboard); Boolean result = True; TRACE(("%s %#x\n", visibleEventType(event->type), event->keycode)); kd->keysym = 0; kd->is_fkey = False; #if OPT_TCAP_QUERY if (screen->tc_query_code >= 0) { kd->keysym = (KeySym) screen->tc_query_code; kd->is_fkey = screen->tc_query_fkey; if (kd->keysym != XK_BackSpace) { kd->nbytes = 0; kd->strbuf[0] = 0; } else { kd->nbytes = 1; kd->strbuf[0] = 8; } } else #endif { #if OPT_I18N_SUPPORT && OPT_INPUT_METHOD TInput *input = lookupTInput(xw, (Widget) xw); if (input && input->xic) { Status status_return; #if OPT_WIDE_CHARS if (screen->utf8_mode) { kd->nbytes = Xutf8LookupString(input->xic, event, kd->strbuf, (int) sizeof(kd->strbuf), &(kd->keysym), &status_return); } else #endif { kd->nbytes = XmbLookupString(input->xic, event, kd->strbuf, (int) sizeof(kd->strbuf), &(kd->keysym), &status_return); } #if OPT_MOD_FKEYS /* * Fill-in some code useful with IsControlAlias(): */ if (status_return == XLookupBoth && kd->nbytes <= 1 && !IsPredefinedKey(kd->keysym) && (keyboard->modify_now.other_keys > 1) && !IsControlInput(kd)) { kd->nbytes = 1; kd->strbuf[0] = (char) kd->keysym; } #endif /* OPT_MOD_FKEYS */ } else #endif /* OPT_I18N_SUPPORT */ { static XComposeStatus compose_status = {NULL, 0}; kd->nbytes = XLookupString(event, kd->strbuf, (int) sizeof(kd->strbuf), &(kd->keysym), &compose_status); } kd->is_fkey = IsFunctionKey(kd->keysym); } return result; } void Input(XtermWidget xw, XKeyEvent * event, Bool eightbit) { Char *string; TKeyboard *keyboard = &(xw->keyboard); TScreen *screen = TScreenOf(xw); int j; int key = False; ANSI reply; int dec_code; unsigned modify_parm = 0; int keypad_mode = ((keyboard->flags & MODE_DECKPAM) != 0); unsigned evt_state = event->state; unsigned mod_state; KEY_DATA kd; /* Ignore characters typed at the keyboard */ if (keyboard->flags & MODE_KAM) return; lookupKeyData(&kd, xw, event); memset(&reply, 0, sizeof(reply)); TRACE(("Input keysym " KEYSYM_FMT ", %d:'%s'%s" FMT_MODIFIER_NAMES "%s%s%s%s%s%s\n", kd.keysym, kd.nbytes, visibleChars((Char *) kd.strbuf, ((kd.nbytes > 0) ? (unsigned) kd.nbytes : 0)), ARG_MODIFIER_NAMES(evt_state), eightbit ? " 8bit" : " 7bit", IsKeypadKey(kd.keysym) ? " KeypadKey" : "", IsCursorKey(kd.keysym) ? " CursorKey" : "", IsPFKey(kd.keysym) ? " PFKey" : "", kd.is_fkey ? " FKey" : "", IsMiscFunctionKey(kd.keysym) ? " MiscFKey" : "", IsEditFunctionKey(xw, kd.keysym) ? " EditFkey" : "")); #if OPT_SUNPC_KBD /* * DEC keyboards don't have keypad(+), but do have keypad(,) instead. * Other (Sun, PC) keyboards commonly have keypad(+), but no keypad(,) * - it's a pain for users to work around. */ if (keyboard->type == keyboardIsVT220 && (evt_state & ShiftMask) == 0) { if (kd.keysym == XK_KP_Add) { kd.keysym = XK_KP_Separator; UIntClr(evt_state, ShiftMask); TRACE(("...Input keypad(+), change keysym to " KEYSYM_FMT "\n", kd.keysym)); } if ((evt_state & ControlMask) != 0 && kd.keysym == XK_KP_Separator) { kd.keysym = XK_KP_Subtract; UIntClr(evt_state, ControlMask); TRACE(("...Input control/keypad(,), change keysym to " KEYSYM_FMT "\n", kd.keysym)); } } #endif /* * The keyboard tables may give us different keypad codes according to * whether NumLock is pressed. Use this check to simplify the process * of determining whether we generate an escape sequence for a keypad * key, or force it to the value kypd_num[]. There is no fixed * modifier for this feature, so we assume that it is the one assigned * to the NumLock key. * * This check used to try to return the contents of strbuf, but that * does not work properly when a control modifier is given (trash is * returned in the buffer in some cases -- perhaps an X bug). */ #if OPT_NUM_LOCK if (kd.nbytes == 1 && IsKeypadKey(kd.keysym) && xw->misc.real_NumLock && (xw->work.num_lock & evt_state) != 0) { keypad_mode = 0; TRACE(("...Input num_lock, force keypad_mode off\n")); } #endif #if OPT_MOD_FKEYS if (evt_state != 0 && allowModifierParm(xw, &kd)) { modify_parm = xtermStateToParam(xw, evt_state); } /* * Shift-tab is often mapped to XK_ISO_Left_Tab which is classified as * IsEditFunctionKey(), and the conversion does not produce any bytes. * Check for this special case so we have data when handling the * modifyOtherKeys resource. */ if (keyboard->modify_now.other_keys > 1) { if (IsTabKey(kd.keysym) && kd.nbytes == 0) { kd.nbytes = 1; kd.strbuf[0] = '\t'; } } #ifdef XK_ISO_Left_Tab else if (IsTabKey(kd.keysym) && kd.nbytes <= 1 && modify_parm == (MOD_NONE + MOD_SHIFT)) { kd.keysym = XK_ISO_Left_Tab; } #endif #endif /* OPT_MOD_FKEYS */ /* VT300 & up: backarrow toggle */ if ((kd.nbytes == 1) && IsBackarrowToggle(keyboard, kd.keysym, evt_state)) { kd.strbuf[0] = ANSI_DEL; TRACE(("...Input backarrow changed to %d\n", kd.strbuf[0])); } #if OPT_SUNPC_KBD /* make an DEC editing-keypad from a Sun or PC editing-keypad */ if (keyboard->type == keyboardIsVT220 && (kd.keysym != XK_Delete || !xtermDeleteIsDEL(xw))) kd.keysym = TranslateFromSUNPC(kd.keysym); else #endif { #ifdef XK_KP_Home if (kd.keysym >= XK_KP_Home && kd.keysym <= XK_KP_Begin) { TRACE(("...Input keypad before was " KEYSYM_FMT "\n", kd.keysym)); kd.keysym += (KeySym) (XK_Home - XK_KP_Home); TRACE(("...Input keypad changed to " KEYSYM_FMT "\n", kd.keysym)); } #endif } /* * Map the Sun afterthought-keys in as F36 and F37. */ #ifdef SunXK_F36 if (!kd.is_fkey) { if (kd.keysym == SunXK_F36) { kd.keysym = XK_Fn(36); kd.is_fkey = True; } if (kd.keysym == SunXK_F37) { kd.keysym = XK_Fn(37); kd.is_fkey = True; } } #endif /* * Use the control- and shift-modifiers to obtain more function keys than * the keyboard provides. We can do this if there is no conflicting use of * those modifiers: * * a) for VT220 keyboard, we use only the control-modifier. The keyboard * uses shift-modifier for UDK's. * * b) for non-VT220 keyboards, we only have to check if the * modifyFunctionKeys resource is inactive. * * Thereafter, we note when we have a function-key and keep that * distinction when testing for "function-key" values. */ if ((evt_state & (ControlMask | ShiftMask)) != 0 && kd.is_fkey) { /* VT220 keyboard uses shift for UDK */ if (keyboard->type == keyboardIsVT220 || keyboard->type == keyboardIsLegacy) { TRACE(("...map XK_F%ld", kd.keysym - XK_Fn(1) + 1)); if (evt_state & ControlMask) { kd.keysym += (KeySym) xw->misc.ctrl_fkeys; UIntClr(evt_state, ControlMask); } TRACE((" to XK_F%ld\n", kd.keysym - XK_Fn(1) + 1)); } #if OPT_MOD_FKEYS else if (keyboard->modify_now.function_keys < 0) { TRACE(("...map XK_F%ld", kd.keysym - XK_Fn(1) + 1)); if (evt_state & ShiftMask) { kd.keysym += (KeySym) (xw->misc.ctrl_fkeys * 1); UIntClr(evt_state, ShiftMask); } if (evt_state & ControlMask) { kd.keysym += (KeySym) (xw->misc.ctrl_fkeys * 2); UIntClr(evt_state, ControlMask); } TRACE((" to XK_F%ld\n", kd.keysym - XK_Fn(1) + 1)); } /* * Reevaluate the modifier parameter, stripping off the modifiers * that we just used. */ if (modify_parm) { modify_parm = xtermStateToParam(xw, evt_state); } #endif /* OPT_MOD_FKEYS */ } /* * Test for one of the keyboard variants. */ switch (keyboard->type) { case keyboardIsHP: hpfuncvalue(&reply, &kd); break; case keyboardIsSCO: scofuncvalue(&reply, &kd); break; case keyboardIsSun: sunfuncvalue(&reply, &kd); break; case keyboardIsTermcap: #if OPT_TCAP_FKEYS if (xtermcapString(xw, (int) kd.keysym, evt_state)) return; #endif break; case keyboardIsDefault: case keyboardIsLegacy: case keyboardIsVT220: break; } if (reply.a_final) { /* * The key symbol matches one of the variants. Most of those are * function-keys, though some cursor- and editing-keys are mixed in. */ modifyCursorKey(&reply, ((kd.is_fkey || IsMiscFunctionKey(kd.keysym) || IsEditFunctionKey(xw, kd.keysym)) ? keyboard->modify_now.function_keys : keyboard->modify_now.cursor_keys), &modify_parm); MODIFIER_PARM; unparseseq(xw, &reply); } else if (((kd.is_fkey || IsMiscFunctionKey(kd.keysym) || IsEditFunctionKey(xw, kd.keysym)) #if OPT_MOD_FKEYS && !ModifyOtherKeys(xw, evt_state, &kd, modify_parm) #endif ) || (kd.keysym == XK_Delete && ((modify_parm != 0) || !xtermDeleteIsDEL(xw)))) { dec_code = decfuncvalue(&kd); if ((evt_state & ShiftMask) #if OPT_SUNPC_KBD && keyboard->type == keyboardIsVT220 #endif && ((string = (Char *) udk_lookup(dec_code, &kd.nbytes)) != 0)) { UIntClr(evt_state, ShiftMask); while (kd.nbytes-- > 0) unparseputc(xw, CharOf(*string++)); } /* * Interpret F1-F4 as PF1-PF4 for VT52, VT100 */ else if (keyboard->type != keyboardIsLegacy && (dec_code >= 11 && dec_code <= 14)) { reply.a_type = ANSI_SS3; VT52_CURSOR_KEYS; reply.a_final = (Char) A2E(dec_code - 11 + E2A('P')); modifyCursorKey(&reply, keyboard->modify_now.function_keys, &modify_parm); MODIFIER_PARM; unparseseq(xw, &reply); } else { reply.a_type = ANSI_CSI; reply.a_final = 0; #ifdef XK_ISO_Left_Tab if (kd.keysym == XK_ISO_Left_Tab) { reply.a_nparam = 0; reply.a_final = 'Z'; #if OPT_MOD_FKEYS if (keyboard->modify_now.other_keys > 1 && computeMaskedModifier(xw, evt_state, ShiftMask)) modifyOtherKey(&reply, '\t', modify_parm, keyboard->format_keys); #endif } else #endif /* XK_ISO_Left_Tab */ { reply.a_nparam = 1; #if OPT_MOD_FKEYS if (kd.is_fkey) { modifyCursorKey(&reply, keyboard->modify_now.function_keys, &modify_parm); } MODIFIER_PARM; #endif reply.a_param[0] = (ParmType) dec_code; reply.a_final = '~'; } if (reply.a_final != 0 && (reply.a_nparam == 0 || reply.a_param[0] >= 0)) unparseseq(xw, &reply); } key = True; } else if (IsPFKey(kd.keysym)) { reply.a_type = ANSI_SS3; reply.a_final = (Char) ((kd.keysym - XK_KP_F1) + 'P'); VT52_CURSOR_KEYS; MODIFIER_PARM; unparseseq(xw, &reply); key = True; } else if (IsKeypadKey(kd.keysym)) { if (keypad_mode) { reply.a_type = ANSI_SS3; reply.a_final = (Char) (kypd_apl[kd.keysym - XK_KP_Space]); VT52_KEYPAD; MODIFIER_PARM; unparseseq(xw, &reply); } else { unparseputc(xw, kypd_num[kd.keysym - XK_KP_Space]); } key = True; } else if (IsCursorKey(kd.keysym)) { if (keyboard->flags & MODE_DECCKM) { reply.a_type = ANSI_SS3; } else { reply.a_type = ANSI_CSI; } modifyCursorKey(&reply, keyboard->modify_now.cursor_keys, &modify_parm); reply.a_final = (Char) (curfinal[kd.keysym - XK_Home]); VT52_CURSOR_KEYS; MODIFIER_PARM; unparseseq(xw, &reply); key = True; } else if (kd.nbytes > 0) { int prefix = 0; #if OPT_TEK4014 if (TEK4014_GIN(tekWidget)) { TekEnqMouse(tekWidget, kd.strbuf[0]); TekGINoff(tekWidget); kd.nbytes--; for (j = 0; j < kd.nbytes; ++j) { kd.strbuf[j] = kd.strbuf[j + 1]; } } #endif #if OPT_MOD_FKEYS if ((keyboard->modify_now.other_keys > 0) && ModifyOtherKeys(xw, evt_state, &kd, modify_parm) && (mod_state = allowedCharModifiers(xw, evt_state, &kd)) != 0) { int input_char; evt_state = mod_state; modify_parm = xtermStateToParam(xw, evt_state); /* * We want to show a keycode that corresponds to the 8-bit value * of the key. If the keysym is less than 256, that is good * enough. Special keys such as Tab may result in a value that * is usable as well. For the latter (special cases), try to use * the result from the X library lookup. */ input_char = ((kd.keysym < 256) ? (int) kd.keysym : ((kd.nbytes == 1) ? CharOf(kd.strbuf[0]) : -1)); TRACE(("...modifyOtherKeys %d;%d\n", modify_parm, input_char)); if (modifyOtherKey(&reply, input_char, modify_parm, keyboard->format_keys)) { unparseseq(xw, &reply); } else { Bell(xw, XkbBI_MinorError, 0); } } else #endif /* OPT_MOD_FKEYS */ { #if OPT_NUM_LOCK /* * Send ESC if we have a META modifier and metaSendsEcape is true. * Like eightBitInput, except that it is not associated with * terminal settings. */ if (kd.nbytes != 0) { if (screen->meta_sends_esc && (evt_state & xw->work.meta_mods) != 0) { TRACE(("...input-char is modified by META\n")); UIntClr(evt_state, xw->work.meta_mods); eightbit = False; prefix = ANSI_ESC; } else if (eightbit) { /* it might be overridden, but this helps for debugging */ TRACE(("...input-char is shifted by META\n")); } if (screen->alt_is_not_meta && (evt_state & xw->work.alt_mods) != 0) { UIntClr(evt_state, xw->work.alt_mods); if (screen->alt_sends_esc) { TRACE(("...input-char is modified by ALT\n")); eightbit = False; prefix = ANSI_ESC; } else if (!eightbit) { TRACE(("...input-char is shifted by ALT\n")); eightbit = True; } } } #endif /* * If metaSendsEscape is false, fall through to this chunk, which * implements the eightBitInput resource. * * It is normally executed when the user presses Meta plus a * printable key, e.g., Meta+space. The presence of the Meta * modifier is not guaranteed since what really happens is the * "insert-eight-bit" or "insert-seven-bit" action, which we * distinguish by the eightbit parameter to this function. So the * eightBitInput resource really means that we use this shifting * logic in the "insert-eight-bit" action. */ if (eightbit && (kd.nbytes == 1) && screen->input_eight_bits) { IChar ch = CharOf(kd.strbuf[0]); if ((ch < 128) && (screen->eight_bit_meta == ebTrue)) { kd.strbuf[0] |= (char) 0x80; TRACE(("...input shift from %d to %d (%#x to %#x)\n", ch, CharOf(kd.strbuf[0]), ch, CharOf(kd.strbuf[0]))); #if OPT_WIDE_CHARS if (screen->utf8_mode) { /* * We could interpret the incoming code as "in the * current locale", but it's simpler to treat it as * a Unicode value to translate to UTF-8. */ ch = CharOf(kd.strbuf[0]); kd.nbytes = 2; kd.strbuf[0] = (char) (0xc0 | ((ch >> 6) & 0x3)); kd.strbuf[1] = (char) (0x80 | (ch & 0x3f)); TRACE(("...encoded %#x in UTF-8 as %#x,%#x\n", ch, CharOf(kd.strbuf[0]), CharOf(kd.strbuf[1]))); } #endif } eightbit = False; } #if OPT_WIDE_CHARS if (kd.nbytes == 1) /* cannot do NRC on UTF-8, for instance */ #endif { /* VT220 & up: National Replacement Characters */ if ((xw->flags & NATIONAL) != 0) { unsigned cmp = xtermCharSetIn(screen, CharOf(kd.strbuf[0]), screen->keyboard_dialect[0]); TRACE(("...input NRC %d, %s %d\n", CharOf(kd.strbuf[0]), (CharOf(kd.strbuf[0]) == cmp) ? "unchanged" : "changed to", CharOf(cmp))); kd.strbuf[0] = (char) cmp; } else if (eightbit) { prefix = ANSI_ESC; } else if (kd.strbuf[0] == '?' && (evt_state & ControlMask) != 0) { kd.strbuf[0] = ANSI_DEL; UIntClr(evt_state, ControlMask); } } if (prefix != 0) unparseputc(xw, prefix); /* escape */ for (j = 0; j < kd.nbytes; ++j) unparseputc(xw, CharOf(kd.strbuf[j])); } key = ((kd.keysym != ANSI_XOFF) && (kd.keysym != ANSI_XON)); } unparse_end(xw); if (key && !TEK4014_ACTIVE(xw)) AdjustAfterInput(xw); xtermShowPointer(xw, False); return; } void StringInput(XtermWidget xw, const Char *string, size_t nbytes) { TRACE(("InputString (%s,%lu)\n", visibleChars(string, (unsigned) nbytes), (unsigned long) nbytes)); #if OPT_TEK4014 if (nbytes && TEK4014_GIN(tekWidget)) { TekEnqMouse(tekWidget, *string++); TekGINoff(tekWidget); nbytes--; } #endif while (nbytes-- != 0) unparseputc(xw, *string++); if (!TEK4014_ACTIVE(xw)) AdjustAfterInput(xw); unparse_end(xw); } /* These definitions are DEC-style (e.g., vt320) */ static int decfuncvalue(KEY_DATA * kd) { int result; if (kd->is_fkey) { switch (kd->keysym) { MAP(XK_Fn(1), 11); MAP(XK_Fn(2), 12); MAP(XK_Fn(3), 13); MAP(XK_Fn(4), 14); MAP(XK_Fn(5), 15); MAP(XK_Fn(6), 17); MAP(XK_Fn(7), 18); MAP(XK_Fn(8), 19); MAP(XK_Fn(9), 20); MAP(XK_Fn(10), 21); MAP(XK_Fn(11), 23); MAP(XK_Fn(12), 24); MAP(XK_Fn(13), 25); MAP(XK_Fn(14), 26); MAP(XK_Fn(15), 28); MAP(XK_Fn(16), 29); MAP(XK_Fn(17), 31); MAP(XK_Fn(18), 32); MAP(XK_Fn(19), 33); MAP(XK_Fn(20), 34); default: /* after F20 the codes are made up and do not correspond to any * real terminal. So they are simply numbered sequentially. */ result = 42 + (int) (kd->keysym - XK_Fn(21)); break; } } else { switch (kd->keysym) { MAP(XK_Find, 1); MAP(XK_Insert, 2); MAP(XK_Delete, 3); #ifdef XK_KP_Insert MAP(XK_KP_Insert, 2); MAP(XK_KP_Delete, 3); #endif #ifdef DXK_Remove MAP(DXK_Remove, 3); #endif MAP(XK_Select, 4); MAP(XK_Prior, 5); MAP(XK_Next, 6); #ifdef XK_ISO_Left_Tab MAP(XK_ISO_Left_Tab, 'Z'); #endif MAP(XK_Help, 28); MAP(XK_Menu, 29); default: result = -1; break; } } return result; } static void hpfuncvalue(ANSI *reply, KEY_DATA * kd) { #if OPT_HP_FUNC_KEYS int result; if (kd->is_fkey) { switch (kd->keysym) { MAP(XK_Fn(1), 'p'); MAP(XK_Fn(2), 'q'); MAP(XK_Fn(3), 'r'); MAP(XK_Fn(4), 's'); MAP(XK_Fn(5), 't'); MAP(XK_Fn(6), 'u'); MAP(XK_Fn(7), 'v'); MAP(XK_Fn(8), 'w'); default: result = -1; break; } } else { switch (kd->keysym) { MAP(XK_Up, 'A'); MAP(XK_Down, 'B'); MAP(XK_Right, 'C'); MAP(XK_Left, 'D'); MAP(XK_End, 'F'); MAP(XK_Clear, 'J'); MAP(XK_Delete, 'P'); MAP(XK_Insert, 'Q'); MAP(XK_Next, 'S'); MAP(XK_Prior, 'T'); MAP(XK_Home, 'h'); #ifdef XK_KP_Insert MAP(XK_KP_Delete, 'P'); MAP(XK_KP_Insert, 'Q'); #endif #ifdef DXK_Remove MAP(DXK_Remove, 'P'); #endif MAP(XK_Select, 'F'); MAP(XK_Find, 'h'); default: result = -1; break; } } if (result > 0) { reply->a_type = ANSI_ESC; reply->a_final = (Char) result; } #else (void) reply; (void) kd; #endif /* OPT_HP_FUNC_KEYS */ } static void scofuncvalue(ANSI *reply, KEY_DATA * kd) { #if OPT_SCO_FUNC_KEYS int result; if (kd->is_fkey) { switch (kd->keysym) { MAP(XK_Fn(1), 'M'); MAP(XK_Fn(2), 'N'); MAP(XK_Fn(3), 'O'); MAP(XK_Fn(4), 'P'); MAP(XK_Fn(5), 'Q'); MAP(XK_Fn(6), 'R'); MAP(XK_Fn(7), 'S'); MAP(XK_Fn(8), 'T'); MAP(XK_Fn(9), 'U'); MAP(XK_Fn(10), 'V'); MAP(XK_Fn(11), 'W'); MAP(XK_Fn(12), 'X'); MAP(XK_Fn(13), 'Y'); MAP(XK_Fn(14), 'Z'); MAP(XK_Fn(15), 'a'); MAP(XK_Fn(16), 'b'); MAP(XK_Fn(17), 'c'); MAP(XK_Fn(18), 'd'); MAP(XK_Fn(19), 'e'); MAP(XK_Fn(20), 'f'); MAP(XK_Fn(21), 'g'); MAP(XK_Fn(22), 'h'); MAP(XK_Fn(23), 'i'); MAP(XK_Fn(24), 'j'); MAP(XK_Fn(25), 'k'); MAP(XK_Fn(26), 'l'); MAP(XK_Fn(27), 'm'); MAP(XK_Fn(28), 'n'); MAP(XK_Fn(29), 'o'); MAP(XK_Fn(30), 'p'); MAP(XK_Fn(31), 'q'); MAP(XK_Fn(32), 'r'); MAP(XK_Fn(33), 's'); MAP(XK_Fn(34), 't'); MAP(XK_Fn(35), 'u'); MAP(XK_Fn(36), 'v'); MAP(XK_Fn(37), 'w'); MAP(XK_Fn(38), 'x'); MAP(XK_Fn(39), 'y'); MAP(XK_Fn(40), 'z'); MAP(XK_Fn(41), '@'); MAP(XK_Fn(42), '['); MAP(XK_Fn(43), '\\'); MAP(XK_Fn(44), ']'); MAP(XK_Fn(45), '^'); MAP(XK_Fn(46), '_'); MAP(XK_Fn(47), '`'); MAP(XK_Fn(48), '{'); /* no matching '}' */ default: result = -1; break; } } else { switch (kd->keysym) { MAP(XK_Up, 'A'); MAP(XK_Down, 'B'); MAP(XK_Right, 'C'); MAP(XK_Left, 'D'); MAP(XK_Begin, 'E'); MAP(XK_End, 'F'); MAP(XK_Insert, 'L'); MAP(XK_Next, 'G'); MAP(XK_Prior, 'I'); MAP(XK_Home, 'H'); #ifdef XK_KP_Insert MAP(XK_KP_Insert, 'L'); #endif default: result = -1; break; } } if (result > 0) { reply->a_type = ANSI_CSI; reply->a_final = (Char) result; } #else (void) reply; (void) kd; #endif /* OPT_SCO_FUNC_KEYS */ } static void sunfuncvalue(ANSI *reply, KEY_DATA * kd) { #if OPT_SUN_FUNC_KEYS ParmType result; if (kd->is_fkey) { switch (kd->keysym) { /* kf1-kf20 are numbered sequentially */ MAP(XK_Fn(1), 224); MAP(XK_Fn(2), 225); MAP(XK_Fn(3), 226); MAP(XK_Fn(4), 227); MAP(XK_Fn(5), 228); MAP(XK_Fn(6), 229); MAP(XK_Fn(7), 230); MAP(XK_Fn(8), 231); MAP(XK_Fn(9), 232); MAP(XK_Fn(10), 233); MAP(XK_Fn(11), 192); MAP(XK_Fn(12), 193); MAP(XK_Fn(13), 194); MAP(XK_Fn(14), 195); /* kund */ MAP(XK_Fn(15), 196); MAP(XK_Fn(16), 197); /* kcpy */ MAP(XK_Fn(17), 198); MAP(XK_Fn(18), 199); MAP(XK_Fn(19), 200); /* kfnd */ MAP(XK_Fn(20), 201); /* kf31-kf36 are numbered sequentially */ MAP(XK_Fn(21), 208); /* kf31 */ MAP(XK_Fn(22), 209); MAP(XK_Fn(23), 210); MAP(XK_Fn(24), 211); MAP(XK_Fn(25), 212); MAP(XK_Fn(26), 213); /* kf36 */ /* kf37-kf47 are interspersed with keypad keys */ MAP(XK_Fn(27), 214); /* khome */ MAP(XK_Fn(28), 215); /* kf38 */ MAP(XK_Fn(29), 216); /* kpp */ MAP(XK_Fn(30), 217); /* kf40 */ MAP(XK_Fn(31), 218); /* kb2 */ MAP(XK_Fn(32), 219); /* kf42 */ MAP(XK_Fn(33), 220); /* kend */ MAP(XK_Fn(34), 221); /* kf44 */ MAP(XK_Fn(35), 222); /* knp */ MAP(XK_Fn(36), 234); /* kf46 */ MAP(XK_Fn(37), 235); /* kf47 */ default: result = -1; break; } } else { switch (kd->keysym) { MAP(XK_Help, 196); /* khlp */ MAP(XK_Menu, 197); MAP(XK_Find, 1); MAP(XK_Insert, 2); /* kich1 */ MAP(XK_Delete, 3); #ifdef XK_KP_Insert MAP(XK_KP_Insert, 2); MAP(XK_KP_Delete, 3); #endif #ifdef DXK_Remove MAP(DXK_Remove, 3); #endif MAP(XK_Select, 4); MAP(XK_Prior, 216); MAP(XK_Next, 222); MAP(XK_Home, 214); MAP(XK_End, 220); MAP(XK_Begin, 218); /* kf41=kb2 */ default: result = -1; break; } } if (result > 0) { reply->a_type = ANSI_CSI; reply->a_nparam = 1; reply->a_param[0] = result; reply->a_final = 'z'; } else if (IsCursorKey(kd->keysym)) { reply->a_type = ANSI_SS3; reply->a_final = (Char) curfinal[kd->keysym - XK_Home]; } #else (void) reply; (void) kd; #endif /* OPT_SUN_FUNC_KEYS */ } #if OPT_NUM_LOCK #define isName(c) ((c) == '_' || (c) == '-' || isalnum(CharOf(c))) static const char * skipName(const char *s) { while (*s != '\0' && isName(CharOf(*s))) ++s; return s; } /* * Found a ":" in a translation, check what is past it to see if it contains * any of the insert-text action names. */ static Boolean keyCanInsert(const char *parse) { Boolean result = False; int ch; Boolean escape = False; Boolean quoted = False; static const char *table[] = { "insert", "insert-seven-bit", "insert-eight-bit", "string", }; Cardinal n; while (*parse != '\0' && *parse != '\n') { ch = CharOf(*parse++); if (escape) { escape = False; } else if (ch == '\\') { escape = True; } else if (ch == '"') { quoted = (Boolean) !quoted; } else if (!quoted && isName(ch)) { const char *next = skipName(--parse); size_t need = (size_t) (next - parse); for (n = 0; n < XtNumber(table); ++n) { if (need == strlen(table[n]) && !strncmp(parse, table[n], need)) { result = True; break; } } parse = next; } } return result; } /* * Strip the entire action, to avoid matching it. */ static char * stripAction(char *base, char *last) { while (last != base) { if (*--last == '\n') { break; } } return last; } static char * stripBlanks(char *base, char *last) { while (last != base) { int ch = CharOf(last[-1]); if (ch != ' ' && ch != '\t') break; --last; } return last; } /* * Strip unneeded whitespace from a translations resource, mono-casing and * returning a malloc'd copy of the result. */ static char * stripTranslations(const char *s, Bool onlyInsert) { char *dst = 0; if (s != 0) { dst = TypeMallocN(char, strlen(s) + 1); if (dst != 0) { int state = 0; int ch = 0; int prv = 0; char *d = dst; TRACE(("stripping:\n%s\n", s)); while (*s != '\0') { ch = *s++; if (ch == '\n') { if (d != dst) *d++ = (char) ch; state = 0; } else if (strchr(":!#", ch) != 0) { d = stripBlanks(dst, d); if (onlyInsert && (ch == ':') && !keyCanInsert(s)) { d = stripAction(dst, d); } state = -1; } else if (state >= 0) { if (isspace(CharOf(ch))) { if (state == 0 || strchr("<>~ \t", prv)) continue; } else if (strchr("<>~", ch)) { d = stripBlanks(dst, d); } *d++ = x_toupper(ch); ++state; } prv = ch; } *d = '\0'; TRACE(("...result:\n%s\n", dst)); } } return dst; } /* * Make a simple check to see if a given translations keyword appears in * xterm's translations resource. It does not attempt to parse the strings, * just makes a case-independent check and ensures that the ends of the match * are on token-boundaries. * * That this can only retrieve translations that are given as resource values; * the default translations in charproc.c for example are not retrievable by * any interface to X. * * Also: We can retrieve only the most-specified translation resource. For * example, if the resource file specifies both "*translations" and * "XTerm*translations", we see only the latter. */ static Bool TranslationsUseKeyword(Widget w, char **cache, const char *keyword, Bool onlyInsert) { static String data; static XtResource key_resources[] = { {XtNtranslations, XtCTranslations, XtRString, sizeof(data), 0, XtRString, (XtPointer) NULL} }; Bool result = False; char *copy; char *test; if ((test = stripTranslations(keyword, onlyInsert)) != 0) { if (*cache == 0) { XtGetSubresources(w, (XtPointer) &data, "vt100", "VT100", key_resources, XtNumber(key_resources), NULL, (Cardinal) 0); if (data != 0 && (copy = stripTranslations(data, onlyInsert)) != 0) { *cache = copy; } } if (*cache != 0) { char *p = *cache; int state = 0; int now = ' ', prv; while (*p != 0) { prv = now; now = *p++; if (now == ':' || now == '!') { state = -1; } else if (now == '\n') { state = 0; } else if (state >= 0) { if (now == test[state]) { if ((state != 0 || !isName(prv)) && ((test[++state] == 0) && !isName(*p))) { result = True; break; } } else { state = 0; } } } } free(test); } TRACE(("TranslationsUseKeyword(%p, %s) = %d\n", (void *) w, keyword, result)); return result; } static Bool xtermHasTranslation(XtermWidget xw, const char *keyword, Bool onlyInsert) { return (TranslationsUseKeyword(SHELL_OF(xw), &(xw->keyboard.shell_translations), keyword, onlyInsert) || TranslationsUseKeyword((Widget) xw, &(xw->keyboard.xterm_translations), keyword, onlyInsert)); } #if OPT_EXTRA_PASTE static void addTranslation(XtermWidget xw, const char *fromString, const char *toString) { size_t have = (xw->keyboard.extra_translations ? strlen(xw->keyboard.extra_translations) : 0); size_t need = (((have != 0) ? (have + 4) : 0) + strlen(fromString) + strlen(toString) + 6); if (!xtermHasTranslation(xw, fromString, False)) { xw->keyboard.extra_translations = TypeRealloc(char, need, xw->keyboard.extra_translations); if ((xw->keyboard.extra_translations) != 0) { TRACE(("adding %s: %s\n", fromString, toString)); if (have) strcat(xw->keyboard.extra_translations, " \\n\\"); sprintf(xw->keyboard.extra_translations, "%s: %s", fromString, toString); TRACE(("...{%s}\n", xw->keyboard.extra_translations)); } } } #endif #define SaveMask(name) xw->work.name |= (unsigned) mask;\ TRACE(("SaveMask(%#x -> %s) %#x (%#x is%s modifier)\n", \ (unsigned) keysym, #name, \ xw->work.name, (unsigned) mask, \ ModifierName((unsigned) mask))); /* * Determine which modifier mask (if any) applies to the Num_Lock keysym. * * Also, determine which modifiers are associated with the ALT keys, so we can * send that information as a parameter for special keys in Sun/PC keyboard * mode. However, if the ALT modifier is used in translations, we do not want * to confuse things by sending the parameter. */ void VTInitModifiers(XtermWidget xw) { Display *dpy = XtDisplay(xw); XModifierKeymap *keymap = XGetModifierMapping(dpy); int i, j, k, l; KeySym keysym; unsigned long mask; int min_keycode, max_keycode, keysyms_per_keycode = 0; if (keymap != 0) { KeySym *theMap; int keycode_count; TRACE(("VTInitModifiers\n")); XDisplayKeycodes(dpy, &min_keycode, &max_keycode); keycode_count = (max_keycode - min_keycode + 1); theMap = XGetKeyboardMapping(dpy, (KeyCode) min_keycode, keycode_count, &keysyms_per_keycode); if (theMap != 0) { #if OPT_EXTRA_PASTE /* * Assume that if we can find the paste keysym in the X keyboard * mapping that the server allows the corresponding translations * resource. */ int limit = (max_keycode - min_keycode) * keysyms_per_keycode; for (i = 0; i < limit; ++i) { #ifdef XF86XK_Paste if (theMap[i] == XF86XK_Paste) { TRACE(("keyboard has XF86XK_Paste\n")); addTranslation(xw, " XF86Paste", "insert-selection(SELECT, CUT_BUFFER0)"); } #endif #ifdef SunXK_Paste if (theMap[i] == SunXK_Paste) { TRACE(("keyboard has SunXK_Paste\n")); addTranslation(xw, " SunPaste", "insert-selection(SELECT, CUT_BUFFER0)"); } #endif } #endif /* OPT_EXTRA_PASTE */ for (i = k = 0, mask = 1; i < 8; i++, mask <<= 1) { for (j = 0; j < keymap->max_keypermod; j++) { KeyCode code = keymap->modifiermap[k++]; if (code == 0) continue; for (l = 0; l < keysyms_per_keycode; ++l) { #ifdef HAVE_XKBKEYCODETOKEYSYM keysym = XkbKeycodeToKeysym(dpy, code, 0, l); #else keysym = XKeycodeToKeysym(dpy, code, l); #endif if (keysym == NoSymbol) { /* EMPTY */ ; } else if (keysym == XK_Num_Lock) { SaveMask(num_lock); } else if (keysym == XK_Alt_L || keysym == XK_Alt_R) { SaveMask(alt_mods); } else if (keysym == XK_Meta_L || keysym == XK_Meta_R) { SaveMask(meta_mods); } } } } XFree(theMap); } /* Don't disable any mods if "alwaysUseMods" is true. */ if (!xw->misc.alwaysUseMods) { /* * Force TranslationsUseKeyword() to reload. */ if (xw->keyboard.shell_translations) { free(xw->keyboard.shell_translations); xw->keyboard.shell_translations = 0; } if (xw->keyboard.xterm_translations) { free(xw->keyboard.xterm_translations); xw->keyboard.xterm_translations = 0; } /* * If the Alt modifier is used in translations, we would rather not * use it to modify function-keys when NumLock is active. */ if ((xw->work.alt_mods != 0) && xtermHasTranslation(xw, "alt", True)) { TRACE(("ALT is used as a modifier in translations (ignore mask)\n")); xw->work.alt_mods = 0; } /* * If the Meta modifier is used in translations, we would rather not * use it to modify function-keys. */ if ((xw->work.meta_mods != 0) && xtermHasTranslation(xw, "meta", True)) { TRACE(("META is used as a modifier in translations\n")); xw->work.meta_mods = 0; } } XFreeModifiermap(keymap); } } #endif /* OPT_NUM_LOCK */