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