[mousetweaks] Drop the use of AT-SPI to get global mouse events



commit 719d151e6312ecadabb2a5facb1b39efbe7be616
Author: Gerd Kohlberger <gerdk src gnome org>
Date:   Sat May 29 20:04:17 2010 +0200

    Drop the use of AT-SPI to get global mouse events
    
    MtListener now uses a combintion of polling and passive grabs to
    get mouse motion and button events. The implementation is
    similar to the one used in the DeviceEventController of the
    AT-SPI registry daemon.

 src/mt-listener.c |  421 +++++++++++++++++++++++++++++++++++++----------------
 src/mt-listener.h |   39 ++---
 src/mt-main.c     |   26 +---
 3 files changed, 317 insertions(+), 169 deletions(-)
---
diff --git a/src/mt-listener.c b/src/mt-listener.c
index 3d5d701..864bbc7 100644
--- a/src/mt-listener.c
+++ b/src/mt-listener.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2007-2009 Gerd Kohlberger <lowfi chello at>
+ * Copyright © 2007-2010 Gerd Kohlberger <gerdko gmail com>
  *
  * This file is part of Mousetweaks.
  *
@@ -17,188 +17,367 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <glib.h>
+#include <gdk/gdkx.h>
 
 #include "mt-listener.h"
 
-struct _MtListenerPrivate {
-    AccessibleEventListener *motion;
-    AccessibleEventListener *button;
-    AccessibleEventListener *focus;
-    Accessible              *current_focus;
+#define XBUTTON_MASK (Button1Mask | Button2Mask | Button3Mask | \
+                      Button4Mask | Button5Mask)
+
+#define POLL_INTERVAL_IDLE    100
+#define POLL_INTERVAL_MOVING  20
+
+struct _MtListenerPrivate
+{
+    gint  last_x;
+    gint  last_y;
+    guint mask_state;
 };
 
-enum {
+enum
+{
     MOTION_EVENT,
     BUTTON_EVENT,
-    FOCUS_CHANGED,
     LAST_SIGNAL
 };
 
-static guint signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0, };
 
-static void mt_listener_dispose      (GObject               *object);
-static void mt_listener_motion_event (const AccessibleEvent *event,
-				      gpointer               data);
-static void mt_listener_button_event (const AccessibleEvent *event,
-				      gpointer               data);
-static void mt_listener_focus_event  (const AccessibleEvent *event,
-				      gpointer               data);
+static void              mt_listener_grab_buttons     (void);
+static void              mt_listener_ungrab_buttons   (void);
+static gboolean          mt_listener_mouse_idle       (gpointer   data);
+static GdkFilterReturn   mt_listener_event_filter     (GdkXEvent *gdk_xevent,
+                                                       GdkEvent  *gdk_event,
+                                                       gpointer   data);
 
 G_DEFINE_TYPE (MtListener, mt_listener, G_TYPE_OBJECT)
 
 static void
