[gnome-settings-daemon] mouse: Add scroll wheel emulation for trackballs



commit 4f30831e0176a775f00b7af86bd19c664a68bc5c
Author: Bastien Nocera <hadess hadess net>
Date:   Sun Oct 26 14:27:16 2014 +0100

    mouse: Add scroll wheel emulation for trackballs
    
    Some trackballs don't have a separate scroll wheel, so one of the
    buttons needs to be repurposed so pressing it will turn the main ball
    into a wheel.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=645666

 ...e.settings-daemon.peripherals.gschema.xml.in.in |    8 +
 plugins/mouse/gsd-mouse-manager.c                  |  141 ++++++++++++++++++++
 2 files changed, 149 insertions(+), 0 deletions(-)
---
diff --git a/data/org.gnome.settings-daemon.peripherals.gschema.xml.in.in 
b/data/org.gnome.settings-daemon.peripherals.gschema.xml.in.in
index f1d1648..d2f70ed 100644
--- a/data/org.gnome.settings-daemon.peripherals.gschema.xml.in.in
+++ b/data/org.gnome.settings-daemon.peripherals.gschema.xml.in.in
@@ -4,6 +4,7 @@
     <child name="touchpad" schema="org.gnome.settings-daemon.peripherals.touchpad"/>
     <child name="keyboard" schema="org.gnome.settings-daemon.peripherals.keyboard"/>
     <child name="mouse" schema="org.gnome.settings-daemon.peripherals.mouse"/>
+    <child name="trackball" schema="org.gnome.settings-daemon.peripherals.trackball"/>
     <child name="touchscreen" schema="org.gnome.settings-daemon.peripherals.touchscreen"/>
     <child name="input-devices" schema="org.gnome.settings-daemon.peripherals.input-devices"/>
   </schema>
@@ -149,6 +150,13 @@
       <_summary>Whether the tablet's orientation is locked, or rotated automatically.</_summary>
     </key>
   </schema>
+  <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.peripherals.trackball" 
path="/org/gnome/settings-daemon/peripherals/trackball/">
+    <key name="scroll-wheel-emulation-button" type="i">
+      <default>0</default>
+      <range min="0" max="24"/>
+      <_summary>Mouse wheel emulation button. 0 to disable the feature.</_summary>
+    </key>
+  </schema>
   <schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.peripherals.input-devices" 
path="/org/gnome/settings-daemon/peripherals/input-devices/">
     <key name="hotplug-command" type="s">
       <default>''</default>
diff --git a/plugins/mouse/gsd-mouse-manager.c b/plugins/mouse/gsd-mouse-manager.c
index 19698c6..60e77c2 100644
--- a/plugins/mouse/gsd-mouse-manager.c
+++ b/plugins/mouse/gsd-mouse-manager.c
@@ -55,6 +55,7 @@
 
 #define SETTINGS_MOUSE_DIR         "org.gnome.settings-daemon.peripherals.mouse"
 #define SETTINGS_TOUCHPAD_DIR      "org.gnome.settings-daemon.peripherals.touchpad"
+#define SETTINGS_TRACKBALL_DIR     "org.gnome.settings-daemon.peripherals.trackball"
 
 /* Keys for both touchpad and mouse */
 #define KEY_LEFT_HANDED         "left-handed"                /* a boolean for mouse, an enum for touchpad */
@@ -75,12 +76,16 @@
 #define KEY_SECONDARY_CLICK_ENABLED      "secondary-click-enabled"
 #define KEY_MIDDLE_BUTTON_EMULATION      "middle-button-enabled"
 
+/* Trackball settings */
+#define KEY_SCROLL_WHEEL_BUTTON          "scroll-wheel-emulation-button"
+
 struct GsdMouseManagerPrivate
 {
         guint start_idle_id;
         GSettings *touchpad_settings;
         GSettings *mouse_settings;
         GSettings *mouse_a11y_settings;
+        GSettings *trackball_settings;
         GdkDeviceManager *device_manager;
         guint device_added_id;
         guint device_removed_id;
@@ -134,6 +139,38 @@ open_gdk_device (GdkDevice *device)
 }
 
 static gboolean
