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