API revisions: tweaks to key event API, added some reserved slots for
[platform/core/uifw/at-spi2-atk.git] / registryd / deviceeventcontroller.c
1 /*
2  * AT-SPI - Assistive Technology Service Provider Interface
3  * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
4  *
5  * Copyright 2001 Sun Microsystems Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 /* deviceeventcontroler.c: implement the DeviceEventController interface */
24
25 #include <config.h>
26
27 #undef SPI_DEBUG
28
29 #ifdef SPI_DEBUG
30 #  include <stdio.h>
31 #endif
32
33 #include <X11/Xlib.h>
34 #include <X11/extensions/XTest.h>
35 #define XK_MISCELLANY
36 #include <X11/keysymdef.h>
37 #include <gdk/gdkx.h> /* TODO: hide dependency (wrap in single porting file) */
38 #include <gdk/gdkwindow.h>
39
40 #include "deviceeventcontroller.h"
41
42 /* Our parent Gtk object type */
43 #define PARENT_TYPE BONOBO_TYPE_OBJECT
44
45 /* A pointer to our parent object class */
46 static GObjectClass *spi_device_event_controller_parent_class;
47
48 static gboolean kbd_registered = FALSE;
49
50 static Window root_window;
51
52 typedef enum {
53   SPI_DEVICE_TYPE_KBD,
54   SPI_DEVICE_TYPE_MOUSE,
55   SPI_DEVICE_TYPE_LAST_DEFINED
56 } SpiDeviceTypeCategory;
57
58 struct _DEControllerGrabMask {
59   Accessibility_ControllerEventMask modmask;
60   CORBA_unsigned_long               keyval;
61   unsigned int                      refcount;
62 };
63
64 typedef struct _DEControllerGrabMask DEControllerGrabMask;
65
66 struct _DEControllerListener {
67   CORBA_Object          object;
68   SpiDeviceTypeCategory type;
69 };
70
71 typedef struct _DEControllerListener DEControllerListener;
72
73 struct _DEControllerKeyListener {
74   DEControllerListener listener;
75   Accessibility_KeySet *keys;
76   Accessibility_ControllerEventMask mask;
77   Accessibility_KeyEventTypeSeq *typeseq;
78   Accessibility_EventListenerMode *mode;        
79 };
80
81 typedef struct _DEControllerKeyListener DEControllerKeyListener;
82
83 static gboolean spi_controller_register_with_devices (SpiDeviceEventController *controller);
84 static gboolean spi_controller_grab_keyboard (SpiDeviceEventController *controller);
85
86 static void spi_controller_register_device_listener (SpiDeviceEventController *controller,
87                                                      DEControllerListener *l,
88                                                      CORBA_Environment *ev);
89
90 /*
91  * Private methods
92  */
93
94 static Display *
95 spi_get_display (void )
96 {
97  static Display *display = NULL;
98  /* We must open a new connection to the server to avoid clashing with the GDK event loop */
99  /*
100   * TODO: fixme, this makes the foolish assumption that registryd uses
101   * the same display as the apps, and the the DISPLAY environment variable is set.
102   */
103  
104  if (!display)
105    {
106      display = XOpenDisplay (g_getenv ("DISPLAY"));
107    }
108  return display;
109 }
110
111 static DEControllerGrabMask *
112 spi_grabmask_clone (DEControllerGrabMask *grabmask)
113 {
114   DEControllerGrabMask *clone = g_new0 (DEControllerGrabMask, 1);
115   memcpy (clone, grabmask, sizeof (DEControllerGrabMask));
116   return clone;
117 }
118
119 static gint
120 spi_compare_corba_objects (gconstpointer p1, gconstpointer p2)
121 {
122   CORBA_Environment ev;
123   gint retval;
124   retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
125
126 #ifdef SPI_DEBUG
127   fprintf (stderr, "comparing %p to %p; result %d\n",
128            p1, p2,
129            retval);
130 #endif
131   return retval;  
132 }
133
134 static gint
135 spi_compare_listeners (gconstpointer p1, gconstpointer p2)
136 {
137   DEControllerListener *l1 = (DEControllerListener *) p1;       
138   DEControllerListener *l2 = (DEControllerListener *) p2;       
139   return spi_compare_corba_objects (l1->object, l2->object);
140 }
141
142 static gint
143 spi_grabmask_compare_values (gconstpointer p1, gconstpointer p2)
144 {
145   DEControllerGrabMask *l1;
146   DEControllerGrabMask *l2;
147   if (p1 == p2)
148     {
149       return 0;
150     }
151   else
152     { 
153       l1 = (DEControllerGrabMask *) p1; 
154       l2 = (DEControllerGrabMask *) p2;
155       return ((l1->modmask != l2->modmask) || (l1->keyval != l2->keyval));
156     }
157 }
158
159 static DEControllerKeyListener *
160 spi_dec_key_listener_new (CORBA_Object l,
161                           const Accessibility_KeySet *keys,
162                           const Accessibility_ControllerEventMask mask,
163                           const Accessibility_KeyEventTypeSeq *typeseq,
164                           const Accessibility_EventListenerMode *mode,
165                           CORBA_Environment *ev)
166 {
167   DEControllerKeyListener *key_listener = g_new0 (DEControllerKeyListener, 1);
168   key_listener->listener.object = bonobo_object_dup_ref (l, ev);
169   key_listener->listener.type = SPI_DEVICE_TYPE_KBD;
170   key_listener->keys = ORBit_copy_value (keys, TC_Accessibility_KeySet);
171   key_listener->mask = mask;
172   key_listener->typeseq = ORBit_copy_value (typeseq, TC_Accessibility_KeyEventTypeSeq);
173   if (mode)
174     key_listener->mode = ORBit_copy_value (mode, TC_Accessibility_EventListenerMode);
175   else
176     key_listener->mode = NULL;
177
178 #ifdef SPI_DEBUG
179   g_print ("new listener, with mask %x, is_global %d, keys %p\n",
180            (unsigned int) key_listener->mask,
181            (int) mode->global,
182            (void *) key_listener->keys);
183 #endif
184   return key_listener;  
185 }
186
187 static void
188 spi_dec_key_listener_free (DEControllerKeyListener *key_listener, CORBA_Environment *ev)
189 {
190   bonobo_object_release_unref (key_listener->listener.object, ev);
191   CORBA_free (key_listener->typeseq);
192   CORBA_free (key_listener->keys);
193   g_free (key_listener);
194 }
195
196 static void
197 spi_controller_deregister_global_keygrabs (SpiDeviceEventController *controller,
198                                            DEControllerKeyListener *key_listener)
199 {
200   GList *list_ptr;
201   DEControllerGrabMask *mask_ptr;
202   /* TODO: implement this! Also remember to release any keygrabs still held */
203   ;
204 }
205
206 static void
207 spi_controller_register_global_keygrabs (SpiDeviceEventController *controller,
208                                          DEControllerKeyListener *key_listener)
209 {
210   DEControllerGrabMask grabmask, *grabmask_ptr;
211   GList *list_ptr;
212   gint i;
213   /* TODO: deregistration version of this function */
214   
215   grabmask.modmask = key_listener->mask;
216   if (key_listener->keys->_length == 0) /* special case means AnyKey/AllKeys */
217     {
218       grabmask.keyval = AnyKey;
219       list_ptr = g_list_find_custom (controller->keygrabs_list, &grabmask,
220                                      spi_grabmask_compare_values);
221       if (list_ptr)
222         {
223           grabmask_ptr = (DEControllerGrabMask *) list_ptr->data;
224           grabmask_ptr->refcount++;
225         }
226       else
227         {
228           controller->keygrabs_list =
229                   g_list_prepend (controller->keygrabs_list,
230                                   spi_grabmask_clone (&grabmask));
231         }
232     }
233   else
234     {
235       for (i = 0; i < key_listener->keys->_length; ++i)
236         {
237           long int keyval = key_listener->keys->_buffer[i];
238           /* X Grabs require keycodes, not keysyms */
239           if (keyval >= 0)
240             {
241               keyval = XKeysymToKeycode(spi_get_display (), (KeySym) keyval);               
242             }
243           grabmask.keyval = keyval;
244           list_ptr = g_list_find_custom (controller->keygrabs_list, &grabmask,
245                                              spi_grabmask_compare_values);
246           if (list_ptr)
247             {
248               grabmask_ptr = (DEControllerGrabMask *) list_ptr->data;
249               grabmask_ptr->refcount++;
250             }
251           else
252             {
253               controller->keygrabs_list =
254                   g_list_prepend (controller->keygrabs_list,
255                                   spi_grabmask_clone (&grabmask));
256               fprintf (stderr, "appending mask with val=%lu\n",
257                        (unsigned long) grabmask.modmask);
258             }
259         }
260     }
261 }
262
263 static void
264 spi_controller_register_device_listener (SpiDeviceEventController *controller,
265                                          DEControllerListener *listener,
266                                          CORBA_Environment *ev)
267 {
268   DEControllerKeyListener *key_listener;
269   
270   switch (listener->type) {
271   case SPI_DEVICE_TYPE_KBD:
272       key_listener = (DEControllerKeyListener *) listener;        
273       controller->key_listeners = g_list_prepend (controller->key_listeners, key_listener);
274       if (key_listener->mode->global)
275         {
276           spi_controller_register_global_keygrabs (controller, key_listener);   
277         }
278       break;
279   case SPI_DEVICE_TYPE_MOUSE:
280 /*    controller->mouse_listeners = g_list_append (controller->mouse_listeners,
281                                                    CORBA_Object_duplicate (l, ev));*/
282
283 /* this interface should only be used for mouse motion events, not mouse clicks events */
284       break;
285   }
286 }
287
288 static void
289 spi_controller_deregister_device_listener (SpiDeviceEventController *controller,
290                                            DEControllerListener *listener,
291                                            CORBA_Environment *ev)
292 {
293   Accessibility_ControllerEventMask *mask_ptr;
294   DEControllerListener *dec_listener;
295   GList *list_ptr;
296   switch (listener->type)
297     {
298       case SPI_DEVICE_TYPE_KBD:
299         spi_controller_deregister_global_keygrabs (controller,
300                                                    (DEControllerKeyListener *) listener);
301
302         /* now, remove this listener from the keylistener list */
303         list_ptr = g_list_find_custom (controller->key_listeners, listener, spi_compare_listeners);
304         if (list_ptr)
305           {
306             dec_listener = (DEControllerListener *) list_ptr->data;
307 #ifdef SPI_DEBUG          
308             g_print ("removing keylistener %p\n", dec_listener->object);
309 #endif
310             controller->key_listeners = g_list_remove_link (controller->key_listeners,
311                                                           list_ptr);
312             spi_dec_key_listener_free ((DEControllerKeyListener *) dec_listener, ev);
313           }
314         break;
315       case SPI_DEVICE_TYPE_MOUSE: /* TODO: implement */
316         break;
317     }
318 }
319
320 static gboolean
321 spi_controller_register_with_devices (SpiDeviceEventController *controller)
322 {
323   gboolean retval = FALSE;
324
325   /* calls to device-specific implementations and routines go here */
326   /* register with: keyboard hardware code handler */
327   /* register with: (translated) keystroke handler */
328
329   /* We must open a new connection to the server to avoid clashing with the GDK event loop */
330   root_window = DefaultRootWindow (spi_get_display ());         
331   XSelectInput (spi_get_display (),
332                 root_window,
333                 KeyPressMask | KeyReleaseMask);
334   /* register with: mouse hardware device handler? */
335   /* register with: mouse event handler */
336   return retval;
337 }
338
339 static gboolean
340 spi_key_set_contains_key (Accessibility_KeySet *key_set, const Accessibility_DeviceEvent *key_event)
341 {
342   gint i;
343   gint len;
344
345   /* g_assert (key_set); */
346   if (!key_set) { g_print ("null key set!"); return TRUE; }
347
348   len = key_set->_length;
349   
350   if (len == 0) /* special case, means "all keys/any key" */
351     {
352       g_print ("anykey\n");         
353       return TRUE;
354     }
355
356   for (i=0; i<len; ++i)
357     {
358 #ifdef SPI_KEYEVENT_DEBUG           
359       g_print ("key_set[%d] = %d; key_event %d, code %d\n",
360                 i,
361                (int) key_set->_buffer[i],
362                (int) key_event->id,
363                (int) key_event->hw_code); 
364 #endif
365       if (key_set->_buffer[i] == (CORBA_long) key_event->id) return TRUE;
366       if (key_set->_buffer[i] == (CORBA_long) -key_event->hw_code) return TRUE;
367     }
368   
369   return FALSE;
370 }
371
372 static gboolean
373 spi_key_eventtype_seq_contains_event (Accessibility_KeyEventTypeSeq *type_seq,
374                                   const Accessibility_DeviceEvent *key_event)
375 {
376   gint i;
377   gint len;
378
379   /* g_assert (type_seq); */
380   if (!type_seq) { g_print ("null type seq!"); return TRUE; }
381
382   len = type_seq->_length;
383   
384   if (len == 0) /* special case, means "all events/any event" */
385     {
386       return TRUE;
387     }
388
389   for (i=0; i<len; ++i)
390     {
391 #ifdef SPI_DEBUG            
392       g_print ("type_seq[%d] = %d; key event type = %d\n", i, (int) type_seq->_buffer[i],
393                (int) key_event->type);
394 #endif      
395       if (type_seq->_buffer[i] == (CORBA_long) key_event->type) return TRUE;        
396     }
397   
398   return FALSE;
399 }
400
401 static gboolean
402 spi_key_event_matches_listener (const Accessibility_DeviceEvent *key_event,
403                             DEControllerKeyListener *listener,
404                             CORBA_boolean is_system_global)
405 {
406   g_print ("checking keycode %d\n", (int) key_event->hw_code);
407   if ((key_event->modifiers == (CORBA_unsigned_short) (listener->mask & 0xFFFF)) &&
408        spi_key_set_contains_key (listener->keys, key_event) &&
409        spi_key_eventtype_seq_contains_event (listener->typeseq, key_event) && 
410       (is_system_global == listener->mode->global))
411     {
412       return TRUE;
413     }
414   else
415     return FALSE;
416 }
417
418 static gboolean
419 spi_notify_keylisteners (GList *key_listeners,
420                          const Accessibility_DeviceEvent *key_event,
421                          CORBA_boolean is_system_global,
422                          CORBA_Environment *ev)
423 {
424   int i, n_listeners = g_list_length (key_listeners);
425   gboolean is_consumed = FALSE;
426
427   for (i=0; i<n_listeners && !is_consumed; ++i)
428     {
429       Accessibility_DeviceEventListener ls;
430       DEControllerKeyListener *key_listener = (DEControllerKeyListener *)
431             g_list_nth_data (key_listeners, i);
432       ls = (Accessibility_DeviceEventListener) key_listener->listener.object;
433       if (spi_key_event_matches_listener (key_event, key_listener, is_system_global))
434         {
435           if (!CORBA_Object_is_nil(ls, ev))
436             {
437               is_consumed = Accessibility_DeviceEventListener_notifyEvent (ls, key_event, ev);
438             }           
439         }
440       else
441         {
442 #ifdef SPI_KEYEVENT_DEBUG
443               g_print ("no match for listener %d\n", i);
444 #endif
445               ;
446         }
447     }
448   return is_consumed;
449 }
450
451 static Accessibility_DeviceEvent
452 spi_keystroke_from_x_key_event (XKeyEvent *x_key_event)
453 {
454   Accessibility_DeviceEvent key_event;
455   KeySym keysym;
456   const int cbuf_bytes = 20;
457   char cbuf [cbuf_bytes];
458   
459   keysym = XLookupKeysym (x_key_event, 0);
460   key_event.id = (CORBA_long)(keysym);
461   key_event.hw_code = (CORBA_short) x_key_event->keycode;
462   if (((XEvent *) x_key_event)->type == KeyPress)
463     {
464       key_event.type = Accessibility_KEY_PRESSED;
465     }
466   else
467     {
468       key_event.type = Accessibility_KEY_RELEASED;
469     } 
470   key_event.modifiers = (CORBA_unsigned_short)(x_key_event->state);
471   key_event.is_text = CORBA_FALSE;
472   switch (keysym)
473     {
474       case ' ':
475         key_event.event_string = CORBA_string_dup ("space");
476         break;
477       case XK_Tab:
478         key_event.event_string = CORBA_string_dup ("Tab");
479         break;
480       case XK_BackSpace:
481         key_event.event_string = CORBA_string_dup ("Backspace");
482         break;
483       case XK_Return:
484         key_event.event_string = CORBA_string_dup ("Return");
485         break;
486       default:
487         if (XLookupString (x_key_event, cbuf, cbuf_bytes, &keysym, NULL) > 0)
488           {
489             key_event.event_string = CORBA_string_dup (cbuf);
490             if (isgraph (keysym))
491               {
492                 key_event.is_text = CORBA_TRUE; /* FIXME: incorrect for some composed chars? */
493               }
494           }
495         else
496           {
497             key_event.event_string = CORBA_string_dup ("");
498           }
499     }
500
501   key_event.timestamp = (CORBA_unsigned_long) x_key_event->time;
502 #ifdef SPI_KEYEVENT_DEBUG
503   fprintf (stderr,
504      "Key %lu pressed (%c), modifiers %d\n",
505      (unsigned long) keysym,
506      keysym ? (int) keysym : '*',
507      (int) x_key_event->state);
508 #endif
509 #ifdef SPI_DEBUG
510   fprintf (stderr, "%s%c",
511      (x_key_event->state & Mod1Mask)?"Alt-":"",
512      ((x_key_event->state & ShiftMask)^(x_key_event->state & LockMask))?
513      g_ascii_toupper (keysym) : g_ascii_tolower (keysym));
514 #endif /* SPI_DEBUG */
515   return key_event;     
516 }
517
518
519 static gboolean
520 spi_check_key_event (SpiDeviceEventController *controller)
521 {
522         static gboolean initialized = FALSE;
523         XEvent *x_event = g_new0 (XEvent, 1);
524         XKeyEvent *x_key_event;
525         gboolean is_consumed = FALSE;
526         Accessibility_DeviceEvent key_event;
527         static CORBA_Environment ev;
528
529         if (!initialized)
530         {
531           initialized = TRUE;
532           CORBA_exception_init (&ev);
533         }
534
535         while (XPending(spi_get_display ()))
536           {
537             XNextEvent (spi_get_display (), x_event);
538             if (XFilterEvent (x_event, None)) continue;   
539             if (x_event->type == KeyPress || x_event->type == KeyRelease)
540               {
541                 key_event = spi_keystroke_from_x_key_event ((XKeyEvent *) x_event);
542                 /* relay to listeners, and decide whether to consume it or not */
543                 is_consumed = spi_notify_keylisteners (controller->key_listeners, &key_event, CORBA_TRUE, &ev);
544               }
545             else
546               {
547 #ifdef SPI_KEYEVENT_DEBUG
548                 fprintf (stderr, "other event, type %d\n", (int) x_event->type);
549 #endif
550               }
551
552             if (is_consumed)
553               {
554                 XAllowEvents (spi_get_display (), AsyncKeyboard, CurrentTime);
555               }
556             else
557               {
558                 XAllowEvents (spi_get_display (), ReplayKeyboard, CurrentTime);
559               }
560           }
561         XUngrabKey (spi_get_display (), AnyKey, AnyModifier, root_window);
562
563         return spi_controller_grab_keyboard (controller);
564 }
565
566 static gboolean
567 spi_controller_grab_keyboard (SpiDeviceEventController *controller)
568 {
569   GList *maskList = controller->keygrabs_list;
570   int i;
571   int last_mask;
572   last_mask = g_list_length (maskList);
573
574 /*
575  * masks known to work with default RH 7.1: 
576  * 0 (no mods), LockMask, Mod1Mask, Mod2Mask, ShiftMask,
577  * ShiftMask|LockMask, Mod1Mask|LockMask, Mod2Mask|LockMask,
578  * ShiftMask|Mod1Mask, ShiftMask|Mod2Mask, Mod1Mask|Mod2Mask,
579  * ShiftMask|LockMask|Mod1Mask, ShiftMask|LockMask|Mod2Mask,
580  *
581  * ControlMask grabs are broken, must be in use already
582  */
583         
584   for (i=0; i < last_mask; ++i)
585     {
586       DEControllerGrabMask * grab_mask
587                 = (DEControllerGrabMask *) g_list_nth_data (maskList, i);
588       unsigned long maskVal = 0xFFFFFFFF;
589       int           keyVal = AnyKey;
590       if (grab_mask)
591         {
592           maskVal = (unsigned long) grab_mask->modmask;
593           keyVal =  grab_mask->keyval;
594         }
595 #ifdef SPI_DEBUG
596       fprintf (stderr, "mask=%lx\n", maskVal);
597 #endif
598       if (!(maskVal & ControlMask))
599         {
600           XGrabKey (spi_get_display (),
601                     keyVal, 
602                     maskVal,
603                     root_window,
604                     True,
605                     GrabModeAsync,
606                     GrabModeAsync);
607           /* TODO: check call for errors and return FALSE if error occurs */
608         }
609       else
610         {
611           return FALSE; /* can't do control key yet */
612         }
613     }
614   return TRUE;
615 }
616
617 /*
618  * Implemented GObject::finalize
619  */
620 static void
621 spi_device_event_controller_object_finalize (GObject *object)
622 {
623
624 #ifdef SPI_DEBUG
625   fprintf(stderr, "spi_device_event_controller_object_finalize called\n");
626 #endif
627   /* disconnect any special listeners, get rid of outstanding keygrabs */
628         
629   spi_device_event_controller_parent_class->finalize (object);
630 }
631
632 /*
633  * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
634  *     method implementation
635  */
636 static void
637 impl_register_keystroke_listener (PortableServer_Servant     servant,
638                                   const Accessibility_DeviceEventListener l,
639                                   const Accessibility_KeySet *keys,
640                                   const Accessibility_ControllerEventMask mask,
641                                   const Accessibility_KeyEventTypeSeq *type,
642                                   const Accessibility_EventListenerMode *mode,
643                                   CORBA_Environment         *ev)
644 {
645   SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
646           bonobo_object_from_servant (servant));
647   DEControllerKeyListener *dec_listener;
648 #ifdef SPI_DEBUG
649   fprintf (stderr, "registering keystroke listener %p with maskVal %lu\n",
650            (void *) l, (unsigned long) mask);
651 #endif
652   dec_listener = spi_dec_key_listener_new (l, keys, mask, type, mode, ev);
653   spi_controller_register_device_listener (controller, (DEControllerListener *) dec_listener, ev);
654 }
655
656 /*
657  * CORBA Accessibility::DeviceEventController::deregisterKeystrokeListener
658  *     method implementation
659  */
660 static void
661 impl_deregister_keystroke_listener (PortableServer_Servant     servant,
662                                     const Accessibility_DeviceEventListener l,
663                                     const Accessibility_KeySet *keys,
664                                     const Accessibility_ControllerEventMask mask,
665                                     const Accessibility_KeyEventTypeSeq *type,
666                                     CORBA_Environment         *ev)
667 {
668         SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
669                 bonobo_object_from_servant (servant));
670         DEControllerKeyListener *key_listener = spi_dec_key_listener_new (l,
671                                                                           keys,
672                                                                           mask,
673                                                                           type,
674                                                                           NULL,
675                                                                           ev);
676 #ifdef SPI_DEREGISTER_DEBUG
677         fprintf (stderr, "deregistering keystroke listener %p with maskVal %lu\n",
678                  (void *) l, (unsigned long) mask->value);
679 #endif
680         spi_controller_deregister_device_listener(controller,
681                                               (DEControllerListener *) key_listener,
682                                               ev);
683         spi_dec_key_listener_free (key_listener, ev);
684 }
685
686 /*
687  * CORBA Accessibility::DeviceEventController::registerMouseListener
688  *     method implementation
689  */
690 /*
691 static void
692 impl_register_mouse_listener (PortableServer_Servant     servant,
693                               const Accessibility_MouseListener *l,
694                               CORBA_Environment         *ev)
695 {
696         SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
697                 bonobo_object_from_servant (servant));
698 #ifdef SPI_DEBUG
699         fprintf (stderr, "registering mouse listener %p\n", l);
700 #endif
701         spi_controller_register_device_listener(controller, DEVICE_TYPE_MOUSE, l, keys, mask, ev);
702 }
703 */
704
705 static KeyCode
706 keycode_for_keysym (long keysym)
707 {
708   return XKeysymToKeycode (spi_get_display (), (KeySym) keysym);
709 }
710
711 #define SPI_DEBUG
712
713 /*
714  * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
715  *     method implementation
716  */
717 static void
718 impl_generate_keyboard_event (PortableServer_Servant     servant,
719                               const CORBA_long           keycode,
720                               const CORBA_char          *keystring,
721                               const Accessibility_KeySynthType synth_type,
722                               CORBA_Environment          *ev)
723 {
724   long key_synth_code;
725 #ifdef SPI_DEBUG
726   fprintf (stderr, "synthesizing keystroke %ld, type %d\n", (long) keycode, (int) synth_type);
727 #endif
728   /* TODO: hide/wrap/remove X dependency */
729
730   /* TODO: be accessX-savvy so that keyrelease occurs after sufficient timeout */
731         
732   /*
733    * TODO: when initializing, query for XTest extension before using,
734    * and fall back to XSendEvent() if XTest is not available.
735    */
736   
737   /* TODO: implement keystring mode also */
738         
739   switch (synth_type)
740     {
741       case Accessibility_KEY_PRESS:
742           XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, True, CurrentTime);
743           break;
744       case Accessibility_KEY_PRESSRELEASE:
745           XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, True, CurrentTime);
746       case Accessibility_KEY_RELEASE:
747           XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, False, CurrentTime);
748           break;
749       case Accessibility_KEY_SYM:
750           key_synth_code = keycode_for_keysym (keycode);
751           XTestFakeKeyEvent (spi_get_display (), (unsigned int) key_synth_code, True, CurrentTime);
752           XTestFakeKeyEvent (spi_get_display (), (unsigned int) key_synth_code, False, CurrentTime);
753           break;
754    }
755 }
756
757 /* Accessibility::DeviceEventController::generateMouseEvent */
758 static void
759 impl_generate_mouse_event (PortableServer_Servant servant,
760                            const CORBA_long       x,
761                            const CORBA_long       y,
762                            const CORBA_char      *eventName,
763                            CORBA_Environment     *ev)
764 {
765 #ifdef SPI_DEBUG
766   fprintf (stderr, "generating mouse %s event at %ld, %ld\n", eventName, x, y);
767 #endif
768 }
769
770 /* Accessibility::DeviceEventController::notifyListenersSync */
771 static CORBA_boolean
772 impl_notify_listeners_sync(PortableServer_Servant     servant,
773                            const Accessibility_DeviceEvent *event,
774                            CORBA_Environment         *ev)
775 {
776   SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
777                                          bonobo_object_from_servant (servant));
778 #ifdef SPI_DEBUG
779   g_print ("notifylistening listeners synchronously: controller %x, event id %d\n",
780            (void *) controller, (int) event->id);
781 #endif
782   return (spi_notify_keylisteners (controller->key_listeners, event, CORBA_FALSE, ev) ?
783           CORBA_TRUE : CORBA_FALSE); 
784 }
785
786 /* Accessibility::DeviceEventController::notifyListenersAsync */
787 static void
788 impl_notify_listeners_async (PortableServer_Servant     servant,
789                              const Accessibility_DeviceEvent *event,
790                              CORBA_Environment         *ev)
791 {
792   SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER(
793                                          bonobo_object_from_servant (servant));
794 #ifdef SPI_DEBUG
795   fprintf (stderr, "notifying listeners asynchronously\n");
796 #endif
797   spi_notify_keylisteners (controller->key_listeners, event, CORBA_FALSE, ev); 
798 }
799
800 static void
801 spi_device_event_controller_class_init (SpiDeviceEventControllerClass *klass)
802 {
803         GObjectClass * object_class = (GObjectClass *) klass;
804         POA_Accessibility_DeviceEventController__epv *epv = &klass->epv;
805         spi_device_event_controller_parent_class = g_type_class_peek_parent (klass);
806
807         object_class->finalize = spi_device_event_controller_object_finalize;
808
809         epv->registerKeystrokeListener = impl_register_keystroke_listener;
810         epv->deregisterKeystrokeListener = impl_deregister_keystroke_listener;
811 /*        epv->registerMouseListener = impl_register_mouse_listener; */
812         epv->generateKeyboardEvent = impl_generate_keyboard_event;
813         epv->generateMouseEvent = impl_generate_mouse_event;
814         epv->notifyListenersSync = impl_notify_listeners_sync;
815         epv->notifyListenersAsync = impl_notify_listeners_async;
816         klass->check_key_event = spi_check_key_event;
817 }
818
819 static void
820 spi_device_event_controller_init (SpiDeviceEventController *device_event_controller)
821 {
822   device_event_controller->key_listeners = NULL;
823   device_event_controller->mouse_listeners = NULL;
824   device_event_controller->keygrabs_list = NULL;
825   kbd_registered = spi_controller_register_with_devices (device_event_controller);
826 }
827
828 gboolean
829 spi_device_event_controller_check_key_event (SpiDeviceEventController *controller)
830 {
831   SpiDeviceEventControllerClass *klass = SPI_DEVICE_EVENT_CONTROLLER_GET_CLASS (controller);
832   if (klass->check_key_event)
833         return (klass->check_key_event) (controller);
834   return FALSE;
835 }
836
837 SpiDeviceEventController *
838 spi_device_event_controller_new (void *registryp)
839 {
840   BonoboObject *registry = (BonoboObject *) registryp;  
841   SpiDeviceEventController *retval = g_object_new (
842           SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL);
843   retval->registry = registry;
844   bonobo_object_ref (registry);
845   return retval;
846 }
847
848 BONOBO_TYPE_FUNC_FULL (SpiDeviceEventController,
849                        Accessibility_DeviceEventController,
850                        PARENT_TYPE,
851                        spi_device_event_controller);
852