f96d7b034c69bdbdb7b26f4c25702521b4f3a466
[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 <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 (x_event->type == KeyPress)
239               {
240                 x_key_event = (XKeyEvent *)x_event;
241                 keysym = XLookupKeysym (x_key_event, 0);
242                 key_event.keyID = (CORBA_long)(keysym);
243                 key_event.keycode = (CORBA_short) x_key_event->keycode;
244                 key_event.type = Accessibility_KEY_PRESSED;
245                 key_event.modifiers = (CORBA_unsigned_short)(x_key_event->state);
246 #ifdef SPI_KEYEVENT_DEBUG
247             fprintf (stderr,
248                      "Key %lu pressed (%c), modifiers %d\n",
249                      (unsigned long) keysym,
250                      keysym ? (int) keysym : '*',
251                      (int) x_key_event->state);
252 #endif
253 #ifdef SPI_DEBUG
254             fprintf(stderr, "%s%c",
255                     (x_key_event->state & Mod1Mask)?"Alt-":"",
256                     ((x_key_event->state & ShiftMask)^(x_key_event->state & LockMask))?
257                     (char) toupper((int) keysym) : (char) tolower((int)keysym));
258 #endif /* SPI_DEBUG */
259               }
260             else
261             {
262 #ifdef SPI_KEYEVENT_DEBUG
263                     fprintf (stderr, "other event, type %d\n", (int) x_event->type);
264 #endif
265             }
266             /* relay to listeners, and decide whether to consume it or not */
267             for (i=0; i<n_listeners && !is_consumed; ++i)
268             {
269                     Accessibility_KeystrokeListener ls;
270                     ls = (Accessibility_KeystrokeListener)
271                             g_list_nth_data (controller->key_listeners, i);
272                     if (!CORBA_Object_is_nil(ls, &ev))
273                     {
274                             is_consumed = Accessibility_KeystrokeListener_keyEvent (ls, &key_event, &ev);
275                     }           
276             }
277             if (is_consumed)
278             {
279               XAllowEvents (display, SyncKeyboard, CurrentTime);
280             }
281             else
282             {
283               XAllowEvents (display, ReplayKeyboard, CurrentTime);
284             }
285           }
286         XUngrabKey (display, AnyKey, AnyModifier, root_window);
287         return _controller_grab_keyboard (controller);
288 }
289
290 static gboolean
291 _controller_grab_keyboard (DeviceEventController *controller)
292 {
293         GList *maskList = controller->keymask_list;
294         int i;
295         int last_mask;
296         last_mask = g_list_length (maskList);
297
298 /*
299  * masks known to work with default RH 7.1: 
300  * 0 (no mods), LockMask, Mod1Mask, Mod2Mask, ShiftMask,
301  * ShiftMask|LockMask, Mod1Mask|LockMask, Mod2Mask|LockMask,
302  * ShiftMask|Mod1Mask, ShiftMask|Mod2Mask, Mod1Mask|Mod2Mask,
303  * ShiftMask|LockMask|Mod1Mask, ShiftMask|LockMask|Mod2Mask,
304  *
305  * ControlMask grabs are broken, must be in use already
306  */
307         
308         for (i=0; i < last_mask; ++i)
309         {
310                 Accessibility_ControllerEventMask *mask
311                         = (Accessibility_ControllerEventMask *)g_list_nth_data (maskList, i);
312                 unsigned long maskVal = 0xFFFFFFFF;
313                 if (mask) maskVal = (unsigned long) mask->value;
314 #ifdef SPI_KEYEVENT_DEBUG
315                 fprintf (stderr, "mask=%lx\n", maskVal);
316 #endif
317                 if (!(maskVal & ControlMask))
318                 {
319                         XGrabKey (display,
320                                   AnyKey,
321                                   maskVal,
322                                   root_window,
323                                   True,
324                                   GrabModeAsync,
325                                   GrabModeSync);
326                         /* TODO: check call for errors and return FALSE if error occurs */
327                 } else {
328                         return FALSE; /* can't do control key yet */
329                 }
330         }
331         return TRUE;
332 }
333
334 /*
335  * Implemented GObject::finalize
336  */
337 static void
338 device_event_controller_object_finalize (GObject *object)
339 {
340
341 #ifdef SPI_DEBUG
342         fprintf(stderr, "device_event_controller_object_finalize called\n");
343 #endif
344         device_event_controller_parent_class->finalize (object);
345 }
346
347 /*
348  * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
349  *     method implementation
350  */
351 static void
352 impl_register_keystroke_listener (PortableServer_Servant     servant,
353                                   const Accessibility_KeystrokeListener l,
354                                   const Accessibility_KeySet *keys,
355                                   const Accessibility_ControllerEventMask *mask,
356                                   const Accessibility_KeyEventTypeSeq *type,
357                                   const CORBA_boolean is_synchronous,
358                                   CORBA_Environment         *ev)
359 {
360         DeviceEventController *controller = DEVICE_EVENT_CONTROLLER (
361                 bonobo_object_from_servant (servant));
362 #ifdef SPI_DEBUG
363         fprintf (stderr, "registering keystroke listener %p with maskVal %lu\n",
364                  (void *) l, (unsigned long) mask->value);
365 #endif
366         /* TODO: change this to an enum, indicating if event can be consumed */
367         if (is_synchronous)
368         _controller_register_device_listener(controller, l, mask, DEVICE_TYPE_KBD, ev);
369         else
370         ; /* register with toolkit instead */   
371 }
372 /*
373  * CORBA Accessibility::DeviceEventController::deregisterKeystrokeListener
374  *     method implementation
375  */
376 static void
377 impl_deregister_keystroke_listener (PortableServer_Servant     servant,
378                                     const Accessibility_KeystrokeListener l,
379                                     const Accessibility_KeySet *keys,
380                                     const Accessibility_ControllerEventMask *mask,
381                                     const Accessibility_KeyEventTypeSeq *type,
382                                     const CORBA_boolean is_synchronous,
383                                     CORBA_Environment         *ev)
384 {
385         DeviceEventController *controller = DEVICE_EVENT_CONTROLLER (
386                 bonobo_object_from_servant (servant));
387 #ifdef SPI_DEBUG
388         fprintf (stderr, "deregistering keystroke listener %p with maskVal %lu\n",
389                  (void *) l, (unsigned long) mask->value);
390 #endif
391         _controller_deregister_device_listener(controller, l, mask, DEVICE_TYPE_KBD, ev);
392 }
393
394 /*
395  * CORBA Accessibility::DeviceEventController::registerMouseListener
396  *     method implementation
397  */
398 /*
399 static void
400 impl_register_mouse_listener (PortableServer_Servant     servant,
401                               const Accessibility_MouseListener *l,
402                               CORBA_Environment         *ev)
403 {
404         DeviceEventController *controller = DEVICE_EVENT_CONTROLLER (
405                 bonobo_object_from_servant (servant));
406 #ifdef SPI_DEBUG
407         fprintf (stderr, "registering mouse listener %p\n", l);
408 #endif
409         _controller_register_device_listener(controller, l, mask, DEVICE_TYPE_MOUSE, ev);
410 }
411 */
412
413 /*
414  * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
415  *     method implementation
416  */
417 static void
418 impl_generate_key_event (PortableServer_Servant     servant,
419                          const CORBA_long           keyEventID,
420                          CORBA_Environment         *ev)
421 {
422 #ifdef SPI_DEBUG
423         fprintf (stderr, "synthesizing keystroke %ld\n", (long) keyEventID);
424 #endif
425         /* TODO: hide/wrap/remove X dependency */
426         XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keyEventID, True, CurrentTime);
427         XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keyEventID, False, CurrentTime);
428 }
429
430 /*
431  * CORBA Accessibility::DeviceEventController::generateMouseEvent
432  *     method implementation
433  */
434 static void
435 impl_generate_mouse_event (PortableServer_Servant     servant,
436                            const CORBA_long x,
437                            const CORBA_long y,
438                            const CORBA_char * eventName,
439                            CORBA_Environment         *ev)
440 {
441 #ifdef SPI_DEBUG
442         fprintf (stderr, "generating mouse %s event at %ld, %ld\n", eventName, x, y);
443 #endif
444 }
445
446 static void
447 device_event_controller_class_init (DeviceEventControllerClass *klass)
448 {
449         GObjectClass * object_class = (GObjectClass *) klass;
450         POA_Accessibility_DeviceEventController__epv *epv = &klass->epv;
451         device_event_controller_parent_class = g_type_class_ref (BONOBO_OBJECT_TYPE);
452
453         object_class->finalize = device_event_controller_object_finalize;
454
455         epv->registerKeystrokeListener = impl_register_keystroke_listener;
456         epv->deregisterKeystrokeListener = impl_deregister_keystroke_listener;
457 /*        epv->registerMouseListener = impl_register_mouse_listener; */
458         epv->generateKeyEvent = impl_generate_key_event;
459         epv->generateMouseEvent = impl_generate_mouse_event;
460         klass->check_key_event = _check_key_event;
461 }
462
463 static void
464 device_event_controller_init (DeviceEventController *device_event_controller)
465 {
466   device_event_controller->key_listeners = NULL;
467   device_event_controller->mouse_listeners = NULL;
468   device_event_controller->keymask_list = NULL;
469   kbd_registered = _controller_register_with_devices (device_event_controller);
470 }
471
472 gboolean device_event_controller_check_key_event (DeviceEventController *controller)
473 {
474         DeviceEventControllerClass *klass = DEVICE_EVENT_CONTROLLER_GET_CLASS (controller);
475         if (klass->check_key_event)
476                 return (klass->check_key_event) (controller);
477 }
478
479 GType
480 device_event_controller_get_type (void)
481 {
482         static GType type = 0;
483
484         if (!type) {
485                 static const GTypeInfo tinfo = {
486                         sizeof (DeviceEventControllerClass),
487                         (GBaseInitFunc) NULL,
488                         (GBaseFinalizeFunc) NULL,
489                         (GClassInitFunc) device_event_controller_class_init,
490                         (GClassFinalizeFunc) NULL,
491                         NULL, /* class data */
492                         sizeof (DeviceEventController),
493                         0, /* n preallocs */
494                         (GInstanceInitFunc) device_event_controller_init,
495                         NULL /* value table */
496                 };
497                 /*
498                  *   Here we use bonobo_type_unique instead of
499                  * gtk_type_unique, this auto-generates a load of
500                  * CORBA structures for us. All derived types must
501                  * use bonobo_type_unique.
502                  */
503                 type = bonobo_type_unique (
504                         PARENT_TYPE,
505                         POA_Accessibility_DeviceEventController__init,
506                         NULL,
507                         G_STRUCT_OFFSET (DeviceEventControllerClass, epv),
508                         &tinfo,
509                         "DeviceEventController");
510         }
511
512         return type;
513 }
514