root/foundation-apps/mxterm-maxx/input.c

Revision 8, 50.8 KB (checked in by emasson, 3 years ago)

initial import for the community edition

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