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