+static GSList *keylist_clone (GSList *s)
+{
+ GSList *d = NULL;
+ GSList *l;
+
+ for (l = s; l; l = g_slist_next(l))
+ {
+ Accessibility_KeyDefinition *kd = (Accessibility_KeyDefinition *)g_malloc(sizeof(Accessibility_KeyDefinition));
+ if (kd)
+ {
+ Accessibility_KeyDefinition *kds = (Accessibility_KeyDefinition *)l->data;
+ kd->keycode = kds->keycode;
+ kd->keysym = kds->keysym;
+ kd->keystring = g_strdup(kds->keystring);
+ d = g_slist_append(d, kd);
+ }
+ }
+ return d;
+}
+
+static DEControllerKeyListener *
+spi_key_listener_clone (DEControllerKeyListener *key_listener)
+{
+ DEControllerKeyListener *clone = g_new0 (DEControllerKeyListener, 1);
+ clone->listener.bus_name = g_strdup (key_listener->listener.bus_name);
+ clone->listener.path = g_strdup (key_listener->listener.path);
+ clone->listener.type = SPI_DEVICE_TYPE_KBD;
+ clone->keys = keylist_clone (key_listener->keys);
+ clone->mask = key_listener->mask;
+ clone->listener.types = key_listener->listener.types;
+ if (key_listener->mode)
+ {
+ clone->mode = (Accessibility_EventListenerMode *)g_malloc(sizeof(Accessibility_EventListenerMode));
+ if (clone->mode) memcpy(clone->mode, key_listener->mode, sizeof(Accessibility_EventListenerMode));
+ }
+ else
+ clone->mode = NULL;
+ return clone;
+}
+
+static void keylist_free(GSList *keys)
+{
+ GSList *l;
+
+ for (l = keys; l; l = g_slist_next(l))
+ {
+ Accessibility_KeyDefinition *kd = (Accessibility_KeyDefinition *)l->data;
+ g_free(kd->keystring);
+ g_free(kd);
+ }
+ g_slist_free (keys);
+}
+
+static void
+spi_key_listener_data_free (DEControllerKeyListener *key_listener)
+{
+ keylist_free(key_listener->keys);
+ if (key_listener->mode) g_free(key_listener->mode);
+ g_free (key_listener);
+}
+
+static void
+spi_key_listener_clone_free (DEControllerKeyListener *clone)
+{
+ spi_key_listener_data_free (clone);
+}
+
+static void
+spi_listener_clone_free (DEControllerListener *clone)
+{
+ g_free (clone->path);
+ g_free (clone->bus_name);
+ g_free (clone);
+}
+
+static void
+spi_dec_listener_free (DEControllerListener *listener)
+{
+ g_free (listener->bus_name);
+ g_free (listener->path);
+ if (listener->type == SPI_DEVICE_TYPE_KBD)
+ spi_key_listener_data_free ((DEControllerKeyListener *) 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)
+ {
+ cur_mask->pending_remove = FALSE;
+ }
+ }
+ else
+ {
+ controller->keygrabs_list =
+ g_list_prepend (controller->keygrabs_list,
+ spi_grab_mask_clone (grab_mask));
+ }
+}
+
+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;
+ }
+ }
+}
+
+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 (g_slist_length (key_listener->keys) == 0) /* special case means AnyKey/AllKeys */
+ {
+ grab_mask.key_val = AnyKey;
+#ifdef SPI_DEBUG
+ fprintf (stderr, "AnyKey grab!");
+#endif
+ process_cb (controller, &grab_mask);
+ }
+ else
+ {
+ GSList *l;
+
+ for (l = key_listener->keys; l; l = g_slist_next(l))
+ {
+ Accessibility_KeyDefinition *keydef = l->data;
+ long int key_val = keydef->keysym;
+ /* X Grabs require keycodes, not keysyms */
+ if (keydef->keystring && keydef->keystring[0])
+ {
+ key_val = XStringToKeysym(keydef->keystring);
+ }
+ if (key_val > 0)
+ {
+ key_val = XKeysymToKeycode (spi_get_display (), (KeySym) key_val);
+ }
+ else
+ {
+ key_val = keydef->keycode;
+ }
+ grab_mask.key_val = key_val;
+ process_cb (controller, &grab_mask);
+ }
+ }
+}
+
+static gboolean
+spi_controller_register_global_keygrabs (SpiDEController *controller,
+ DEControllerKeyListener *key_listener)
+{
+ handle_keygrab (controller, key_listener, _register_keygrab);
+ if (controller->xevie_display == NULL)
+ return spi_controller_update_key_grabs (controller, NULL);
+ else
+ return TRUE;
+}
+
+static void
+spi_controller_deregister_global_keygrabs (SpiDEController *controller,
+ DEControllerKeyListener *key_listener)
+{
+ handle_keygrab (controller, key_listener, _deregister_keygrab);
+ if (controller->xevie_display == NULL)
+ spi_controller_update_key_grabs (controller, NULL);
+}
+
+static gboolean
+spi_controller_register_device_listener (SpiDEController *controller,
+ DEControllerListener *listener)
+{
+ DEControllerKeyListener *key_listener;
+
+ switch (listener->type) {
+ case SPI_DEVICE_TYPE_KBD:
+ key_listener = (DEControllerKeyListener *) listener;
+
+ controller->key_listeners = g_list_prepend (controller->key_listeners,
+ key_listener);
+ spi_dbus_add_disconnect_match (controller->droute->bus, key_listener->listener.bus_name);
+ if (key_listener->mode->global)
+ {
+ return spi_controller_register_global_keygrabs (controller, key_listener);
+ }
+ else
+ return TRUE;
+ break;
+ case SPI_DEVICE_TYPE_MOUSE:
+ controller->mouse_listeners = g_list_prepend (controller->mouse_listeners, listener);
+ spi_dbus_add_disconnect_match (controller->droute->bus, listener->bus_name);
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+static gboolean
+Accessibility_DeviceEventListener_notifyEvent(SpiDEController *controller,
+ SpiRegistry *registry,
+ DEControllerListener *listener,
+ const Accessibility_DeviceEvent *key_event)
+{
+ DBusMessage *message = dbus_message_new_method_call(listener->bus_name, listener->path, "org.freedesktop.atspi.Registry", "notifyEvent");
+ DBusError error;
+ dbus_bool_t consumed = FALSE;
+
+ dbus_error_init(&error);
+ if (spi_dbus_marshal_deviceEvent(message, key_event))
+ {
+ // TODO: Evaluate performance: perhaps rework this whole architecture
+ // to avoid blocking calls
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block(controller->droute->bus, message, 1000, &error);
+ if (reply)
+ {
+ DBusError error;
+ dbus_error_init(&error);
+ dbus_message_get_args(reply, &error, DBUS_TYPE_BOOLEAN, &consumed, DBUS_TYPE_INVALID);
+ dbus_message_unref(reply);
+ }
+ }
+ dbus_message_unref(message);
+ return consumed;
+}
+
+static gboolean
+spi_controller_notify_mouselisteners (SpiDEController *controller,
+ const Accessibility_DeviceEvent *event)
+{
+ GList *l;
+ GSList *notify = NULL, *l2;
+ GList **listeners = &controller->mouse_listeners;
+ gboolean is_consumed;
+#ifdef SPI_KEYEVENT_DEBUG
+ gboolean found = FALSE;
+#endif
+ if (!listeners)
+ {
+ return FALSE;
+ }
+
+ for (l = *listeners; l; l = l->next)
+ {
+ DEControllerListener *listener = l->data;
+
+ if (spi_eventtype_seq_contains_event (listener->types, event))
+ {
+ /* we clone (don't dup) the listener, to avoid refcount inc. */
+ notify = g_slist_prepend (notify,
+ spi_listener_clone (listener));
+#ifdef SPI_KEYEVENT_DEBUG
+ found = TRUE;
+#endif
+ }
+ }
+
+#ifdef SPI_KEYEVENT_DEBUG
+ if (!found)
+ {
+ g_print ("no match for event\n");
+ }
+#endif
+
+ is_consumed = FALSE;
+ for (l2 = notify; l2 && !is_consumed; l2 = l2->next)
+ {
+ DEControllerListener *listener = l2->data;
+
+ is_consumed = Accessibility_DeviceEventListener_notifyEvent (controller, controller->registry, listener, event);
+
+ spi_listener_clone_free ((DEControllerListener *) l2->data);
+ }
+
+ for (; l2; l2 = l2->next)
+ {
+ DEControllerListener *listener = l2->data;
+ spi_listener_clone_free (listener);
+ /* clone doesn't have its own ref, so don't use spi_device_listener_free */
+ }
+
+ g_slist_free (notify);
+
+#ifdef SPI_DEBUG
+ if (is_consumed) g_message ("consumed\n");
+#endif
+ return is_consumed;
+}
+
+static void
+spi_device_event_controller_forward_mouse_event (SpiDEController *controller,
+ XEvent *xevent)
+{
+ Accessibility_DeviceEvent mouse_e;
+ gchar event_name[24];
+ gboolean is_consumed = FALSE;
+ gboolean xkb_mod_unlatch_occurred;
+ XButtonEvent *xbutton_event = (XButtonEvent *) xevent;
+ dbus_uint32_t ix, iy;
+
+ int button = xbutton_event->button;
+
+ unsigned int mouse_button_state = xbutton_event->state;
+
+ switch (button)
+ {
+ case 1:
+ mouse_button_state |= Button1Mask;
+ break;
+ case 2:
+ mouse_button_state |= Button2Mask;
+ break;
+ case 3:
+ mouse_button_state |= Button3Mask;
+ break;
+ case 4:
+ mouse_button_state |= Button4Mask;
+ break;
+ case 5:
+ mouse_button_state |= Button5Mask;
+ break;
+ }
+
+ last_mouse_pos->x = ((XButtonEvent *) xevent)->x_root;
+ last_mouse_pos->y = ((XButtonEvent *) xevent)->y_root;
+
+#ifdef SPI_DEBUG
+ fprintf (stderr, "mouse button %d %s (%x)\n",
+ xbutton_event->button,
+ (xevent->type == ButtonPress) ? "Press" : "Release",
+ mouse_button_state);
+#endif
+ snprintf (event_name, 22, "mouse:button_%d%c", button,
+ (xevent->type == ButtonPress) ? 'p' : 'r');
+
+ /* TODO: FIXME distinguish between physical and logical buttons */
+ mouse_e.type = (xevent->type == ButtonPress) ?
+ Accessibility_BUTTON_PRESSED_EVENT :
+ Accessibility_BUTTON_RELEASED_EVENT;
+ mouse_e.id = button;
+ mouse_e.hw_code = button;
+ mouse_e.modifiers = (dbus_uint16_t) xbutton_event->state;
+ mouse_e.timestamp = (dbus_uint32_t) xbutton_event->time;
+ mouse_e.event_string = "";
+ mouse_e.is_text = FALSE;
+ if ((mouse_button_state & mouse_button_mask) !=
+ (mouse_mask_state & mouse_button_mask))
+ {
+ if ((mouse_mask_state & key_modifier_mask) !=
+ (mouse_button_state & key_modifier_mask))
+ spi_dec_emit_modifier_event (controller,
+ mouse_mask_state, mouse_button_state);
+ mouse_mask_state = mouse_button_state;
+ is_consumed =
+ spi_controller_notify_mouselisteners (controller, &mouse_e);
+ ix = last_mouse_pos->x;
+ iy = last_mouse_pos->y;
+ /* TODO - Work out which part of the spec this emit is fulfilling */
+ //emit(controller, event_name, DBUS_TYPE_UINT32, &ix, DBUS_TYPE_UINT32, &iy, DBUS_TYPE_INVALID);
+ }
+
+ xkb_mod_unlatch_occurred = (xevent->type == ButtonPress ||
+ xevent->type == ButtonRelease);
+
+ /* if client wants to consume this event, and XKB latch state was
+ * unset by this button event, we reset it
+ */
+ if (is_consumed && xkb_mod_unlatch_occurred)
+ spi_dec_set_unlatch_pending (controller, mouse_mask_state);
+
+ XAllowEvents (spi_get_display (),
+ (is_consumed) ? SyncPointer : ReplayPointer,
+ CurrentTime);
+}
+
+static GdkFilterReturn
+global_filter_fn (GdkXEvent *gdk_xevent, GdkEvent *event, gpointer data)
+{
+ XEvent *xevent = gdk_xevent;
+ SpiDEController *controller;
+ DEControllerPrivateData *priv;
+ Display *display = spi_get_display ();
+ controller = SPI_DEVICE_EVENT_CONTROLLER (data);
+ priv = (DEControllerPrivateData *)
+ g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+
+ if (xevent->type == MappingNotify)
+ xmkeymap = NULL;
+
+ if (xevent->type == KeyPress || xevent->type == KeyRelease)
+ {
+ if (controller->xevie_display == NULL)
+ {
+ gboolean is_consumed;
+
+ is_consumed =
+ spi_device_event_controller_forward_key_event (controller, xevent);
+
+ if (is_consumed)
+ {
+ int n_events;
+ int i;
+ XEvent next_event;
+ n_events = XPending (display);