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