+device_is_trackball (GdkDevice *device)
+{
+        XDeviceInfo *device_info;
+        gboolean retval = FALSE;
+        gint n_devices;
+        guint i;
+        int id;
+
+        g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+
+        gdk_error_trap_push ();
+
+        device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices);
+        if (device_info == NULL)
+                return retval;
+
+        for (i = 0; i < n_devices; i++) {
+                if (device_info[i].id != id)
+                        continue;
+
+                retval = device_info_is_trackball (&device_info[i]);
+                break;
+        }
+        XFreeDeviceList (device_info);
+
+        if (gdk_error_trap_pop () != 0)
+                return FALSE;
+
+        return retval;
+}
+
+static gboolean
 device_is_blacklisted (GsdMouseManager *manager,
                        GdkDevice       *device)
 {
@@ -988,6 +1025,83 @@ set_natural_scroll (GsdMouseManager *manager,
 }
 
 static void
+set_scroll_wheel_button (GsdMouseManager *manager,
+                         GdkDevice       *device)
+{
+        Atom wheel_prop, button_prop;
+        XDevice *xdevice;
+        Atom type;
+        int format;
+        unsigned long nitems, bytes_after;
+        unsigned char *data = NULL;
+        int button;
+        int rc;
+
+        if (!device_is_trackball (device))
+                return;
+
+        xdevice = open_gdk_device (device);
+        if (xdevice == NULL)
+                return;
+
+        wheel_prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+                                  "Evdev Wheel Emulation", True);
+        button_prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+                                   "Evdev Wheel Emulation Button", True);
+
+        if (!wheel_prop || !button_prop) {
+                xdevice_close (xdevice);
+                return;
+        }
+
+        g_debug ("setting scroll wheel emulation on %s", gdk_device_get_name (device));
+
+        gdk_error_trap_push ();
+
+        button = g_settings_get_int (manager->priv->trackball_settings, KEY_SCROLL_WHEEL_BUTTON);
+
+        /* Whether scroll wheel emulation is enabled */
+        rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+                                 xdevice, wheel_prop, 0, 1, False, XA_INTEGER, &type, &format,
+                                 &nitems, &bytes_after, &data);
+
+        if (rc == Success && format == 8 && type == XA_INTEGER && nitems == 1) {
+                data[0] = button > 0 ? 1 : 0;
+
+                gdk_error_trap_push ();
+                XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+                                       xdevice, wheel_prop, type, format, PropModeReplace, data, nitems);
+        }
+
+        if (data) {
+                XFree (data);
+                data = NULL;
+        }
+
+        /* Which button is used for the emulation */
+        if (button > 0) {
+                rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+                                         xdevice, button_prop, 0, 1, False, XA_INTEGER, &type, &format,
+                                         &nitems, &bytes_after, &data);
+
+                if (rc == Success && format == 8 && type == XA_INTEGER && nitems == 1) {
+                        data[0] = button;
+
+                        XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+                                               xdevice, button_prop, type, format, PropModeReplace, data, 
nitems);
+                }
+
+                if (data)
+                        XFree (data);
+        }
+
+        if (gdk_error_trap_pop ())
+                g_warning ("Error in setting scroll wheel emulation on \"%s\"", gdk_device_get_name 
(device));
+
+        xdevice_close (xdevice);
+}
+
+static void
 set_mouse_settings (GsdMouseManager *manager,
                     GdkDevice       *device)
 {
@@ -1006,6 +1120,8 @@ set_mouse_settings (GsdMouseManager *manager,
         set_natural_scroll (manager, device, g_settings_get_boolean (manager->priv->touchpad_settings, 
KEY_NATURAL_SCROLL_ENABLED));
         if (g_settings_get_boolean (manager->priv->touchpad_settings, KEY_TOUCHPAD_ENABLED) == FALSE)
                 set_touchpad_disabled (device);
+
+        set_scroll_wheel_button (manager, device);
 }
 
 static void
@@ -1109,6 +1225,26 @@ touchpad_callback (GSettings       *settings,
         }
 }
 
+static void
+trackball_callback (GSettings       *settings,
+                    const gchar     *key,
+                    GsdMouseManager *manager)
+{
+        GList *devices, *l;
+
+        devices = gdk_device_manager_list_devices (manager->priv->device_manager, GDK_DEVICE_TYPE_SLAVE);
+
+        for (l = devices; l != NULL; l = l->next) {
+                GdkDevice *device = l->data;
+
+                if (device_is_ignored (manager, device))
+                        continue;
+
+                set_scroll_wheel_button (manager, device);
+        }
+        g_list_free (devices);
+}
+
 /* Re-enable touchpad when any other pointing device isn't present. */
 static void
 ensure_touchpad_active (GsdMouseManager *manager)
@@ -1202,6 +1338,10 @@ gsd_mouse_manager_idle_cb (GsdMouseManager *manager)
         g_signal_connect (manager->priv->touchpad_settings, "changed",
                           G_CALLBACK (touchpad_callback), manager);
 
+        manager->priv->trackball_settings = g_settings_new (SETTINGS_TRACKBALL_DIR);
+        g_signal_connect (manager->priv->trackball_settings, "changed",
+                          G_CALLBACK (trackball_callback), manager);
+
         manager->priv->syndaemon_spawned = FALSE;
 
         set_locate_pointer (manager, g_settings_get_boolean (manager->priv->mouse_settings, 
KEY_LOCATE_POINTER));
@@ -1288,6 +1428,7 @@ gsd_mouse_manager_stop (GsdMouseManager *manager)
         g_clear_object (&p->mouse_a11y_settings);
         g_clear_object (&p->mouse_settings);
         g_clear_object (&p->touchpad_settings);
+        g_clear_object (&p->trackball_settings);
 
         set_locate_pointer (manager, FALSE);
 }


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