2 * AT-SPI - Assistive Technology Service Provider Interface
3 * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
5 * Copyright 2001 Sun Microsystems Inc.
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.
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.
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.
24 * listener.c: test for accessibility implementation
33 #include <X11/extensions/XTest.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>
41 * This pulls the definition for the BonoboObject (GType)
43 #include "deviceeventcontroller.h"
46 * Our parent Gtk object type
48 #define PARENT_TYPE BONOBO_OBJECT_TYPE
51 * A pointer to our parent object class
53 static GObjectClass *device_event_controller_parent_class;
55 static gboolean kbd_registered = FALSE;
57 static Display *display;
59 static Window root_window;
64 DEVICE_TYPE_LAST_DEFINED
67 static gboolean _controller_register_with_devices (DeviceEventController *controller);
68 static gboolean _controller_grab_keyboard (DeviceEventController *controller);
70 static void _controller_register_device_listener (DeviceEventController *controller,
72 const Accessibility_ControllerEventMask *mask,
73 DeviceTypeCategory type,
74 CORBA_Environment *ev);
81 _compare_corba_objects (gconstpointer p1, gconstpointer p2)
85 retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev);
88 fprintf (stderr, "comparing %p to %p; result %d\n",
96 _eventmask_compare_value (gconstpointer p1, gconstpointer p2)
100 return (gint) (p1?1:(p2?-1:0));
102 d = ((long)((Accessibility_ControllerEventMask*)p2)->value) -
103 ((long)((Accessibility_ControllerEventMask*)p1)->value);
108 _controller_register_device_listener (DeviceEventController *controller,
109 const CORBA_Object l,
110 const Accessibility_ControllerEventMask *mask,
111 DeviceTypeCategory type,
112 CORBA_Environment *ev)
114 Accessibility_ControllerEventMask *mask_ptr = NULL;
117 case DEVICE_TYPE_KBD:
118 controller->key_listeners = g_list_append (controller->key_listeners,
119 CORBA_Object_duplicate (l, ev));
121 mask_ptr = (Accessibility_ControllerEventMask *)
122 g_list_find_custom (controller->keymask_list, (gpointer) mask,
123 _eventmask_compare_value);
125 ++(mask_ptr->refcount);
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);
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);
143 case DEVICE_TYPE_MOUSE:
144 /* controller->mouse_listeners = g_list_append (controller->mouse_listeners,
145 CORBA_Object_duplicate (l, ev));*/
147 /* possibly this interface should NOT be used for mouse events ? */
153 _controller_deregister_device_listener (DeviceEventController *controller,
154 const CORBA_Object l,
155 const Accessibility_ControllerEventMask *mask,
156 DeviceTypeCategory type,
157 CORBA_Environment *ev)
159 Accessibility_ControllerEventMask *mask_ptr;
162 case DEVICE_TYPE_KBD:
163 list_ptr = g_list_find_custom (controller->key_listeners, l, _compare_corba_objects);
165 controller->key_listeners = g_list_remove (controller->key_listeners, list_ptr);
167 g_list_find_custom (controller->keymask_list, (gpointer) mask,
168 _eventmask_compare_value);
171 mask_ptr = (Accessibility_ControllerEventMask *) list_ptr->data;
173 --mask_ptr->refcount;
174 if (!mask_ptr->refcount)
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 */
182 case DEVICE_TYPE_MOUSE:
183 /* controller->mouse_listeners = g_list_append (controller->mouse_listeners,
184 CORBA_Object_duplicate (l, ev));*/
186 /* possibly this interface should NOT be used for mouse events ? */
192 _controller_register_with_devices (DeviceEventController *controller)
194 gboolean retval = FALSE;
196 /* calls to device-specific implementations and routines go here */
197 /* register with: keyboard hardware code handler */
198 /* register with: (translated) keystroke handler */
200 fprintf (stderr, "About to request events on window %ld of display %x\n",
201 (unsigned long) GDK_ROOT_WINDOW(), GDK_DISPLAY());
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,
208 KeyPressMask | KeyReleaseMask);
209 /* register with: mouse hardware device handler? */
210 /* register with: mouse event handler */
215 _check_key_event (DeviceEventController *controller)
217 static gboolean initialized = FALSE;
218 static gboolean is_active = FALSE;
219 XEvent *x_event = g_new0 (XEvent, 1);
220 XKeyEvent *x_key_event;
222 gboolean is_consumed = FALSE;
225 int n_listeners = g_list_length (controller->key_listeners);
226 Accessibility_KeyStroke key_event;
227 static CORBA_Environment ev;
232 CORBA_exception_init (&ev);
235 while (XPending(display))
237 XNextEvent (display, x_event);
238 if (XFilterEvent (x_event, None)) continue;
239 if (x_event->type == KeyPress)
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
249 "Key %lu pressed (%c), modifiers %d\n",
250 (unsigned long) keysym,
251 keysym ? (int) keysym : '*',
252 (int) x_key_event->state);
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 */
263 #ifdef SPI_KEYEVENT_DEBUG
264 fprintf (stderr, "other event, type %d\n", (int) x_event->type);
267 /* relay to listeners, and decide whether to consume it or not */
268 for (i=0; i<n_listeners && !is_consumed; ++i)
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))
275 is_consumed = Accessibility_KeystrokeListener_keyEvent (ls, &key_event, &ev);
280 XAllowEvents (display, SyncKeyboard, CurrentTime);
284 XAllowEvents (display, ReplayKeyboard, CurrentTime);
287 XUngrabKey (display, AnyKey, AnyModifier, root_window);
288 return _controller_grab_keyboard (controller);
292 _controller_grab_keyboard (DeviceEventController *controller)
294 GList *maskList = controller->keymask_list;
297 last_mask = g_list_length (maskList);
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,
306 * ControlMask grabs are broken, must be in use already
309 for (i=0; i < last_mask; ++i)
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);
318 if (!(maskVal & ControlMask))
327 /* TODO: check call for errors and return FALSE if error occurs */
329 return FALSE; /* can't do control key yet */
336 * Implemented GObject::finalize
339 device_event_controller_object_finalize (GObject *object)
343 fprintf(stderr, "device_event_controller_object_finalize called\n");
345 device_event_controller_parent_class->finalize (object);
349 * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
350 * method implementation
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)
361 DeviceEventController *controller = DEVICE_EVENT_CONTROLLER (
362 bonobo_object_from_servant (servant));
364 fprintf (stderr, "registering keystroke listener %p with maskVal %lu\n",
365 (void *) l, (unsigned long) mask->value);
367 /* TODO: change this to an enum, indicating if event can be consumed */
369 _controller_register_device_listener(controller, l, mask, DEVICE_TYPE_KBD, ev);
371 ; /* register with toolkit instead */
374 * CORBA Accessibility::DeviceEventController::deregisterKeystrokeListener
375 * method implementation
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)
386 DeviceEventController *controller = DEVICE_EVENT_CONTROLLER (
387 bonobo_object_from_servant (servant));
389 fprintf (stderr, "deregistering keystroke listener %p with maskVal %lu\n",
390 (void *) l, (unsigned long) mask->value);
392 _controller_deregister_device_listener(controller, l, mask, DEVICE_TYPE_KBD, ev);
396 * CORBA Accessibility::DeviceEventController::registerMouseListener
397 * method implementation
401 impl_register_mouse_listener (PortableServer_Servant servant,
402 const Accessibility_MouseListener *l,
403 CORBA_Environment *ev)
405 DeviceEventController *controller = DEVICE_EVENT_CONTROLLER (
406 bonobo_object_from_servant (servant));
408 fprintf (stderr, "registering mouse listener %p\n", l);
410 _controller_register_device_listener(controller, l, mask, DEVICE_TYPE_MOUSE, ev);
415 keycode_for_keysym (long keysym)
417 return XKeysymToKeycode (display, (KeySym) keysym);
421 * CORBA Accessibility::DeviceEventController::registerKeystrokeListener
422 * method implementation
425 impl_generate_key_event (PortableServer_Servant servant,
426 const CORBA_long keycode,
427 const CORBA_long synth_type,
428 CORBA_Environment *ev)
432 fprintf (stderr, "synthesizing keystroke %ld\n", (long) keycode);
434 /* TODO: hide/wrap/remove X dependency */
436 /* TODO: be accessX-savvy so that keyrelease occurs after sufficient timeout */
439 * TODO: when initializing, query for XTest extension before using,
440 * and fall back to XSendEvent() if XTest is not available.
445 case Accessibility_KEY_PRESS:
446 XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime);
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);
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);
462 * CORBA Accessibility::DeviceEventController::generateMouseEvent
463 * method implementation
466 impl_generate_mouse_event (PortableServer_Servant servant,
469 const CORBA_char * eventName,
470 CORBA_Environment *ev)
473 fprintf (stderr, "generating mouse %s event at %ld, %ld\n", eventName, x, y);
478 device_event_controller_class_init (DeviceEventControllerClass *klass)
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);
484 object_class->finalize = device_event_controller_object_finalize;
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;
495 device_event_controller_init (DeviceEventController *device_event_controller)
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);
503 gboolean device_event_controller_check_key_event (DeviceEventController *controller)
505 DeviceEventControllerClass *klass = DEVICE_EVENT_CONTROLLER_GET_CLASS (controller);
506 if (klass->check_key_event)
507 return (klass->check_key_event) (controller);
511 device_event_controller_get_type (void)
513 static GType type = 0;
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),
525 (GInstanceInitFunc) device_event_controller_init,
526 NULL /* value table */
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.
534 type = bonobo_type_unique (
536 POA_Accessibility_DeviceEventController__init,
538 G_STRUCT_OFFSET (DeviceEventControllerClass, epv),
540 "DeviceEventController");