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