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