1 /* xkeycaps, Copyright (c) 1991, 1992, 1993, 1996
2  *  Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or
10  * implied warranty.
11  */
12 
13 #include <stdio.h>
14 #include "KbdWidgetP.h"
15 #include "KeyWidgetP.h"
16 #include "xkeycaps.h"
17 
18 static void highlight_key P((KeyWidget widget));
19 static void dehighlight_key P((KeyWidget widget));
20 static void mouse_highlight_key P((KeyWidget widget));
21 static void mouse_dehighlight_key P((KeyWidget widget));
22 static void simulate_key_event P((KeyWidget widget, int down_p));
23 
24 
25 
26 /* The goal of the mouse-tracking code in the following functions is for the
27    mouse with a button down to be just like your finger on the keyboard: so
28    long as a button is down, the key under it is depressed.  If the mouse
29    moves to another key without the button going up first, the previous key
30    is released and the new key is depressed.
31 
32    When a ButtonDown event comes in, the server gives you an implicit grab
33    of the mouse so that you will get the corresponding ButtonUp event no
34    matter what happens to focus or mouse-position in the meantime.  This
35    is good, and totally necessary to our purposes.  But, we want to be able
36    to track the mouse while the button is down.  Initially, I did this by
37    selecting EnterNotify/LeaveNotify events on the child windows, but those
38    aren't generated while the parent window has a grab.  So instead, the
39    parent window gets PointerMotion events, and looks at the "child" window
40    that the mouse is over to notice the crossings.
41 
42    This loses slightly in that if you click down on the A key and move
43    quickly over to the L key, we won't necessarily notice that *all* of the
44    intervening keys went down; only the ones that we happened to get motion
45    events for.  Others down/up pairs may be compressed out of existence.
46  */
47 
48 
49 static KeyWidget
50 #ifdef __STDC__
parse_key_action_args(char * name,XEvent * event,char ** argv,int argc,KeyboardWidget widget,int * mouse_p_ret)51 parse_key_action_args (char *name, XEvent *event, char **argv, int argc,
52 		       KeyboardWidget widget, int *mouse_p_ret)
53 #else /* ! __STDC__ */
54 parse_key_action_args (name, event, argv, argc, widget, mouse_p_ret)
55      char *name;
56      XEvent *event;
57      char **argv;
58      int argc;
59      KeyboardWidget widget;
60      int *mouse_p_ret;
61 #endif /* ! __STDC__ */
62 {
63   KeyWidget key = 0;
64   int mouse_p = 0;
65   int mod_p = 0;
66   int track_p = 0;
67   int highlighted_p = 0;
68   char *key_desc = 0;
69   int orig_argc = argc;
70 
71   for (; argc > 0; argc--)
72     {
73       if (string_equal (argv[argc-1], "ifmod"))
74 	mod_p = 1;
75       else if (string_equal (argv[argc-1], "unlessmod"))
76 	mod_p = -1;
77       else if (string_equal (argv[argc-1], "iftracking"))
78 	track_p = 1;
79       else if (string_equal (argv[argc-1], "unlesstracking"))
80 	track_p = -1;
81       else if (string_equal (argv[argc-1], "ifhighlighted"))
82 	highlighted_p = 1;
83       else if (string_equal (argv[argc-1], "unlesshighlighted"))
84 	highlighted_p = -1;
85       else if (key_desc)
86 	fprintf (stderr, "%s: too many args (%d) passed to %s action\n",
87 		 progname, orig_argc, name);
88       else
89 	key_desc = argv[argc-1];
90     }
91 
92   if (key_desc && string_equal (key_desc, "mouse"))
93     key = widget->keyboard.key_under_mouse, mouse_p = 1;
94   else if (key_desc && string_equal (key_desc, "highlighted"))
95     key = widget->keyboard.mouse_highlighted_key, mouse_p = 1;
96   else if (key_desc && string_equal (key_desc, "displayed"))
97     key = widget->keyboard.documented_key, mouse_p = 1;
98   else if (key_desc)
99     fprintf (stderr, "%s: unrecognised arg to %s: %s\n", progname,
100 	     name, key_desc);
101   else if (event->xany.type == ButtonPress ||
102 	   event->xany.type == ButtonRelease ||
103 	   event->xany.type == MotionNotify)
104     key = widget->keyboard.key_under_mouse, mouse_p = 1;
105   else if (event->xany.type == KeyPress ||
106 	   event->xany.type == KeyRelease)
107     key = keycode_to_key (widget, event->xkey.keycode);
108   else
109     fprintf (stderr, "%s: %d isn't a key or button press or release in %s\n",
110 	     progname, event->xany.type, name);
111 
112   if (mod_p != 0) {
113     if (!key) return 0;
114     if (mod_p ==  1 && !key->key.modifier_bits) return 0;
115     if (mod_p == -1 &&  key->key.modifier_bits) return 0;
116   }
117 
118   if (track_p ==  1 && !widget->keyboard.tracking_key) return 0;
119   if (track_p == -1 &&  widget->keyboard.tracking_key) return 0;
120 
121   if (highlighted_p ==  1 && key != widget->keyboard.mouse_highlighted_key)
122     return 0;
123   if (highlighted_p == -1 && key == widget->keyboard.mouse_highlighted_key)
124     return 0;
125 
126   if (mouse_p_ret) *mouse_p_ret = mouse_p;
127   return key;
128 }
129 
130 
131 void
132 #ifdef __STDC__
highlight_key_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)133 highlight_key_action (Widget ww, XEvent *event, String *argv, Cardinal *argc)
134 #else /* ! __STDC__ */
135 highlight_key_action (ww, event, argv, argc)
136      Widget ww;
137      XEvent *event;
138      String *argv;
139      Cardinal *argc;
140 #endif /* ! __STDC__ */
141 {
142   KeyboardWidget widget = (KeyboardWidget) ww;
143   int mouse_p;
144   KeyWidget key = parse_key_action_args ("HighlightKey", event,
145 					 argv, *argc, widget, &mouse_p);
146   if (! key) return;
147   if (mouse_p)
148     mouse_highlight_key (key);
149   else
150     highlight_key (key);
151 }
152 
153 
154 void
155 #ifdef __STDC__
unhighlight_key_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)156 unhighlight_key_action (Widget ww, XEvent *event, String *argv, Cardinal *argc)
157 #else /* ! __STDC__ */
158 unhighlight_key_action (ww, event, argv, argc)
159      Widget ww;
160      XEvent *event;
161      String *argv;
162      Cardinal *argc;
163 #endif /* ! __STDC__ */
164 {
165   KeyboardWidget widget = (KeyboardWidget) ww;
166   int mouse_p;
167   KeyWidget key = parse_key_action_args ("UnhighlightKey", event,
168 					 argv, *argc, widget, &mouse_p);
169   if (! key) return;
170   if (mouse_p)
171     mouse_dehighlight_key (key);
172   else
173     dehighlight_key (key);
174 }
175 
176 
177 void
178 #ifdef __STDC__
toggle_key_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)179 toggle_key_action (Widget ww, XEvent *event, String *argv, Cardinal *argc)
180 #else /* ! __STDC__ */
181 toggle_key_action (ww, event, argv, argc)
182      Widget ww;
183      XEvent *event;
184      String *argv;
185      Cardinal *argc;
186 #endif /* ! __STDC__ */
187 {
188   KeyboardWidget widget = (KeyboardWidget) ww;
189   int mouse_p;
190   KeyWidget key = parse_key_action_args ("ToggleKey", event, argv, *argc,
191 					 widget, &mouse_p);
192   if (! key) return;
193   if (mouse_p)
194     {
195       if (key->key.mouse_highlighted)
196 	mouse_dehighlight_key (key);
197       else
198 	mouse_highlight_key (key);
199     }
200   else
201     {
202       if (key->key.key_highlighted)
203 	dehighlight_key (key);
204       else
205 	highlight_key (key);
206     }
207 }
208 
209 
210 void
211 #ifdef __STDC__
simulate_KeyPress_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)212 simulate_KeyPress_action (Widget ww, XEvent *event,
213 			  String *argv, Cardinal *argc)
214 #else /* ! __STDC__ */
215 simulate_KeyPress_action (ww, event, argv, argc)
216      Widget ww;
217      XEvent *event;
218      String *argv;
219      Cardinal *argc;
220 #endif /* ! __STDC__ */
221 {
222   KeyboardWidget widget = (KeyboardWidget) ww;
223   KeyWidget key = parse_key_action_args ("SimulateKeyPress", event, argv,
224 					 *argc, widget, 0);
225   if (key) simulate_key_event (key, 1);
226 }
227 
228 
229 void
230 #ifdef __STDC__
simulate_KeyRelease_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)231 simulate_KeyRelease_action (Widget ww, XEvent *event,
232 			    String *argv, Cardinal *argc)
233 #else /* ! __STDC__ */
234 simulate_KeyRelease_action (ww, event, argv, argc)
235      Widget ww;
236      XEvent *event;
237      String *argv;
238      Cardinal *argc;
239 #endif /* ! __STDC__ */
240 {
241   KeyboardWidget widget = (KeyboardWidget) ww;
242   KeyWidget key = parse_key_action_args ("SimulateKeyRelease", event,
243 					 argv, *argc, widget, 0);
244   if (key) simulate_key_event (key, 0);
245 }
246 
247 
248 void
249 #ifdef __STDC__
describe_key_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)250 describe_key_action (Widget ww, XEvent *event, String *argv, Cardinal *argc)
251 #else /* ! __STDC__ */
252 describe_key_action (ww, event, argv, argc)
253      Widget ww;
254      XEvent *event;
255      String *argv;
256      Cardinal *argc;
257 #endif /* ! __STDC__ */
258 {
259   KeyboardWidget widget = (KeyboardWidget) ww;
260   KeyWidget key = parse_key_action_args ("DescribeKey", event,
261 					 argv, *argc, widget, 0);
262   if (! key) return;
263 
264   /* If the event invoking this is a KeyPress or KeyRelease, and the
265      key in question is a modifier key, don't do the optimization of
266      not redisplaying because we're already displaying that key.  This
267      is so that the "DescribeKey(displayed)" action can be bound to
268      KeyDown and KeyUp, and will change the display when modifier keys
269      are pressed and released.
270    */
271   if ((event->xany.type == KeyPress || event->xany.type == KeyRelease)
272       && (widget->keyboard.modifier_vector [event->xkey.keycode / 8]
273 	  & (1 << (event->xkey.keycode % 8))))
274     widget->keyboard.documented_key = 0;
275 
276   if (key != widget->keyboard.documented_key)
277     {
278       describe_key (key);
279       widget->keyboard.documented_key = key;
280     }
281 }
282 
283 
284 void
285 #ifdef __STDC__
track_key_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)286 track_key_action (Widget ww, XEvent *event, String *argv, Cardinal *argc)
287 #else /* ! __STDC__ */
288 track_key_action (ww, event, argv, argc)
289      Widget ww;
290      XEvent *event;
291      String *argv;
292      Cardinal *argc;
293 #endif /* ! __STDC__ */
294 {
295   KeyboardWidget widget = (KeyboardWidget) ww;
296   KeyWidget key = parse_key_action_args ("TrackKey", event,
297 					 argv, *argc, widget, 0);
298   if (!key) return;
299   if (event->xany.type != ButtonPress && event->xany.type != ButtonRelease)
300     {
301       fprintf (stderr, "%s: TrackKey action invoked on a non-button event\n",
302 	       progname);
303       return;
304     }
305   widget->keyboard.tracking_key |= (1 << (event->xbutton.button - 1));
306 }
307 
308 void
309 #ifdef __STDC__
untrack_key_action(Widget ww,XEvent * event,String * argv,Cardinal * argc)310 untrack_key_action (Widget ww, XEvent *event, String *argv, Cardinal *argc)
311 #else /* ! __STDC__ */
312 untrack_key_action (ww, event, argv, argc)
313      Widget ww;
314      XEvent *event;
315      String *argv;
316      Cardinal *argc;
317 #endif /* ! __STDC__ */
318 {
319   KeyboardWidget widget = (KeyboardWidget) ww;
320   parse_key_action_args ("UntrackKey", event, argv, *argc, widget, 0);
321   if (event->xany.type != ButtonPress && event->xany.type != ButtonRelease)
322     {
323       fprintf (stderr,
324 	       "%s: UntrackKey action invoked on a non-button event\n",
325 	       progname);
326       return;
327     }
328 /*  widget->keyboard.tracking_key &= ~(1 << (event->xbutton.button - 1)); */
329   widget->keyboard.tracking_key = 0;
330 }
331 
332 
333 void
334 #ifdef __STDC__
key_menu_pre_popup_hook(Widget menu,XtPointer client_data,XtPointer call_data)335 key_menu_pre_popup_hook (Widget menu, XtPointer client_data,
336 			 XtPointer call_data)
337 #else /* ! __STDC__ */
338 key_menu_pre_popup_hook (menu, client_data, call_data)
339      Widget menu;
340      XtPointer client_data, call_data;
341 #endif /* ! __STDC__ */
342 {
343   KeyboardWidget widget = (KeyboardWidget) client_data;
344   KeyWidget key = widget->keyboard.key_under_mouse;
345   Arg av[10];
346   int ac = 0;
347   char buf1 [255];	/* so that the "!=" test in SimpleMenu succeeds... */
348   char buf2 [255];
349   static int toggle = 0;
350   char *b = (toggle ? buf1 : buf2);
351   message (widget, ""); /* clear the message area */
352   if (key)
353     {
354       toggle = !toggle;
355       sprintf (b, " Key %s (0x%02X) ", key->key.key_name,
356 	       key->key.keycode);
357       XtSetArg (av[ac], XtNlabel, b); ac++;
358     }
359   else
360     XtSetArg (av[ac], XtNlabel, "--  No Key  --"), ac++;
361   XtSetValues (menu, av, ac);
362   sensitize_menu (widget, menu, (key ? True : False));
363 }
364 
365 
366 
367 /* This is called before every action is invoked.  It does two things:
368    it keeps the variable key_under_mouse up to date, and, if in TrackKey
369    mode, it simulates ButtonPress and ButtonRelease events when the mouse
370    moves from one key to another.
371  */
372 
373 void
374 #ifdef __STDC__
keyboard_track_motion_hook(Widget action_widget,XtPointer client_data,String action_name,XEvent * event,String * ac,Cardinal * av)375 keyboard_track_motion_hook (Widget action_widget, XtPointer client_data,
376 			    String action_name, XEvent *event,
377 			    String *ac, Cardinal *av)
378 #else /* ! __STDC__ */
379 keyboard_track_motion_hook (action_widget, client_data,
380 			    action_name, event, ac, av)
381      Widget action_widget;
382      XtPointer client_data;
383      String action_name;
384      XEvent *event;
385      String *ac;
386      Cardinal *av;
387 #endif /* ! __STDC__ */
388 {
389   if (action_widget != (Widget) client_data)	/* not the KeyboardWidget */
390     return;
391   if (event->xany.type != MotionNotify)
392     return;
393   {
394     KeyboardWidget widget = (KeyboardWidget) action_widget;
395     Display *dpy = XtDisplay (widget);
396     KeyWidget last_key_under_mouse = widget->keyboard.key_under_mouse;
397     Window root, child;
398     int root_x, root_y, x, y;
399     unsigned int mask;
400 
401     /* This call to XQueryPointer is just to tell the server that it's
402        ok to start sending motion events again.  When MotionHintMask
403        is used, we only get one MotionNotify event until something
404        interesting happens again.
405      */
406     XQueryPointer (dpy, XtWindow (widget), &root, &child,
407 		   &root_x, &root_y, &x, &y, &mask);
408 
409     widget->keyboard.key_under_mouse = window_to_key (dpy, child);
410 
411     if (widget->keyboard.tracking_key &&
412 	widget->keyboard.key_under_mouse
413 	&& widget->keyboard.key_under_mouse != last_key_under_mouse
414 	&& !widget->keyboard.key_under_mouse->key.modifier_bits)
415       /* If we're in tracking-mode and the mouse has entered a key that it
416 	 wasn't within before, then simulate a ButtonUp event on the key
417 	 the mouse was last over, and then simulate a ButtonDown event on
418 	 the key the mouse is over now.  But don't do that if the key the
419 	 mouse is over now is a modifier key, because that breaks the
420 	 "ifmod" arguments to actions.
421 
422 	 We simulate these events so that the correct sets of actions get
423 	 invoked.  Remember that the user may have changed the actions.
424        */
425       {
426 	XEvent sim_event;
427 
428 	/* Put back a ButtonDown event for the current key first.
429 	 */
430 	sim_event.xany.type = ButtonPress;
431 	sim_event.xbutton.send_event = 1;
432 	sim_event.xbutton.display = dpy;
433 	sim_event.xbutton.window = XtWindow (widget); /* the KeyboardWidget */
434 	sim_event.xbutton.root = event->xmotion.root;
435 	sim_event.xbutton.subwindow =
436 	  (widget->keyboard.key_under_mouse
437 	   ? XtWindow (widget->keyboard.key_under_mouse)
438 	   : 0);
439 	sim_event.xbutton.time = event->xmotion.time;
440 	sim_event.xbutton.x = event->xmotion.x;
441 	sim_event.xbutton.y = event->xmotion.y;
442 	sim_event.xbutton.x_root = event->xmotion.x_root;
443 	sim_event.xbutton.y_root = event->xmotion.y_root;
444 	sim_event.xbutton.state = event->xmotion.state;
445 	sim_event.xbutton.button = widget->keyboard.tracking_key;
446 	sim_event.xbutton.same_screen = 1;
447 	XPutBackEvent (dpy, &sim_event);
448 
449 	/* Now put back a ButtonUp event for the previous key.
450 	   We do it in this order because we're pushing on the front.
451 	 */
452 	sim_event.xany.type = ButtonRelease;
453 	sim_event.xbutton.subwindow =
454 	  (last_key_under_mouse ? XtWindow (last_key_under_mouse) : 0);
455 	sim_event.xbutton.x = (last_key_under_mouse
456 			       ? last_key_under_mouse->core.x : 0);
457 	sim_event.xbutton.y = (last_key_under_mouse
458 			       ? last_key_under_mouse->core.y : 0);
459 	sim_event.xbutton.x_root = -1; /* This is hard, blow it off. */
460 	sim_event.xbutton.y_root = -1;
461 	XPutBackEvent (dpy, &sim_event);
462       }
463   }
464 }
465 
466 
467 
468 static unsigned long simulated_modifier_state = 0;
469 static unsigned long real_modifier_state = 0;
470 
471 
472 #define key_highlighted_p(KEY) \
473   (((KEY)->key.mouse_highlighted > 0) || ((KEY)->key.key_highlighted > 0))
474 
475 static void
476 #ifdef __STDC__
mouse_highlight_key(KeyWidget widget)477 mouse_highlight_key (KeyWidget widget)
478 #else /* ! __STDC__ */
479 mouse_highlight_key (widget)
480      KeyWidget widget;
481 #endif /* ! __STDC__ */
482 {
483   KeyboardWidget keyboard = (KeyboardWidget) widget->core.parent;
484   if (!key_highlighted_p (widget)) {
485     KeyHighlight (widget);
486 #if 0
487     printf (" simulated: 0x%02x | 0x%02x = 0x%02x\n",
488 	    simulated_modifier_state, widget->key.modifier_bits,
489 	    simulated_modifier_state | widget->key.modifier_bits);
490 #endif
491     simulated_modifier_state |= widget->key.modifier_bits;
492     widget->key.mouse_highlighted++;
493     keyboard->keyboard.mouse_highlighted_key = widget;
494   }
495 }
496 
497 
498 static void
499 #ifdef __STDC__
mouse_dehighlight_key(KeyWidget widget)500 mouse_dehighlight_key (KeyWidget widget)
501 #else /* ! __STDC__ */
502 mouse_dehighlight_key (widget)
503      KeyWidget widget;
504 #endif /* ! __STDC__ */
505 {
506   KeyboardWidget keyboard = (KeyboardWidget) widget->core.parent;
507 
508   widget->key.mouse_highlighted--;
509   if (key_highlighted_p (widget)) return;
510   if (widget->key.mouse_highlighted < 0)
511     {
512       printf ("%s: ERROR: more ButtonUps than ButtonDowns on \"%s\"\n",
513 	      progname, widget->key.key_name);
514       widget->key.mouse_highlighted = 0;
515     }
516   KeyDehighlight (widget);
517 
518   keyboard->keyboard.mouse_highlighted_key = 0;
519 #if 0
520   printf (" simulated: 0x%02x & ~0x%02x = 0x%02x\n",
521 	  simulated_modifier_state, widget->key.modifier_bits,
522 	  simulated_modifier_state & ~widget->key.modifier_bits);
523 #endif
524   simulated_modifier_state &= ~widget->key.modifier_bits;
525 }
526 
527 
528 static void
529 #ifdef __STDC__
highlight_key(KeyWidget widget)530 highlight_key (KeyWidget widget)
531 #else /* ! __STDC__ */
532 highlight_key (widget)
533      KeyWidget widget;
534 #endif /* ! __STDC__ */
535 {
536   KeyboardWidget keyboard = (KeyboardWidget) widget->core.parent;
537 
538 /*  printf ("%s: KeyPress %s\n", progname, widget->key.key_name);*/
539 
540   if (!key_highlighted_p (widget)) {
541     KeyHighlight (widget);
542 #if 0
543     printf (" real     : 0x%02x | 0x%02x = 0x%02x\n",
544 	    real_modifier_state, widget->key.modifier_bits,
545 	    real_modifier_state | widget->key.modifier_bits);
546 #endif
547     real_modifier_state |= widget->key.modifier_bits;
548   }
549   if (widget->key.key_highlighted)
550     /* We really shouldn't ever see more than one KeyPress on a given keycode
551        without an intervening KeyRelease, but we can if the keyboard focus
552        moves away from our window between the KeyPress and KeyRelease.  Cope
553        with this by not incrementing the key-press-count to >1.  This could
554        also happen if two keys on the keyboard had the same keycode, which
555        would make for a terribly bogus keyboard.
556      */
557     printf ("%s: ERROR: more KeyPresses than KeyReleases on \"%s\"\n",
558 	    progname, widget->key.key_name);
559   else
560     widget->key.key_highlighted++;
561 
562   keyboard->keyboard.key_state_vector [widget->key.keycode / 8]
563     |= (1 << (widget->key.keycode % 8));
564 }
565 
566 static void
567 #ifdef __STDC__
dehighlight_key(KeyWidget widget)568 dehighlight_key (KeyWidget widget)
569 #else /* ! __STDC__ */
570 dehighlight_key (widget)
571      KeyWidget widget;
572 #endif /* ! __STDC__ */
573 {
574   KeyboardWidget keyboard = (KeyboardWidget) widget->core.parent;
575   widget->key.key_highlighted--;
576 
577 /*  printf ("%s: KeyRelease %s\n", progname, widget->key.key_name); */
578 
579   keyboard->keyboard.key_state_vector [widget->key.keycode / 8] &=
580     ~(1 << (widget->key.keycode % 8));
581 
582   if (widget->key.key_highlighted < 0)
583     {
584       printf ("%s: ERROR: more KeyReleases than KeyPresses on \"%s\"\n",
585 	      progname, widget->key.key_name);
586       widget->key.key_highlighted = 0;
587     }
588   if (key_highlighted_p (widget)) return;
589 
590   KeyDehighlight (widget);
591 #if 0
592   printf (" real     : 0x%02x & ~0x%02x = 0x%02x\n",
593 	  real_modifier_state, widget->key.modifier_bits,
594 	  real_modifier_state & ~widget->key.modifier_bits);
595 #endif
596   real_modifier_state &= ~widget->key.modifier_bits;
597 }
598 
599 
600 
601 void
602 #ifdef __STDC__
key_to_event(KeyWidget key,XEvent * event,int down_p)603 key_to_event (KeyWidget key, XEvent *event, int down_p)
604 #else /* ! __STDC__ */
605 key_to_event (key, event, down_p)
606      KeyWidget key;
607      XEvent *event;
608      int down_p;
609 #endif /* ! __STDC__ */
610 {
611   event->xkey.type = down_p ? KeyPress : KeyRelease;
612   event->xkey.display = XtDisplay (key);
613   event->xkey.time = CurrentTime;
614   event->xkey.x = event->xkey.y = 0;
615   event->xkey.x_root = event->xkey.y_root = 0;
616   event->xkey.state = (simulated_modifier_state | real_modifier_state);
617   event->xkey.keycode = key->key.keycode;
618 }
619 
620 
621 static void
622 #ifdef __STDC__
simulate_key_event(KeyWidget widget,int down_p)623 simulate_key_event (KeyWidget widget, int down_p)
624 #else /* ! __STDC__ */
625 simulate_key_event (widget, down_p)
626      KeyWidget widget;
627      int down_p;
628 #endif /* ! __STDC__ */
629 {
630   KeyboardWidget keyboard = (KeyboardWidget) widget->core.parent;
631   XEvent event;
632   Window window;
633   int revert_to;
634   if (! widget->key.keycode) return;
635   XGetInputFocus (XtDisplay (widget), &window, &revert_to);
636 
637   /* If this widget (or any parent of it) has the keyboard focus, then send
638      the events to the window the user has picked.
639    */
640   if (window == PointerRoot)
641     window = keyboard->keyboard.target_window;
642   else
643     {
644       Widget parent;
645       for (parent = (Widget) widget; parent; parent = XtParent (parent))
646 	if (XtWindow (parent) == window)
647 	  {
648 	    window = keyboard->keyboard.target_window;
649 	    break;
650 	  }
651     }
652   if (!window) return;
653   key_to_event (widget, &event, down_p);
654   event.xkey.window = window;
655 #ifdef HAVE_XTRAP
656   if (keyboard->keyboard.trap_data)
657     xkeycaps_xtrap_simulate_event (keyboard, &event);
658   else
659 #endif /* HAVE_XTRAP */
660   if (! XSendEvent (XtDisplay (widget), window, True,
661 		    down_p ? KeyPressMask : KeyReleaseMask,
662 		    &event))
663     {
664       fprintf (stderr, "%s: Couldn't simulate a keypress: ", progname);
665       if (window == None) fprintf (stderr, "No focus window\n");
666       else
667 	fprintf (stderr, "focus window doesn't accept XSendEvent input\n");
668     }
669   XSync (XtDisplay (widget), 0);
670   if (XSendEvent_BadWindow &&
671       keyboard->keyboard.target_window == XSendEvent_BadWindow)
672     {
673       char buf [255];
674       sprintf (buf,
675 	       "Window 0x%X seems to have gone away.  Keyboard focus cleared.",
676 	       (int) XSendEvent_BadWindow);
677       message (keyboard, buf);
678       XBell (XtDisplay ((Widget) widget), 0);
679       XSendEvent_BadWindow = 0;
680       keyboard->keyboard.target_window = 0;
681     }
682 }
683 
684 
685 int mappingNotify_event_expected = 0;
686 
687 static Bool
688 #ifdef __STDC__
mapping_event_p(Display * dpy,XEvent * event,char * closure)689 mapping_event_p (Display *dpy, XEvent *event, char *closure)
690 #else /* ! __STDC__ */
691 mapping_event_p (dpy, event, closure)
692      Display *dpy;
693      XEvent *event;
694      char *closure;
695 #endif /* ! __STDC__ */
696 {
697   if (event->xany.type == MappingNotify) *closure = 1;
698   return False;
699 }
700 
701 void
702 #ifdef __STDC__
keyboard_handle_mapping_notify(Widget widget,XtPointer client_data,XEvent * event,Boolean * cont)703 keyboard_handle_mapping_notify (Widget widget, XtPointer client_data,
704 				XEvent *event, Boolean *cont)
705 #else /* ! __STDC__ */
706 keyboard_handle_mapping_notify (widget, client_data, event, cont)
707      Widget widget;
708      XtPointer client_data;
709      XEvent *event;
710      Boolean *cont;
711 #endif /* ! __STDC__ */
712 {
713   char events_queued_p = 0;
714   XRefreshKeyboardMapping (&event->xmapping);
715   init_modifier_mapping ((KeyboardWidget) widget);
716   XSync (XtDisplay (widget), 0);
717   if (XCheckIfEvent (XtDisplay (widget), event,
718 		     mapping_event_p, &events_queued_p))
719     exit (-69); /* can't happen. */
720 
721   if (!events_queued_p)
722     /* refresh, in case the documented key has been modified */
723     {
724       if (((KeyboardWidget) widget)->keyboard.documented_key)
725 	describe_key (((KeyboardWidget) widget)->keyboard.documented_key);
726       if (! mappingNotify_event_expected)
727 	message ((KeyboardWidget) widget,
728 		 "MappingNotify event received; keyboard state refreshed.");
729       else
730 	mappingNotify_event_expected = 0;
731     }
732 }
733 
734 void
735 #ifdef __STDC__
keyboard_handle_keymap_notify(Widget w,XtPointer client_data,XEvent * event,Boolean * cont)736 keyboard_handle_keymap_notify (Widget w, XtPointer client_data, XEvent *event,
737 			       Boolean *cont)
738 #else /* ! __STDC__ */
739 keyboard_handle_keymap_notify (w, client_data, event, cont)
740      Widget w;
741      XtPointer client_data;
742      XEvent *event;
743      Boolean *cont;
744 #endif /* ! __STDC__ */
745 {
746   /* To prevent getting out of sync with the real state of the
747      keyboard during those unfortunate moments when we don't have
748      the keyboard focus, we select KeymapNotify, which gives us
749      the same data as XQueryKeymap would, but sends it just after
750      any FocusIn or EnterNotify event would have been generated.
751      Unfortunately, this means that this event will be generated
752      every time the cursor move from inside of a key to the top-
753      level keyboard window (since a particular kind of EnterNotify
754      event would be generated there.)  This shouldn't be too big
755      a deal: it's just another 40 or so bytes, and a call to bcmp.
756    */
757   KeyboardWidget widget = (KeyboardWidget) w;
758   int i, j;
759 
760 #ifdef HAVE_XTRAP
761   if (((KeyboardWidget) widget)->keyboard.trap_data)
762     return;	/* if we're in XTrap mode, this function is redundant */
763 #endif
764 
765   if (widget->core.being_destroyed)
766     return;
767 
768   if (! memcmp (event->xkeymap.key_vector,
769 		((KeyboardWidget) widget)->keyboard.key_state_vector,
770 		32))
771     return;	/* Nothing's changed, bug out. */
772 
773   for (i = 0; i < widget->keyboard.nrows; i++)
774     for (j = 0; j < widget->keyboard.rows[i].nkeys; j++)
775       {
776 	KeyWidget key = widget->keyboard.rows[i].keys[j];
777 	int down = ((event->xkeymap.key_vector [key->key.keycode / 8])
778 		    & (1 << (key->key.keycode % 8)));
779 	if (! key->key.keycode) continue;
780 	if (down && !key->key.key_highlighted)
781 	  /* Down now, wasn't before */
782 	  highlight_key (key);
783 	else if (!down && key->key.key_highlighted)
784 	  /* Not down now, was before */
785 	  dehighlight_key (key);
786       }
787 #if 0
788   /* This sometimes fails because the event contains random noise in the
789      bits which correspond to a keycode for which there is no physical key.
790      Presumably this is because X events are padded with uninitialized data.
791    */
792   if (bcmp (event->xkeymap.key_vector, kbd->key_state_vector, 32))
793     fprintf (stderr,
794 	     "%s: KeymapNotify handling is way broken...\n",
795 	     progname);
796 #endif
797 }
798 
799 
800 
801 
802 KeyWidget
803 #ifdef __STDC__
window_to_key(Display * dpy,Window window)804 window_to_key (Display *dpy, Window window)
805 #else /* ! __STDC__ */
806 window_to_key (dpy, window)
807      Display *dpy;
808      Window window;
809 #endif /* ! __STDC__ */
810 {
811   KeyWidget key = (KeyWidget) XtWindowToWidget (dpy, window);
812   if (! key) return 0;
813   if (! XtIsSubclass ((Widget) key, keyWidgetClass)) return 0;
814   return key;
815 }
816 
817 
818 KeyWidget
819 #ifdef __STDC__
keycode_to_key(KeyboardWidget keyboard,KeyCode code)820 keycode_to_key (KeyboardWidget keyboard, KeyCode code)
821 #else /* ! __STDC__ */
822 keycode_to_key (keyboard, code)
823      KeyboardWidget keyboard;
824      KeyCode code;
825 #endif /* ! __STDC__ */
826 {
827   int i, j;
828   if (! code) return 0;
829   for (i = 0; i < keyboard->keyboard.nrows; i++)
830     for (j = 0; j < keyboard->keyboard.rows[i].nkeys; j++)
831       if (keyboard->keyboard.rows[i].keys[j]->key.keycode == code)
832 	return keyboard->keyboard.rows[i].keys[j];
833   return 0;
834 }
835 
836 
837 static void
838 #ifdef __STDC__
clear_modifier_flags(KeyboardWidget widget)839 clear_modifier_flags (KeyboardWidget widget)
840 #else /* ! __STDC__ */
841 clear_modifier_flags (widget)
842      KeyboardWidget widget;
843 #endif /* ! __STDC__ */
844 {
845   int i, j;
846   memset (widget->keyboard.modifier_vector, 0, 32);
847   for (i = 0; i < widget->keyboard.nrows; i++)
848     for (j = 0; j < widget->keyboard.rows[i].nkeys; j++)
849       if (widget->keyboard.rows[i].keys[j])
850 	widget->keyboard.rows[i].keys[j]->key.modifier_bits = 0;
851 }
852 
853 
854 void
855 #ifdef __STDC__
init_modifier_mapping(KeyboardWidget widget)856 init_modifier_mapping (KeyboardWidget widget)
857 #else /* ! __STDC__ */
858 init_modifier_mapping (widget)
859      KeyboardWidget widget;
860 #endif /* ! __STDC__ */
861 {
862   Display *dpy = XtDisplay (widget);
863   XModifierKeymap *modifier_map;
864   int modifier_index, modifier_key, i, j;
865   XKeyboardState kbd_state;
866 /*  XKeyboardControl kbd_control;*/
867   int mkpm;
868 
869   clear_modifier_flags (widget);
870   modifier_map = XGetModifierMapping (dpy);
871   /* Look at the defined modifier keys, and annotate our key structures
872      with which modifier bits they set.  This will be done each time the
873      user runs xmodmap as well.
874    */
875   mkpm = modifier_map->max_keypermod;
876   for (modifier_index = 0; modifier_index < 8; modifier_index++)
877     for (modifier_key = 0; modifier_key < mkpm; modifier_key++)
878       {
879 	int index = modifier_index * mkpm + modifier_key;
880 	int keycode = modifier_map->modifiermap [index];
881 	KeyWidget key = keycode_to_key (widget, keycode);
882 	if (! key) continue;
883 	key->key.modifier_bits |= (1 << modifier_index);
884 	widget->keyboard.modifier_vector [keycode / 8]
885 	  |= (1 << (keycode % 8));
886       }
887   XFreeModifiermap (modifier_map);
888 
889 # if 0
890   kbd_control.auto_repeat_mode = 0;
891   XChangeKeyboardControl (dpy, KBAutoRepeatMode, &kbd_control);
892 # endif
893 
894   XGetKeyboardControl (dpy, &kbd_state);
895   for (i = 0; i < widget->keyboard.nrows; i++)
896     for (j = 0; j < widget->keyboard.rows[i].nkeys; j++)
897       {
898 	KeyWidget key = widget->keyboard.rows[i].keys[j];
899 	if (key)
900 	  key->key.auto_repeat_p
901 	    = !!((kbd_state.auto_repeats [key->key.keycode / 8])
902 		 & (1 << (key->key.keycode % 8)));
903       }
904 }
905