+mt_listener_init (MtListener *listener)
+{
+    listener->priv = G_TYPE_INSTANCE_GET_PRIVATE (listener,
+                                                  MT_TYPE_LISTENER,
+                                                  MtListenerPrivate);
+
+    gdk_window_add_filter (NULL, mt_listener_event_filter, listener);
+    mt_listener_grab_buttons ();
+    mt_listener_mouse_idle (listener);
+}
+
+static void
+mt_listener_finalize (GObject *object)
+{
+    gdk_window_remove_filter (NULL, mt_listener_event_filter, object);
+    mt_listener_ungrab_buttons ();
+
+    G_OBJECT_CLASS (mt_listener_parent_class)->finalize (object);
+}
+
+static void
 mt_listener_class_init (MtListenerClass *klass)
 {
     GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-    object_class->dispose = mt_listener_dispose;
-
-    signals[MOTION_EVENT] = 
-	g_signal_new (g_intern_static_string ("motion_event"),
-		      G_OBJECT_CLASS_TYPE (klass),
-		      G_SIGNAL_RUN_LAST,
-		      0, NULL, NULL,
-		      g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE,
-		      1, MT_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
-
-    signals[BUTTON_EVENT] = 
-	g_signal_new (g_intern_static_string ("button_event"),
-		      G_OBJECT_CLASS_TYPE (klass),
-		      G_SIGNAL_RUN_LAST,
-		      0, NULL, NULL,
-		      g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE,
-		      1, MT_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
-
-    signals[FOCUS_CHANGED] =
-	g_signal_new (g_intern_static_string ("focus_changed"),
-		      G_OBJECT_CLASS_TYPE (klass),
-		      G_SIGNAL_RUN_LAST,
-		      0, NULL, NULL,
-		      g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
+    object_class->finalize = mt_listener_finalize;
+
+    signals[MOTION_EVENT] =
+        g_signal_new (g_intern_static_string ("motion_event"),
+                      G_OBJECT_CLASS_TYPE (klass),
+                      G_SIGNAL_RUN_LAST,
+                      0, NULL, NULL,
+                      g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE,
+                      1, MT_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
+
+    signals[BUTTON_EVENT] =
+        g_signal_new (g_intern_static_string ("button_event"),
+                      G_OBJECT_CLASS_TYPE (klass),
+                      G_SIGNAL_RUN_LAST,
+                      0, NULL, NULL,
+                      g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE,
+                      1, MT_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE);
 
     g_type_class_add_private (klass, sizeof (MtListenerPrivate));
 }
 
+static MtEvent *
+mt_event_copy (const MtEvent *event)
+{
+    return g_memdup (event, sizeof (MtEvent));
+}
+
 static void
-mt_listener_init (MtListener *listener)
+mt_event_free (MtEvent *event)
 {
-    MtListenerPrivate *priv;
+    g_free (event);
+}
 
-    listener->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (listener,
-							 MT_TYPE_LISTENER,
-							 MtListenerPrivate);
+GType
+mt_event_get_type (void)
+{
+    static GType event = 0;
+
+    if (G_UNLIKELY (event == 0))
+    {
+        event = g_boxed_type_register_static (g_intern_static_string ("MtEvent"),
+                                              (GBoxedCopyFunc) mt_event_copy,
+                                              (GBoxedFreeFunc) mt_event_free);
+    }
+    return event;
+}
 
-    priv->motion = SPI_createAccessibleEventListener (mt_listener_motion_event,
-						      listener);
-    SPI_registerGlobalEventListener (priv->motion, "mouse:abs");
+static void
+mt_listener_emit_button_event (MtListener *listener,
+                               MtEventType type,
+                               gint        button,
+                               gint        x,
+                               gint        y)
+{
+    MtEvent ev;
 
-    priv->button = SPI_createAccessibleEventListener (mt_listener_button_event,
-						      listener);
-    SPI_registerGlobalEventListener (priv->button, "mouse:button:");
+    ev.type = type;
+    ev.button = button;
+    ev.x = x;
+    ev.y = y;
 
-    priv->focus = SPI_createAccessibleEventListener (mt_listener_focus_event,
-						     listener);
-    SPI_registerGlobalEventListener (priv->focus, "focus:");
+    g_signal_emit (listener, signals[BUTTON_EVENT], 0, &ev);
 }
 
 static void
-mt_listener_dispose (GObject *object)
+mt_listener_emit_motion_event (MtListener *listener,
+                               gint        x,
+                               gint        y)
+{
+    MtEvent ev;
+
+    ev.type = MT_EVENT_MOTION;
+    ev.button = 0;
+    ev.x = x;
+    ev.y = y;
+
+    g_signal_emit (listener, signals[MOTION_EVENT], 0, &ev);
+}
+
+static Display *
+mt_listener_display (void)
+{
+    return GDK_DISPLAY ();
+}
+
+static gboolean
+mt_listener_maybe_emit (MtListener *listener, guint mask)
 {
-    MtListenerPrivate *priv = MT_LISTENER (object)->priv;
+    MtListenerPrivate *priv = listener->priv;
+    gint button = 0;
+    gboolean pressed = FALSE;
+
+    if ((mask & XBUTTON_MASK) == (priv->mask_state & XBUTTON_MASK))
+        return FALSE;
 
-    if (priv->motion) {
-	SPI_deregisterGlobalEventListenerAll (priv->motion);
-	AccessibleEventListener_unref (priv->motion);
-	priv->motion = NULL;
+    if (!(mask & Button1Mask) && (priv->mask_state & Button1Mask))
+    {
+        priv->mask_state &= ~Button1Mask;
+        button = 1;
+    }
+    else if ((mask & Button1Mask) && !(priv->mask_state & Button1Mask))
+    {
+        priv->mask_state |= Button1Mask;
+        button = 1;
+        pressed = TRUE;
+    }
+    else if (!(mask & Button2Mask) && (priv->mask_state & Button2Mask))
+    {
+        priv->mask_state &= ~Button2Mask;
+        button = 2;
+    }
+    else if ((mask & Button2Mask) && !(priv->mask_state & Button2Mask))
+    {
+        priv->mask_state |= Button2Mask;
+        button = 2;
+        pressed = TRUE;
     }
-    if (priv->button) {
-	SPI_deregisterGlobalEventListenerAll (priv->button);
-	AccessibleEventListener_unref (priv->button);
-	priv->button = NULL;
+    else if (!(mask & Button3Mask) && (priv->mask_state & Button3Mask))
+    {
+        priv->mask_state &= ~Button3Mask;
+        button = 3;
     }
-    if (priv->focus) {
-	SPI_deregisterGlobalEventListenerAll (priv->focus);
-	AccessibleEventListener_unref (priv->focus);
-	priv->focus = NULL;
+    else if ((mask & Button3Mask) && !(priv->mask_state & Button3Mask))
+    {
+        priv->mask_state |= Button3Mask;
+        button = 3;
+        pressed = TRUE;
     }
-    if (priv->current_focus) {
-	Accessible_unref (priv->current_focus);
-	priv->current_focus = NULL;
+    /*
+     * scroll-wheel events come in press-release pairs,
+     * so this is mostly an exercise in futility.
+     */
+    else if (!(mask & Button4Mask) && (priv->mask_state & Button4Mask))
+    {
+        priv->mask_state &= ~Button4Mask;
+        button = 4;
+    }
+    else if ((mask & Button4Mask) && !(priv->mask_state & Button4Mask))
+    {
+        priv->mask_state |= Button4Mask;
+        button = 4;
+        pressed = TRUE;
+    }
+    else if (!(mask & Button5Mask) && (priv->mask_state & Button5Mask))
+    {
+        priv->mask_state &= ~Button5Mask;
+        button = 5;
+    }
+    else if ((mask & Button5Mask) && !(priv->mask_state & Button5Mask))
+    {
+        priv->mask_state |= Button5Mask;
+        button = 5;
+        pressed = TRUE;
     }
 
-    G_OBJECT_CLASS (mt_listener_parent_class)->dispose (object);
+    if (button)
+    {
+        mt_listener_emit_button_event (listener,
+                                       pressed ? MT_EVENT_BUTTON_PRESS :
+                                                 MT_EVENT_BUTTON_RELEASE,
+                                       button,
+                                       priv->last_x,
+                                       priv->last_y);
+    }
+    return TRUE;
 }
 
-GType
-mt_event_get_type (void)
+static gboolean
+mt_listener_mouse_moved (MtListener *listener)
 {
-    static GType event = 0;
+    MtListenerPrivate *priv = listener->priv;
+    Display *dpy;
+    Window root_return, child_return;
+    gint x, y, win_x_return, win_y_return;
+    guint mask_return;
 
-    if (G_UNLIKELY (event == 0))
-	event = g_boxed_type_register_static (g_intern_static_string ("MtEvent"),
-					      (GBoxedCopyFunc) mt_event_copy,
-					      (GBoxedFreeFunc) mt_event_free);
-    return event;
+    dpy = mt_listener_display ();
+
+    gdk_error_trap_push ();
+    XQueryPointer (dpy, DefaultRootWindow (dpy),
+                   &root_return, &child_return, &x, &y,
+                   &win_x_return, &win_y_return, &mask_return);
+    gdk_error_trap_pop ();
+
+    while (mt_listener_maybe_emit (listener, mask_return));
+
+    if (x != priv->last_x || y != priv->last_y)
+    {
+        mt_listener_emit_motion_event (listener, x, y);
+
+        priv->last_x = x;
+        priv->last_y = y;
+
+        return TRUE;
+    }
+    return FALSE;
 }
 
-MtEvent *
-mt_event_copy (const MtEvent *event)
+static gboolean
+mt_listener_mouse_moving (gpointer data)
 {
-    return (MtEvent *) g_memdup (event, sizeof (MtEvent));
+    if (!mt_listener_mouse_moved (data))
+    {
+        g_timeout_add (POLL_INTERVAL_IDLE, mt_listener_mouse_idle, data);
+        return FALSE;
+    }
+    return TRUE;
 }
 
-void
-mt_event_free (MtEvent *event)
+static gboolean
+mt_listener_mouse_idle (gpointer data)
 {
-    g_free (event);
+    if (mt_listener_mouse_moved (data))
+    {
+        g_timeout_add (POLL_INTERVAL_MOVING, mt_listener_mouse_moving, data);
+        return FALSE;
+    }
+    return TRUE;
 }
 
 static void
-mt_listener_motion_event (const AccessibleEvent *event, gpointer data)
+mt_listener_grab_buttons (void)
 {
-    MtEvent ev;
+    Display *dpy = mt_listener_display ();
+
+    gdk_error_trap_push ();
+    XGrabButton (dpy, AnyButton, AnyModifier,
+                 gdk_x11_get_default_root_xwindow (),
+                 True, ButtonPressMask | ButtonReleaseMask,
+                 GrabModeSync, GrabModeAsync, None, None);
+    XSync (dpy, False);
+    gdk_error_trap_pop ();
+}
 
-    ev.type = EV_MOTION;
-    ev.x = (gint) event->detail1;
-    ev.y = (gint) event->detail2;
-    ev.button = 0;
+static void
+mt_listener_ungrab_buttons (void)
+{
+    Display *dpy = mt_listener_display ();
 
-    g_signal_emit (data, signals[MOTION_EVENT], 0, &ev);
+    gdk_error_trap_push ();
+    XUngrabButton (dpy, AnyButton, AnyModifier, XDefaultRootWindow (dpy));
+    gdk_error_trap_pop ();
 }
 
 static void
-mt_listener_button_event (const AccessibleEvent *event, gpointer data)
+mt_listener_forward_button_event (MtListener *listener, XButtonEvent *bev)
 {
-    MtEvent ev;
+    MtListenerPrivate *priv = listener->priv;
+    guint button_state = bev->state;
+
+    switch (bev->button)
+    {
+        case 1:
+            button_state |= Button1Mask;
+            break;
+        case 2:
+            button_state |= Button2Mask;
+            break;
+        case 3:
+            button_state |= Button3Mask;
+            break;
+        case 4:
+            button_state |= Button4Mask;
+            break;
+        case 5:
+            button_state |= Button5Mask;
+            break;
+    }
 
-    /*
-     * This is obviously dangerous, but it should be
-     * guarantied that event-type strings will always
-     * be in the form of "mouse:button:[1,2,3][p,r]"
-     */
-    ev.type = event->type[14] == 'p' ? EV_BUTTON_PRESS : EV_BUTTON_RELEASE;
-    ev.x = (gint) event->detail1;
-    ev.y = (gint) event->detail2;
-    ev.button = event->type[13] == '1' ? 1 : (event->type[13] == '2' ? 2 : 3);
+    mt_listener_emit_button_event (listener,
+                                   bev->type == ButtonPress ? MT_EVENT_BUTTON_PRESS :
+                                                              MT_EVENT_BUTTON_RELEASE,
+                                   bev->button,
+                                   bev->x_root,
+                                   bev->y_root);
 
-    g_signal_emit (data, signals[BUTTON_EVENT], 0, &ev);
+    priv->last_x = bev->x_root;
+    priv->last_y = bev->y_root;
+
+    if ((button_state & XBUTTON_MASK) != (priv->mask_state & XBUTTON_MASK))
+    {
+        priv->mask_state = button_state;
+    }
 }
 
-static void
-mt_listener_focus_event (const AccessibleEvent *event, gpointer data)
+static GdkFilterReturn
+mt_listener_event_filter (GdkXEvent *gdk_xevent,
+                          GdkEvent  *gdk_event,
+                          gpointer   data)
 {
-    MtListenerPrivate *priv = MT_LISTENER (data)->priv;
+    XEvent *xev = gdk_xevent;
 
-    if (event->source) {
-	if (priv->current_focus)
-	    Accessible_unref (priv->current_focus);
+    if (xev->type == ButtonPress || xev->type == ButtonRelease)
+    {
+        XButtonEvent *bev = (XButtonEvent *) xev;
 
-	Accessible_ref (event->source);
-	priv->current_focus = event->source;
-
-	g_signal_emit (data, signals[FOCUS_CHANGED], 0);
+        mt_listener_forward_button_event (data, bev);
+        XAllowEvents (bev->display, ReplayPointer, bev->time);
     }
+    return GDK_FILTER_CONTINUE;
 }
 
 MtListener *
@@ -207,15 +386,7 @@ mt_listener_get_default (void)
     static MtListener *listener = NULL;
 
     if (!listener)
-	listener = g_object_new (MT_TYPE_LISTENER, NULL);
+        listener = g_object_new (MT_TYPE_LISTENER, NULL);
 
     return listener;
 }
-
-Accessible *
-mt_listener_current_focus (MtListener *listener)
-{
-    g_return_val_if_fail (MT_IS_LISTENER (listener), NULL);
-
-    return listener->priv->current_focus;
-}
diff --git a/src/mt-listener.h b/src/mt-listener.h
index e000835..d5e9f0a 100644
--- a/src/mt-listener.h
+++ b/src/mt-listener.h
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2007-2009 Gerd Kohlberger <lowfi chello at>
+ * Copyright © 2007-2010 Gerd Kohlberger <gerdko gmail com>
  *
  * This file is part of Mousetweaks.
  *
@@ -21,48 +21,43 @@
 #define __MT_LISTENER_H__
 
 #include <glib-object.h>
-#include <cspi/spi.h>
 
 G_BEGIN_DECLS
 
-#define MT_TYPE_EVENT            (mt_event_get_type ())
-#define MT_TYPE_LISTENER         (mt_listener_get_type ())
-#define MT_LISTENER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), MT_TYPE_LISTENER, MtListener))
-#define MT_LISTENER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), MT_TYPE_LISTENER, MtListenerClass))
-#define MT_IS_LISTENER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), MT_TYPE_LISTENER))
-#define MT_IS_LISTENER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), MT_TYPE_LISTENER))
-#define MT_LISTENER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), MT_TYPE_LISTENER, MtListenerClass))
+#define MT_TYPE_EVENT     (mt_event_get_type ())
+#define MT_TYPE_LISTENER  (mt_listener_get_type ())
+#define MT_LISTENER(o)    (G_TYPE_CHECK_INSTANCE_CAST ((o), MT_TYPE_LISTENER, MtListener))
+#define MT_IS_LISTENER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), MT_TYPE_LISTENER))
 
 typedef GObjectClass              MtListenerClass;
 typedef struct _MtListener        MtListener;
 typedef struct _MtListenerPrivate MtListenerPrivate;
 
-struct _MtListener {
+struct _MtListener
+{
     GObject            parent;
     MtListenerPrivate *priv;
 };
 
-GType        mt_listener_get_type       (void) G_GNUC_CONST;
-MtListener * mt_listener_get_default    (void);
-Accessible * mt_listener_current_focus  (MtListener *listener);
-
-typedef enum {
-    EV_MOTION = 0,
-    EV_BUTTON_PRESS,
-    EV_BUTTON_RELEASE
+typedef enum
+{
+    MT_EVENT_MOTION = 0,
+    MT_EVENT_BUTTON_PRESS,
+    MT_EVENT_BUTTON_RELEASE
 } MtEventType;
 
 typedef struct _MtEvent MtEvent;
-struct _MtEvent {
+struct _MtEvent
+{
     MtEventType type;
     gint        x;
     gint        y;
     gint        button;
 };
 
-GType     mt_event_get_type (void) G_GNUC_CONST;
-MtEvent * mt_event_copy     (const MtEvent *event);
-void      mt_event_free     (MtEvent       *event);
+GType              mt_event_get_type          (void) G_GNUC_CONST;
+GType              mt_listener_get_type       (void) G_GNUC_CONST;
+MtListener *       mt_listener_get_default    (void);
 
 G_END_DECLS
 
diff --git a/src/mt-main.c b/src/mt-main.c
index 449eada..6f7e23e 100644
--- a/src/mt-main.c
+++ b/src/mt-main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2007-2009 Gerd Kohlberger <lowfi chello at>
+ * Copyright © 2007-2010 Gerd Kohlberger <gerdko gmail com>
  *
  * This file is part of Mousetweaks.
  *
@@ -497,7 +497,7 @@ global_button_event (MtListener *listener,
     MtData *mt = data;
 
     if (mt->delay_enabled && event->button == 1) {
-	if (event->type == EV_BUTTON_PRESS) {
+	if (event->type == MT_EVENT_BUTTON_PRESS) {
 	    mt->pointer_x = event->x;
 	    mt->pointer_y = event->y;
 	    mt_timer_start (mt->delay_timer);
@@ -515,28 +515,12 @@ global_button_event (MtListener *listener,
      * cancel a dwell-click in progress if a physical button
      * is pressed - useful for mixed use-cases and testing
      */
-    if ((event->type == EV_BUTTON_PRESS && mt_timer_is_running (mt->dwell_timer)) ||
-        (event->type == EV_BUTTON_RELEASE && mt->dwell_drag_started)) {
+    if ((event->type == MT_EVENT_BUTTON_PRESS && mt_timer_is_running (mt->dwell_timer)) ||
+        (event->type == MT_EVENT_BUTTON_RELEASE && mt->dwell_drag_started)) {
 	mt_dwell_click_cancel (mt);
     }
 }
 
-static void
-global_focus_event (MtListener *listener,
-		    gpointer    data)
-{
-    MtData *mt = data;
-    Accessible *accessible;
-
-    if (mt->delay_enabled) {
-	accessible = mt_listener_current_focus (listener);
-	/* TODO: check for more objects and conditions.
-	 * Some links don't have jump actions, eg: text-mails in thunderbird.
-	 */
-	mt->move_release = mt_accessible_supports_action (accessible, "jump");
-    }
-}
-
 static gboolean
 cursor_overlay_time (MtData  *mt,
 		     guchar  *image,
@@ -997,8 +981,6 @@ mt_main (int argc, char **argv, MtCliArgs cli_args)
                       G_CALLBACK (global_motion_event), mt);
     g_signal_connect (listener, "button_event",
                       G_CALLBACK (global_button_event), mt);
-    g_signal_connect (listener, "focus_changed",
-                      G_CALLBACK (global_focus_event), mt);
 
     gtk_main ();
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]