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