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