Sugar devs:
This is a copy of my bug report for #447. I have completed a first pass
of the grab key implementation.
-Erik
Summary:
I used the grab keys to convert the touchpad into a virtual
mouse scrollwheel. Holding down the grab key and moving the mouse a
small number pixels causes a fake mouse button 4/5/6/7 press, depending
on the direction of motion. This approach works in all applications
which support mouse scroll buttons.
Patches:
The attached patch to sugar-toolkit adds glib/C-side hooks to grab an
ungrab the mouse, and pass motion-notify events to the python side of
the Sugar shell. I have packaged this patch in an rpm which should be
installable on an XO running a recent joyride (tested on joyride-2123).
(RPMS: tested but slightly older git snapshot:
http://dev.laptop.org/~erik/rpms/sugar-toolkit-debuginfo-0.81.5-4.20080705gitab8c054dfb.fc9.i386.rpm
or, untested but slightly newer git snapshot:
http://dev.laptop.org/~erik/rpms/sugar-toolkit-0.81.6-1.fc9.i386.rpm)
The attached patch to sugar (specifically keyhandler.py) adds the
python-side hooks required to enable the grab/scroll button
functionality.
The patches work in the following manner:
When the left or right grab buttons are pressed, XGrabPointer is
called. Subsequently, we capture all of the motion-notify events
which occur when the user moves the mouse, and each event hits
KeyHandler._motion_notify_cb() with the coordinates of the mouse.
After we move N pixels (currently 10) we issue a fake mouse scroll
button press corresponding to the direction of motion of the mouse
(4/5/6/7). To issue the fake button press I have found it is
necessary to ungrab the mouse, issue the press, and then re-grab.
Known issues:
In some cases key-releases are not registered. This is problematic
because without the release signal the mouse grabbing does not
stop and Sugar becomes entirely unusable. I have not been able to
establish why, but have noticed that hitting the journal view key
after Sugar startup before any of the other special keys
(registered in keyhandler.py) tends to resolve the issue for the
Super_L and Super_R keys (the grab buttons).
The mouse still scrolls around the screen, and the cursor is
visible during the grab. Solution: Hide the mouse; When a grab key
press is registered, hide the mouse by setting the cursor pixmap
to a blank map (and set it back when the grab key release is
registered).
Eventually, after scrolling in one direction, the mouse can move
out of or to the edge of the scrolling window and the scrolling
stops. Solution: Every time we issue a fake button press, warp the
mouse back to the position it was at when we first pressed the
grab key.
>From 9fd6e08513d7829c9919b109d49a554588d63c71 Mon Sep 17 00:00:00 2001
From: Erik Garrison <[EMAIL PROTECTED]>
Date: Wed, 9 Jul 2008 18:48:33 -0400
Subject: [PATCH] #447: Enable grab/scroll button functionality.
---
src/view/keyhandler.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 46 insertions(+), 0 deletions(-)
diff --git a/src/view/keyhandler.py b/src/view/keyhandler.py
index 44ea759..d1140be 100644
--- a/src/view/keyhandler.py
+++ b/src/view/keyhandler.py
@@ -62,6 +62,8 @@ _actions_table = {
'<alt><shift>o' : 'open_search',
'<alt><shift>r' : 'rotate',
'<alt><shift>s' : 'say_text',
+ 'Super_L' : 'grab_button_pressed',
+ 'Super_R' : 'grab_button_pressed',
}
J_DBUS_SERVICE = 'org.laptop.Journal'
@@ -78,6 +80,8 @@ class KeyHandler(object):
self._key_pressed = None
self._keycode_pressed = 0
self._keystate_pressed = 0
+ self._mouse_xy = [None, None]
+ self._grab_key_mouse_delta = 10
self._speech_proxy = None
self._key_grabber = KeyGrabber()
@@ -85,6 +89,8 @@ class KeyHandler(object):
self._key_pressed_cb)
self._key_grabber.connect('key-released',
self._key_released_cb)
+ self._key_grabber.connect('motion-notify',
+ self._motion_notify_cb)
self._tabbing_handler = TabbingHandler(_TABBING_MODIFIER)
@@ -137,6 +143,13 @@ class KeyHandler(object):
self._get_speech_proxy().SayText(text, reply_handler=lambda: None, \
error_handler=self._on_speech_err)
+ def handle_grab_button_pressed(self):
+ self._key_grabber.grab_pointer()
+
+ def handle_grab_button_released(self):
+ self._key_grabber.ungrab_pointer()
+ self._mouse_xy = [None, None]
+
def handle_say_text(self):
clipboard = gtk.clipboard_get(selection="PRIMARY")
clipboard.request_text(self._primary_selection_cb)
@@ -278,6 +291,10 @@ class KeyHandler(object):
return False
def _key_released_cb(self, grabber, keycode, state):
+ logging.debug('_key_released_cb: %i %i' % (keycode, state))
+ if keycode in [133, 134]: # handle grab button
+ self.handle_grab_button_released()
+ return True
if self._tabbing_handler.is_tabbing():
# We stop tabbing and switch to the new window as soon as the
# modifier key is raised again.
@@ -287,3 +304,32 @@ class KeyHandler(object):
return True
return False
+ def _fake_button_event(self, event):
+ self._key_grabber.ungrab_pointer()
+ self._key_grabber.fake_button_event(event)
+ self._key_grabber.grab_pointer()
+
+ def _motion_notify_cb(self, grabber, x, y):
+ logging.debug('_motion_notify_cb: %i %i' % (x, y))
+ if self._mouse_xy == [None, None]:
+ logging.debug('_motion_notify_cb: first hit')
+ self._mouse_xy = [x, y]
+ else:
+ x_prev, y_prev = self._mouse_xy
+
+ if y_prev - y > self._grab_key_mouse_delta:
+ self._fake_button_event(4)
+ self._mouse_xy[1] = y
+ elif y - y_prev > self._grab_key_mouse_delta:
+ self._fake_button_event(5)
+ self._mouse_xy[1] = y
+
+ if x_prev - x > self._grab_key_mouse_delta:
+ self._fake_button_event(6)
+ self._mouse_xy[0] = x
+ elif x - x_prev > self._grab_key_mouse_delta:
+ self._fake_button_event(7)
+ self._mouse_xy[0] = x
+
+
+
--
1.5.4.3
>From dd745c9a46c3722959a109d41fbda2331939b085 Mon Sep 17 00:00:00 2001
From: Erik Garrison <[EMAIL PROTECTED]>
Date: Wed, 9 Jul 2008 18:26:39 -0400
Subject: [PATCH] #447: Add hooks to enable grab/scroll button functionality.
---
src/sugar/Makefile.am | 2 +-
src/sugar/_sugarext.defs | 20 ++++++++++++
src/sugar/sugar-key-grabber.c | 66 +++++++++++++++++++++++++++++++++++++++++
src/sugar/sugar-key-grabber.h | 10 ++++++
4 files changed, 97 insertions(+), 1 deletions(-)
diff --git a/src/sugar/Makefile.am b/src/sugar/Makefile.am
index ef91efe..7e91769 100644
--- a/src/sugar/Makefile.am
+++ b/src/sugar/Makefile.am
@@ -18,7 +18,7 @@ _sugarext_la_CFLAGS = \
$(PYTHON_INCLUDES)
_sugarext_la_LDFLAGS = -module -avoid-version
-_sugarext_la_LIBADD = $(EXT_LIBS) -lSM -lICE
+_sugarext_la_LIBADD = $(EXT_LIBS) -lSM -lICE -lXtst
_sugarext_la_SOURCES = \
$(BUILT_SOURCES) \
diff --git a/src/sugar/_sugarext.defs b/src/sugar/_sugarext.defs
index 6e741dc..e57001d 100644
--- a/src/sugar/_sugarext.defs
+++ b/src/sugar/_sugarext.defs
@@ -131,6 +131,26 @@
)
)
+(define-method grab_pointer
+ (of-object "SugarKeyGrabber")
+ (c-name "sugar_key_grabber_grab_pointer")
+ (return-type "gboolean")
+)
+
+(define-method ungrab_pointer
+ (of-object "SugarKeyGrabber")
+ (c-name "sugar_key_grabber_ungrab_pointer")
+ (return-type "gboolean")
+)
+
+(define-method fake_button_event
+ (of-object "SugarKeyGrabber")
+ (c-name "sugar_key_grabber_fake_button_event")
+ (return-type "none")
+ (parameters
+ '("guint" "button"))
+)
+
;; From sexy-icon-entry.h
(define-function sexy_icon_entry_get_type
diff --git a/src/sugar/sugar-key-grabber.c b/src/sugar/sugar-key-grabber.c
index 8e18c86..1bd3c14 100644
--- a/src/sugar/sugar-key-grabber.c
+++ b/src/sugar/sugar-key-grabber.c
@@ -18,6 +18,7 @@
*/
#include <X11/X.h>
+#include <X11/extensions/XTest.h>
#include <gdk/gdkscreen.h>
#include <gdk/gdkx.h>
#include <gdk/gdk.h>
@@ -38,6 +39,7 @@
enum {
KEY_PRESSED,
KEY_RELEASED,
+ MOTION_NOTIFY,
N_SIGNALS
};
@@ -96,6 +98,15 @@ sugar_key_grabber_class_init(SugarKeyGrabberClass *grabber_class)
G_TYPE_BOOLEAN, 2,
G_TYPE_UINT,
G_TYPE_UINT);
+ signals[MOTION_NOTIFY] = g_signal_new ("motion-notify",
+ G_TYPE_FROM_CLASS (grabber_class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (SugarKeyGrabberClass, motion_notify),
+ NULL, NULL,
+ sugar_marshal_BOOLEAN__UINT_UINT,
+ G_TYPE_BOOLEAN, 2,
+ G_TYPE_UINT,
+ G_TYPE_UINT);
}
char *
@@ -136,6 +147,18 @@ filter_events(GdkXEvent *xevent, GdkEvent *event, gpointer data)
return GDK_FILTER_REMOVE;
}
+ if (xev->type == MotionNotify) {
+ /* the following give us mouse x and y coords
+ xev->xmotion.x
+ xev->xmotion.y
+ */
+ int return_value;
+ g_signal_emit (grabber, signals[MOTION_NOTIFY], 0, xev->xmotion.x,
+ xev->xmotion.y, &return_value);
+ if(return_value)
+ return GDK_FILTER_REMOVE;
+ }
+
return GDK_FILTER_CONTINUE;
}
@@ -151,6 +174,49 @@ sugar_key_grabber_init(SugarKeyGrabber *grabber)
gdk_window_add_filter(grabber->root, filter_events, grabber);
}
+gboolean
+sugar_key_grabber_grab_pointer (SugarKeyGrabber *grabber)
+{
+
+ int result = XGrabPointer( GDK_DISPLAY(),
+ GDK_WINDOW_XID (grabber->root),
+ False, // owner_events
+ PointerMotionMask|ButtonPressMask|ButtonReleaseMask, // event_mask
+ GrabModeAsync,
+ GrabModeAsync,
+ None,
+ None,
+ CurrentTime );
+ if (result)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean
+sugar_key_grabber_ungrab_pointer (SugarKeyGrabber *grabber)
+{
+ int result = XUngrabPointer(GDK_DISPLAY(), CurrentTime);
+ if (result)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Used to issue fake button 4/5/6/7 events when the grab button is pressed. */
+void
+sugar_key_grabber_fake_button_event (SugarKeyGrabber *grabber,
+ unsigned int button_number)
+{
+
+ Display *display = GDK_DISPLAY();
+
+ XTestFakeButtonEvent(display, button_number, True, CurrentTime);
+ XTestFakeButtonEvent(display, button_number, False, CurrentTime);
+ XFlush(display);
+
+}
+
/* grab_key and grab_key_real are from
* gnome-control-center/gnome-settings-daemon/gnome-settings-multimedia-keys.c
*/
diff --git a/src/sugar/sugar-key-grabber.h b/src/sugar/sugar-key-grabber.h
index f131ee3..99b5ea8 100644
--- a/src/sugar/sugar-key-grabber.h
+++ b/src/sugar/sugar-key-grabber.h
@@ -52,6 +52,9 @@ struct _SugarKeyGrabberClass {
gboolean (* key_released) (SugarKeyGrabber *grabber,
guint keycode,
guint state);
+ gboolean (* motion_notify) (SugarKeyGrabber *grabber,
+ guint x,
+ guint y);
};
GType sugar_key_grabber_get_type (void);
@@ -64,6 +67,13 @@ gboolean sugar_key_grabber_is_modifier (SugarKeyGrabber *grabber,
guint keycode,
guint mask);
+
+gboolean sugar_key_grabber_grab_pointer (SugarKeyGrabber *grabber);
+gboolean sugar_key_grabber_ungrab_pointer (SugarKeyGrabber *grabber);
+
+void sugar_key_grabber_fake_button_event (SugarKeyGrabber *grabber,
+ unsigned int button_number);
+
G_END_DECLS
#endif /* __SUGAR_KEY_GRABBER_H__ */
--
1.5.4.3
_______________________________________________
Sugar mailing list
[email protected]
http://lists.laptop.org/listinfo/sugar