X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=registryd%2Fdeviceeventcontroller.c;h=3054e6a9fac47491583b17226277b895bfe6680a;hb=a90cc5cacb5efd233b714287297034c079def8aa;hp=75ec325e14e968e3b87532923f0d501a3eb26fdc;hpb=6e6409841585862b4952fce1ccffaf6e2c89c0d6;p=platform%2Fcore%2Fuifw%2Fat-spi2-atk.git diff --git a/registryd/deviceeventcontroller.c b/registryd/deviceeventcontroller.c index 75ec325..3054e6a 100644 --- a/registryd/deviceeventcontroller.c +++ b/registryd/deviceeventcontroller.c @@ -24,16 +24,24 @@ #include -#ifdef SPI_DEBUG -# include -#endif +#define SPI_DEBUG + +#include +#include +#include +#include #include #include +#define XK_MISCELLANY +#include +#include #include /* TODO: hide dependency (wrap in single porting file) */ +#include #include -#include +#include "../libspi/spi-private.h" +#include "deviceeventcontroller.h" /* Our parent Gtk object type */ #define PARENT_TYPE BONOBO_TYPE_OBJECT @@ -41,256 +49,357 @@ /* A pointer to our parent object class */ static GObjectClass *spi_device_event_controller_parent_class; -static gboolean kbd_registered = FALSE; - -static Display *display; - -static Window root_window; +int (*x_default_error_handler) (Display *display, XErrorEvent *error_event); typedef enum { - SPI_DEVICE_TYPE_KBD, - SPI_DEVICE_TYPE_MOUSE, - SPI_DEVICE_TYPE_LAST_DEFINED + SPI_DEVICE_TYPE_KBD, + SPI_DEVICE_TYPE_MOUSE, + SPI_DEVICE_TYPE_LAST_DEFINED } SpiDeviceTypeCategory; -struct _DEControllerListener { - CORBA_Object object; - SpiDeviceTypeCategory type; -}; +typedef struct { + guint ref_count : 30; + guint pending_add : 1; + guint pending_remove : 1; -typedef struct _DEControllerListener DEControllerListener; + Accessibility_ControllerEventMask mod_mask; + CORBA_unsigned_long key_val; /* KeyCode */ +} DEControllerGrabMask; -struct _DEControllerKeyListener { - DEControllerListener listener; - Accessibility_KeySet *keys; - Accessibility_ControllerEventMask *mask; - Accessibility_KeyEventTypeSeq *typeseq; - gboolean is_system_global; -}; +typedef struct { + CORBA_Object object; + SpiDeviceTypeCategory type; +} DEControllerListener; -typedef struct _DEControllerKeyListener DEControllerKeyListener; +typedef struct { + DEControllerListener listener; -static gboolean _controller_register_with_devices (SpiDeviceEventController *controller); -static gboolean _controller_grab_keyboard (SpiDeviceEventController *controller); + Accessibility_KeySet *keys; + Accessibility_ControllerEventMask mask; + Accessibility_KeyEventTypeSeq *typeseq; + Accessibility_EventListenerMode *mode; +} DEControllerKeyListener; -static void controller_register_device_listener (SpiDeviceEventController *controller, - DEControllerListener *l, - CORBA_Environment *ev); +static void spi_controller_register_with_devices (SpiDEController *controller); +static gboolean spi_controller_update_key_grabs (SpiDEController *controller, + Accessibility_DeviceEvent *recv); +static void spi_controller_register_device_listener (SpiDEController *controller, + DEControllerListener *l, + CORBA_Environment *ev); +static void spi_device_event_controller_forward_key_event (SpiDEController *controller, + const XEvent *event); -/* - * Private methods - */ +#define spi_get_display() GDK_DISPLAY() -static gint -_compare_corba_objects (gconstpointer p1, gconstpointer p2) +/* Private methods */ + +static KeyCode +keycode_for_keysym (long keysym) { - CORBA_Environment ev; - gint retval; - retval = !CORBA_Object_is_equivalent ((CORBA_Object) p1, (CORBA_Object) p2, &ev); + return XKeysymToKeycode (spi_get_display (), (KeySym) keysym); +} -#ifdef SPI_DEBUG - fprintf (stderr, "comparing %p to %p; result %d\n", - p1, p2, - retval); -#endif - return retval; +static DEControllerGrabMask * +spi_grab_mask_clone (DEControllerGrabMask *grab_mask) +{ + DEControllerGrabMask *clone = g_new (DEControllerGrabMask, 1); + + memcpy (clone, grab_mask, sizeof (DEControllerGrabMask)); + + clone->ref_count = 1; + clone->pending_add = TRUE; + clone->pending_remove = FALSE; + + return clone; } -static gint -_compare_listeners (gconstpointer p1, gconstpointer p2) +static void +spi_grab_mask_free (DEControllerGrabMask *grab_mask) { - DEControllerListener *l1 = (DEControllerListener *) p1; - DEControllerListener *l2 = (DEControllerListener *) p2; - return _compare_corba_objects (l1->object, l2->object); + g_free (grab_mask); } static gint -_eventmask_compare_value (gconstpointer p1, gconstpointer p2) +spi_grab_mask_compare_values (gconstpointer p1, gconstpointer p2) { - long d; - if (!p1 || !p2) - return (gint) (p1?1:(p2?-1:0)); - else - d = ((long)((Accessibility_ControllerEventMask*)p2)->value) - - ((long)((Accessibility_ControllerEventMask*)p1)->value); - return (gint) d; + DEControllerGrabMask *l1 = (DEControllerGrabMask *) p1; + DEControllerGrabMask *l2 = (DEControllerGrabMask *) p2; + + if (p1 == p2) + { + return 0; + } + else + { + return ((l1->mod_mask != l2->mod_mask) || (l1->key_val != l2->key_val)); + } } static DEControllerKeyListener * -dec_key_listener_new (CORBA_Object l, - const Accessibility_KeySet *keys, - const Accessibility_ControllerEventMask *mask, - const Accessibility_KeyEventTypeSeq *typeseq, - const CORBA_boolean is_system_global, - CORBA_Environment *ev) +spi_dec_key_listener_new (CORBA_Object l, + const Accessibility_KeySet *keys, + const Accessibility_ControllerEventMask mask, + const Accessibility_KeyEventTypeSeq *typeseq, + const Accessibility_EventListenerMode *mode, + CORBA_Environment *ev) { DEControllerKeyListener *key_listener = g_new0 (DEControllerKeyListener, 1); - key_listener->listener.object = CORBA_Object_duplicate (l, ev); + key_listener->listener.object = bonobo_object_dup_ref (l, ev); key_listener->listener.type = SPI_DEVICE_TYPE_KBD; key_listener->keys = ORBit_copy_value (keys, TC_Accessibility_KeySet); - key_listener->mask = ORBit_copy_value (mask, TC_Accessibility_ControllerEventMask); + key_listener->mask = mask; key_listener->typeseq = ORBit_copy_value (typeseq, TC_Accessibility_KeyEventTypeSeq); - key_listener->is_system_global = is_system_global; + if (mode) + key_listener->mode = ORBit_copy_value (mode, TC_Accessibility_EventListenerMode); + else + key_listener->mode = NULL; #ifdef SPI_DEBUG - g_print ("new listener, with mask %x, is_global %d, keys %p\n", - (unsigned int) key_listener->mask->value, - (int) key_listener->is_system_global, - (void *) key_listener->keys); + g_print ("new listener, with mask %x, is_global %d, keys %p (%d)\n", + (unsigned int) key_listener->mask, + (int) (mode ? mode->global : 0), + (void *) key_listener->keys, + (int) (key_listener->keys ? key_listener->keys->_length : 0)); #endif + return key_listener; } static void -controller_register_device_listener (SpiDeviceEventController *controller, - DEControllerListener *listener, - CORBA_Environment *ev) +spi_dec_key_listener_free (DEControllerKeyListener *key_listener, + CORBA_Environment *ev) { - Accessibility_ControllerEventMask *mask_ptr = NULL; - DEControllerKeyListener *key_listener; - - switch (listener->type) { - case SPI_DEVICE_TYPE_KBD: - key_listener = (DEControllerKeyListener *) listener; - controller->key_listeners = g_list_append (controller->key_listeners, key_listener); - if (key_listener->is_system_global) + bonobo_object_release_unref (key_listener->listener.object, ev); + CORBA_free (key_listener->typeseq); + CORBA_free (key_listener->keys); + g_free (key_listener); +} + +static void +_register_keygrab (SpiDEController *controller, + DEControllerGrabMask *grab_mask) +{ + GList *l; + + l = g_list_find_custom (controller->keygrabs_list, grab_mask, + spi_grab_mask_compare_values); + if (l) + { + DEControllerGrabMask *cur_mask = l->data; + + cur_mask->ref_count++; + if (cur_mask->pending_remove) { - mask_ptr = (Accessibility_ControllerEventMask *) - g_list_find_custom (controller->keymask_list, (gpointer) key_listener->mask, - _eventmask_compare_value); - if (mask_ptr) - ++(mask_ptr->refcount); - else - { - if (key_listener->mask->refcount != (CORBA_unsigned_short) 1) - fprintf (stderr, "mask initial refcount is not 1!\n"); - if (key_listener->mask->value > (CORBA_unsigned_long) 2048) - fprintf (stderr, "mask value looks invalid (%lu)\n", - (unsigned long) key_listener->mask->value); - else - fprintf (stderr, "appending mask with val=%lu\n", - (unsigned long) key_listener->mask->value); - mask_ptr = Accessibility_ControllerEventMask__alloc(); - mask_ptr->value = key_listener->mask->value; - mask_ptr->refcount = (CORBA_unsigned_short) 1; - controller->keymask_list = g_list_append (controller->keymask_list, - (gpointer) mask_ptr); - } + cur_mask->pending_remove = FALSE; } - break; - case SPI_DEVICE_TYPE_MOUSE: -/* controller->mouse_listeners = g_list_append (controller->mouse_listeners, - CORBA_Object_duplicate (l, ev));*/ + } + else + { + controller->keygrabs_list = + g_list_prepend (controller->keygrabs_list, + spi_grab_mask_clone (grab_mask)); + } +} -/* possibly this interface should NOT be used for mouse events ? */ - break; - } +static void +_deregister_keygrab (SpiDEController *controller, + DEControllerGrabMask *grab_mask) +{ + GList *l; + + l = g_list_find_custom (controller->keygrabs_list, grab_mask, + spi_grab_mask_compare_values); + + if (l) + { + DEControllerGrabMask *cur_mask = l->data; + + cur_mask->ref_count--; + if (cur_mask->ref_count <= 0) + { + cur_mask->pending_remove = TRUE; + } + } + else + { + g_warning ("De-registering non-existant grab"); + } +} + +static void +handle_keygrab (SpiDEController *controller, + DEControllerKeyListener *key_listener, + void (*process_cb) (SpiDEController *controller, + DEControllerGrabMask *grab_mask)) +{ + DEControllerGrabMask grab_mask = { 0 }; + + grab_mask.mod_mask = key_listener->mask; + if (key_listener->keys->_length == 0) /* special case means AnyKey/AllKeys */ + { + grab_mask.key_val = AnyKey; + process_cb (controller, &grab_mask); + } + else + { + int i; + + for (i = 0; i < key_listener->keys->_length; ++i) + { + long int key_val = key_listener->keys->_buffer[i]; + /* X Grabs require keycodes, not keysyms */ + if (key_val >= 0) + { + key_val = XKeysymToKeycode (spi_get_display (), (KeySym) key_val); + } + grab_mask.key_val = key_val; + + process_cb (controller, &grab_mask); + } + } +} + +static void +spi_controller_register_global_keygrabs (SpiDEController *controller, + DEControllerKeyListener *key_listener) +{ + handle_keygrab (controller, key_listener, _register_keygrab); + spi_controller_update_key_grabs (controller, NULL); } static void -controller_deregister_device_listener (SpiDeviceEventController *controller, - DEControllerListener *listener, - CORBA_Environment *ev) +spi_controller_deregister_global_keygrabs (SpiDEController *controller, + DEControllerKeyListener *key_listener) +{ + handle_keygrab (controller, key_listener, _deregister_keygrab); + spi_controller_update_key_grabs (controller, NULL); +} + +static void +spi_controller_register_device_listener (SpiDEController *controller, + DEControllerListener *listener, + CORBA_Environment *ev) { - Accessibility_ControllerEventMask *mask_ptr; DEControllerKeyListener *key_listener; - GList *list_ptr; + switch (listener->type) { case SPI_DEVICE_TYPE_KBD: key_listener = (DEControllerKeyListener *) listener; - list_ptr = g_list_find_custom (controller->key_listeners, listener, _compare_listeners); - /* TODO: need a different custom compare func */ - if (list_ptr) - controller->key_listeners = g_list_remove (controller->key_listeners, list_ptr); - list_ptr = (GList *) - g_list_find_custom (controller->keymask_list, (gpointer) key_listener->mask, - _eventmask_compare_value); - if (list_ptr) + + controller->key_listeners = g_list_prepend (controller->key_listeners, + key_listener); + if (key_listener->mode->global) { - mask_ptr = (Accessibility_ControllerEventMask *) list_ptr->data; - if (mask_ptr) - --mask_ptr->refcount; - if (!mask_ptr->refcount) - { - controller->keymask_list = - g_list_remove (controller->keymask_list, mask_ptr); - ; /* TODO: release any key grabs that are in place for this key mask */ - } + spi_controller_register_global_keygrabs (controller, key_listener); } break; - case SPI_DEVICE_TYPE_MOUSE: -/* controller->mouse_listeners = g_list_append (controller->mouse_listeners, - CORBA_Object_duplicate (l, ev));*/ - -/* possibly this interface should NOT be used for mouse events ? */ + default: break; } } -static gboolean -_controller_register_with_devices (SpiDeviceEventController *controller) +static GdkFilterReturn +global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data) { - gboolean retval = FALSE; + XEvent *xevent = gdk_xevent; + SpiDEController *controller; + + if (xevent->type != KeyPress && xevent->type != KeyRelease) + { + return GDK_FILTER_CONTINUE; + } + controller = SPI_DEVICE_EVENT_CONTROLLER (data); + + spi_device_event_controller_forward_key_event (controller, xevent); + + /* FIXME: is this right ? */ + return GDK_FILTER_CONTINUE; +} + +int +_spi_controller_device_error_handler (Display *display, XErrorEvent *error) +{ + if (error->error_code == BadAccess) + { + g_message ("Could not complete key grab: grab already in use.\n"); + return 0; + } + else + { + return (*x_default_error_handler) (display, error); + } +} + +static void +spi_controller_register_with_devices (SpiDEController *controller) +{ /* calls to device-specific implementations and routines go here */ /* register with: keyboard hardware code handler */ /* register with: (translated) keystroke handler */ -#ifdef SPI_DEBUG - fprintf (stderr, "About to request events on window %ld of display %p\n", - (unsigned long) GDK_ROOT_WINDOW(), GDK_DISPLAY()); -#endif - /* We must open a new connection to the server to avoid clashing with the GDK event loop */ - display = XOpenDisplay (g_getenv ("DISPLAY")); - root_window = DefaultRootWindow (display); - XSelectInput (display, - root_window, - KeyPressMask | KeyReleaseMask); - /* register with: mouse hardware device handler? */ - /* register with: mouse event handler */ - return retval; + + gdk_window_add_filter (NULL, global_filter_fn, controller); + + gdk_window_set_events (gdk_get_default_root_window (), + GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); + + x_default_error_handler = XSetErrorHandler (_spi_controller_device_error_handler); } static gboolean -key_set_contains_key (Accessibility_KeySet *key_set, Accessibility_KeyStroke *key_event) +spi_key_set_contains_key (Accessibility_KeySet *key_set, + const Accessibility_DeviceEvent *key_event) { gint i; gint len; - /* g_assert (key_set); */ - if (!key_set) { g_print ("null key set!"); return TRUE; } + if (!key_set) + { + g_print ("null key set!"); + return TRUE; + } len = key_set->_length; if (len == 0) /* special case, means "all keys/any key" */ { + g_print ("anykey\n"); return TRUE; } - for (i=0; i_buffer[i], - (int) key_event->keyID, - (int) key_event->keycode); + i, (int) key_set->_buffer[i], + (int) key_event->id, (int) key_event->hw_code); #endif - if (key_set->_buffer[i] == (CORBA_long) key_event->keyID) return TRUE; - if (key_set->_buffer[i] == (CORBA_long) -key_event->keycode) return TRUE; + if (key_set->_buffer[i] == (CORBA_long) key_event->id) + { + return TRUE; + } + if (key_set->_buffer[i] == (CORBA_long) -key_event->hw_code) + { + return TRUE; + } } return FALSE; } static gboolean -key_eventtype_seq_contains_event (Accessibility_KeyEventTypeSeq *type_seq, - Accessibility_KeyStroke *key_event) +spi_key_eventtype_seq_contains_event (Accessibility_KeyEventTypeSeq *type_seq, + const Accessibility_DeviceEvent *key_event) { gint i; gint len; - /* g_assert (type_seq); */ - if (!type_seq) { g_print ("null type seq!"); return TRUE; } + + if (!type_seq) + { + g_print ("null type seq!"); + return TRUE; + } len = type_seq->_length; @@ -299,171 +408,261 @@ key_eventtype_seq_contains_event (Accessibility_KeyEventTypeSeq *type_seq, return TRUE; } - for (i=0; i_buffer[i]); */ - if (type_seq->_buffer[i] == (CORBA_long) key_event->type) return TRUE; +#ifdef SPI_DEBUG + g_print ("type_seq[%d] = %d; key event type = %d\n", i, + (int) type_seq->_buffer[i], (int) key_event->type); +#endif + if (type_seq->_buffer[i] == (CORBA_long) key_event->type) + { + return TRUE; + } } - return TRUE; + return FALSE; } static gboolean -key_event_matches_listener (Accessibility_KeyStroke *key_event, - DEControllerKeyListener *listener, - CORBA_boolean is_system_global) +spi_key_event_matches_listener (const Accessibility_DeviceEvent *key_event, + DEControllerKeyListener *listener, + CORBA_boolean is_system_global) { - if ((key_event->modifiers == (CORBA_unsigned_short) (listener->mask->value & 0xFFFF)) && - key_set_contains_key (listener->keys, key_event) && - key_eventtype_seq_contains_event (listener->typeseq, key_event) && - (is_system_global == listener->is_system_global)) + if ((key_event->modifiers == (CORBA_unsigned_short) (listener->mask & 0xFFFF)) && + spi_key_set_contains_key (listener->keys, key_event) && + spi_key_eventtype_seq_contains_event (listener->typeseq, key_event) && + (is_system_global == listener->mode->global)) { return TRUE; } else - return FALSE; + { + return FALSE; + } } static gboolean -notify_keylisteners (GList *key_listeners, - Accessibility_KeyStroke *key_event, - CORBA_boolean is_system_global, - CORBA_Environment *ev) +spi_notify_keylisteners (GList **key_listeners, + const Accessibility_DeviceEvent *key_event, + CORBA_boolean is_system_global, + CORBA_Environment *ev) { - int i, n_listeners = g_list_length (key_listeners); - gboolean is_consumed = FALSE; + GList *l; + GSList *notify = NULL, *l2; + gboolean is_consumed; - for (i=0; ilistener.object; - if (key_event_matches_listener (key_event, key_listener, is_system_global)) - { - if (!CORBA_Object_is_nil(ls, ev)) - { - is_consumed = Accessibility_KeystrokeListener_keyEvent (ls, key_event, ev); - } - } - else - { + return FALSE; + } + + for (l = *key_listeners; l; l = l->next) + { + DEControllerKeyListener *key_listener = l->data; + + if (spi_key_event_matches_listener (key_event, key_listener, is_system_global)) + { + Accessibility_DeviceEventListener ls = key_listener->listener.object; + + if (ls != CORBA_OBJECT_NIL) + { + notify = g_slist_prepend (notify, CORBA_Object_duplicate (ls, ev)); + } + } + } + #ifdef SPI_KEYEVENT_DEBUG - g_print ("no match for listener %d\n", i); + if (!notify) + { + g_print ("no match for listener %d\n", i); + } #endif - ; - } + + is_consumed = FALSE; + for (l2 = notify; l2 && !is_consumed; l2 = l2->next) + { + Accessibility_DeviceEventListener ls = l2->data; + + is_consumed = Accessibility_DeviceEventListener_notifyEvent (ls, key_event, ev); + + if (BONOBO_EX (ev)) + { + is_consumed = FALSE; + CORBA_exception_free (ev); + } + + CORBA_Object_release (ls, ev); + } + + for (; l2; l2 = l2->next) + { + CORBA_Object_release (l2->data, ev); } + + g_slist_free (notify); + +#ifdef SPI_DEBUG + if (is_consumed) g_message ("consumed\n"); +#endif return is_consumed; } -static gboolean -_check_key_event (SpiDeviceEventController *controller) -{ - static gboolean initialized = FALSE; - XEvent *x_event = g_new0 (XEvent, 1); - XKeyEvent *x_key_event; - KeySym keysym; - gboolean is_consumed = FALSE; - Accessibility_KeyStroke key_event; - static CORBA_Environment ev; - - if (!initialized) - { - initialized = TRUE; - CORBA_exception_init (&ev); - } - - while (XPending(display)) - { - XNextEvent (display, x_event); - if (XFilterEvent (x_event, None)) continue; - if (x_event->type == KeyPress) +static Accessibility_DeviceEvent +spi_keystroke_from_x_key_event (XKeyEvent *x_key_event) +{ + Accessibility_DeviceEvent key_event; + KeySym keysym; + const int cbuf_bytes = 20; + char cbuf [cbuf_bytes]; + + keysym = XLookupKeysym (x_key_event, 0); + key_event.id = (CORBA_long)(keysym); + key_event.hw_code = (CORBA_short) x_key_event->keycode; + if (((XEvent *) x_key_event)->type == KeyPress) + { + key_event.type = Accessibility_KEY_PRESSED; + } + else + { + key_event.type = Accessibility_KEY_RELEASED; + } + key_event.modifiers = (CORBA_unsigned_short)(x_key_event->state); + key_event.is_text = CORBA_FALSE; + switch (keysym) + { + case ' ': + key_event.event_string = CORBA_string_dup ("space"); + break; + case XK_Tab: + key_event.event_string = CORBA_string_dup ("Tab"); + break; + case XK_BackSpace: + key_event.event_string = CORBA_string_dup ("Backspace"); + break; + case XK_Return: + key_event.event_string = CORBA_string_dup ("Return"); + break; + default: + if (XLookupString (x_key_event, cbuf, cbuf_bytes, &keysym, NULL) > 0) + { + key_event.event_string = CORBA_string_dup (cbuf); + if (isgraph (keysym)) { - x_key_event = (XKeyEvent *)x_event; - keysym = XLookupKeysym (x_key_event, 0); - key_event.keyID = (CORBA_long)(keysym); - key_event.keycode = (CORBA_short) x_key_event->keycode; - key_event.type = Accessibility_KEY_PRESSED; - key_event.modifiers = (CORBA_unsigned_short)(x_key_event->state); + key_event.is_text = CORBA_TRUE; /* FIXME: incorrect for some composed chars? */ + } + } + else + { + key_event.event_string = CORBA_string_dup (""); + } + } + + key_event.timestamp = (CORBA_unsigned_long) x_key_event->time; #ifdef SPI_KEYEVENT_DEBUG - fprintf (stderr, - "Key %lu pressed (%c), modifiers %d\n", - (unsigned long) keysym, - keysym ? (int) keysym : '*', - (int) x_key_event->state); + fprintf (stderr, + "Key %lu pressed (%c), modifiers %d\n", + (unsigned long) keysym, + keysym ? (int) keysym : '*', + (int) x_key_event->state); #endif #ifdef SPI_DEBUG - fprintf (stderr, "%s%c", - (x_key_event->state & Mod1Mask)?"Alt-":"", - ((x_key_event->state & ShiftMask)^(x_key_event->state & LockMask))? - g_ascii_toupper (keysym) : g_ascii_tolower (keysym)); + fprintf (stderr, "%s%c", + (x_key_event->state & Mod1Mask)?"Alt-":"", + ((x_key_event->state & ShiftMask)^(x_key_event->state & LockMask))? + g_ascii_toupper (keysym) : g_ascii_tolower (keysym)); #endif /* SPI_DEBUG */ - } - else - { -#ifdef SPI_KEYEVENT_DEBUG - fprintf (stderr, "other event, type %d\n", (int) x_event->type); -#endif - } - /* relay to listeners, and decide whether to consume it or not */ - is_consumed = notify_keylisteners (controller->key_listeners, &key_event, CORBA_TRUE, &ev); - - if (is_consumed) - { - XAllowEvents (display, AsyncKeyboard, CurrentTime); - } - else - { - XAllowEvents (display, ReplayKeyboard, CurrentTime); - } - } - XUngrabKey (display, AnyKey, AnyModifier, root_window); - return _controller_grab_keyboard (controller); + return key_event; } static gboolean -_controller_grab_keyboard (SpiDeviceEventController *controller) +spi_controller_update_key_grabs (SpiDEController *controller, + Accessibility_DeviceEvent *recv) { - GList *maskList = controller->keymask_list; - int i; - int last_mask; - last_mask = g_list_length (maskList); + GList *l, *next; + + g_return_val_if_fail (controller != NULL, FALSE); + + /* + * masks known to work with default RH 7.1+: + * 0 (no mods), LockMask, Mod1Mask, Mod2Mask, ShiftMask, + * ShiftMask|LockMask, Mod1Mask|LockMask, Mod2Mask|LockMask, + * ShiftMask|Mod1Mask, ShiftMask|Mod2Mask, Mod1Mask|Mod2Mask, + * ShiftMask|LockMask|Mod1Mask, ShiftMask|LockMask|Mod2Mask, + * + * ControlMask grabs are broken, must be in use already + */ + for (l = controller->keygrabs_list; l; l = next) + { + gboolean do_remove; + gboolean re_issue_grab; + DEControllerGrabMask *grab_mask = l->data; -/* - * masks known to work with default RH 7.1: - * 0 (no mods), LockMask, Mod1Mask, Mod2Mask, ShiftMask, - * ShiftMask|LockMask, Mod1Mask|LockMask, Mod2Mask|LockMask, - * ShiftMask|Mod1Mask, ShiftMask|Mod2Mask, Mod1Mask|Mod2Mask, - * ShiftMask|LockMask|Mod1Mask, ShiftMask|LockMask|Mod2Mask, - * - * ControlMask grabs are broken, must be in use already - */ - - for (i=0; i < last_mask; ++i) - { - Accessibility_ControllerEventMask *mask - = (Accessibility_ControllerEventMask *)g_list_nth_data (maskList, i); - unsigned long maskVal = 0xFFFFFFFF; - if (mask) maskVal = (unsigned long) mask->value; -#ifdef SPI_KEYEVENT_DEBUG - fprintf (stderr, "mask=%lx\n", maskVal); + next = l->next; + + re_issue_grab = recv && +/* (recv->type == Accessibility_KEY_RELEASED) && - (?) */ + (recv->modifiers & grab_mask->mod_mask) && + (grab_mask->key_val == keycode_for_keysym (recv->id)); + +#ifdef SPI_DEBUG + fprintf (stderr, "mask=%lx %lx (%c%c) %s\n", + (long int) grab_mask->key_val, + (long int) grab_mask->mod_mask, + grab_mask->pending_add ? '+' : '.', + grab_mask->pending_remove ? '-' : '.', + re_issue_grab ? "re-issue": ""); +#endif + + do_remove = FALSE; + + if (grab_mask->pending_add && grab_mask->pending_remove) + { + do_remove = TRUE; + } + else if (grab_mask->pending_remove) + { +#ifdef SPI_DEBUG + fprintf (stderr, "ungrabbing, mask=%x\n", grab_mask->mod_mask); +#endif + XUngrabKey (spi_get_display (), + grab_mask->key_val, + grab_mask->mod_mask, + gdk_x11_get_default_root_xwindow ()); + + do_remove = TRUE; + } + else if (grab_mask->pending_add || re_issue_grab) + { + +#ifdef SPI_DEBUG + fprintf (stderr, "grab with mask %x\n", grab_mask->mod_mask); #endif - if (!(maskVal & ControlMask)) - { - XGrabKey (display, - AnyKey, - maskVal, - root_window, - True, - GrabModeAsync, - GrabModeAsync); - /* TODO: check call for errors and return FALSE if error occurs */ - } else { - return FALSE; /* can't do control key yet */ - } + XGrabKey (spi_get_display (), + grab_mask->key_val, + grab_mask->mod_mask, + gdk_x11_get_default_root_xwindow (), + True, + GrabModeAsync, + GrabModeAsync); + } + + grab_mask->pending_add = FALSE; + grab_mask->pending_remove = FALSE; + + if (do_remove) + { + g_assert (grab_mask->ref_count <= 0); + + controller->keygrabs_list = g_list_delete_link ( + controller->keygrabs_list, l); + + spi_grab_mask_free (grab_mask); } - return TRUE; + + /* TODO: check calls for errors and return FALSE if error occurs */ + } + + return TRUE; } /* @@ -472,134 +671,183 @@ _controller_grab_keyboard (SpiDeviceEventController *controller) static void spi_device_event_controller_object_finalize (GObject *object) { + SpiDEController *controller; + + controller = SPI_DEVICE_EVENT_CONTROLLER (object); #ifdef SPI_DEBUG - fprintf(stderr, "spi_device_event_controller_object_finalize called\n"); + fprintf(stderr, "spi_device_event_controller_object_finalize called\n"); #endif - spi_device_event_controller_parent_class->finalize (object); + /* disconnect any special listeners, get rid of outstanding keygrabs */ + XUngrabKey (spi_get_display (), AnyKey, AnyModifier, DefaultRootWindow (spi_get_display ())); + + spi_device_event_controller_parent_class->finalize (object); } /* - * CORBA Accessibility::DeviceEventController::registerKeystrokeListener + * CORBA Accessibility::DEController::registerKeystrokeListener * method implementation */ static void -impl_register_keystroke_listener (PortableServer_Servant servant, - const Accessibility_KeystrokeListener l, - const Accessibility_KeySet *keys, - const Accessibility_ControllerEventMask *mask, - const Accessibility_KeyEventTypeSeq *type, - const CORBA_boolean is_system_global, - CORBA_Environment *ev) -{ - SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER ( - bonobo_object_from_servant (servant)); - DEControllerKeyListener *dec_listener; +impl_register_keystroke_listener (PortableServer_Servant servant, + const Accessibility_DeviceEventListener l, + const Accessibility_KeySet *keys, + const Accessibility_ControllerEventMask mask, + const Accessibility_KeyEventTypeSeq *type, + const Accessibility_EventListenerMode *mode, + CORBA_Environment *ev) +{ + SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER ( + bonobo_object_from_servant (servant)); + DEControllerKeyListener *dec_listener; #ifdef SPI_DEBUG - fprintf (stderr, "registering keystroke listener %p with maskVal %lu\n", - (void *) l, (unsigned long) mask->value); + fprintf (stderr, "registering keystroke listener %p with maskVal %lu\n", + (void *) l, (unsigned long) mask); #endif - dec_listener = dec_key_listener_new (l, keys, mask, type, is_system_global, ev); - controller_register_device_listener (controller, (DEControllerListener *) dec_listener, ev); + dec_listener = spi_dec_key_listener_new (l, keys, mask, type, mode, ev); + spi_controller_register_device_listener ( + controller, (DEControllerListener *) dec_listener, ev); } -/* - * CORBA Accessibility::DeviceEventController::deregisterKeystrokeListener - * method implementation - */ -static void -impl_deregister_keystroke_listener (PortableServer_Servant servant, - const Accessibility_KeystrokeListener l, - const Accessibility_KeySet *keys, - const Accessibility_ControllerEventMask *mask, - const Accessibility_KeyEventTypeSeq *type, - const CORBA_boolean is_system_global, - CORBA_Environment *ev) -{ - SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER ( - bonobo_object_from_servant (servant)); - DEControllerKeyListener *key_listener = dec_key_listener_new (l, - keys, - mask, - type, - is_system_global, - ev); -#ifdef SPI_DEBUG - fprintf (stderr, "deregistering keystroke listener %p with maskVal %lu\n", - (void *) l, (unsigned long) mask->value); -#endif - controller_deregister_device_listener(controller, - (DEControllerListener *) key_listener, - ev); + +typedef struct { + CORBA_Environment *ev; + DEControllerKeyListener *key_listener; +} RemoveKeyListenerClosure; + +static SpiReEntrantContinue +remove_key_listener_cb (GList * const *list, + gpointer user_data) +{ + DEControllerKeyListener *key_listener = (*list)->data; + RemoveKeyListenerClosure *ctx = user_data; + + if (CORBA_Object_is_equivalent (ctx->key_listener->listener.object, + key_listener->listener.object, ctx->ev)) + { + spi_re_entrant_list_delete_link (list); + spi_dec_key_listener_free (key_listener, ctx->ev); + } + + return SPI_RE_ENTRANT_CONTINUE; +} + +static SpiReEntrantContinue +copy_key_listener_cb (GList * const *list, + gpointer user_data) +{ + DEControllerKeyListener *key_listener = (*list)->data; + RemoveKeyListenerClosure *ctx = user_data; + + if (CORBA_Object_is_equivalent (ctx->key_listener->listener.object, + key_listener->listener.object, ctx->ev)) + { + /* TODO: FIXME aggregate keys in case the listener is registered twice */ + CORBA_free (ctx->key_listener->keys); + ctx->key_listener->keys = ORBit_copy_value (key_listener->keys, TC_Accessibility_KeySet); + } + + return SPI_RE_ENTRANT_CONTINUE; } /* - * CORBA Accessibility::DeviceEventController::registerMouseListener + * CORBA Accessibility::DEController::deregisterKeystrokeListener * method implementation */ -/* static void -impl_register_mouse_listener (PortableServer_Servant servant, - const Accessibility_MouseListener *l, - CORBA_Environment *ev) +impl_deregister_keystroke_listener (PortableServer_Servant servant, + const Accessibility_DeviceEventListener l, + const Accessibility_KeySet *keys, + const Accessibility_ControllerEventMask mask, + const Accessibility_KeyEventTypeSeq *type, + CORBA_Environment *ev) { - SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER ( - bonobo_object_from_servant (servant)); -#ifdef SPI_DEBUG - fprintf (stderr, "registering mouse listener %p\n", l); + DEControllerKeyListener *key_listener; + RemoveKeyListenerClosure ctx; + SpiDEController *controller; + + controller = SPI_DEVICE_EVENT_CONTROLLER (bonobo_object (servant)); + + key_listener = spi_dec_key_listener_new (l, keys, mask, type, NULL, ev); + +#ifdef SPI_DEREGISTER_DEBUG + fprintf (stderr, "deregistering keystroke listener %p with maskVal %lu\n", + (void *) l, (unsigned long) mask->value); #endif - controller_register_device_listener(controller, DEVICE_TYPE_MOUSE, l, keys, mask, ev); -} -*/ -static KeyCode -keycode_for_keysym (long keysym) -{ - return XKeysymToKeycode (display, (KeySym) keysym); + ctx.ev = ev; + ctx.key_listener = key_listener; + + /* special case, copy keyset from existing controller list entry */ + if (keys->_length == 0) + { + spi_re_entrant_list_foreach (&controller->key_listeners, + copy_key_listener_cb, &ctx); + } + + spi_controller_deregister_global_keygrabs (controller, key_listener); + + spi_re_entrant_list_foreach (&controller->key_listeners, + remove_key_listener_cb, &ctx); + + spi_dec_key_listener_free (key_listener, ev); } /* - * CORBA Accessibility::DeviceEventController::registerKeystrokeListener + * CORBA Accessibility::DEController::registerKeystrokeListener * method implementation */ static void -impl_generate_key_event (PortableServer_Servant servant, - const CORBA_long keycode, - const Accessibility_KeySynthType synth_type, - CORBA_Environment *ev) +impl_generate_keyboard_event (PortableServer_Servant servant, + const CORBA_long keycode, + const CORBA_char *keystring, + const Accessibility_KeySynthType synth_type, + CORBA_Environment *ev) { - long key_synth_code; + long key_synth_code; + #ifdef SPI_DEBUG - fprintf (stderr, "synthesizing keystroke %ld\n", (long) keycode); + fprintf (stderr, "synthesizing keystroke %ld, type %d\n", + (long) keycode, (int) synth_type); #endif - /* TODO: hide/wrap/remove X dependency */ + /* TODO: hide/wrap/remove X dependency */ - /* TODO: be accessX-savvy so that keyrelease occurs after sufficient timeout */ + /* TODO: be accessX-savvy so that keyrelease occurs after sufficient timeout */ - /* - * TODO: when initializing, query for XTest extension before using, - * and fall back to XSendEvent() if XTest is not available. - */ - - switch (synth_type) - { - case Accessibility_KEY_PRESS: - XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime); - break; - case Accessibility_KEY_PRESSRELEASE: - XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, True, CurrentTime); - case Accessibility_KEY_RELEASE: - XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) keycode, False, CurrentTime); - break; - case Accessibility_KEY_SYM: - key_synth_code = keycode_for_keysym (keycode); - XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, True, CurrentTime); - XTestFakeKeyEvent (GDK_DISPLAY(), (unsigned int) key_synth_code, False, CurrentTime); - break; - } + /* + * TODO: when initializing, query for XTest extension before using, + * and fall back to XSendEvent() if XTest is not available. + */ + + /* TODO: implement keystring mode also */ + gdk_error_trap_push (); + + switch (synth_type) + { + case Accessibility_KEY_PRESS: + XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, True, CurrentTime); + break; + case Accessibility_KEY_PRESSRELEASE: + XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, True, CurrentTime); + case Accessibility_KEY_RELEASE: + XTestFakeKeyEvent (spi_get_display (), (unsigned int) keycode, False, CurrentTime); + break; + case Accessibility_KEY_SYM: + key_synth_code = keycode_for_keysym (keycode); + XTestFakeKeyEvent (spi_get_display (), (unsigned int) key_synth_code, True, CurrentTime); + XTestFakeKeyEvent (spi_get_display (), (unsigned int) key_synth_code, False, CurrentTime); + break; + case Accessibility_KEY_STRING: + fprintf (stderr, "Not yet implemented\n"); + break; + } + if (gdk_error_trap_pop ()) + { + g_warning ("Error emitting keystroke"); + } } -/* Accessibility::DeviceEventController::generateMouseEvent */ +/* Accessibility::DEController::generateMouseEvent */ static void impl_generate_mouse_event (PortableServer_Servant servant, const CORBA_long x, @@ -607,93 +855,170 @@ impl_generate_mouse_event (PortableServer_Servant servant, const CORBA_char *eventName, CORBA_Environment *ev) { + int button; + gboolean error = FALSE; + Display *display = spi_get_display (); #ifdef SPI_DEBUG - fprintf (stderr, "generating mouse %s event at %ld, %ld\n", eventName, x, y); + fprintf (stderr, "generating mouse %s event at %ld, %ld\n", + eventName, (long int) x, (long int) y); #endif + g_message ("mouse event synthesis\n"); + switch (eventName[0]) + { + case 'b': + switch (eventName[1]) + { + /* TODO: check number of buttons before parsing */ + case '1': + button = 1; + break; + case '2': + button = 2; + break; + case '3': + button = 3; + break; + default: + error = TRUE; + } + if (!error) + { + if (x != -1 && y != -1) + { + XTestFakeMotionEvent (display, DefaultScreen (display), + x, y, 0); + } + XTestFakeButtonEvent (display, button, !(eventName[2] == 'r'), 0); + if (eventName[2] == 'c') + XTestFakeButtonEvent (display, button, FALSE, 1); + else if (eventName[2] == 'd') + { + XTestFakeButtonEvent (display, button, FALSE, 1); + XTestFakeButtonEvent (display, button, TRUE, 2); + XTestFakeButtonEvent (display, button, FALSE, 3); + } + } + break; + case 'r': /* relative motion */ + XTestFakeRelativeMotionEvent (display, x, y, 0); + break; + case 'a': /* absolute motion */ + XTestFakeMotionEvent (display, DefaultScreen (display), + x, y, 0); + break; + } } -/* Accessibility::DeviceEventController::notifyListenersSync */ +/* Accessibility::DEController::notifyListenersSync */ static CORBA_boolean -impl_notify_listeners_sync(PortableServer_Servant servant, - const Accessibility_DeviceEvent *event, - CORBA_Environment *ev) +impl_notify_listeners_sync (PortableServer_Servant servant, + const Accessibility_DeviceEvent *event, + CORBA_Environment *ev) { - SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER ( - bonobo_object_from_servant (servant)); - Accessibility_KeyStroke *key_event = (Accessibility_KeyStroke *) event; + SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER ( + bonobo_object_from_servant (servant)); #ifdef SPI_DEBUG - g_print ("notifying listeners synchronously: controller %x, event id %d\n", - (void *) controller, (int) event->eventID); + g_print ("notifylistening listeners synchronously: controller %p, event id %d\n", + controller, (int) event->id); #endif - return (notify_keylisteners (controller->key_listeners, key_event, CORBA_FALSE, ev) ? - CORBA_TRUE : CORBA_FALSE); + return spi_notify_keylisteners (&controller->key_listeners, event, CORBA_FALSE, ev) ? + CORBA_TRUE : CORBA_FALSE; } -/* Accessibility::DeviceEventController::notifyListenersAsync */ +/* Accessibility::DEController::notifyListenersAsync */ static void -impl_notify_listeners_async (PortableServer_Servant servant, +impl_notify_listeners_async (PortableServer_Servant servant, const Accessibility_DeviceEvent *event, - CORBA_Environment *ev) + CORBA_Environment *ev) { - SpiDeviceEventController *controller = SPI_DEVICE_EVENT_CONTROLLER( - bonobo_object_from_servant (servant)); - Accessibility_KeyStroke *key_event = (Accessibility_KeyStroke *) event; + SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER ( + bonobo_object_from_servant (servant)); #ifdef SPI_DEBUG fprintf (stderr, "notifying listeners asynchronously\n"); #endif - notify_keylisteners (controller->key_listeners, key_event, CORBA_FALSE, ev); + spi_notify_keylisteners (&controller->key_listeners, event, CORBA_FALSE, ev); } static void -spi_device_event_controller_class_init (SpiDeviceEventControllerClass *klass) +spi_device_event_controller_class_init (SpiDEControllerClass *klass) { - GObjectClass * object_class = (GObjectClass *) klass; - POA_Accessibility_DeviceEventController__epv *epv = &klass->epv; - spi_device_event_controller_parent_class = g_type_class_peek_parent (klass); + GObjectClass * object_class = (GObjectClass *) klass; + POA_Accessibility_DeviceEventController__epv *epv = &klass->epv; - object_class->finalize = spi_device_event_controller_object_finalize; - - epv->registerKeystrokeListener = impl_register_keystroke_listener; - epv->deregisterKeystrokeListener = impl_deregister_keystroke_listener; -/* epv->registerMouseListener = impl_register_mouse_listener; */ - epv->generateKeyEvent = impl_generate_key_event; - epv->generateMouseEvent = impl_generate_mouse_event; - epv->notifyListenersSync = impl_notify_listeners_sync; - epv->notifyListenersAsync = impl_notify_listeners_async; - klass->check_key_event = _check_key_event; + spi_device_event_controller_parent_class = g_type_class_peek_parent (klass); + + object_class->finalize = spi_device_event_controller_object_finalize; + + epv->registerKeystrokeListener = impl_register_keystroke_listener; + epv->deregisterKeystrokeListener = impl_deregister_keystroke_listener; + epv->generateKeyboardEvent = impl_generate_keyboard_event; + epv->generateMouseEvent = impl_generate_mouse_event; + epv->notifyListenersSync = impl_notify_listeners_sync; + epv->notifyListenersAsync = impl_notify_listeners_async; } static void -spi_device_event_controller_init (SpiDeviceEventController *device_event_controller) +spi_device_event_controller_init (SpiDEController *device_event_controller) { - device_event_controller->key_listeners = NULL; + device_event_controller->key_listeners = NULL; device_event_controller->mouse_listeners = NULL; - device_event_controller->keymask_list = NULL; - kbd_registered = _controller_register_with_devices (device_event_controller); + device_event_controller->keygrabs_list = NULL; + + /* + * TODO: fixme, this module makes the foolish assumptions that + * registryd uses the same display as the apps, and that the + * DISPLAY environment variable is set. + */ + gdk_init (NULL, NULL); + + spi_controller_register_with_devices (device_event_controller); } -gboolean -spi_device_event_controller_check_key_event (SpiDeviceEventController *controller) +static void +spi_device_event_controller_forward_key_event (SpiDEController *controller, + const XEvent *event) { - SpiDeviceEventControllerClass *klass = SPI_DEVICE_EVENT_CONTROLLER_GET_CLASS (controller); - if (klass->check_key_event) - return (klass->check_key_event) (controller); - return FALSE; + gboolean is_consumed = FALSE; + CORBA_Environment ev; + Accessibility_DeviceEvent key_event; + + g_assert (event->type == KeyPress || event->type == KeyRelease); + + CORBA_exception_init (&ev); + + key_event = spi_keystroke_from_x_key_event ((XKeyEvent *) event); + + spi_controller_update_key_grabs (controller, &key_event); + + /* relay to listeners, and decide whether to consume it or not */ + is_consumed = spi_notify_keylisteners ( + &controller->key_listeners, &key_event, CORBA_TRUE, &ev); + + CORBA_exception_free (&ev); + + if (is_consumed) + { + XAllowEvents (spi_get_display (), AsyncKeyboard, CurrentTime); + } + else + { + XAllowEvents (spi_get_display (), ReplayKeyboard, CurrentTime); + } } -SpiDeviceEventController * -spi_device_event_controller_new (void *registryp) +SpiDEController * +spi_device_event_controller_new (SpiRegistry *registry) { - BonoboObject *registry = (BonoboObject *) registryp; - SpiDeviceEventController *retval = g_object_new ( - SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL); - retval->registry = registry; - bonobo_object_ref (registry); + SpiDEController *retval = g_object_new ( + SPI_DEVICE_EVENT_CONTROLLER_TYPE, NULL); + + retval->registry = SPI_REGISTRY (bonobo_object_ref ( + BONOBO_OBJECT (registry))); + return retval; } -BONOBO_TYPE_FUNC_FULL (SpiDeviceEventController, +BONOBO_TYPE_FUNC_FULL (SpiDEController, Accessibility_DeviceEventController, PARENT_TYPE, spi_device_event_controller); -