2001-11-20 Michael Meeks <michael@ximian.com>
[platform/core/uifw/at-spi2-atk.git] / libspi / 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 #ifdef SPI_DEBUG
28 #  include <stdio.h>
29 #endif
30
31 #include <X11/Xlib.h>
32 #include <X11/extensions/XTest.h>
33 #include <gdk/gdkx.h> /* TODO: hide dependency (wrap in single porting file) */
34 #include <gdk/gdkwindow.h>
35
36 #include <libspi/deviceeventcontroller.h>
37
38 /* Our parent Gtk object type */
39 #define PARENT_TYPE BONOBO_TYPE_OBJECT
40
41 /* A pointer to our parent object class */
42 static GObjectClass *spi_device_event_controller_parent_class;
43
44 static gboolean kbd_registered = FALSE;
45
46 static Display *display;
47
48 static Window root_window;
49
50 typedef enum {
51         SPI_DEVICE_TYPE_KBD,
52         SPI_DEVICE_TYPE_MOUSE,
53         SPI_DEVICE_TYPE_LAST_DEFINED
54 } SpiDeviceTypeCategory;
55
56 struct _DEControllerListener {
57         CORBA_Object          object;
58         SpiDeviceTypeCategory type;
59 };
60
61 typedef struct _DEControllerListener DEControllerListener;
62
63 struct _DEControllerKeyListener {
64         DEControllerListener listener;
65         Accessibility_KeySet *keys;
66         Accessibility_ControllerEventMask *mask;
67         Accessibility_KeyEventTypeSeq *typeseq;
68         gboolean is_system_global;      
69 };
70
71 typedef struct _DEControllerKeyListener DEControllerKeyListener;
72
73 static gboolean _controller_register_with_devices (SpiDeviceEventController *controller);
74 static gboolean _controller_grab_keyboard (SpiDeviceEventController *controller);
75
76 static void controller_register_device_listener (SpiDeviceEventController *controller,
77                                                  DEControllerListener *l,
78                                                  CORBA_Environment *ev);
79
80 /*
81  * Private methods
82  */
83
84 static gint
85 _compare_corba_objects (gconstpointer p1, gconstpointer p2)
86 {
87   CORBA_Environment ev;
88   gint retval;
89   retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
90
91 #ifdef SPI_DEBUG
92   fprintf (stderr, "comparing %p to %p; result %d\n",
93            p1, p2,
94            retval);
95 #endif
96   return retval;  
97 }
98
99 static gint
100 _compare_listeners (gconstpointer p1, gconstpointer p2)
101 {
102   DEControllerListener *l1 = (DEControllerListener *) p1;       
103   DEControllerListener *l2 = (DEControllerListener *) p2;       
104   return _compare_corba_objects (l1->object, l2->object);
105 }
106
107 static gint
108 _eventmask_compare_value (gconstpointer p1, gconstpointer p2)
109 {
110     long d;
111     if (!p1 || !p2)
112         return (gint) (p1?1:(p2?-1:0));
113     else
114         d = ((long)((Accessibility_ControllerEventMask*)p2)->value) -
115                 ((long)((Accessibility_ControllerEventMask*)p1)->value);
116     return (gint) d;
117 }
118
119 static DEControllerKeyListener *
120 dec_key_listener_new (CORBA_Object l,
121                       const Accessibility_KeySet *keys,
122                       const Accessibility_ControllerEventMask *mask,
123                       const Accessibility_KeyEventTypeSeq *typeseq,
124                       const CORBA_boolean is_system_global,
125                       CORBA_Environment *ev)
126 {
127   DEControllerKeyListener *key_listener = g_new0 (DEControllerKeyListener, 1);
128   key_listener->listener.object = CORBA_Object_duplicate (l, ev);
129   key_listener->listener.type = SPI_DEVICE_TYPE_KBD;
130   key_listener->keys = keys;
131   key_listener->mask = mask;
132   key_listener->is_system_global = is_system_global;
133
134   return key_listener;  
135 }
136
137 static void
138 controller_register_device_listener (SpiDeviceEventController *controller,
139                                      DEControllerListener *listener,
140                                      CORBA_Environment *ev)
141 {
142   Accessibility_ControllerEventMask *mask_ptr = NULL;
143   DEControllerKeyListener *key_listener;
144   
145   switch (listener->type) {
146   case SPI_DEVICE_TYPE_KBD:
147       key_listener = (DEControllerKeyListener *) listener;        
148       controller->key_listeners = g_list_append (controller->key_listeners, key_listener);
149       
150       mask_ptr = (Accessibility_ControllerEventMask *)
151               g_list_find_custom (controller->keymask_list, (gpointer) key_listener->mask,
152                                   _eventmask_compare_value);
153       if (mask_ptr)
154               ++(mask_ptr->refcount);
155       else
156       {
157               if (key_listener->mask->refcount != (CORBA_unsigned_short) 1)
158                       fprintf (stderr, "mask initial refcount is not 1!\n");
159               if (key_listener->mask->value > (CORBA_unsigned_long) 2048)
160                       fprintf (stderr, "mask value looks invalid (%lu)\n",
161                                (unsigned long) key_listener->mask->value);
162               else
163                       fprintf (stderr, "appending mask with val=%lu\n",
164                                (unsigned long) key_listener->mask->value);
165               mask_ptr = Accessibility_ControllerEventMask__alloc();
166               mask_ptr->value = key_listener->mask->value;
167               mask_ptr->refcount = (CORBA_unsigned_short) 1;
168               controller->keymask_list = g_list_append (controller->keymask_list,
169                                                         (gpointer) mask_ptr);
170       }
171       break;
172   case SPI_DEVICE_TYPE_MOUSE:
173 /*    controller->mouse_listeners = g_list_append (controller->mouse_listeners,
174                                                    CORBA_Object_duplicate (l, ev));*/
175
176 /* possibly this interface should NOT be used for mouse events ? */
177       break;
178   }
179 }
180
181 static void
182 controller_deregister_device_listener (SpiDeviceEventController *controller,
183                                        DEControllerListener *listener,
184                                        CORBA_Environment *ev)
185 {
186   Accessibility_ControllerEventMask *mask_ptr;
187   DEControllerKeyListener *key_listener;
188   GList *list_ptr;
189   switch (listener->type) {
190   case SPI_DEVICE_TYPE_KBD:
191       key_listener = (DEControllerKeyListener *) listener;
192       list_ptr = g_list_find_custom (controller->key_listeners, listener, _compare_listeners);
193       /* TODO: need a different custom compare func */
194       if (list_ptr)
195           controller->key_listeners = g_list_remove (controller->key_listeners, list_ptr);
196       list_ptr = (GList *)
197                   g_list_find_custom (controller->keymask_list, (gpointer) key_listener->mask,
198                                      _eventmask_compare_value);
199       if (list_ptr)
200         {
201           mask_ptr = (Accessibility_ControllerEventMask *) list_ptr->data;
202           if (mask_ptr)
203               --mask_ptr->refcount;
204           if (!mask_ptr->refcount)
205             {
206               controller->keymask_list =
207                       g_list_remove (controller->keymask_list, mask_ptr);
208               ;  /* TODO: release any key grabs that are in place for this key mask */
209             }
210         }
211       break;
212   case SPI_DEVICE_TYPE_MOUSE:
213 /*    controller->mouse_listeners = g_list_append (controller->mouse_listeners,
214                                                    CORBA_Object_duplicate (l, ev));*/
215
216 /* possibly this interface should NOT be used for mouse events ? */
217       break;
218   }
219 }
220
221 static gboolean
222 _controller_register_with_devices (SpiDeviceEventController *controller)
223 {
224   gboolean retval = FALSE;
225
226   /* calls to device-specific implementations and routines go here */
227   /* register with: keyboard hardware code handler */
228   /* register with: (translated) keystroke handler */
229 #ifdef SPI_DEBUG
230   fprintf (stderr, "About to request events on window %ld of display %x\n",
231            (unsigned long) GDK_ROOT_WINDOW(), GDK_DISPLAY());
232 #endif
233   /* We must open a new connection to the server to avoid clashing with the GDK event loop */
234   display = XOpenDisplay (g_getenv ("DISPLAY"));
235   root_window = DefaultRootWindow (display);            
236   XSelectInput (display,
237                 root_window,
238                 KeyPressMask | KeyReleaseMask);
239   /* register with: mouse hardware device handler? */
240   /* register with: mouse event handler */
241   return retval;
242 }
243
244
245 static gboolean
246 notify_keylisteners (GList *key_listeners, Accessibility_KeyStroke *key_event, CORBA_Environment *ev)
247 {
248   int i, n_listeners = g_list_length (key_listeners);
249   gboolean is_consumed = FALSE;
250   for (i=0; i<n_listeners && !is_consumed; ++i)
251     {
252       Accessibility_KeystrokeListener ls;
253       ls = (Accessibility_KeystrokeListener)
254             g_list_nth_data (key_listeners, i);
255       if (!CORBA_Object_is_nil(ls, ev))
256         {
257             is_consumed = Accessibility_KeystrokeListener_keyEvent (ls, key_event, ev);
258         }               
259     }
260   return is_consumed;
261 }
262
263
264 static gboolean
265 _check_key_event (SpiDeviceEventController *controller)
266 {
267         static gboolean initialized = FALSE;
268         static gboolean is_active = FALSE;
269         XEvent *x_event = g_new0 (XEvent, 1);
270         XKeyEvent *x_key_event;
271         KeySym keysym;
272         gboolean is_consumed = FALSE;
273         char key_name[16];
274         int i;
275         Accessibility_KeyStroke key_event;
276         static CORBA_Environment ev;
277
278         if (!initialized)
279         {
280           initialized = TRUE;
281           CORBA_exception_init (&ev);
282         }
283
284         while (XPending(display))
285           {
286             XNextEvent (display, x_event);
287             if (XFilterEvent (x_event, None)) continue;   
288             if (x_event->type == KeyPress)
289               {
290                 x_key_event = (XKeyEvent *)x_event;
291                 keysym = XLookupKeysym (x_key_event, 0);
292                 key_event.keyID = (CORBA_long)(keysym);
293                 key_event.keycode = (CORBA_short) x_key_event->keycode;
294                 key_event.type = Accessibility_KEY_PRESSED;
295                 key_event.modifiers = (CORBA_unsigned_short)(x_key_event->state);
296 #ifdef SPI_KEYEVENT_DEBUG
297             fprintf (stderr,
298                      "Key %lu pressed (%c), modifiers %d\n",
299                      (unsigned long) keysym,
300                      keysym ? (int) keysym : '*',
301                      (int) x_key_event->state);
302 #endif
303 #ifdef SPI_DEBUG
304             fprintf(stderr, "%s%c",
305                     (x_key_event->state & Mod1Mask)?"Alt-":"",
306                     ((x_key_event->state & ShiftMask)^(x_key_event->state & LockMask))?
307                     (char) toupper((int) keysym) : (char) tolower((int)keysym));
308 #endif /* SPI_DEBUG */
309               }
310             else
311             {
312 #ifdef SPI_KEYEVENT_DEBUG
313                     fprintf (stderr, "other event, type %d\n", (int) x_event->type);
314 #endif
315             }
316             /* relay to listeners, and decide whether to consume it or not */
317             is_consumed = notify_keylisteners (controller->key_listeners, &key_event, &ev);
318
319             if (is_consumed)
320             {
321               XAllowEvents (display, SyncKeyboard, CurrentTime);
322             }
323             else
324             {
325               XAllowEvents (display, ReplayKeyboard, CurrentTime);
326             }
327           }
328         XUngrabKey (display, AnyKey, AnyModifier, root_window);
329         return _controller_grab_keyboard (controller);
330 }
331
332 static gboolean
333 _controller_grab_keyboard (SpiDeviceEventController *controller)
334 {
335         GList *maskList = controller->keymask_list;
336         int i;
337         int last_mask;
338         last_mask = g_list_length (maskList);
339
340 /*
341  * masks known to work with default RH 7.1: 
342  * 0 (no mods), LockMask, Mod1Mask, Mod2Mask, ShiftMask,
343  * ShiftMask|LockMask, Mod1Mask|LockMask, Mod2Mask|LockMask,
344  * ShiftMask|Mod1Mask, ShiftMask|Mod2Mask, Mod1Mask|Mod2Mask,
345  * ShiftMask|LockMask|Mod1Mask, ShiftMask|LockMask|Mod2Mask,
346  *
347  * ControlMask grabs are broken, must be in use already
348  */
349         
350         for (i=0; i < last_mask; ++i)
351         {
352                 Accessibility_ControllerEventMask *mask
353                         = (Accessibility_ControllerEventMask *)g_list_nth_data (maskList, i);
354                 unsigned long maskVal = 0xFFFFFFFF;
355                 if (mask) maskVal = (unsigned long) mask->value;
356 #ifdef SPI_KEYEVENT_DEBUG
357                 fprintf (stderr, "mask=%lx\n", maskVal);
358 #endif
359                 if (!(maskVal & ControlMask))
360                 {
361                         XGrabKey (display,
362                                   AnyKey,
363                                   maskVal,
364                                   root_window,
365                                   True,
366                                   GrabModeAsync,
367                                   GrabModeSync);
368                         /* TODO: check call for errors and return FALSE if error occurs */
369                 } else {
370                         return FALSE; /* can't do control key yet */
371                 }
372         }
373         return TRUE;
374 }
375
376 /*
377  * Implemented GObject::finalize
378  */
379 static void
380 spi_device_event_controller_object_finalize (GObject *object)
381 {
382
383 #ifdef SPI_DEBUG
384         fprintf(stderr, "spi_device_event_controller_object_finalize called\n");
385 #endif
386         spi_device_event_controller_parent_class->finalize (object);
387 }
388
389 /*
390  * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
391  *     method implementation
392  */
393 static void
394 impl_register_keystroke_listener (PortableServer_Servant     servant,
395                                   const Accessibility_KeystrokeListener l,
396                                   const Accessibility_KeySet *keys,
397                                   const Accessibility_ControllerEventMask *mask,
398                                   const Accessibility_KeyEventTypeSeq *type,
399                                   const CORBA_boolean is_system_global,
400                                   CORBA_Environment         *ev)
401 {
402         SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
403                 bonobo_object_from_servant (servant));
404         DEControllerKeyListener *dec_listener;
405 #ifdef SPI_DEBUG
406         fprintf (stderr, "registering keystroke listener %p with maskVal %lu\n",
407                  (void *) l, (unsigned long) mask->value);
408 #endif
409         dec_listener = dec_key_listener_new (l, keys, mask, type, is_system_global, ev);
410         controller_register_device_listener (controller, (DEControllerListener *) dec_listener, ev);
411 }
412
413 /*
414  * CORBA Accessibility::DeviceEventController::deregisterKeystrokeListener
415  *     method implementation
416  */
417 static void
418 impl_deregister_keystroke_listener (PortableServer_Servant     servant,
419                                     const Accessibility_KeystrokeListener l,
420                                     const Accessibility_KeySet *keys,
421                                     const Accessibility_ControllerEventMask *mask,
422                                     const Accessibility_KeyEventTypeSeq *type,
423                                     const CORBA_boolean is_system_global,
424                                     CORBA_Environment         *ev)
425 {
426         SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
427                 bonobo_object_from_servant (servant));
428         DEControllerKeyListener *key_listener = dec_key_listener_new (l,
429                                                                       keys,
430                                                                       mask,
431                                                                       type,
432                                                                       is_system_global,
433                                                                       ev);
434 #ifdef SPI_DEBUG
435         fprintf (stderr, "deregistering keystroke listener %p with maskVal %lu\n",
436                  (void *) l, (unsigned long) mask->value);
437 #endif
438         controller_deregister_device_listener(controller,
439                                               (DEControllerListener *) key_listener,
440                                               ev);
441 }
442
443 /*
444  * CORBA Accessibility::DeviceEventController::registerMouseListener
445  *     method implementation
446  */
447 /*
448 static void
449 impl_register_mouse_listener (PortableServer_Servant     servant,
450                               const Accessibility_MouseListener *l,
451                               CORBA_Environment         *ev)
452 {
453         SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER (
454                 bonobo_object_from_servant (servant));
455 #ifdef SPI_DEBUG
456         fprintf (stderr, "registering mouse listener %p\n", l);
457 #endif
458         controller_register_device_listener(controller, DEVICE_TYPE_MOUSE, l, keys, mask, ev);
459 }
460 */
461
462 static KeyCode
463 keycode_for_keysym (long keysym)
464 {
465   return XKeysymToKeycode (display, (KeySym) keysym);
466 }
467
468 /*
469  * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
470  *     method implementation
471  */
472 static void
473 impl_generate_key_event (PortableServer_Servant     servant,
474                          const CORBA_long           keycode,
475                          const Accessibility_KeySynthType synth_type,
476                          CORBA_Environment          *ev)
477 {
478         long key_synth_code;
479 #ifdef SPI_DEBUG
480         fprintf (stderr, "synthesizing keystroke %ld\n", (long) keycode);
481 #endif
482         /* TODO: hide/wrap/remove X dependency */
483
484         /* TODO: be accessX-savvy so that keyrelease occurs after sufficient timeout */
485         
486         /*
487          * TODO: when initializing, query for XTest extension before using,
488          * and fall back to XSendEvent() if XTest is not available.
489          */
490         
491         switch (synth_type)
492         {
493         case Accessibility_KEY_PRESS:
494                 XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
495                 break;
496         case Accessibility_KEY_PRESSRELEASE:
497                 XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
498         case Accessibility_KEY_RELEASE:
499                 XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, False, CurrentTime);
500                 break;
501         case Accessibility_KEY_SYM:
502                 key_synth_code = keycode_for_keysym (keycode);
503                 XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, True, CurrentTime);
504                 XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, False, CurrentTime);
505                 break;
506         }
507 }
508
509 /*
510  * CORBA Accessibility::DeviceEventController::generateMouseEvent
511  *     method implementation
512  */
513 static void
514 impl_generate_mouse_event (PortableServer_Servant     servant,
515                            const CORBA_long x,
516                            const CORBA_long y,
517                            const CORBA_char * eventName,
518                            CORBA_Environment         *ev)
519 {
520 #ifdef SPI_DEBUG
521         fprintf (stderr, "generating mouse %s event at %ld, %ld\n", eventName, x, y);
522 #endif
523 }
524
525 static void
526 spi_device_event_controller_class_init (SpiDeviceEventControllerClass *klass)
527 {
528         GObjectClass * object_class = (GObjectClass *) klass;
529         POA_Accessibility_DeviceEventController__epv *epv = &klass->epv;
530         spi_device_event_controller_parent_class = g_type_class_peek_parent (klass);
531
532         object_class->finalize = spi_device_event_controller_object_finalize;
533
534         epv->registerKeystrokeListener = impl_register_keystroke_listener;
535         epv->deregisterKeystrokeListener = impl_deregister_keystroke_listener;
536 /*        epv->registerMouseListener = impl_register_mouse_listener; */
537         epv->generateKeyEvent = impl_generate_key_event;
538         epv->generateMouseEvent = impl_generate_mouse_event;
539         klass->check_key_event = _check_key_event;
540 }
541
542 static void
543 spi_device_event_controller_init (SpiDeviceEventController *device_event_controller)
544 {
545   device_event_controller->key_listeners = NULL;
546   device_event_controller->mouse_listeners = NULL;
547   device_event_controller->keymask_list = NULL;
548   kbd_registered = _controller_register_with_devices (device_event_controller);
549 }
550
551 gboolean
552 spi_device_event_controller_check_key_event (SpiDeviceEventController *controller)
553 {
554         SpiDeviceEventControllerClass *klass = SPI_DEVICE_EVENT_CONTROLLER_GET_CLASS (controller);
555         if (klass->check_key_event)
556                 return (klass->check_key_event) (controller);
557 }
558
559 SpiDeviceEventController *
560 spi_device_event_controller_new (void *registryp)
561 {
562   SpiRegistry *registry = SPI_REGISTRY (registryp);     
563   SpiDeviceEventController *retval = 
564           SPI_DEVICE_EVENT_CONTROLLER (g_object_new (SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL));
565   retval->registry = registry;
566   bonobo_object_ref (registry);
567   return retval;
568 }
569
570 BONOBO_TYPE_FUNC_FULL (SpiDeviceEventController,
571                        Accessibility_DeviceEventController,
572                        PARENT_TYPE,
573                        spi_device_event_controller);
574