[mousetweaks] Drop the use of AT-SPI to get global mouse events
- From: Gerd Kohlberger <gerdk src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mousetweaks] Drop the use of AT-SPI to get global mouse events
- Date: Sat, 29 May 2010 18:13:27 +0000 (UTC)
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]