+static void
+spi_dec_set_unlatch_pending (SpiDEController *controller, unsigned mask)
+{
+ DEControllerPrivateData *priv =
+ g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+#ifdef SPI_XKB_DEBUG
+ if (priv->xkb_latch_mask) fprintf (stderr, "unlatch pending! %x\n",
+ priv->xkb_latch_mask);
+#endif
+ priv->pending_xkb_mod_relatch_mask |= priv->xkb_latch_mask;
+}
+
+static void
+spi_dec_clear_unlatch_pending (SpiDEController *controller)
+{
+ DEControllerPrivateData *priv =
+ g_object_get_qdata (G_OBJECT (controller), spi_dec_private_quark);
+ priv->xkb_latch_mask = 0;
+}
+
+static void emit(SpiDEController *controller, const char *interface, const char *name, int a1, int a2)
+{
+ DBusMessage *signal = NULL;
+ DBusMessageIter iter, iter_variant;
+ int nil = 0;
+ const char *minor = "";
+ const char *path = SPI_DBUS_PATH_DEC;
+
+ signal = dbus_message_new_signal (path, interface, name);
+
+ dbus_message_iter_init_append (signal, &iter);
+
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &minor);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &a1);
+ dbus_message_iter_append_basic (&iter, DBUS_TYPE_UINT32, &a2);
+ dbus_message_iter_open_container (&iter, DBUS_TYPE_VARIANT, "i", &iter_variant);
+ dbus_message_iter_append_basic (&iter_variant, DBUS_TYPE_INT32, &nil);
+ dbus_message_iter_close_container (&iter, &iter_variant);
+
+ dbus_connection_send (controller->bus, signal, NULL);
+}
+
+static gboolean
+spi_dec_button_update_and_emit (SpiDEController *controller,
+ guint mask_return)
+{
+ Accessibility_DeviceEvent mouse_e;
+ gchar event_detail[24];
+ gboolean is_consumed = FALSE;
+
+ if ((mask_return & mouse_button_mask) !=
+ (mouse_mask_state & mouse_button_mask))
+ {
+ int button_number = 0;
+ gboolean is_down = False;
+
+ if (!(mask_return & Button1Mask) &&
+ (mouse_mask_state & Button1Mask))
+ {
+ mouse_mask_state &= ~Button1Mask;
+ button_number = 1;
+ }
+ else if ((mask_return & Button1Mask) &&
+ !(mouse_mask_state & Button1Mask))
+ {
+ mouse_mask_state |= Button1Mask;
+ button_number = 1;
+ is_down = True;
+ }
+ else if (!(mask_return & Button2Mask) &&
+ (mouse_mask_state & Button2Mask))
+ {
+ mouse_mask_state &= ~Button2Mask;
+ button_number = 2;
+ }
+ else if ((mask_return & Button2Mask) &&
+ !(mouse_mask_state & Button2Mask))
+ {
+ mouse_mask_state |= Button2Mask;
+ button_number = 2;
+ is_down = True;
+ }
+ else if (!(mask_return & Button3Mask) &&
+ (mouse_mask_state & Button3Mask))
+ {
+ mouse_mask_state &= ~Button3Mask;
+ button_number = 3;
+ }
+ else if ((mask_return & Button3Mask) &&
+ !(mouse_mask_state & Button3Mask))
+ {
+ mouse_mask_state |= Button3Mask;
+ button_number = 3;
+ is_down = True;
+ }
+ else if (!(mask_return & Button4Mask) &&
+ (mouse_mask_state & Button4Mask))
+ {
+ mouse_mask_state &= ~Button4Mask;
+ button_number = 4;
+ }
+ else if ((mask_return & Button4Mask) &&
+ !(mouse_mask_state & Button4Mask))
+ {
+ mouse_mask_state |= Button4Mask;
+ button_number = 4;
+ is_down = True;
+ }
+ else if (!(mask_return & Button5Mask) &&
+ (mouse_mask_state & Button5Mask))
+ {
+ mouse_mask_state &= ~Button5Mask;
+ button_number = 5;
+ }
+ else if ((mask_return & Button5Mask) &&
+ !(mouse_mask_state & Button5Mask))
+ {
+ mouse_mask_state |= Button5Mask;
+ button_number = 5;
+ is_down = True;
+ }
+ if (button_number) {
+#ifdef SPI_DEBUG
+ fprintf (stderr, "Button %d %s\n",
+ button_number, (is_down) ? "Pressed" : "Released");
+#endif
+ snprintf (event_detail, 22, "%d%c", button_number,
+ (is_down) ? 'p' : 'r');
+ /* TODO: FIXME distinguish between physical and
+ * logical buttons
+ */
+ mouse_e.type = (is_down) ?
+ Accessibility_BUTTON_PRESSED_EVENT :
+ Accessibility_BUTTON_RELEASED_EVENT;
+ mouse_e.id = button_number;
+ mouse_e.hw_code = button_number;
+ mouse_e.modifiers = (dbus_uint16_t) mouse_mask_state;
+ mouse_e.timestamp = 0;
+ mouse_e.event_string = "";
+ mouse_e.is_text = FALSE;
+ is_consumed =
+ spi_controller_notify_mouselisteners (controller,
+ &mouse_e);
+ if (!is_consumed)
+ {
+ dbus_uint32_t x = last_mouse_pos->x, y = last_mouse_pos->y;
+ emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "button", x, y);
+ }
+ else
+ spi_dec_set_unlatch_pending (controller, mask_return);
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+static guint
+spi_dec_mouse_check (SpiDEController *controller,
+ int *x, int *y, gboolean *moved)
+{
+ int win_x_return,win_y_return;
+ unsigned int mask_return;
+ Window root_return, child_return;
+ Display *display = spi_get_display ();
+
+ if (display != NULL)
+ XQueryPointer(display, DefaultRootWindow (display),
+ &root_return, &child_return,
+ x, y,
+ &win_x_return, &win_y_return, &mask_return);
+ /*
+ * Since many clients grab the pointer, and X goes an automatic
+ * pointer grab on mouse-down, we often must detect mouse button events
+ * by polling rather than via a button grab.
+ * The while loop (rather than if) is used since it's possible that
+ * multiple buttons have changed state since we last checked.
+ */
+ if (mask_return != mouse_mask_state)
+ {
+ while (spi_dec_button_update_and_emit (controller, mask_return));
+ }
+
+ if (*x != last_mouse_pos->x || *y != last_mouse_pos->y)
+ {
+ // TODO: combine these two signals?
+ dbus_uint32_t ix = *x, iy = *y;
+ emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "abs", ix, iy);
+ ix -= last_mouse_pos->x;
+ iy -= last_mouse_pos->y;
+ emit(controller, SPI_DBUS_INTERFACE_EVENT_MOUSE, "rel", ix, iy);
+ last_mouse_pos->x = *x;
+ last_mouse_pos->y = *y;
+ *moved = True;
+ }
+ else
+ {
+ *moved = False;
+ }
+
+ return mask_return;
+}
+
+static void
+spi_dec_emit_modifier_event (SpiDEController *controller, guint prev_mask,
+ guint current_mask)
+{
+ dbus_uint32_t d1, d2;
+
+#ifdef SPI_XKB_DEBUG
+ fprintf (stderr, "MODIFIER CHANGE EVENT! %x to %x\n",
+ prev_mask, current_mask);
+#endif
+
+ /* set bits for the virtual modifiers like NUMLOCK */
+ if (prev_mask & _numlock_physical_mask)
+ prev_mask |= SPI_KEYMASK_NUMLOCK;
+ if (current_mask & _numlock_physical_mask)
+ current_mask |= SPI_KEYMASK_NUMLOCK;
+
+ d1 = prev_mask & key_modifier_mask;
+ d2 = current_mask & key_modifier_mask;
+ emit(controller, SPI_DBUS_INTERFACE_EVENT_KEYBOARD, "modifiers", d1, d2);
+}
+
+static gboolean
+spi_dec_poll_mouse_moved (gpointer data)
+{
+ SpiDEController *controller = SPI_DEVICE_EVENT_CONTROLLER(data);
+ int x, y;
+ gboolean moved;
+ guint mask_return;
+
+ mask_return = spi_dec_mouse_check (controller, &x, &y, &moved);
+
+ if ((mask_return & key_modifier_mask) !=
+ (mouse_mask_state & key_modifier_mask))
+ {
+ spi_dec_emit_modifier_event (controller, mouse_mask_state, mask_return);
+ mouse_mask_state = mask_return;
+ }
+
+ return moved;
+}
+
+static gboolean
+spi_dec_poll_mouse_idle (gpointer data)
+{
+ if (! spi_dec_poll_mouse_moved (data))
+ return TRUE;
+ else
+ {
+ g_timeout_add (20, spi_dec_poll_mouse_moving, data);
+ return FALSE;
+ }
+}
+
+static gboolean
+spi_dec_poll_mouse_moving (gpointer data)
+{
+ if (spi_dec_poll_mouse_moved (data))
+ return TRUE;
+ else
+ {
+ g_timeout_add (100, spi_dec_poll_mouse_idle, data);
+ return FALSE;
+ }
+}
+
+#ifdef WE_NEED_UGRAB_MOUSE
+static int
+spi_dec_ungrab_mouse (gpointer data)
+{
+ Display *display = spi_get_display ();
+ if (display)
+ {
+ XUngrabButton (spi_get_display (), AnyButton, AnyModifier,
+ XDefaultRootWindow (spi_get_display ()));
+ }
+ return FALSE;
+}
+#endif
+
+static void
+spi_dec_init_mouse_listener (SpiDEController *dec)
+{
+#ifdef GRAB_BUTTON
+ Display *display = spi_get_display ();
+#endif
+ g_timeout_add (100, spi_dec_poll_mouse_idle, dec);
+
+#ifdef GRAB_BUTTON
+ if (display)
+ {
+ if (XGrabButton (display, AnyButton, AnyModifier,
+ gdk_x11_get_default_root_xwindow (),
+ True, ButtonPressMask | ButtonReleaseMask,
+ GrabModeSync, GrabModeAsync, None, None) != Success) {
+#ifdef SPI_DEBUG
+ fprintf (stderr, "WARNING: could not grab mouse buttons!\n");
+#endif
+ ;
+ }
+ XSync (display, False);
+#ifdef SPI_DEBUG
+ fprintf (stderr, "mouse buttons grabbed\n");
+#endif
+ }
+#endif
+}
+
+/**
+ * Eventually we can use this to make the marshalling of mask types
+ * more sane, but for now we just use this to detect
+ * the use of 'virtual' masks such as numlock and convert them to
+ * system-specific mask values (i.e. ModMask).
+ *
+ **/
+static Accessibility_ControllerEventMask
+spi_dec_translate_mask (Accessibility_ControllerEventMask mask)
+{
+ Accessibility_ControllerEventMask tmp_mask;
+ gboolean has_numlock;
+
+ has_numlock = (mask & SPI_KEYMASK_NUMLOCK);
+ tmp_mask = mask;
+ if (has_numlock)
+ {
+ tmp_mask = mask ^ SPI_KEYMASK_NUMLOCK;
+ tmp_mask |= _numlock_physical_mask;
+ }
+
+ return tmp_mask;
+}
+