1 /**
2  *
3  * $Id: VirtKeys.c,v 1.1 2004/08/28 19:22:46 dannybackx Exp $
4  *
5  * Copyright (C) 1995 Free Software Foundation, Inc.
6  * Copyright (C) 1995-2001 LessTif Development Team
7  *
8  * This file is part of the GNU LessTif Library.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the Free
22  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  **/
25 
26 static const char rcsid[] = "$Id: VirtKeys.c,v 1.1 2004/08/28 19:22:46 dannybackx Exp $";
27 
28 #include <LTconfig.h>
29 
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 
34 #include <X11/keysym.h>
35 
36 #include <XmI/XmI.h>
37 #include <Xm/XmP.h>
38 #include <Xm/AtomMgr.h>
39 #include <Xm/DisplayP.h>
40 #include <Xm/MwmUtil.h>
41 #include <Xm/VirtKeysP.h>
42 #include <Xm/TransltnsP.h>
43 
44 #include <Xm/XmosP.h>
45 
46 #include <XmI/DebugUtil.h>
47 
48 
49 /*
50  * Order of binding precedences:
51  *  1) defaultVirtualBindings resource
52  *  2) _MOTIF_BINDINGS property on root window
53  *  3) _MOTIF_DEFAULT_BINDINGS property on root window
54  *  4) .motifbind in $(HOME)
55  *  5) xmbind.alias in $(HOME)
56  *     look for "<VENDOR> <VERSION>"pathname, and load that file
57  *  6) if all else fails, look for xmbind.alias in XMBINDDIR,
58  *     or /usr/lib/Xm/bindings if XMBINDDIR is not set.
59  *  7) otherwise, load fallback bindings.
60  * For 4) till 7) put the bindings in the _MOTIF_BINDINGS property on
61  * the root window.
62  */
63 
64 /*
65  * The properties we have to deal with...
66  */
67 #define DEFAULT_BINDINGS_PROPERTY_NAME _XA_MOTIF_DEFAULT_BINDINGS
68 #define BINDINGS_PROPERTY_NAME         _XA_MOTIF_BINDINGS
69 
70 
71 XmConst char _XmVirtKeys_hpFallbackBindingString[] = "\
72 osfAddMode    : Shift<Key>F8   \n\
73 osfBackSpace  : <Key>BackSpace \n\
74 osfBeginLine  : <Key>Home      \n\
75 osfCancel     : <Key>Escape    \n\
76 osfClear      : <Key>Clear     \n\
77 osfDelete     : <Key>DeleteChar\n\
78 osfEndLine    : <Key>F7        \n\
79 osfHelp       : <Key>F1        \n\
80 osfInsert     : <Key>InsertChar\n\
81 osfLeft       : <Key>Left      \n\
82 osfMenu       : Shift<Key>F10  \n\
83 osfMenuBar    : <Key>F10       \n\
84 osfPageDown   : <Key>Next      \n\
85 osfPageUp     : <Key>Prior     \n\
86 osfPrimaryPaste:<Key>InsertLine\n\
87 osfQuickPaste : <Key>DeleteLine\n\
88 osfRight      : <Key>Right     \n\
89 osfSelect     : <Key>Select    \n\
90 osfUndo       : <Key>Undo      \n\
91 osfUp         : <Key>Up        \n\
92 osfDown       : <Key>Down      ";
93 
94 
95 XmConst char _XmVirtKeys_ibmFallbackBindingString[] = "\
96 osfAddMode    : Shift<Key>F8   \n\
97 osfBackSpace  : <Key>BackSpace \n\
98 osfBeginLine  : <Key>Home      \n\
99 osfCancel     : <Key>Escape    \n\
100 osfDelete     : <Key>Delete    \n\
101 osfEndLine    : <Key>End       \n\
102 osfHelp       : <Key>F1        \n\
103 osfInsert     : <Key>Insert    \n\
104 osfLeft       : <Key>Left      \n\
105 osfMenu       : Shift<Key>F10  \n\
106 osfMenuBar    : <Key>F10       \n\
107 osfPageDown   : <Key>Next      \n\
108 osfPageUp     : <Key>Prior     \n\
109 osfRight      : <Key>Right     \n\
110 osfUp         : <Key>Up        \n\
111 osfDown       : <Key>Down      ";
112 
113 
114 XmConst char _XmVirtKeys_pcFallbackBindingString[] = "\
115 osfActivate   : <Key>KP_Enter  \n\
116 osfAddMode    : Shift<Key>F8   \n\
117 osfBackSpace  : <Key>BackSpace \n\
118 osfBeginLine  : <Key>Home      \n\
119 osfCancel     : <Key>Escape    \n\
120 osfClear      : <Key>Clear     \n\
121 osfCopy       : Ctrl<Key>Insert\n\
122 osfCut        : Shift<Key>Delete\n\
123 osfDelete     : <Key>Delete    \n\
124 osfEndLine    : <Key>End       \n\
125 osfHelp       : <Key>F1        \n\
126 osfInsert     : <Key>Insert    \n\
127 osfLeft       : <Key>Left      \n\
128 osfMenu       : Shift<Key>F10  \n\
129 osfMenuBar    : <Key>F10       \n\
130 osfPageDown   : <Key>Next      \n\
131 osfPageLeft   : Ctrl<Key>Prior \n\
132 osfPageRight  : Ctrl<Key>Next  \n\
133 osfPageUp     : <Key>Prior     \n\
134 osfPaste      : Shift<Key>Insert\n\
135 osfPrimaryPaste:Meta Ctrl<Key>Insert\n\
136 osfRight      : <Key>Right     \n\
137 osfSelect     : <Key>Select    \n\
138 osfUndo       : <Key>Undo      \n\
139 osfUp         : <Key>Up        \n\
140 osfDown       : <Key>Down      ";
141 
142 
143 XmConst char _XmVirtKeys_sgiFallbackBindingString[] = "\
144 osfActivate   : <Key>KP_Enter  \n\
145 osfAddMode    : Shift<Key>F8   \n\
146 osfBackSpace  : <Key>BackSpace \n\
147 osfBeginLine  : <Key>Home      \n\
148 osfCancel     : <Key>Escape    \n\
149 osfDelete     : <Key>Delete    \n\
150 osfEndLine    : <Key>End       \n\
151 osfHelp       : <Key>F1        \n\
152 osfInsert     : <Key>Insert    \n\
153 osfLeft       : <Key>Left      \n\
154 osfMenu       : Shift<Key>F10  \n\
155 osfMenuBar    : <Key>F10       \n\
156 osfPageDown   : <Key>Next      \n\
157 osfPageUp     : <Key>Prior     \n\
158 osfRight      : <Key>Right     \n\
159 osfUp         : <Key>Up        \n\
160 osfDown       : <Key>Down      ";
161 
162 
163 XmConst char _XmVirtKeys_sunFallbackBindingString[] = "\
164 osfActivate   : <Key>KP_Enter  \n\
165 osfAddMode    : Shift<Key>F8   \n\
166 osfBackSpace  : <Key>BackSpace \n\
167 osfBeginLine  : <Key>F27       \n\
168 osfCancel     : <Key>Escape    \n\
169 osfClear      : <Key>Clear     \n\
170 osfDelete     : <Key>Delete    \n\
171 osfEndLine    : <Key>F13       \n\
172 osfCopy       : <Key>F16       \n\
173 osfCut        : <Key>F20       \n\
174 osfHelp       : <Key>F1        \n\
175 osfInsert     : <Key>Insert    \n\
176 osfLeft       : <Key>Left      \n\
177 osfMenu       : Shift<Key>F10  \n\
178 osfMenuBar    : <Key>F10       \n\
179 osfPaste      : <Key>F18       \n\
180 osfPageDown   : <Key>F35       \n\
181 osfPageUp     : <Key>F29       \n\
182 osfRight      : <Key>Right     \n\
183 osfSelect     : <Key>Select    \n\
184 osfUndo       : <Key>Undo      \n\
185 osfUp         : <Key>Up        \n\
186 osfDown       : <Key>Down      ";
187 
188 /*
189  * List of predefined virtual key bindings for several server vendors. I
190  * don't claim that the list is complete.
191  */
192 static XmDefaultBindingStringRec defaultBindings[] =
193 {
194     {"The XFree86 Project, Inc", _XmVirtKeys_pcFallbackBindingString},
195     {"Hewlett-Packard Company", _XmVirtKeys_hpFallbackBindingString},
196     {"International Business Machines",
197      _XmVirtKeys_ibmFallbackBindingString},
198     {"Silicon Graphics Inc.", _XmVirtKeys_sgiFallbackBindingString},
199     {"Silicon Graphics", _XmVirtKeys_sgiFallbackBindingString},
200     {"X11/NeWS - Sun Microsystems Inc.",
201      _XmVirtKeys_sunFallbackBindingString},
202 };
203 
204 /*
205  * Stick the actual bindings string to the root window of the display. Here,
206  * the CSF docu lacks the details: bindings are set on a per-display basis.
207  * But displays can have more man one screen and thus more than one root
208  * window. So which one to take? Seems like the CSF stores the property
209  * always to the root window of screen #0, and not the default screen. And
210  * in fact this makes sense.
211  */
212 static void
StickBindingsToRootWindow(Display * Dsp,String Bindings,String PropertyName)213 StickBindingsToRootWindow(Display *Dsp, String Bindings,
214 			  String PropertyName)
215 {
216     Atom BindingsProperty;
217 
218     BindingsProperty = XmInternAtom(Dsp, PropertyName, False);
219     XChangeProperty(Dsp, RootWindowOfScreen(ScreenOfDisplay(Dsp, 0)),
220 		    BindingsProperty, XA_STRING, 8, PropModeReplace,
221 		    (unsigned char *)Bindings, strlen(Bindings) + 1);
222 }				/* StickBindingsToRootWindow */
223 
224 /*
225  * This is a list of all currently defined virtual csf keysyms. This allows
226  * us to look up virtual keysyms in order to convert them back to their real
227  * (vendor) keysyms. The accompanying table with the vendor keysyms is
228  * property of a XmDisplay widget.
229  */
230 static XmVirtualKeysymRec VirtualKeysyms[] =
231 {
232     {"osfActivate", osfXK_Activate},
233     {"osfAddMode", osfXK_AddMode},
234     {"osfBackSpace", osfXK_BackSpace},
235     {"osfBeginLine", osfXK_BeginLine},
236     {"osfCancel", osfXK_Cancel},
237     {"osfClear", osfXK_Clear},
238     {"osfCopy", osfXK_Copy},
239     {"osfCut", osfXK_Cut},
240     {"osfDelete", osfXK_Delete},
241     {"osfDown", osfXK_Down},
242     {"osfEndLine", osfXK_EndLine},
243     {"osfHelp", osfXK_Help},
244     {"osfInsert", osfXK_Insert},
245     {"osfLeft", osfXK_Left},
246     {"osfMenu", osfXK_Menu},
247     {"osfMenuBar", osfXK_MenuBar},
248     {"osfPageDown", osfXK_PageDown},
249     {"osfPageLeft", osfXK_PageLeft},
250     {"osfPageRight", osfXK_PageRight},
251     {"osfPageUp", osfXK_PageUp},
252     {"osfPaste", osfXK_Paste},
253     {"osfPrimaryPaste", osfXK_PrimaryPaste},
254     {"osfQuickPaste", osfXK_QuickPaste},
255     {"osfRight", osfXK_Right},
256     {"osfSelect", osfXK_Select},
257     {"osfUndo", osfXK_Undo},
258     {"osfUp", osfXK_Up}
259 };				/* VirtualKeysyms */
260 
261 
262 /*
263  * Armed with a virtual keysym (one of the osfXK_xxxx keysyms) return the
264  * keysym and modifiers, which trigger this virtual keysym.
265  */
266 extern void
_XmVirtualToActualKeysym(Display * Dsp,KeySym VirtualKeysym,KeySym * RealKeysymReturn,Modifiers * ModifierReturn)267 _XmVirtualToActualKeysym(Display *Dsp,
268 			 KeySym VirtualKeysym,
269 			 KeySym *RealKeysymReturn,
270 			 Modifiers *ModifierReturn)
271 {
272     XmDisplayRec *wd;
273     Cardinal i;
274 
275     wd = (XmDisplayRec *)XmGetXmDisplay(Dsp);
276     /*
277      * Because the table is soooooo small (only 27 entries), we can afford
278      * it to do a linear search.
279      */
280     for (i = 0; i < XtNumber(VirtualKeysyms); i++)
281     {
282 	if (VirtualKeysym == VirtualKeysyms[i].keysym)
283 	{
284 	    *RealKeysymReturn = (wd->display.bindings)[i].keysym;
285 	    *ModifierReturn = (wd->display.bindings)[i].modifiers;
286 
287 	    DEBUGOUT(_LtDebug(__FILE__, NULL,
288 		"_XmVirtualToActualKeysym %d -> 0x%X\n",
289 		VirtualKeysym, *RealKeysymReturn));
290 
291 	    return;
292 	}
293     }
294 
295     /*
296      * Nothing found. So return no symbol at all...
297      */
298     *RealKeysymReturn = NoSymbol;
299     *ModifierReturn = (Modifiers)0;
300 }				/* _XmVirtualToActualKeysym */
301 
302 /*
303  * GetModifierMapping -- returns the modifier masks for the ALT and META
304  * modifier keys. If there is no such mapping, then a mask of zero will
305  * be returned for that particular modifier.
306  *
307  * This trick allows us to understand the ALT and META modifiers when the
308  * user specifies the virtual keysyms. In contrast to the translation
309  * manager of the X intrinsics toolkit we can handle this particular
310  * modifiers as true modifiers, not as keypress sequences.
311  *
312  * And in contrast to the closed software foundation we can handle ALT
313  * not just as a fixed alias to Mod1 but as the true modifier as set up
314  * with xmodmap...
315  */
316 #define SET_MODIFIER(m_idx, mask_cnt)                                \
317     if ( ModifierMasks[m_idx] == (Modifiers) 0 ) {                   \
318         ModifierMasks[m_idx] = (Modifiers) (1 << mask_cnt);          \
319     }
320 
321 static void
GetModifierMapping(Display * Dsp,XmModifierMaskSetReference ModifierMasks)322 GetModifierMapping(Display *Dsp,
323 		   XmModifierMaskSetReference ModifierMasks)
324 {
325     XModifierKeymap *ModifierKeymap;
326     KeySym ModifierKeysym;
327     int ModifierSet, SetIndex, SetSize;
328 
329     /*
330      * First reset the modifier masks in question to zero, that is, these
331      * particular modifiers are not bound.
332      */
333     for (SetIndex = 0; SetIndex < MAX_MODIFIERS; SetIndex++)
334     {
335 	ModifierMasks[SetIndex] = (Modifiers)0;
336     }
337 
338     /*
339      * Ask the server for the current modifier mapping and parse it for
340      * the META and ALT keysyms. If one of them is bound to a modifier,
341      * then remember the mask of that modifier.
342      */
343     ModifierKeymap = XGetModifierMapping(Dsp);
344     SetSize = ModifierKeymap->max_keypermod;
345     for (ModifierSet = 0; ModifierSet < 8; ModifierSet++)
346     {
347 	for (SetIndex = 0; SetIndex < SetSize; SetIndex++)
348 	{
349 	    ModifierKeysym = XKeycodeToKeysym(Dsp,
350 					      ModifierKeymap->modifiermap[
351 					     SetIndex + ModifierSet * SetSize],
352 					      0);
353 	    switch (ModifierKeysym)
354 	    {
355 		/*
356 		 * If we've found one of the well known modifier keysyms,
357 		 * we'll remember the modifier - but only if we havn't yet
358 		 * seen another one.
359 		 */
360 	    case XK_Meta_L:
361 	    case XK_Meta_R:
362 		SET_MODIFIER(METAModifier, ModifierSet);
363 		break;
364 
365 	    case XK_Alt_L:
366 	    case XK_Alt_R:
367 		SET_MODIFIER(ALTModifier, ModifierSet);
368 		break;
369 
370 	    case XK_Super_L:
371 	    case XK_Super_R:
372 		SET_MODIFIER(SUPERModifier, ModifierSet);
373 		break;
374 
375 	    case XK_Hyper_L:
376 	    case XK_Hyper_R:
377 		SET_MODIFIER(HYPERModifier, ModifierSet);
378 		break;
379 	    }
380 	}
381     }
382 
383     /*
384      * And don't forget to clean up, we don't want to waste memory here --
385      * because we're not the Closed Software Foundation! And then a last
386      * paranoic checking for missing ALT modifiers...
387      */
388     XFreeModifiermap(ModifierKeymap);
389     SET_MODIFIER(ALTModifier, Mod1MapIndex);
390 }				/* GetModifierMapping */
391 
392 /*
393  * This implements the cache for speeding up lookups within
394  * _XmGetAltModifierForDisplay(). X contexts are nice things!
395  */
396 #define MCC_None             ((XContext) 0)
397 #define MCC_RID_DisplayCache ((XID) 0)
398 
399 static XContext ModifierCacheContext = MCC_None;
400 
401 /*
402  * Ask for the modifier masks of the ALT & Co. modifiers. This internal
403  * function is specific to LessTif as the csf does not provide adequate
404  * functionality. The results are cached so all but the first query won't
405  * cause a round trip to the server. We currently have only one problem:
406  * the cache must be invalidated whenever a MappingNotify event occours
407  * on the wire. So a XmDisplay widget must trigger the invalidation
408  * whenever it sees the notification coming along the wire.
409  */
410 extern XmModifierMaskSetReference
_XmGetModifierMappingsForDisplay(Display * Dsp)411 _XmGetModifierMappingsForDisplay(Display *Dsp)
412 {
413     XmModifierMaskSetReference ModifierMasks;
414 
415     /*
416      * If we haven't yet initialized the cache, create a context.
417      * This context will associate displays with modifiers.
418      */
419     if (ModifierCacheContext == MCC_None)
420     {
421 	ModifierCacheContext = XUniqueContext();
422     }
423     if (XFindContext(Dsp, MCC_RID_DisplayCache,
424 		     ModifierCacheContext, (XPointer *)&ModifierMasks)
425 	!= XCSUCCESS)
426     {
427 	/*
428 	 * No modifier set yet queried for this particular display. Now,
429 	 * ask for the modifiers and store it in the cache.
430 	 */
431 	ModifierMasks = (XmModifierMaskSetReference)
432 	    XtCalloc(1, sizeof(XmModifierMaskSet));
433 
434 	GetModifierMapping(Dsp, ModifierMasks);
435 
436 	XSaveContext(Dsp, MCC_RID_DisplayCache,
437 		     ModifierCacheContext, (XPointer)ModifierMasks);
438     }
439 
440     return ModifierMasks;
441 }				/* _XmGetModifierMappingsForDisplay */
442 
443 
444 /*
445  * Invalidate any cached modifier mappings for a specific display. The
446  * next lookup with _XmGetModifierMappingsForDisplay() will then ask
447  * the X server for a fresh setting and cache the new results. The
448  * cache should be invalidated whenever the modifier mappings got reset
449  * and a broadcast was send to us. This function is intented for
450  * internal use only.
451  */
452 extern void
_XmInvalidateModifierMappingsForDisplay(Display * Dsp)453 _XmInvalidateModifierMappingsForDisplay(Display *Dsp)
454 {
455     XmModifierMaskSetReference ModifierMasks;
456 
457     /*
458      * If there is no context yet, then there isn't a cache there yet,
459      * and therefor there is nothing to invalidate yet. Return (yet).
460      */
461     if (ModifierCacheContext == MCC_None)
462     {
463 	return;
464     }
465 
466     /*
467      * Otherwise ask for a set of modifier mappings and dispose it.
468      * This way the next query on that particular display will cause
469      * a fresh modifier lookup.
470      */
471     if (XFindContext(Dsp, MCC_RID_DisplayCache,
472 		     ModifierCacheContext, (XPointer *)&ModifierMasks)
473 	== XCSUCCESS)
474     {
475 	XDeleteContext(Dsp, MCC_RID_DisplayCache, ModifierCacheContext);
476 	XtFree((char *)ModifierMasks);
477     }
478 }				/* _XmInvalidateModifierMappingsForDisplay */
479 
480 
481 /*
482  * Spit out a warning during conversion of virtual bindings.
483  */
484 static void
DecomposeWarning(Display * Dsp,String str)485 DecomposeWarning(Display *Dsp, String str)
486 {
487     char Buffer[80], *p;
488     Cardinal len;
489 
490     p = str;
491     while (*p && (*p != '\n'))
492     {
493 	p++;
494     }
495 
496     len = p - str;
497     if (len >= sizeof(Buffer))
498     {
499 	len = sizeof(Buffer) - 1;
500     }
501 
502     strncpy(Buffer, str, len);
503     Buffer[len] = '\0';
504     _XmWarning(XmGetXmDisplay(Dsp),
505 	       "Cannot convert string \"%s\" to type VirtualBinding.",
506 	       Buffer);
507 }				/* DecomposeWarning */
508 
509 /*
510  * Some easy, useful macros for use with parsing...
511  */
512 #define SKIP_WS(p)                                                      \
513     while ( ((c = *p) == ' ') || (c == '\t') ) {                        \
514         p++;                                                            \
515     }
516 
517 #define PARSE_ID(p)                                                     \
518     while ( (((c = *p) >= 'A') && (c <= 'Z')) ||                        \
519             ((c >= 'a') && (c <= 'z'))        ||                        \
520             ((c >= '0') && (c <= '9'))        ||                        \
521             (c == '_') ) {                                              \
522         p++;                                                            \
523     }
524 
525 #define EAT_LINE_AND_RETURN(p)                                          \
526  {                                                                      \
527     while ( *p && (*p++ != '\n') ) { /* empty */ }                      \
528     return p;                                                           \
529  }
530 
531 #define PARSE_IDENTIFIER(p) {                                           \
532     start = p; PARSE_ID(p); end = p;                                    \
533     len = end - start;                                                  \
534     if ( len >= sizeof(Identifier) ) { len = sizeof(Identifier) - 1; }  \
535     strncpy(Identifier, start, len);                                    \
536     Identifier[len] = '\0';                                             \
537 }
538 
539 
540 /*
541  * This proc gets a string specifying a binding and decomposes it into
542  * the vendor keysym to be translated and the destination osfkeysym. The
543  * binding string is terminated either by a trailing zero or a newline.
544  * If this function cannot convert the binding, it returns NoSymbol as
545  * *VendorKey. This way the caller can realize, that there are no useful
546  * values in the other parameters returned from the call.
547  */
548 static String
DecomposeBindingString(Display * Dsp,String Binding,XmModifierMaskSet ModifierMasks,KeySym * VendorKey,Modifiers * ModifierMask,KeySym * osfKey)549 DecomposeBindingString(Display *Dsp,
550 		       String Binding,
551 		       XmModifierMaskSet ModifierMasks,
552 		       KeySym *VendorKey,
553 		       Modifiers *ModifierMask,
554 		       KeySym *osfKey)
555 {
556     String start, end, p, osfStart;
557     char Identifier[80];
558     char c;
559     int len;
560 
561     /*
562      * First skip all leading white space. If we then encounter an excla-
563      * mation mark, this line is a comment. So skip it and return to the
564      * caller.
565      */
566     *VendorKey = NoSymbol;
567 
568     p = Binding;
569 
570     SKIP_WS(p);
571 
572     if (!*p || (*p == '\n'))
573     {
574 	return *p ? ++p : p;
575     }
576     if (*p == '!')
577     {
578 	EAT_LINE_AND_RETURN(p);
579     }
580 
581     /*
582      * Now read in the osf keysym's name to be mapped to a vendor keysym.
583      * If it is not valid, then silently ignore the whole line. So much
584      * for compatibility...
585      */
586     PARSE_IDENTIFIER(p);
587 
588     *osfKey = XStringToKeysym(Identifier);
589 
590     if (*osfKey == NoSymbol)
591     {
592 	EAT_LINE_AND_RETURN(p);
593     }
594 
595     SKIP_WS(p);
596     if (*p != ':')
597     {
598 	EAT_LINE_AND_RETURN(p);
599     }
600 
601     /*
602 
603      */
604     *ModifierMask = (Modifiers)0;
605 
606     p++;
607 
608     SKIP_WS(p);
609 
610     osfStart = p;
611 
612     do
613     {
614 	SKIP_WS(p);
615 	if (*p == '<')
616 	{
617 	    /*
618 	     * This is unofficial support as this is really non-sense!
619 	     * No one should ever specify an unbound key in a binding file...
620 	     * well, despite this, I'm not sure, whether there are users...
621 	     */
622 	    if (strncmp(p, "<unbound>", 9) == 0)
623 	    {
624 		EAT_LINE_AND_RETURN(p);
625 	    }
626 
627 	    /*
628 	     * This looks like the <Key> keyword. Check it. If we're
629 	     * wrong, print out a warning message to the terminal.
630 	     */
631 	    if (strncmp(p, "<Key>", 5) != 0)
632 	    {
633 		DecomposeWarning(Dsp, osfStart);
634 		EAT_LINE_AND_RETURN(p);
635 	    }
636 
637 	    /*
638 	     * Skip the string <Key> and fetch the name of the keysym to
639 	     * be bound to the osf keysym.
640 	     */
641 	    p += 5;
642 
643 	    SKIP_WS(p);
644 
645 	    PARSE_IDENTIFIER(p);
646 
647 	    *VendorKey = XStringToKeysym(Identifier);
648 	    if (*VendorKey == NoSymbol)
649 	    {
650 		DecomposeWarning(Dsp, osfStart);
651 		EAT_LINE_AND_RETURN(p);
652 	    }
653 
654 	    SKIP_WS(p);
655 	    if (*p && (*p++ != '\n'))
656 	    {
657 		DecomposeWarning(Dsp, osfStart);
658 		*VendorKey = NoSymbol;
659 		EAT_LINE_AND_RETURN(p);
660 	    }
661 
662 	    return p;
663 	}
664 	else
665 	{
666 	    /*
667 	     * At this time only names of modifiers are allowed here. De-
668 	     * code the name and add it to the set of required modifier
669 	     * flags when decoding virtual keysyms.
670 	     */
671 	    PARSE_IDENTIFIER(p);
672 	    if (strcmp(Identifier, "Shift") == 0)
673 	    {
674 		*ModifierMask |= ShiftMask;
675 	    }
676 	    else if (strcmp(Identifier, "Ctrl") == 0)
677 	    {
678 		*ModifierMask |= ControlMask;
679 	    }
680 	    else if (strcmp(Identifier, "Alt") == 0)
681 	    {
682 		*ModifierMask |= ModifierMasks[ALTModifier];
683 	    }
684 	    else if (strcmp(Identifier, "Meta") == 0)
685 	    {
686 		*ModifierMask |= ModifierMasks[ALTModifier];
687 	    }
688 	    else if (strcmp(Identifier, "Super") == 0)
689 	    {
690 		*ModifierMask |= ModifierMasks[ALTModifier];
691 	    }
692 	    else if (strcmp(Identifier, "Hyper") == 0)
693 	    {
694 		*ModifierMask |= ModifierMasks[ALTModifier];
695 	    }
696 	    else
697 	    {
698 		DecomposeWarning(Dsp, osfStart);
699 		EAT_LINE_AND_RETURN(p);
700 	    }
701 	}
702     }
703     while (*p && (*p != '\n'));
704 
705     EAT_LINE_AND_RETURN(p);
706 }
707 
708 
709 /*
710  * Armed with a string full of bindings, this proc creates the binding table
711  * for use with a display. The memory for storing the bindings must have
712  * been (already) allocated!
713  */
714 static void
ParseBindings(Display * Dsp,String Bindings)715 ParseBindings(Display *Dsp, String Bindings)
716 {
717     XmModifierMaskSet ModifierMasks;
718     String p;
719     KeySym VendorKey, csfKey;
720     Modifiers VendorModifiers;
721     XmKeyBinding BindingTable;
722     int i;
723 
724     BindingTable = ((XmDisplayRec *)XmGetXmDisplay(Dsp))->display.bindings;
725 
726     GetModifierMapping(Dsp, ModifierMasks);
727 
728     p = Bindings;
729 
730     while (p && *p)
731     {
732 	/*
733 	 * For every line of text parse the binding into the keysyms and
734 	 * modifiers. Then try to find the csfKeysym in the table of
735 	 * allowed virtual bindings. If it is found, then store the keysym
736 	 * and modifiers to be converted into the bindings table of the
737 	 * display widget.
738 	 */
739 	p = DecomposeBindingString(Dsp, p, ModifierMasks,
740 				   &VendorKey, &VendorModifiers,
741 				   &csfKey);
742 	if (VendorKey != NoSymbol)
743 	{
744 	    for (i = 0; i < XtNumber(VirtualKeysyms); i++)
745 	    {
746 		if (csfKey == VirtualKeysyms[i].keysym)
747 		{
748 		    BindingTable[i].keysym = VendorKey;
749 		    BindingTable[i].modifiers = VendorModifiers;
750 		    break;
751 		}
752 	    }
753 	}
754     }
755 }
756 
757 
758 /*
759  * If all fails, install the default bindings... But try to install vendor
760  * specific default bindings, if possible. I don't know that the return
761  * value is good for -- seems to be zero all the time.
762  */
763 extern int
_XmVirtKeysLoadFallbackBindings(Display * Dsp,String * Bindings)764 _XmVirtKeysLoadFallbackBindings(Display *Dsp, String *Bindings)
765 {
766     String VendorString, BindingString;
767     Cardinal i;
768 
769     VendorString = XServerVendor(Dsp);
770     BindingString = _XmVirtKeys_fallbackBindingString;
771     for (i = 0; i < XtNumber(defaultBindings); i++)
772     {
773 	if (strcmp(VendorString, defaultBindings[i].vendorName) == 0)
774 	{
775 	    BindingString = defaultBindings[i].defaults;
776 	    break;
777 	}
778     }
779 
780     *Bindings = XtNewString(BindingString);
781     ParseBindings(Dsp, BindingString);
782     StickBindingsToRootWindow(Dsp, BindingString,
783 			      DEFAULT_BINDINGS_PROPERTY_NAME);
784 
785     return 0;
786 }
787 
788 
789 /*
790  * Read in a file containing (hopefuly) bindings for our ******* virtual
791  * keysyms. The file is read in chunks of 1k to speed the reading while
792  * not eating up to much memory. We're not using ftell here so we can
793  * (in principle) even read the bindings from a pipe...
794  */
795 #define QUANTUMLEAPS 1024
796 extern Boolean
_XmVirtKeysLoadFileBindings(String filename,String * binding)797 _XmVirtKeysLoadFileBindings(String filename, String *binding)
798 {
799     FILE *fp;
800     int BindingLen, BytesRead;
801 
802     *binding = NULL;
803     fp = fopen(filename, "r");
804     if (fp)
805     {
806 	BindingLen = 0;
807 
808 	do
809 	{
810 	    *binding = XtRealloc(*binding, (BindingLen + QUANTUMLEAPS) *
811 				 sizeof(char));
812 	    BytesRead = fread(*binding + BindingLen, sizeof(char),
813 			      QUANTUMLEAPS, fp);
814 	    BindingLen += BytesRead;
815 	}
816 	while (BytesRead == QUANTUMLEAPS);
817 
818 	fclose(fp);
819 
820 	/*
821 	 * Shrink memory block to the exact size of the data read and add
822 	 * a trailing zero to the string.
823 	 */
824 	BindingLen++;
825 	*binding = XtRealloc(*binding, BindingLen);
826 	(*binding)[BindingLen - 1] = '\0';
827 
828 	return True;
829     }
830 
831     return False;
832 }
833 
834 
835 /*
836  * This is a helper proc for FindXmBindAliasBinding(). It tries to locate
837  * the binding file name and then to load that file.
838  */
839 static Boolean
LoadFileBindingsFromAlias(Display * Dsp,String dir,String r,String * bindingSpec,String xmbind_alias,int lineno)840 LoadFileBindingsFromAlias(Display *Dsp, String dir, String r,
841 			  String *bindingSpec,
842 			  String xmbind_alias, int lineno)
843 {
844     String re, motifbind;
845     Boolean found;
846 
847     /*
848      * Skip the trailing quote and all following white
849      * space. Then cut off any trailing white space.
850      */
851     ++r;
852     while ((*r == ' ') || (*r == '\t') || (*r == '\n'))
853     {
854 	++r;
855     }
856 
857     re = r + strlen(r);
858     if (re > r)
859     {
860 	--re;		/* Otherwise we would start on the final \0... */
861 	while ((re != r) && ((*re == ' ') || (*re == '\t') ||
862 			     (*re == '\n')))
863 	{
864 	    --re;
865 	}
866 
867 	*++re = '\0';
868 
869 	if (*r == '/')
870 	{
871 	    /* an absolute pathname */
872 	    return _XmVirtKeysLoadFileBindings(r, bindingSpec);
873 	}
874 	else
875 	{
876 	    /* it's a relative one. */
877 	    motifbind = XtMalloc(sizeof(char) *
878 				   (strlen(dir) + 1 + strlen(r) + 1));
879 
880 	    sprintf(motifbind, "%s/%s", dir, r);
881 	    found = _XmVirtKeysLoadFileBindings(motifbind,
882 						bindingSpec);
883 	    XtFree(motifbind);
884 	    return found;
885 	}
886     }
887     else
888     {
889 	_XmWarning(XmGetXmDisplay(Dsp),
890 		   "Malformed line in file \"%s\" (line number %d):\n"
891 		   "Missing binding file name.", xmbind_alias, lineno);
892     }
893     return False;
894 }
895 
896 /*
897  * Look for an xmbind.alias file which specifies the file to load for a
898  * specific X server vendor.
899  */
900 static Boolean
FindXmBindAliasBindings(Display * Dsp,String * bindingSpec,String dir)901 FindXmBindAliasBindings(Display *Dsp, String *bindingSpec,
902 			String dir)
903 {
904     FILE *fp;
905     String xmbind_alias = XtMalloc(sizeof(char) *
906 				   (strlen(dir) + strlen("/xmbind.alias") + 1));
907     String server_vendor = XServerVendor(Dsp);
908     int server_version = XVendorRelease(Dsp);
909     String r;
910 
911     /*
912      * Try to open the xmbind.alias in the directory specified by dir. This
913      * can be either the user's home directory, $XMBINDDIR or a hardcoded
914      * directory, if $XMBINDDIR isn't set.
915      */
916     sprintf(xmbind_alias, "%s/xmbind.alias", dir);
917 
918     fp = fopen(xmbind_alias, "r");
919 
920     if (fp)
921     {
922 	char buf[256];
923 	String p;
924 	int lineno = 0;
925 	int len;
926 
927 	/*
928 	 * Read in every line and look for a match of the vendor name and
929 	 * vendor release. For convenience, count the line number, so error
930 	 * messages can be more precise.
931 	 */
932 	while (fgets(buf, sizeof(buf), fp))
933 	{
934 	    lineno++;
935 	    /*
936 	     * Skip lines containing only comments -- these lines start
937 	     * with an exclamation mark. Also skip empty lines.
938 	     */
939 	    p = buf;
940 	    while ((*p == ' ') || (*p == '\t'))
941 	    {
942 		++p;
943 	    }
944 	    if ((*p == '!') || (*p == '\n') || (*p == '\0'))
945 	    {
946 		continue;
947 	    }
948 	    if (*p != '"')
949 	    {
950 		_XmWarning(XmGetXmDisplay(Dsp),
951 			   "Malformed line in file \"%s\" (line number %d):\n"
952 			   "Missing opening double quote. The vendor name"
953 			   " (and optionally the vendor\nrelease number) must"
954 			   " be enclosed in double quotes.",
955 			   xmbind_alias, lineno);
956 		continue;
957 	    }
958 
959 	    /*
960 	     * Check to see if at least the vendor is correct...
961 	     */
962 	    len = strlen(server_vendor);
963 	    if (strncmp(p + 1, server_vendor, len) == 0)
964 	    {
965 		/*
966 		 * Was it followed by a quote? If not, check for the release
967 		 * number. If it is the right release number then try to
968 		 * find the file name.
969 		 */
970 		if (p[len + 1] == ' ')
971 		{
972 		    r = strchr(p + len + 2, '"');
973 		    if (r == NULL)
974 		    {
975 			_XmWarning(XmGetXmDisplay(Dsp),
976 			    "Malformed line in file \"%s\" (line number %d):\n"
977 			       "Missing closing double quote. The vendor name "
978 			     "(and optionally the vendor\nrelease number) must"
979 				   " be enclosed in double quotes.",
980 				   xmbind_alias, lineno);
981 			continue;
982 		    }
983 		    if (server_version != atoi(p + len + 2))
984 		    {
985 			continue;
986 		    }
987 
988 		    if (LoadFileBindingsFromAlias(Dsp, dir, r,
989 						  bindingSpec,
990 						  xmbind_alias, lineno))
991 		    {
992 			XtFree(xmbind_alias);
993 			fclose(fp);
994 			return True;
995 		    }
996 		}
997 		else
998 		{
999 		    if ((r = strchr(p + len + 1, '"')) == NULL)
1000 		    {
1001 			_XmWarning(XmGetXmDisplay(Dsp),
1002 			    "Malformed line in file \"%s\" (line number %d):\n"
1003 			   "Missing closing double quote. The vendor name (and"
1004 			     " optionally the vendor\nrelease number) must be "
1005 				   "enclosed in double quotes.",
1006 				   xmbind_alias, lineno);
1007 		    }
1008 		    else
1009 		    {
1010 			if (LoadFileBindingsFromAlias(Dsp, dir, r,
1011 						      bindingSpec,
1012 						      xmbind_alias, lineno))
1013 			{
1014 			    XtFree(xmbind_alias);
1015 			    fclose(fp);
1016 			    return True;
1017 			}
1018 		    }
1019 		}
1020 	    }
1021 	}
1022 
1023 	fclose(fp);
1024     }
1025 
1026     XtFree(xmbind_alias);
1027 
1028     return False;
1029 }
1030 
1031 
1032 /*
1033  * Initialize the virtual key bindings for a XmDisplay widget. We will
1034  * look in a couple of places for binding specifications. If all else fails,
1035  * we take some default fallbacks.
1036  */
1037 static void
VirtKeysInitialize(Widget w)1038 VirtKeysInitialize(Widget w)
1039 {
1040     String HOME = _XmOSGetHomeDirName();	/* getenv("HOME"); */
1041     String bindingSpec = NULL;
1042     String motifbind, XMBINDDIR;
1043     XmDisplayRec *wd;
1044     Display *Dsp;
1045     char *type;
1046     XrmValue value;
1047     Atom _motif_default_bindings, _motif_bindings;
1048     Atom actual_type;
1049     int actual_format;
1050     unsigned long numitems;
1051     unsigned long bytes_left;
1052 
1053     wd = (XmDisplayRec *)w;
1054     wd->display.bindings = (XmKeyBinding)XtCalloc(sizeof(XmKeyBindingRec),
1055 						  XtNumber(VirtualKeysyms));
1056     Dsp = XtDisplay(w);
1057 
1058     /*
1059      * First check for the defaultVirtualBindings resource... In order to
1060      * be compatible with M*tif we first check for a resource on the display
1061      * widget. If that's not NULL, then we'll use its contents, otherwise
1062      * we fall back on other strategies. But we'll never touch that resource
1063      * again (i.e. we won't SET it!).
1064      */
1065     if (wd->display.bindingsString != NULL)
1066     {
1067 	ParseBindings(Dsp, wd->display.bindingsString);
1068 	return;
1069     }
1070 
1071     if (XrmGetResource(XtDatabase(Dsp),
1072 		       "defaultVirtualBindings",
1073 		       "DefaultVirtualBindings",
1074 		       &type, &value))
1075     {
1076 	DEBUGOUT(_LtDebug(__FILE__, w, "Found resource\n"));
1077 
1078 	/*
1079 	 * As long as we're not modifying the resource database during
1080 	 * parsing, it's perfectly secure to use the string stored inside
1081 	 * the database. This way, we don't need to create a copy, which
1082 	 * can be slow -- depending on this platform's malloc performance.
1083 	 */
1084 	bindingSpec = (String)value.addr;
1085 
1086 	ParseBindings(Dsp, bindingSpec);
1087 
1088 	return;
1089     }
1090 
1091     /*
1092      * Check the _MOTIF_DEFAULT_BINDINGS and _MOTIF_BINDINGS properties on
1093      * the root window (of screen #0, of course. See above for an
1094      * explanation).
1095      */
1096     _motif_bindings = XmInternAtom(Dsp,
1097 				   BINDINGS_PROPERTY_NAME, False);
1098 
1099     _motif_default_bindings = XmInternAtom(Dsp,
1100 					   DEFAULT_BINDINGS_PROPERTY_NAME,
1101 					   False);
1102     if ((XGetWindowProperty(Dsp,
1103 			    RootWindowOfScreen(ScreenOfDisplay(Dsp, 0)),
1104 			    _motif_bindings,
1105 			    0, 10000 /* some really big number */ ,
1106 			    False, XA_STRING,
1107 			    &actual_type, &actual_format,
1108 			    &numitems, &bytes_left,
1109 			    (unsigned char **)&bindingSpec) == Success)
1110 	||
1111 	(XGetWindowProperty(Dsp,
1112 			    RootWindowOfScreen(ScreenOfDisplay(Dsp, 0)),
1113 			    _motif_default_bindings,
1114 			    0, 10000 /* some really big number */ ,
1115 			    False, XA_STRING,
1116 			    &actual_type, &actual_format,
1117 			    &numitems, &bytes_left,
1118 			    (unsigned char **)&bindingSpec) == Success))
1119     {
1120 	if (bindingSpec)
1121 	{
1122 	    DEBUGOUT(_LtDebug(__FILE__, w, "Found property\n"));
1123 
1124 	    ParseBindings(Dsp, bindingSpec);
1125 
1126 	    XFree(bindingSpec);
1127 
1128 	    return;
1129 	}
1130     }
1131     /*
1132      * Check for .motifbind in $(HOME). If we find one, parse it and stick
1133      * a copy of it to property of the root window.
1134      */
1135     if (HOME == NULL)
1136     {
1137 	HOME = ".";
1138     }
1139 
1140     motifbind = XtMalloc(sizeof(char) *
1141 			   (strlen(HOME) + strlen("/.motifbind") + 1));
1142 
1143     sprintf(motifbind, "%s/.motifbind", HOME);
1144 
1145     if (_XmVirtKeysLoadFileBindings(motifbind, &bindingSpec))
1146     {
1147 	ParseBindings(Dsp, bindingSpec);
1148 
1149 	StickBindingsToRootWindow(Dsp, bindingSpec, BINDINGS_PROPERTY_NAME);
1150 
1151 	XtFree(bindingSpec);
1152 
1153 	return;
1154     }
1155 
1156     /*
1157      * Try to find an xmbind.alias file, looking first in $(HOME) and then
1158      * in XMBINDDIR/some-other-dir
1159      */
1160     if (FindXmBindAliasBindings(Dsp, &bindingSpec, HOME))
1161     {
1162 	ParseBindings(Dsp, bindingSpec);
1163 
1164 	StickBindingsToRootWindow(Dsp, bindingSpec, BINDINGS_PROPERTY_NAME);
1165 
1166 	XtFree(bindingSpec);
1167 
1168 	return;
1169     }
1170 
1171     XMBINDDIR = getenv("XMBINDDIR");
1172     if (XMBINDDIR == NULL)
1173     {
1174 	XMBINDDIR = "/usr/lib/Xm/bindings";
1175     }
1176     if (FindXmBindAliasBindings(Dsp, &bindingSpec, XMBINDDIR))
1177     {
1178 	ParseBindings(Dsp, bindingSpec);
1179 
1180 	StickBindingsToRootWindow(Dsp, bindingSpec, BINDINGS_PROPERTY_NAME);
1181 
1182 	XtFree(bindingSpec);
1183 
1184 	return;
1185     }
1186 
1187     /*
1188      * If all fails, load the fallback bindings.
1189      */
1190     _XmVirtKeysLoadFallbackBindings(Dsp, &bindingSpec);
1191     XtFree(bindingSpec);
1192 }
1193 
1194 
1195 /*
1196  * This is completely new: refresh the virtual binding stuff on the fly
1197  * during run-time.
1198  */
1199 extern void
_XmRefreshVirtKeys(Widget w)1200 _XmRefreshVirtKeys(Widget w)
1201 {
1202     if (!XmIsDisplay(w))
1203     {
1204 	_XmWarning(w,
1205 	      "_XmVirtKeysInitialize(): Thou shall not try to create virtual\n"
1206 		 "bindings on a widget which is not a subclass of XmDisplay.");
1207 	return;
1208     }
1209     _XmInvalidateModifierMappingsForDisplay(XtDisplay(w));
1210     VirtKeysInitialize(w);
1211 }
1212 
1213 
1214 /*
1215  * Initialize the virtual key mechanism of a display widget. Although
1216  * the interface says, that w is any widget, we will handle only display
1217  * widgets here. Otherwise it would make no sense.
1218  */
1219 extern void
_XmVirtKeysInitialize(Widget w)1220 _XmVirtKeysInitialize(Widget w)
1221 {
1222     /*
1223      * It's always better to double-check the parameters...
1224      */
1225     if (!XmIsDisplay(w))
1226     {
1227 	_XmWarning(w,
1228 	      "_XmVirtKeysInitialize(): Thou shall not try to create virtual\n"
1229 		 "bindings on a widget which is not a subclass of XmDisplay.");
1230 	return;
1231     }
1232 
1233     VirtKeysInitialize(w);
1234 
1235     /*
1236      * Initialize some fields for compatibility reasons...
1237      */
1238     memset(((XmDisplayRec *)w)->display.keycode_tag, 0, XmKEYCODE_TAG_SIZE);
1239 
1240     ((XmDisplayRec *)w)->display.lastKeyEvent =
1241 	(XKeyEvent *)XtMalloc(sizeof(XKeyEvent));
1242 }
1243 
1244 
1245 /*
1246  * Cleanup any leftovers from the binding horror when a XmDisplay is
1247  * getting destroyed.
1248  */
1249 extern void
_XmVirtKeysDestroy(Widget w)1250 _XmVirtKeysDestroy(Widget w)
1251 {
1252     if (!XmIsDisplay(w))
1253     {
1254 	_XmWarning(w,
1255 	     "_XmVirtKeysInitialize(): Thou shall not try to destroy virtual\n"
1256 		 "bindings on a widget which is not a subclass of XmDisplay.");
1257 	return;
1258     }
1259 
1260     if (((XmDisplayRec *)w)->display.lastKeyEvent)
1261     {
1262 	XtFree((char *)((XmDisplayRec *)w)->display.lastKeyEvent);
1263     }
1264     if (((XmDisplayRec *)w)->display.bindings)
1265     {
1266 	XtFree((char *)((XmDisplayRec *)w)->display.bindings);
1267     }
1268 }
1269 
1270 
1271 /*
1272  * If we get our hands on some of the dreaded vendor keysyms, we have to
1273  * convert them to the even more dreaded csf keysyms.
1274  */
1275 static void
CheckForVirtualKey(Display * Dsp,KeyCode Keycode,Modifiers CurrentModifiers,Modifiers * ModifiersReturn,KeySym * KeysymReturn)1276 CheckForVirtualKey(Display *Dsp, KeyCode Keycode,
1277 		   Modifiers CurrentModifiers,
1278 		   Modifiers *ModifiersReturn,
1279 		   KeySym *KeysymReturn)
1280 {
1281     XmDisplayRec *wd;
1282     XmKeyBinding Bindings;
1283     KeySym Key;
1284     Modifiers BestMods;
1285     Cardinal i;
1286 
1287     wd = (XmDisplayRec *)XmGetXmDisplay(Dsp);
1288     Bindings = wd->display.bindings;
1289     Key = *KeysymReturn;	/* as set by the caller!!! */
1290 
1291     if (Key == NoSymbol)
1292     {
1293 	return;
1294     }
1295 
1296 #if 0
1297 /*
1298  * This is a hack to make nedit support PageUp and PageDown.
1299  * A more permanent solution seems to have been found. It's not
1300  * nice though. Look in XmTranslateKey and _XmVirtualHandler.
1301  */
1302     if (Key == 0xFF56 && CurrentModifiers == 4) {	/* PageDown */
1303 	*KeysymReturn = 0x1004FF42;			/* osfXK_PageDown */
1304 	return;
1305     }
1306     if (Key == 0xFF55 && CurrentModifiers == 4) {	/* PageUp */
1307 	*KeysymReturn = 0x1004FF41;			/* osfXK_PageUp */
1308 	return;
1309     }
1310 #endif
1311 
1312     /*
1313      * Because the table is soooooo small (only 27 entries), we can afford
1314      * it to do a linear search (well, we can afford it once again, like in
1315      * _XmVirtualToActualKeysym()). For every entry in the bindings table
1316      * of the appropiate XmDisplay widget we check for a keysym match. If
1317      * we get one, we must make sure, that the modifier flags are allright.
1318      * Also we MUST do an exhaustive search as there might be another binding
1319      * with "better matching" modifiers in the table.
1320      */
1321     BestMods = (Modifiers)0;
1322     for (i = 0; i < XtNumber(VirtualKeysyms); i++, Bindings++)
1323     {
1324 	if (Key == Bindings->keysym)
1325 	{
1326 	    if (((CurrentModifiers & Bindings->modifiers) ==
1327 		 Bindings->modifiers) &&
1328 		(Bindings->modifiers >= BestMods))
1329 	    {
1330 		*KeysymReturn = VirtualKeysyms[i].keysym;
1331 		BestMods = Bindings->modifiers;
1332 	    }
1333 
1334 	    *ModifiersReturn |= Bindings->modifiers;
1335 	}
1336     }
1337 
1338     DEBUGOUT(_LtDebug(__FILE__, NULL,
1339 	"CheckForVirtualKey 0x%X, mod 0x%X -> 0x%X, mod 0x%X\n",
1340 	Key, CurrentModifiers, *KeysymReturn, BestMods));
1341 }
1342 
1343 
1344 /*
1345  * Fortunatly, we can rely on XtTranslateKey() doing most of the work for
1346  * ordinary key presses: shift (lock) state, upper and lower case, worrying
1347  * with display locking procedures, and, and, and... We "only" have to
1348  * check for an outstanding conversion from a vendor keysym (ordinary
1349  * keysym) to a virtual keysym.
1350  */
1351 extern void
XmTranslateKey(Display * Dsp,KeyCode Keycode,Modifiers modifiers,Modifiers * modifiers_return,KeySym * Keysym_return)1352 XmTranslateKey(Display *Dsp, KeyCode Keycode,
1353 	       Modifiers modifiers, Modifiers *modifiers_return,
1354 	       KeySym *Keysym_return)
1355 {
1356 #if 1
1357     /*
1358      * Tie the keysym and the current modifier (from the lastKeyEvent) into
1359      * eachother to figure out whether this maps into a virtual keysym.
1360      */
1361     XmDisplay d = (XmDisplay)XmGetXmDisplay(Dsp);
1362 
1363     DEBUGOUT(_LtDebug(__FILE__, NULL,
1364 	"XmTranslateKey (KeyCode %d, modifiers 0x%X), event %d\n",
1365 	Keycode, modifiers, d->display.lastKeyEvent->state));
1366     DEBUGOUT(_LtDebug("NMEM", NULL,
1367 	"XmTranslateKey (KeyCode %d, modifiers 0x%X), event %d\n",
1368 	Keycode, modifiers, d->display.lastKeyEvent->state));
1369 
1370     XtTranslateKey(Dsp, Keycode, modifiers, modifiers_return, Keysym_return);
1371 
1372     CheckForVirtualKey(Dsp, Keycode, (Modifiers)d->display.lastKeyEvent->state,
1373 		       modifiers_return, Keysym_return);
1374 #else
1375     /*
1376      * Same as above without the funky stuff.
1377      * This doesn't seem to work in all cases.
1378      * Nedit shows that PageUp/PageDown don't work. Instead they behave as
1379      * PageRight and PageLeft. Which lead me to look at the modifiers, which
1380      * eventually got me to find all this. Sigh.
1381      */
1382     XtTranslateKey(Dsp, Keycode, modifiers, modifiers_return, Keysym_return);
1383 
1384     CheckForVirtualKey(Dsp, Keycode, modifiers,
1385 		       modifiers_return, Keysym_return);
1386 #endif
1387 }
1388 
1389 
1390 extern void
_XmVirtKeysStoreBindings(Widget shell,String binding)1391 _XmVirtKeysStoreBindings(Widget shell, String binding)
1392 {
1393 }
1394 
1395 /*
1396  * This handler just collects any key presses and stores the least one
1397  * with the appropiate display widget object. This one is in mainly for
1398  * compatibility reasons -- maybe someone needs the information collected
1399  * here lateron...
1400  *
1401  * Yes we do. This appears to be the only way to get hold of which
1402  * modifiers are valid right now (i.e. when the user presses this key).
1403  */
1404 extern void
_XmVirtKeysHandler(Widget w,XtPointer Data,XEvent * Event,Boolean * ContDispatch)1405 _XmVirtKeysHandler(Widget w, XtPointer Data, XEvent *Event,
1406 		   Boolean *ContDispatch)
1407 {
1408     XmDisplay	d;
1409 
1410     DEBUGOUT(_LtDebug(__FILE__, w, "_XmVirtKeysHandler\n"));
1411     DEBUGOUT(_LtDebug("NMEM", w, "_XmVirtKeysHandler\n"));
1412 
1413     if (!w->core.being_destroyed && (Event->xany.type == KeyPress))
1414     {
1415 	d = (XmDisplay)XmGetXmDisplay(XtDisplay(w));
1416 
1417 	*(d->display.lastKeyEvent) = *((XKeyEvent *)Event);
1418 
1419 	/*
1420 	 * Looks like Xt has a cache to prevent it from calling XmTranslateKey
1421 	 * all the time.
1422 	 *
1423 	 * Disable it for now.
1424 	 * That might be the only way to get XmTranslateKey processing (i.e.
1425 	 * our virtual bindings) to work right: we need to tie the modifier(s)
1426 	 * currently valid with the keysym that Xt comes up with to figure out
1427 	 * if this is one of our virtual keysyms.
1428 	 * Looks like this Xt caching business prevents this from working.
1429 	 *
1430 	 * There must be a better way to do this - FIX ME. Sigh.
1431 	 */
1432 	XtSetKeyTranslator(XtDisplay(w), (XtKeyProc)XmTranslateKey);
1433     }
1434 }
1435