1/*
2 * tkWinKey.c --
3 *
4 *	This file contains X emulation routines for keyboard related
5 *	functions.
6 *
7 * Copyright (c) 1995 Sun Microsystems, Inc.
8 *
9 * See the file "license.terms" for information on usage and redistribution
10 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11 *
12 * RCS: @(#) $Id: tkWinKey.c,v 1.14.4.1 2006/08/30 21:53:46 hobbs Exp $
13 */
14
15#include "tkWinInt.h"
16/*
17 * The keymap table holds mappings of Windows keycodes to X keysyms.
18 * If Windows ever comes along and changes the value of their keycodes,
19 * this will break all kinds of things.  However, this table lookup is much
20 * faster than the alternative, in which we walked a list of keycodes looking
21 * for a match.  Since this lookup is performed for every Windows keypress
22 * event, it seems like a worthwhile improvement to use the table.
23 */
24#define MAX_KEYCODE 145 /* VK_SCROLL is the last entry in our table below */
25static KeySym keymap[] = {
26    NoSymbol, NoSymbol, NoSymbol, XK_Cancel, NoSymbol,
27	NoSymbol, NoSymbol, NoSymbol, XK_BackSpace, XK_Tab,
28	NoSymbol, NoSymbol, XK_Clear, XK_Return, NoSymbol,
29	NoSymbol, XK_Shift_L, XK_Control_L, XK_Alt_L, XK_Pause,
30	XK_Caps_Lock, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
31	NoSymbol, NoSymbol, XK_Escape, NoSymbol, NoSymbol,
32	NoSymbol, NoSymbol, XK_space, XK_Prior, XK_Next,
33	XK_End, XK_Home, XK_Left, XK_Up, XK_Right,
34	XK_Down, XK_Select, XK_Print, XK_Execute, NoSymbol,
35	XK_Insert, XK_Delete, XK_Help, NoSymbol, NoSymbol,
36	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
37	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
38	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
39	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
40	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
41	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
42	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
43	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
44	NoSymbol, XK_Win_L, XK_Win_R, XK_App, NoSymbol,
45	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
46	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
47	NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol,
48	NoSymbol, NoSymbol, XK_F1, XK_F2, XK_F3,
49	XK_F4, XK_F5, XK_F6, XK_F7, XK_F8,
50	XK_F9, XK_F10, XK_F11, XK_F12, XK_F13,
51	XK_F14, XK_F15, XK_F16, XK_F17, XK_F18,
52	XK_F19,	XK_F20, XK_F21, XK_F22, XK_F23,
53	XK_F24,	NoSymbol, NoSymbol, NoSymbol, NoSymbol,
54	NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_Num_Lock,
55	XK_Scroll_Lock
56};
57
58/*
59 * Prototypes for local procedures defined in this file:
60 */
61
62static KeySym		KeycodeToKeysym _ANSI_ARGS_((unsigned int keycode,
63			    int state, int noascii));
64
65/*
66 *----------------------------------------------------------------------
67 *
68 * TkpGetString --
69 *
70 *	Retrieve the UTF string equivalent for the given keyboard event.
71 *
72 * Results:
73 *	Returns the UTF string.
74 *
75 * Side effects:
76 *	None.
77 *
78 *----------------------------------------------------------------------
79 */
80
81char *
82TkpGetString(winPtr, eventPtr, dsPtr)
83    TkWindow *winPtr;		/* Window where event occurred:  needed to
84				 * get input context. */
85    XEvent *eventPtr;		/* X keyboard event. */
86    Tcl_DString *dsPtr;		/* Uninitialized or empty string to hold
87				 * result. */
88{
89    KeySym keysym;
90    XKeyEvent* keyEv = &eventPtr->xkey;
91
92    Tcl_DStringInit(dsPtr);
93    if (eventPtr->xkey.send_event == -1) {
94        if (eventPtr->xkey.nbytes > 0) {
95	    Tcl_ExternalToUtfDString(TkWinGetKeyInputEncoding(),
96                    eventPtr->xkey.trans_chars, eventPtr->xkey.nbytes, dsPtr);
97        }
98    } else if (eventPtr->xkey.send_event == -2) {
99        /*
100         * Special case for win2000 multi-lingal IME input.
101         * xkey.trans_chars[] already contains a UNICODE char.
102         */
103
104        int unichar;
105        char buf[TCL_UTF_MAX];
106        int len;
107
108        unichar = (eventPtr->xkey.trans_chars[1] & 0xff);
109        unichar <<= 8;
110        unichar |= (eventPtr->xkey.trans_chars[0] & 0xff);
111
112        len = Tcl_UniCharToUtf((Tcl_UniChar) unichar, buf);
113
114        Tcl_DStringAppend(dsPtr, buf, len);
115    } else if (eventPtr->xkey.send_event == -3) {
116	/*
117	 * Special case for WM_UNICHAR.
118	 * xkey.trans_chars[] already contains a UTF-8 char.
119	 */
120	Tcl_DStringAppend(dsPtr, eventPtr->xkey.trans_chars,
121		eventPtr->xkey.nbytes);
122    } else  {
123	/*
124	 * This is an event generated from generic code.  It has no
125	 * nchars or trans_chars members.
126	 */
127
128	keysym = KeycodeToKeysym(eventPtr->xkey.keycode,
129		eventPtr->xkey.state, 0);
130	if (((keysym != NoSymbol) && (keysym > 0) && (keysym < 256))
131		|| (keysym == XK_Return)
132		|| (keysym == XK_Tab)) {
133	    char buf[TCL_UTF_MAX];
134	    int len = Tcl_UniCharToUtf((Tcl_UniChar) (keysym & 255), buf);
135	    Tcl_DStringAppend(dsPtr, buf, len);
136	}
137    }
138    return Tcl_DStringValue(dsPtr);
139}
140
141/*
142 *----------------------------------------------------------------------
143 *
144 * XKeycodeToKeysym --
145 *
146 *	Translate from a system-dependent keycode to a
147 *	system-independent keysym.
148 *
149 * Results:
150 *	Returns the translated keysym, or NoSymbol on failure.
151 *
152 * Side effects:
153 *	None.
154 *
155 *----------------------------------------------------------------------
156 */
157
158KeySym
159XKeycodeToKeysym(display, keycode, index)
160    Display* display;
161    unsigned int keycode;
162    int index;
163{
164    int state = 0;
165
166    if (index & 0x01) {
167	state |= ShiftMask;
168    }
169    return KeycodeToKeysym(keycode, state, 0);
170}
171
172
173
174/*
175 *----------------------------------------------------------------------
176 *
177 * KeycodeToKeysym --
178 *
179 *	Translate from a system-dependent keycode to a
180 *	system-independent keysym.
181 *
182 * Results:
183 *	Returns the translated keysym, or NoSymbol on failure.
184 *
185 * Side effects:
186 *	It may affect the internal state of the keyboard, such as
187 *      remembered dead key or lock indicator lamps.
188 *
189 *----------------------------------------------------------------------
190 */
191
192static KeySym
193KeycodeToKeysym(keycode, state, noascii)
194    unsigned int keycode;
195    int state;
196    int noascii;
197{
198    BYTE keys[256];
199    int result, deadkey, shift;
200    char buf[4];
201    unsigned int scancode = MapVirtualKey(keycode, 0);
202
203    /*
204     * Do not run keycodes of lock keys through ToAscii().
205     * One of ToAscii()'s side effects is to handle the lights
206     * on the keyboard, and we don't want to mess that up.
207     */
208
209    if (noascii || keycode == VK_CAPITAL || keycode == VK_SCROLL ||
210	    keycode == VK_NUMLOCK)
211        goto skipToAscii;
212
213    /*
214     * Use MapVirtualKey() to detect some dead keys.
215     */
216
217    if (MapVirtualKey(keycode, 2) > 0x7fffUL)
218        return XK_Multi_key;
219
220    /*
221     * Set up a keyboard with correct modifiers
222     */
223
224    memset(keys, 0, 256);
225    if (state & ShiftMask)
226        keys[VK_SHIFT] = 0x80;
227    if (state & ControlMask)
228	keys[VK_CONTROL] = 0x80;
229    if (state & Mod2Mask)
230	keys[VK_MENU] = 0x80;
231
232    /*
233     * Make sure all lock button info is correct so we don't mess up the
234     * lights
235     */
236
237    if (state & LockMask)
238	keys[VK_CAPITAL] = 1;
239    if (state & Mod3Mask)
240	keys[VK_SCROLL] = 1;
241    if (state & Mod1Mask)
242	keys[VK_NUMLOCK] = 1;
243
244    result = ToAscii(keycode, scancode, keys, (LPWORD) buf, 0);
245
246    if (result < 0) {
247        /*
248         * Win95/98:
249         * This was a dead char, which is now remembered by the keyboard.
250         * Call ToAscii() again to forget it.
251         * WinNT:
252         * This was a dead char, overwriting any previously remembered
253         * key. Calling ToAscii() again does not affect anything.
254         */
255
256        ToAscii(keycode, scancode, keys, (LPWORD) buf, 0);
257        return XK_Multi_key;
258    }
259    if (result == 2) {
260        /*
261         * This was a dead char, and there were one previously remembered
262         * by the keyboard.
263         * Call ToAscii() again with proper parameters to restore it.
264         */
265
266        /*
267	 * Get information about the old char
268	 */
269
270        deadkey = VkKeyScan(buf[0]);
271        shift = deadkey >> 8;
272        deadkey &= 255;
273        scancode = MapVirtualKey(deadkey, 0);
274
275        /*
276	 * Set up a keyboard with proper modifier keys
277	 */
278
279        memset(keys, 0, 256);
280        if (shift & 1)
281            keys[VK_SHIFT] = 0x80;
282        if (shift & 2)
283            keys[VK_CONTROL] = 0x80;
284        if (shift & 4)
285            keys[VK_MENU] = 0x80;
286        ToAscii(deadkey, scancode, keys, (LPWORD) buf, 0);
287        return XK_Multi_key;
288    }
289
290    /*
291     * Keycode mapped to a valid Latin-1 character.  Since the keysyms
292     * for alphanumeric characters map onto Latin-1, we just return it.
293     *
294     * We treat 0x7F as a special case mostly for backwards compatibility.
295     * In versions of Tk<=8.2, Control-Backspace returned "XK_BackSpace"
296     * as the X Keysym.  This was due to the fact that we did not
297     * initialize the keys array properly when we passed it to ToAscii, above.
298     * We had previously not been setting the state bit for the Control key.
299     * When we fixed that, we found that Control-Backspace on Windows is
300     * interpreted as ASCII-127 (0x7F), which corresponds to the Delete key.
301     *
302     * Upon discovering this, we realized we had two choices:  return XK_Delete
303     * or return XK_BackSpace.  If we returned XK_Delete, that could be
304     * considered "more correct" (although the correctness would be dependant
305     * on whether you believe that ToAscii is doing the right thing in that
306     * case); however, this would break backwards compatibility, and worse,
307     * it would limit application programmers -- they would effectively be
308     * unable to bind to <Control-Backspace> on Windows.  We therefore chose
309     * instead to return XK_BackSpace (handled here by letting the code
310     * "fall-through" to the return statement below, which works because the
311     * keycode for this event is VK_BACKSPACE, and the keymap table maps that
312     * keycode to XK_BackSpace).
313     */
314
315    if (result == 1 && UCHAR(buf[0]) >= 0x20 && UCHAR(buf[0]) != 0x7F) {
316	return (KeySym) UCHAR(buf[0]);
317    }
318
319    /*
320     * Keycode is a non-alphanumeric key, so we have to do the lookup.
321     */
322
323    skipToAscii:
324    if (keycode < 0 || keycode > MAX_KEYCODE) {
325	return NoSymbol;
326    }
327    switch (keycode) {
328	/*
329	 * Windows only gives us an undifferentiated VK_CONTROL
330	 * code (for example) when either Control key is pressed.
331	 * To distinguish between left and right, we have to query the
332	 * state of one of the two to determine which was actually
333	 * pressed.  So if the keycode indicates Control, Shift, or Menu
334	 * (the key that everybody else calls Alt), do this extra test.
335	 * If the right-side key was pressed, return the appropriate
336	 * keycode.  Otherwise, we fall through and rely on the
337	 * keymap table to hold the correct keysym value.
338	 */
339	case VK_CONTROL: {
340	    if (GetKeyState(VK_RCONTROL) & 0x80) {
341		return XK_Control_R;
342	    }
343	    break;
344	}
345	case VK_SHIFT: {
346	    if (GetKeyState(VK_RSHIFT) & 0x80) {
347		return XK_Shift_R;
348	    }
349	    break;
350	}
351	case VK_MENU: {
352	    if (GetKeyState(VK_RMENU) & 0x80) {
353		return XK_Alt_R;
354	    }
355	    break;
356	}
357    }
358    return keymap[keycode];
359}
360
361
362/*
363 *----------------------------------------------------------------------
364 *
365 * TkpGetKeySym --
366 *
367 *	Given an X KeyPress or KeyRelease event, map the
368 *	keycode in the event into a KeySym.
369 *
370 * Results:
371 *	The return value is the KeySym corresponding to
372 *	eventPtr, or NoSymbol if no matching Keysym could be
373 *	found.
374 *
375 * Side effects:
376 *	In the first call for a given display, keycode-to-
377 *	KeySym maps get loaded.
378 *
379 *----------------------------------------------------------------------
380 */
381
382KeySym
383TkpGetKeySym(dispPtr, eventPtr)
384    TkDisplay *dispPtr;		/* Display in which to map keycode. */
385    XEvent *eventPtr;		/* Description of X event. */
386{
387    KeySym sym;
388    int state = eventPtr->xkey.state;
389
390    /*
391     * Refresh the mapping information if it's stale
392     */
393
394    if (dispPtr->bindInfoStale) {
395	TkpInitKeymapInfo(dispPtr);
396    }
397
398    sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0);
399
400    /*
401     * Special handling: if this is a ctrl-alt or shifted key, and there
402     * is no keysym defined, try without the modifiers.
403     */
404
405    if ((sym == NoSymbol) && ((state & ControlMask) || (state & Mod2Mask))) {
406        state &=  ~(ControlMask | Mod2Mask);
407        sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0);
408    }
409    if ((sym == NoSymbol) && (state & ShiftMask)) {
410        state &=  ~ShiftMask;
411        sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0);
412    }
413    return sym;
414}
415
416/*
417 *--------------------------------------------------------------
418 *
419 * TkpInitKeymapInfo --
420 *
421 *	This procedure is invoked to scan keymap information
422 *	to recompute stuff that's important for binding, such
423 *	as the modifier key (if any) that corresponds to "mode
424 *	switch".
425 *
426 * Results:
427 *	None.
428 *
429 * Side effects:
430 *	Keymap-related information in dispPtr is updated.
431 *
432 *--------------------------------------------------------------
433 */
434
435void
436TkpInitKeymapInfo(dispPtr)
437    TkDisplay *dispPtr;		/* Display for which to recompute keymap
438				 * information. */
439{
440    XModifierKeymap *modMapPtr;
441    KeyCode *codePtr;
442    KeySym keysym;
443    int count, i, j, max, arraySize;
444#define KEYCODE_ARRAY_SIZE 20
445
446    dispPtr->bindInfoStale = 0;
447    modMapPtr = XGetModifierMapping(dispPtr->display);
448
449    /*
450     * Check the keycodes associated with the Lock modifier.  If
451     * any of them is associated with the XK_Shift_Lock modifier,
452     * then Lock has to be interpreted as Shift Lock, not Caps Lock.
453     */
454
455    dispPtr->lockUsage = LU_IGNORE;
456    codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex;
457    for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) {
458	if (*codePtr == 0) {
459	    continue;
460	}
461	keysym = KeycodeToKeysym(*codePtr, 0, 1);
462	if (keysym == XK_Shift_Lock) {
463	    dispPtr->lockUsage = LU_SHIFT;
464	    break;
465	}
466	if (keysym == XK_Caps_Lock) {
467	    dispPtr->lockUsage = LU_CAPS;
468	    break;
469	}
470    }
471
472    /*
473     * Look through the keycodes associated with modifiers to see if
474     * the the "mode switch", "meta", or "alt" keysyms are associated
475     * with any modifiers.  If so, remember their modifier mask bits.
476     */
477
478    dispPtr->modeModMask = 0;
479    dispPtr->metaModMask = 0;
480    dispPtr->altModMask = 0;
481    codePtr = modMapPtr->modifiermap;
482    max = 8*modMapPtr->max_keypermod;
483    for (i = 0; i < max; i++, codePtr++) {
484	if (*codePtr == 0) {
485	    continue;
486	}
487	keysym = KeycodeToKeysym(*codePtr, 0, 1);
488	if (keysym == XK_Mode_switch) {
489	    dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
490	}
491	if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {
492	    dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
493	}
494	if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {
495	    dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
496	}
497    }
498
499    /*
500     * Create an array of the keycodes for all modifier keys.
501     */
502
503    if (dispPtr->modKeyCodes != NULL) {
504	ckfree((char *) dispPtr->modKeyCodes);
505    }
506    dispPtr->numModKeyCodes = 0;
507    arraySize = KEYCODE_ARRAY_SIZE;
508    dispPtr->modKeyCodes = (KeyCode *) ckalloc((unsigned)
509	    (KEYCODE_ARRAY_SIZE * sizeof(KeyCode)));
510    for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) {
511	if (*codePtr == 0) {
512	    continue;
513	}
514
515	/*
516	 * Make sure that the keycode isn't already in the array.
517	 */
518
519	for (j = 0; j < dispPtr->numModKeyCodes; j++) {
520	    if (dispPtr->modKeyCodes[j] == *codePtr) {
521		goto nextModCode;
522	    }
523	}
524	if (dispPtr->numModKeyCodes >= arraySize) {
525	    KeyCode *new;
526
527	    /*
528	     * Ran out of space in the array;  grow it.
529	     */
530
531	    arraySize *= 2;
532	    new = (KeyCode *) ckalloc((unsigned)
533		    (arraySize * sizeof(KeyCode)));
534	    memcpy((VOID *) new, (VOID *) dispPtr->modKeyCodes,
535		    (dispPtr->numModKeyCodes * sizeof(KeyCode)));
536	    ckfree((char *) dispPtr->modKeyCodes);
537	    dispPtr->modKeyCodes = new;
538	}
539	dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr;
540	dispPtr->numModKeyCodes++;
541	nextModCode: continue;
542    }
543    XFreeModifiermap(modMapPtr);
544}
545
546/*
547 * When mapping from a keysym to a keycode, need
548 * information about the modifier state that should be used
549 * so that when they call XKeycodeToKeysym taking into
550 * account the xkey.state, they will get back the original
551 * keysym.
552 */
553
554void
555TkpSetKeycodeAndState(tkwin, keySym, eventPtr)
556    Tk_Window tkwin;
557    KeySym keySym;
558    XEvent *eventPtr;
559{
560    int i;
561    SHORT result;
562    int shift;
563
564    eventPtr->xkey.keycode = 0;
565    if (keySym == NoSymbol) {
566        return;
567    }
568
569    /*
570     * We check our private map first for a virtual keycode,
571     * as VkKeyScan will return values that don't map to X
572     * for the "extended" Syms.  This may be due to just casting
573     * problems below, but this works.
574     */
575    for (i = 0; i <= MAX_KEYCODE; i++) {
576	if (keymap[i] == keySym) {
577            eventPtr->xkey.keycode = i;
578            return;
579	}
580    }
581    if (keySym >= 0x20) {
582	result = VkKeyScan((char) keySym);
583	if (result != -1) {
584            shift = result >> 8;
585            if (shift & 1)
586                eventPtr->xkey.state |= ShiftMask;
587            if (shift & 2)
588                eventPtr->xkey.state |= ControlMask;
589            if (shift & 4)
590                eventPtr->xkey.state |= Mod2Mask;
591            eventPtr->xkey.keycode = (KeyCode) (result & 0xff);
592	}
593    }
594}
595
596/*
597 *----------------------------------------------------------------------
598 *
599 * XKeysymToKeycode --
600 *
601 *	Translate a keysym back into a keycode.
602 *
603 * Results:
604 *	Returns the keycode that would generate the specified keysym.
605 *
606 * Side effects:
607 *	None.
608 *
609 *----------------------------------------------------------------------
610 */
611
612KeyCode
613XKeysymToKeycode(display, keysym)
614    Display* display;
615    KeySym keysym;
616{
617    int i;
618    SHORT result;
619
620    /*
621     * We check our private map first for a virtual keycode,
622     * as VkKeyScan will return values that don't map to X
623     * for the "extended" Syms.  This may be due to just casting
624     * problems below, but this works.
625     */
626    if (keysym == NoSymbol) {
627	return 0;
628    }
629    for (i = 0; i <= MAX_KEYCODE; i++) {
630	if (keymap[i] == keysym) {
631	    return ((KeyCode) i);
632	}
633    }
634    if (keysym >= 0x20) {
635	result = VkKeyScan((char) keysym);
636	if (result != -1) {
637	    return (KeyCode) (result & 0xff);
638	}
639    }
640
641    return 0;
642}
643
644/*
645 *----------------------------------------------------------------------
646 *
647 * XGetModifierMapping --
648 *
649 *	Fetch the current keycodes used as modifiers.
650 *
651 * Results:
652 *	Returns a new modifier map.
653 *
654 * Side effects:
655 *	Allocates a new modifier map data structure.
656 *
657 *----------------------------------------------------------------------
658 */
659
660XModifierKeymap	*
661XGetModifierMapping(display)
662    Display* display;
663{
664    XModifierKeymap *map = (XModifierKeymap *)ckalloc(sizeof(XModifierKeymap));
665
666    map->max_keypermod = 1;
667    map->modifiermap = (KeyCode *) ckalloc(sizeof(KeyCode)*8);
668    map->modifiermap[ShiftMapIndex] = VK_SHIFT;
669    map->modifiermap[LockMapIndex] = VK_CAPITAL;
670    map->modifiermap[ControlMapIndex] = VK_CONTROL;
671    map->modifiermap[Mod1MapIndex] = VK_NUMLOCK;
672    map->modifiermap[Mod2MapIndex] = VK_MENU;
673    map->modifiermap[Mod3MapIndex] = VK_SCROLL;
674    map->modifiermap[Mod4MapIndex] = 0;
675    map->modifiermap[Mod5MapIndex] = 0;
676    return map;
677}
678
679/*
680 *----------------------------------------------------------------------
681 *
682 * XFreeModifiermap --
683 *
684 *	Deallocate a modifier map that was created by
685 *	XGetModifierMapping.
686 *
687 * Results:
688 *	None.
689 *
690 * Side effects:
691 *	Frees the datastructure referenced by modmap.
692 *
693 *----------------------------------------------------------------------
694 */
695
696void
697XFreeModifiermap(modmap)
698    XModifierKeymap* modmap;
699{
700    ckfree((char *) modmap->modifiermap);
701    ckfree((char *) modmap);
702}
703
704/*
705 *----------------------------------------------------------------------
706 *
707 * XStringToKeysym --
708 *
709 *	Translate a keysym name to the matching keysym.
710 *
711 * Results:
712 *	Returns the keysym.  Since this is already handled by
713 *	Tk's StringToKeysym function, we just return NoSymbol.
714 *
715 * Side effects:
716 *	None.
717 *
718 *----------------------------------------------------------------------
719 */
720
721KeySym
722XStringToKeysym(string)
723    _Xconst char *string;
724{
725    return NoSymbol;
726}
727
728/*
729 *----------------------------------------------------------------------
730 *
731 * XKeysymToString --
732 *
733 *	Convert a keysym to character form.
734 *
735 * Results:
736 *	Returns NULL, since Tk will have handled this already.
737 *
738 * Side effects:
739 *	None.
740 *
741 *----------------------------------------------------------------------
742 */
743
744char *
745XKeysymToString(keysym)
746    KeySym keysym;
747{
748    return NULL;
749}
750