2016-03-08 16:53:53 +01:00
From f5aa66f135c9e7e9c57363c8933e6a87d0900112 Mon Sep 17 00:00:00 2001
From: Frederic Crozat <fcrozat@suse.com>
Date: Fri, 4 Mar 2016 15:47:30 +0100
Subject: [PATCH 1/3] Revert "mouse: Remove support for non-libinput mouse
configurations"
This reverts commit 66c211ff24bec6a938d6a6a0dd8730f4689ef383.
---
plugins/mouse/gsd-mouse-manager.c | 1255 ++++++++++++++++++++++++++++++++++++-
1 file changed, 1252 insertions(+), 3 deletions(-)
diff --git a/plugins/mouse/gsd-mouse-manager.c b/plugins/mouse/gsd-mouse-manager.c
index cf2c28c..da7b378 100644
--- a/plugins/mouse/gsd-mouse-manager.c
+++ b/plugins/mouse/gsd-mouse-manager.c
@@ -19,17 +19,39 @@
#include "config.h"
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#ifdef __linux
+#include <sys/prctl.h>
+#endif
+
#include <locale.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <gio/gio.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkkeysyms.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
#include <gdesktop-enums.h>
+#include <X11/extensions/XInput.h>
+#include <X11/extensions/XIproto.h>
+
#include "gnome-settings-bus.h"
#include "gnome-settings-profile.h"
#include "gsd-mouse-manager.h"
+#include "gsd-input-helper.h"
#include "gsd-enums.h"
#include "gsd-settings-migrate.h"
@@ -38,20 +60,42 @@
#define GSD_SETTINGS_MOUSE_SCHEMA "org.gnome.settings-daemon.peripherals.mouse"
#define GSETTINGS_MOUSE_SCHEMA "org.gnome.desktop.peripherals.mouse"
#define GSETTINGS_TOUCHPAD_SCHEMA "org.gnome.desktop.peripherals.touchpad"
+#define GSETTINGS_TRACKBALL_SCHEMA "org.gnome.desktop.peripherals.trackball"
+
+/* Keys for both touchpad and mouse */
+#define KEY_LEFT_HANDED "left-handed" /* a boolean for mouse, an enum for touchpad */
+#define KEY_SPEED "speed"
+
+/* Touchpad settings */
+#define KEY_EDGE_SCROLLING_ENABLED "edge-scrolling-enabled"
+#define KEY_TAP_TO_CLICK "tap-to-click"
+#define KEY_SEND_EVENTS "send-events"
+#define KEY_NATURAL_SCROLL_ENABLED "natural-scroll"
/* Mouse settings */
#define KEY_LOCATE_POINTER "locate-pointer"
#define KEY_DWELL_CLICK_ENABLED "dwell-click-enabled"
#define KEY_SECONDARY_CLICK_ENABLED "secondary-click-enabled"
+/* Trackball settings */
+#define KEY_SCROLL_WHEEL_BUTTON "scroll-wheel-emulation-button"
+
struct GsdMouseManagerPrivate
{
guint start_idle_id;
GSettings *touchpad_settings;
- GSettings *mouse_a11y_settings;
GSettings *mouse_settings;
+ GSettings *mouse_a11y_settings;
+ GSettings *trackball_settings;
GSettings *gsd_mouse_settings;
+ GdkDeviceManager *device_manager;
+ guint device_added_id;
+ guint device_removed_id;
+ GHashTable *blacklist;
+
gboolean mousetweaks_daemon_running;
+ gboolean syndaemon_spawned;
+ GPid syndaemon_pid;
gboolean locate_pointer_spawned;
GPid locate_pointer_pid;
};
@@ -59,6 +103,12 @@ struct GsdMouseManagerPrivate
static void gsd_mouse_manager_class_init (GsdMouseManagerClass *klass);
static void gsd_mouse_manager_init (GsdMouseManager *mouse_manager);
static void gsd_mouse_manager_finalize (GObject *object);
+static void set_tap_to_click (GdkDevice *device,
+ gboolean state,
+ gboolean left_handed);
+static void ensure_touchpad_active (GsdMouseManager *manager);
+static gboolean get_touchpad_enabled (GsdMouseManager *manager);
+
G_DEFINE_TYPE (GsdMouseManager, gsd_mouse_manager, G_TYPE_OBJECT)
@@ -76,6 +126,775 @@ gsd_mouse_manager_class_init (GsdMouseManagerClass *klass)
g_type_class_add_private (klass, sizeof (GsdMouseManagerPrivate));
}
+static XDevice *
+open_gdk_device (GdkDevice *device)
+{
+ XDevice *xdevice;
+ int id;
+
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+
+ gdk_error_trap_push ();
+
+ xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id);
+
+ if (gdk_error_trap_pop () != 0)
+ return NULL;
+
+ return xdevice;
+}
+
+static gboolean
+device_info_is_trackball (XDeviceInfo *device_info)
+{
+ gboolean retval;
+
+ retval = (device_info->type == XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), XI_TRACKBALL, False));
+ if (retval == FALSE &&
+ device_info->name != NULL) {
+ char *lowercase;
+
+ lowercase = g_ascii_strdown (device_info->name, -1);
+ retval = strstr (lowercase, "trackball") != NULL;
+ g_free (lowercase);
+ }
+
+ return retval;
+}
+
+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)
+{
+ int id;
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+ if (g_hash_table_lookup (manager->priv->blacklist, GINT_TO_POINTER (id)) != NULL) {
+ g_debug ("device %s (%d) is blacklisted", gdk_device_get_name (device), id);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static gboolean
+device_is_ignored (GsdMouseManager *manager,
+ GdkDevice *device)
+{
+ GdkInputSource source;
+ const char *name;
+
+ if (device_is_blacklisted (manager, device))
+ return TRUE;
+
+ source = gdk_device_get_source (device);
+ if (source != GDK_SOURCE_MOUSE &&
+ source != GDK_SOURCE_TOUCHPAD &&
+ source != GDK_SOURCE_CURSOR)
+ return TRUE;
+
+ name = gdk_device_get_name (device);
+ if (name != NULL && g_str_equal ("Virtual core XTEST pointer", name))
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+configure_button_layout (guchar *buttons,
+ gint n_buttons,
+ gboolean left_handed)
+{
+ const gint left_button = 1;
+ gint right_button;
+ gint i;
+
+ /* if the button is higher than 2 (3rd button) then it's
+ * probably one direction of a scroll wheel or something else
+ * uninteresting
+ */
+ right_button = MIN (n_buttons, 3);
+
+ /* If we change things we need to make sure we only swap buttons.
+ * If we end up with multiple physical buttons assigned to the same
+ * logical button the server will complain. This code assumes physical
+ * button 0 is the physical left mouse button, and that the physical
+ * button other than 0 currently assigned left_button or right_button
+ * is the physical right mouse button.
+ */
+
+ /* check if the current mapping satisfies the above assumptions */
+ if (buttons[left_button - 1] != left_button &&
+ buttons[left_button - 1] != right_button)
+ /* The current mapping is weird. Swapping buttons is probably not a
+ * good idea.
+ */
+ return;
+
+ /* check if we are left_handed and currently not swapped */
+ if (left_handed && buttons[left_button - 1] == left_button) {
+ /* find the right button */
+ for (i = 0; i < n_buttons; i++) {
+ if (buttons[i] == right_button) {
+ buttons[i] = left_button;
+ break;
+ }
+ }
+ /* swap the buttons */
+ buttons[left_button - 1] = right_button;
+ }
+ /* check if we are not left_handed but are swapped */
+ else if (!left_handed && buttons[left_button - 1] == right_button) {
+ /* find the right button */
+ for (i = 0; i < n_buttons; i++) {
+ if (buttons[i] == left_button) {
+ buttons[i] = right_button;
+ break;
+ }
+ }
+ /* swap the buttons */
+ buttons[left_button - 1] = left_button;
+ }
+}
+
+static gboolean
+xinput_device_has_buttons (GdkDevice *device)
+{
+ int i;
+ XAnyClassInfo *class_info;
+
+ /* FIXME can we use the XDevice's classes here instead? */
+ XDeviceInfo *device_info, *info;
+ gint n_devices;
+ int id;
+
+ /* Find the XDeviceInfo for the GdkDevice */
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+
+ device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices);
+ if (device_info == NULL)
+ return FALSE;
+
+ info = NULL;
+ for (i = 0; i < n_devices; i++) {
+ if (device_info[i].id == id) {
+ info = &device_info[i];
+ break;
+ }
+ }
+ if (info == NULL)
+ goto bail;
+
+ class_info = info->inputclassinfo;
+ for (i = 0; i < info->num_classes; i++) {
+ if (class_info->class == ButtonClass) {
+ XButtonInfo *button_info;
+
+ button_info = (XButtonInfo *) class_info;
+ if (button_info->num_buttons > 0) {
+ XFreeDeviceList (device_info);
+ return TRUE;
+ }
+ }
+
+ class_info = (XAnyClassInfo *) (((guchar *) class_info) +
+ class_info->length);
+ }
+
+bail:
+ XFreeDeviceList (device_info);
+
+ return FALSE;
+}
+
+static gboolean
+touchpad_has_single_button (XDevice *device)
+{
+ Atom type, prop;
+ int format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+ gboolean is_single_button = FALSE;
+ int rc;
+
+ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Capabilities", False);
+ if (!prop)
+ return FALSE;
+
+ gdk_error_trap_push ();
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device, prop, 0, 1, False,
+ XA_INTEGER, &type, &format, &nitems,
+ &bytes_after, &data);
+ if (rc == Success && type == XA_INTEGER && format == 8 && nitems >= 3)
+ is_single_button = (data[0] == 1 && data[1] == 0 && data[2] == 0);
+
+ if (rc == Success)
+ XFree (data);
+
+ gdk_error_trap_pop_ignored ();
+
+ return is_single_button;
+}
+
+static void
+set_left_handed (GsdMouseManager *manager,
+ GdkDevice *device,
+ gboolean mouse_left_handed,
+ gboolean touchpad_left_handed)
+{
+ XDevice *xdevice;
+ guchar *buttons;
+ gsize buttons_capacity = 16;
+ gboolean left_handed;
+ gint n_buttons;
+
+ if (!xinput_device_has_buttons (device))
+ return;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ g_debug ("setting handedness on %s", gdk_device_get_name (device));
+
+ buttons = g_new (guchar, buttons_capacity);
+
+ /* If the device is a touchpad, swap tap buttons
+ * around too, otherwise a tap would be a right-click */
+ if (xdevice_is_synaptics (xdevice)) {
+ gboolean tap = g_settings_get_boolean (manager->priv->touchpad_settings, KEY_TAP_TO_CLICK);
+ gboolean single_button = touchpad_has_single_button (xdevice);
+
+ left_handed = touchpad_left_handed;
+
+ if (tap && !single_button)
+ set_tap_to_click (device, tap, left_handed);
+
+ if (single_button)
+ goto out;
+ } else {
+ left_handed = mouse_left_handed;
+ }
+
+ gdk_error_trap_push ();
+
+ n_buttons = XGetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ buttons,
+ buttons_capacity);
+
+ while (n_buttons > buttons_capacity) {
+ buttons_capacity = n_buttons;
+ buttons = (guchar *) g_realloc (buttons,
+ buttons_capacity * sizeof (guchar));
+
+ n_buttons = XGetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ buttons,
+ buttons_capacity);
+ }
+
+ configure_button_layout (buttons, n_buttons, left_handed);
+
+ XSetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, buttons, n_buttons);
+ gdk_error_trap_pop_ignored ();
+
+out:
+ xdevice_close (xdevice);
+ g_free (buttons);
+}
+
+static void
+set_motion (GsdMouseManager *manager,
+ GdkDevice *device)
+{
+ XDevice *xdevice;
+ XPtrFeedbackControl feedback;
+ XFeedbackState *states, *state;
+ int num_feedbacks;
+ int numerator, denominator;
+ gfloat motion_acceleration;
+ int motion_threshold;
+ GSettings *settings;
+ gdouble speed;
+ guint i;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ g_debug ("setting motion on %s", gdk_device_get_name (device));
+
+ if (xdevice_is_synaptics (xdevice))
+ settings = manager->priv->touchpad_settings;
+ else
+ settings = manager->priv->mouse_settings;
+
+ speed = g_settings_get_double (settings, KEY_SPEED);
+
+ /* Calculate acceleration and threshold */
+ motion_acceleration = (speed + 1) * 5; /* speed is [-1..1], map to [0..10] */
+ motion_threshold = CLAMP (10 - floor (motion_acceleration), 1, 10);
+
+ if (motion_acceleration >= 1.0) {
+ /* we want to get the acceleration, with a resolution of 0.5
+ */
+ if ((motion_acceleration - floor (motion_acceleration)) < 0.25) {
+ numerator = floor (motion_acceleration);
+ denominator = 1;
+ } else if ((motion_acceleration - floor (motion_acceleration)) < 0.5) {
+ numerator = ceil (2.0 * motion_acceleration);
+ denominator = 2;
+ } else if ((motion_acceleration - floor (motion_acceleration)) < 0.75) {
+ numerator = floor (2.0 *motion_acceleration);
+ denominator = 2;
+ } else {
+ numerator = ceil (motion_acceleration);
+ denominator = 1;
+ }
+ } else if (motion_acceleration < 1.0 && motion_acceleration > 0) {
+ /* This we do to 1/10ths */
+ numerator = floor (motion_acceleration * 10) + 1;
+ denominator= 10;
+ } else {
+ numerator = -1;
+ denominator = -1;
+ }
+
+ gdk_error_trap_push ();
+
+ /* Get the list of feedbacks for the device */
+ states = XGetFeedbackControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, &num_feedbacks);
+ if (states == NULL)
+ goto out;
+ state = (XFeedbackState *) states;
+ for (i = 0; i < num_feedbacks; i++) {
+ if (state->class == PtrFeedbackClass) {
+ /* And tell the device */
+ feedback.class = PtrFeedbackClass;
+ feedback.length = sizeof (XPtrFeedbackControl);
+ feedback.id = state->id;
+ feedback.threshold = motion_threshold;
+ feedback.accelNum = numerator;
+ feedback.accelDenom = denominator;
+
+ g_debug ("Setting accel %d/%d, threshold %d for device '%s'",
+ numerator, denominator, motion_threshold, gdk_device_get_name (device));
+
+ XChangeFeedbackControl (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+ xdevice,
+ DvAccelNum | DvAccelDenom | DvThreshold,
+ (XFeedbackControl *) &feedback);
+
+ break;
+ }
+ state = (XFeedbackState *) ((char *) state + state->length);
+ }
+
+ if (gdk_error_trap_pop ())
+ g_warning ("Error setting acceleration on \"%s\"", gdk_device_get_name (device));
+
+ XFreeFeedbackList (states);
+
+ out:
+
+ xdevice_close (xdevice);
+}
+
+/* Ensure that syndaemon dies together with us, to avoid running several of
+ * them */
+static void
+setup_syndaemon (gpointer user_data)
+{
+#ifdef __linux
+ prctl (PR_SET_PDEATHSIG, SIGHUP);
+#endif
+}
+
+static gboolean
+have_program_in_path (const char *name)
+{
+ gchar *path;
+ gboolean result;
+
+ path = g_find_program_in_path (name);
+ result = (path != NULL);
+ g_free (path);
+ return result;
+}
+
+static void
+syndaemon_died (GPid pid, gint status, gpointer user_data)
+{
+ GsdMouseManager *manager = GSD_MOUSE_MANAGER (user_data);
+
+ g_debug ("syndaemon stopped with status %i", status);
+ g_spawn_close_pid (pid);
+ manager->priv->syndaemon_spawned = FALSE;
+}
+
+static int
+set_disable_w_typing (GsdMouseManager *manager, gboolean state)
+{
+ if (state && synaptics_is_present ()) {
+ GError *error = NULL;
+ GPtrArray *args;
+
+ if (manager->priv->syndaemon_spawned)
+ return 0;
+
+ if (!have_program_in_path ("syndaemon"))
+ return 0;
+
+ args = g_ptr_array_new ();
+
+ g_ptr_array_add (args, "syndaemon");
+ g_ptr_array_add (args, "-i");
+ g_ptr_array_add (args, "1.0");
+ g_ptr_array_add (args, "-t");
+ g_ptr_array_add (args, "-K");
+ g_ptr_array_add (args, "-R");
+ g_ptr_array_add (args, NULL);
+
+ /* we must use G_SPAWN_DO_NOT_REAP_CHILD to avoid
+ * double-forking, otherwise syndaemon will immediately get
+ * killed again through (PR_SET_PDEATHSIG when the intermediate
+ * process dies */
+ g_spawn_async (g_get_home_dir (), (char **) args->pdata, NULL,
+ G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD, setup_syndaemon, NULL,
+ &manager->priv->syndaemon_pid, &error);
+
+ manager->priv->syndaemon_spawned = (error == NULL);
+ g_ptr_array_free (args, FALSE);
+
+ if (error) {
+ g_warning ("Failed to launch syndaemon: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_child_watch_add (manager->priv->syndaemon_pid, syndaemon_died, manager);
+ g_debug ("Launched syndaemon");
+ }
+ } else if (manager->priv->syndaemon_spawned) {
+ kill (manager->priv->syndaemon_pid, SIGHUP);
+ g_spawn_close_pid (manager->priv->syndaemon_pid);
+ manager->priv->syndaemon_spawned = FALSE;
+ g_debug ("Killed syndaemon");
+ }
+
+ return 0;
+}
+
+static void
+set_tap_to_click (GdkDevice *device,
+ gboolean state,
+ gboolean left_handed)
+{
+ int format, rc;
+ unsigned long nitems, bytes_after;
+ XDevice *xdevice;
+ unsigned char* data;
+ Atom prop_capabilities, prop, type;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Tap Action", False);
+ prop_capabilities = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Capabilities", False);
+ if (!prop || !prop_capabilities)
+ return;
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ if (!xdevice_is_synaptics (xdevice)) {
+ xdevice_close (xdevice);
+ return;
+ }
+
+ g_debug ("setting tap to click on %s", gdk_device_get_name (device));
+
+ gdk_error_trap_push ();
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop_capabilities, 0, 1,
+ False, XA_INTEGER, &type, &format, &nitems, &bytes_after, &data);
+ if (rc == Success && type == XA_INTEGER && format == 8 && nitems >= 1) {
+ if (!(data[0])) {
+ g_debug ("No hardware buttons, enabling tap to click on %s", gdk_device_get_name (device));
+ state = TRUE;
+ }
+
+ XFree (data);
+ }
+
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, 2,
+ False, XA_INTEGER, &type, &format, &nitems,
+ &bytes_after, &data);
+
+ if (rc == Success && type == XA_INTEGER && format == 8 && nitems >= 7) {
+ /* Set RLM mapping for 1/2/3 fingers*/
+ data[4] = (state) ? ((left_handed) ? 3 : 1) : 0;
+ data[5] = (state) ? ((left_handed) ? 1 : 3) : 0;
+ data[6] = (state) ? 2 : 0;
+ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, XA_INTEGER, 8,
+ PropModeReplace, data, nitems);
+ }
+
+ if (rc == Success)
+ XFree (data);
+
+ if (gdk_error_trap_pop ())
+ g_warning ("Error in setting tap to click on \"%s\"", gdk_device_get_name (device));
+
+ xdevice_close (xdevice);
+}
+
+static void
+set_horiz_scroll (GdkDevice *device,
+ gboolean state)
+{
+ int rc;
+ XDevice *xdevice;
+ Atom act_type, prop_edge, prop_twofinger;
+ int act_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ prop_edge = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Edge Scrolling", False);
+ prop_twofinger = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Two-Finger Scrolling", False);
+
+ if (!prop_edge || !prop_twofinger)
+ return;
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ if (!xdevice_is_synaptics (xdevice)) {
+ xdevice_close (xdevice);
+ return;
+ }
+
+ g_debug ("setting horiz scroll on %s", gdk_device_get_name (device));
+
+ gdk_error_trap_push ();
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_edge, 0, 1, False,
+ XA_INTEGER, &act_type, &act_format, &nitems,
+ &bytes_after, &data);
+ if (rc == Success && act_type == XA_INTEGER &&
+ act_format == 8 && nitems >= 2) {
+ data[1] = (state && data[0]);
+ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_edge, XA_INTEGER, 8,
+ PropModeReplace, data, nitems);
+ }
+
+ XFree (data);
+
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_twofinger, 0, 1, False,
+ XA_INTEGER, &act_type, &act_format, &nitems,
+ &bytes_after, &data);
+ if (rc == Success && act_type == XA_INTEGER &&
+ act_format == 8 && nitems >= 2) {
+ data[1] = (state && data[0]);
+ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_twofinger, XA_INTEGER, 8,
+ PropModeReplace, data, nitems);
+ }
+
+ if (gdk_error_trap_pop ())
+ g_warning ("Error in setting horiz scroll on \"%s\"", gdk_device_get_name (device));
+
+ if (rc == Success)
+ XFree (data);
+
+ xdevice_close (xdevice);
+
+}
+
+static void
+set_edge_scrolling_enabled (GsdMouseManager *manager,
+ GdkDevice *device,
+ gboolean enabled)
+{
+ int rc;
+ XDevice *xdevice;
+ Atom act_type, prop, prop_edge, prop_twofinger;
+ int act_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+ GsdTouchpadScrollMethod method;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Capabilities", True);
+ prop_edge = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Edge Scrolling", False);
+ prop_twofinger = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Two-Finger Scrolling", False);
+
+ if (!prop_edge || !prop_twofinger || !prop)
+ return;
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ if (!xdevice_is_synaptics (xdevice)) {
+ xdevice_close (xdevice);
+ return;
+ }
+
+ g_debug ("setting edge scroll on %s", gdk_device_get_name (device));
+
+ gdk_error_trap_push ();
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop, 0, 2, False,
+ XA_INTEGER, &act_type, &act_format, &nitems,
+ &bytes_after, &data);
+ if (rc == Success && act_type != None) {
+ /* Two-finger scrolling is supported, so enable it */
+ if (data[3])
+ method = GSD_TOUCHPAD_SCROLL_METHOD_TWO_FINGER_SCROLLING;
+
+ XFree (data);
+ }
+
+ if (enabled && method != GSD_TOUCHPAD_SCROLL_METHOD_TWO_FINGER_SCROLLING)
+ method = GSD_TOUCHPAD_SCROLL_METHOD_EDGE_SCROLLING;
+ else
+ method = GSD_TOUCHPAD_SCROLL_METHOD_DISABLED;
+
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_edge, 0, 1, False,
+ XA_INTEGER, &act_type, &act_format, &nitems,
+ &bytes_after, &data);
+ if (rc == Success && act_type == XA_INTEGER &&
+ act_format == 8 && nitems >= 2) {
+ data[0] = (method == GSD_TOUCHPAD_SCROLL_METHOD_EDGE_SCROLLING) ? 1 : 0;
+ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_edge, XA_INTEGER, 8,
+ PropModeReplace, data, nitems);
+ }
+
+ XFree (data);
+
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_twofinger, 0, 1, False,
+ XA_INTEGER, &act_type, &act_format, &nitems,
+ &bytes_after, &data);
+ if (rc == Success && act_type == XA_INTEGER &&
+ act_format == 8 && nitems >= 2) {
+ data[0] = (method == GSD_TOUCHPAD_SCROLL_METHOD_TWO_FINGER_SCROLLING) ? 1 : 0;
+ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ prop_twofinger, XA_INTEGER, 8,
+ PropModeReplace, data, nitems);
+ }
+
+ if (gdk_error_trap_pop ())
+ g_warning ("Error in setting edge scroll on \"%s\"", gdk_device_get_name (device));
+
+ if (rc == Success)
+ XFree (data);
+
+ xdevice_close (xdevice);
+}
+
+static void
+set_touchpad_disabled (GdkDevice *device)
+{
+ int id;
+ XDevice *xdevice;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+
+ g_debug ("Trying to set device disabled for \"%s\" (%d)", gdk_device_get_name (device), id);
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ if (!xdevice_is_synaptics (xdevice)) {
+ xdevice_close (xdevice);
+ return;
+ }
+
+ if (set_synaptics_device_enabled (id, FALSE) == FALSE)
+ g_warning ("Error disabling device \"%s\" (%d)", gdk_device_get_name (device), id);
+ else
+ g_debug ("Disabled device \"%s\" (%d)", gdk_device_get_name (device), id);
+
+ xdevice_close (xdevice);
+}
+
+static void
+set_touchpad_enabled (int id)
+{
+ XDevice *xdevice;
+
+ if (xdevice_is_libinput (id))
+ return;
+
+ g_debug ("Trying to set device enabled for %d", id);
+
+ gdk_error_trap_push ();
+ xdevice = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), id);
+ if (gdk_error_trap_pop () != 0)
+ return;
+
+ if (!xdevice_is_synaptics (xdevice)) {
+ xdevice_close (xdevice);
+ return;
+ }
+
+ if (set_synaptics_device_enabled (id, TRUE) == FALSE)
+ g_warning ("Error enabling device \"%d\"", id);
+ else
+ g_debug ("Enabled device %d", id);
+
+ xdevice_close (xdevice);
+}
+
static void
set_locate_pointer (GsdMouseManager *manager,
gboolean state)
@@ -143,32 +962,418 @@ set_mousetweaks_daemon (GsdMouseManager *manager,
g_free (comm);
}
+static gboolean
+get_touchpad_handedness (GsdMouseManager *manager, gboolean mouse_left_handed)
+{
+ switch (g_settings_get_enum (manager->priv->touchpad_settings, KEY_LEFT_HANDED)) {
+ case GSD_TOUCHPAD_HANDEDNESS_RIGHT:
+ return FALSE;
+ case GSD_TOUCHPAD_HANDEDNESS_LEFT:
+ return TRUE;
+ case GSD_TOUCHPAD_HANDEDNESS_MOUSE:
+ return mouse_left_handed;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+set_natural_scroll (GsdMouseManager *manager,
+ GdkDevice *device,
+ gboolean natural_scroll)
+{
+ XDevice *xdevice;
+ Atom scrolling_distance, act_type;
+ int rc, act_format;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+ glong *ptr;
+
+ xdevice = open_gdk_device (device);
+ if (xdevice == NULL)
+ return;
+
+ if (!xdevice_is_synaptics (xdevice)) {
+ xdevice_close (xdevice);
+ return;
+ }
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ g_debug ("Trying to set %s for \"%s\"",
+ natural_scroll ? "natural (reverse) scroll" : "normal scroll",
+ gdk_device_get_name (device));
+
+ scrolling_distance = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+ "Synaptics Scrolling Distance", False);
+
+ gdk_error_trap_push ();
+ rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ scrolling_distance, 0, 2, False,
+ XA_INTEGER, &act_type, &act_format, &nitems,
+ &bytes_after, &data);
+
+ if (rc == Success && act_type == XA_INTEGER && act_format == 32 && nitems >= 2) {
+ ptr = (glong *) data;
+
+ if (natural_scroll) {
+ ptr[0] = -abs(ptr[0]);
+ ptr[1] = -abs(ptr[1]);
+ } else {
+ ptr[0] = abs(ptr[0]);
+ ptr[1] = abs(ptr[1]);
+ }
+
+ XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice,
+ scrolling_distance, XA_INTEGER, act_format,
+ PropModeReplace, data, nitems);
+ }
+
+ if (gdk_error_trap_pop ())
+ g_warning ("Error setting %s for \"%s\"",
+ natural_scroll ? "natural (reverse) scroll" : "normal scroll",
+ gdk_device_get_name (device));
+
+ if (rc == Success)
+ XFree (data);
+
+ xdevice_close (xdevice);
+}
+
+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;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (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;
+
+ 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 gboolean
+get_touchpad_enabled (GsdMouseManager *manager)
+{
+ GDesktopDeviceSendEvents send_events;
+
+ send_events = g_settings_get_enum (manager->priv->touchpad_settings, KEY_SEND_EVENTS);
+
+ if (send_events == G_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE) {
+ /* FIXME: mouse_is_present() also finds internal ones... */
+ return (!mouse_is_present () && !trackball_is_present ());
+
+ }
+
+ return send_events == G_DESKTOP_DEVICE_SEND_EVENTS_ENABLED ? TRUE : FALSE;
+}
+
+static void
+set_mouse_settings (GsdMouseManager *manager,
+ GdkDevice *device)
+{
+ gboolean mouse_left_handed, touchpad_left_handed;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ mouse_left_handed = g_settings_get_boolean (manager->priv->mouse_settings, KEY_LEFT_HANDED);
+ touchpad_left_handed = get_touchpad_handedness (manager, mouse_left_handed);
+ set_left_handed (manager, device, mouse_left_handed, touchpad_left_handed);
+
+ set_motion (manager, device);
+
+ set_tap_to_click (device, g_settings_get_boolean (manager->priv->touchpad_settings, KEY_TAP_TO_CLICK), touchpad_left_handed);
+ set_edge_scrolling_enabled (manager, device, g_settings_get_boolean (manager->priv->touchpad_settings, KEY_EDGE_SCROLLING_ENABLED));
+ set_horiz_scroll (device, TRUE);
+ set_natural_scroll (manager, device, g_settings_get_boolean (manager->priv->touchpad_settings, KEY_NATURAL_SCROLL_ENABLED));
+
+ set_scroll_wheel_button (manager, device);
+}
+
static void
mouse_callback (GSettings *settings,
const gchar *key,
GsdMouseManager *manager)
{
+ GList *devices, *l;
+
if (g_str_equal (key, KEY_DWELL_CLICK_ENABLED) ||
g_str_equal (key, KEY_SECONDARY_CLICK_ENABLED)) {
set_mousetweaks_daemon (manager,
g_settings_get_boolean (settings, KEY_DWELL_CLICK_ENABLED),
g_settings_get_boolean (settings, KEY_SECONDARY_CLICK_ENABLED));
+ return;
} else if (g_str_equal (key, KEY_LOCATE_POINTER)) {
set_locate_pointer (manager, g_settings_get_boolean (settings, KEY_LOCATE_POINTER));
+ return;
}
+
+ 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;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ continue;
+
+ if (g_str_equal (key, KEY_LEFT_HANDED)) {
+ gboolean mouse_left_handed;
+ mouse_left_handed = g_settings_get_boolean (settings, KEY_LEFT_HANDED);
+ set_left_handed (manager, device, mouse_left_handed, get_touchpad_handedness (manager, mouse_left_handed));
+ } else if (g_str_equal (key, KEY_SPEED)) {
+ set_motion (manager, device);
+ }
+ }
+ g_list_free (devices);
+}
+
+static void
+touchpad_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;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ continue;
+
+ if (g_str_equal (key, KEY_TAP_TO_CLICK)) {
+ gboolean mouse_left_handed;
+ mouse_left_handed = g_settings_get_boolean (manager->priv->mouse_settings, KEY_LEFT_HANDED);
+ set_tap_to_click (device, g_settings_get_boolean (settings, key),
+ get_touchpad_handedness (manager, mouse_left_handed));
+ } else if (g_str_equal (key, KEY_EDGE_SCROLLING_ENABLED)) {
+ set_edge_scrolling_enabled (manager, device, g_settings_get_boolean (settings, key));
+ set_horiz_scroll (device, TRUE);
+ } else if (g_str_equal (key, KEY_SPEED)) {
+ set_motion (manager, device);
+ } else if (g_str_equal (key, KEY_LEFT_HANDED)) {
+ gboolean mouse_left_handed;
+ mouse_left_handed = g_settings_get_boolean (manager->priv->mouse_settings, KEY_LEFT_HANDED);
+ set_left_handed (manager, device, mouse_left_handed, get_touchpad_handedness (manager, mouse_left_handed));
+ } else if (g_str_equal (key, KEY_NATURAL_SCROLL_ENABLED)) {
+ set_natural_scroll (manager, device, g_settings_get_boolean (settings, key));
+ }
+ }
+ g_list_free (devices);
+
+ if (g_str_equal (key, KEY_SEND_EVENTS)) {
+ ensure_touchpad_active (manager);
+ }
+}
+
+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;
+
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ return;
+
+ 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)
+{
+ GList *devices, *l;
+ gboolean state;
+
+ state = get_touchpad_enabled (manager);
+ if (state) {
+ devices = get_disabled_synaptics ();
+ for (l = devices; l != NULL; l = l->next) {
+ int device_id;
+
+ device_id = GPOINTER_TO_INT (l->data);
+ set_touchpad_enabled (device_id);
+ }
+ g_list_free (devices);
+ } else {
+ 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;
+ if (xdevice_is_libinput (gdk_x11_device_get_id (device)))
+ continue;
+ if (gdk_device_get_source (device) != GDK_SOURCE_TOUCHPAD)
+ continue;
+
+ set_touchpad_disabled (device);
+ }
+
+ g_list_free (devices);
+ }
+
+ set_disable_w_typing (manager, state);
+}
+
+static void
+device_added_cb (GdkDeviceManager *device_manager,
+ GdkDevice *device,
+ GsdMouseManager *manager)
+{
+ if (device_is_ignored (manager, device) == FALSE) {
+ if (run_custom_command (device, COMMAND_DEVICE_ADDED) == FALSE) {
+ set_mouse_settings (manager, device);
+ } else {
+ int id;
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+ g_hash_table_insert (manager->priv->blacklist,
+ GINT_TO_POINTER (id), GINT_TO_POINTER (1));
+ }
+
+ ensure_touchpad_active (manager);
+ }
+}
+
+static void
+device_removed_cb (GdkDeviceManager *device_manager,
+ GdkDevice *device,
+ GsdMouseManager *manager)
+{
+ int id;
+
+ /* Remove the device from the hash table so that
+ * device_is_ignored () doesn't check for blacklisted devices */
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+ g_hash_table_remove (manager->priv->blacklist,
+ GINT_TO_POINTER (id));
+
+ if (device_is_ignored (manager, device) == FALSE) {
+ run_custom_command (device, COMMAND_DEVICE_REMOVED);
+
+ ensure_touchpad_active (manager);
+ }
+}
+
+static void
+set_devicepresence_handler (GsdMouseManager *manager)
+{
+ GdkDeviceManager *device_manager;
+
+ device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
+
+ manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added",
+ G_CALLBACK (device_added_cb), manager);
+ manager->priv->device_removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed",
+ G_CALLBACK (device_removed_cb), manager);
+ manager->priv->device_manager = device_manager;
}
static void
gsd_mouse_manager_init (GsdMouseManager *manager)
{
manager->priv = GSD_MOUSE_MANAGER_GET_PRIVATE (manager);
+ manager->priv->blacklist = g_hash_table_new (g_direct_hash, g_direct_equal);
}
static gboolean
gsd_mouse_manager_idle_cb (GsdMouseManager *manager)
{
+ GList *devices, *l;
+
gnome_settings_profile_start (NULL);
+ set_devicepresence_handler (manager);
+
manager->priv->gsd_mouse_settings = g_settings_new (GSD_SETTINGS_MOUSE_SCHEMA);
g_signal_connect (manager->priv->gsd_mouse_settings, "changed",
G_CALLBACK (mouse_callback), manager);
@@ -176,17 +1381,46 @@ gsd_mouse_manager_idle_cb (GsdMouseManager *manager)
manager->priv->mouse_a11y_settings = g_settings_new ("org.gnome.desktop.a11y.mouse");
g_signal_connect (manager->priv->mouse_a11y_settings, "changed",
G_CALLBACK (mouse_callback), manager);
-#if 0
+
manager->priv->mouse_settings = g_settings_new (GSETTINGS_MOUSE_SCHEMA);
g_signal_connect (manager->priv->mouse_settings, "changed",
G_CALLBACK (mouse_callback), manager);
-#endif
+
+ manager->priv->touchpad_settings = g_settings_new (GSETTINGS_TOUCHPAD_SCHEMA);
+ g_signal_connect (manager->priv->touchpad_settings, "changed",
+ G_CALLBACK (touchpad_callback), manager);
+
+ manager->priv->trackball_settings = g_settings_new (GSETTINGS_TRACKBALL_SCHEMA);
+ 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->gsd_mouse_settings, KEY_LOCATE_POINTER));
set_mousetweaks_daemon (manager,
g_settings_get_boolean (manager->priv->mouse_a11y_settings, KEY_DWELL_CLICK_ENABLED),
g_settings_get_boolean (manager->priv->mouse_a11y_settings, KEY_SECONDARY_CLICK_ENABLED));
+ 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;
+
+ if (run_custom_command (device, COMMAND_DEVICE_PRESENT) == FALSE) {
+ set_mouse_settings (manager, device);
+ } else {
+ int id;
+ g_object_get (G_OBJECT (device), "device-id", &id, NULL);
+ g_hash_table_insert (manager->priv->blacklist,
+ GINT_TO_POINTER (id), GINT_TO_POINTER (1));
+ }
+ }
+ g_list_free (devices);
+
+ ensure_touchpad_active (manager);
+
gnome_settings_profile_end (NULL);
manager->priv->start_idle_id = 0;
@@ -202,6 +1436,11 @@ gsd_mouse_manager_start (GsdMouseManager *manager,
migrate_mouse_settings ();
+ if (!supports_xinput_devices ()) {
+ g_debug ("XInput is not supported, not applying any settings");
+ return TRUE;
+ }
+
if (gnome_settings_is_wayland ())
return TRUE;
@@ -225,9 +1464,16 @@ gsd_mouse_manager_stop (GsdMouseManager *manager)
manager->priv->start_idle_id = 0;
}
+ if (p->device_manager != NULL) {
+ g_signal_handler_disconnect (p->device_manager, p->device_added_id);
+ g_signal_handler_disconnect (p->device_manager, p->device_removed_id);
+ p->device_manager = NULL;
+ }
+
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);
g_clear_object (&p->gsd_mouse_settings);
set_locate_pointer (manager, FALSE);
@@ -247,6 +1493,9 @@ gsd_mouse_manager_finalize (GObject *object)
gsd_mouse_manager_stop (mouse_manager);
+ if (mouse_manager->priv->blacklist != NULL)
+ g_hash_table_destroy (mouse_manager->priv->blacklist);
+
G_OBJECT_CLASS (gsd_mouse_manager_parent_class)->finalize (object);
}
--
2.6.2
From 4b1ed6c7aec9dea2b70fe19c0156fed72899eba6 Mon Sep 17 00:00:00 2001
From: Frederic Crozat <fcrozat@suse.com>
Date: Fri, 4 Mar 2016 15:49:20 +0100
Subject: [PATCH 2/3] Revert "common: Remove unused functions"
This reverts commit 9287ef9ac5b119abdcbbabd920c19f353e577f90.
---
plugins/common/gsd-input-helper.c | 279 +++++++++++++++++++++++++++++++++++++
plugins/common/gsd-input-helper.h | 15 ++
plugins/common/test-input-helper.c | 5 +-
3 files changed, 298 insertions(+), 1 deletion(-)
diff --git a/plugins/common/gsd-input-helper.c b/plugins/common/gsd-input-helper.c
index 077ff1c..4dca795 100644
--- a/plugins/common/gsd-input-helper.c
+++ b/plugins/common/gsd-input-helper.c
@@ -115,6 +115,12 @@ supports_xinput_devices_with_opcode (int *opcode)
}
gboolean
+supports_xinput_devices (void)
+{
+ return supports_xinput_devices_with_opcode (NULL);
+}
+
+gboolean
supports_xtest (void)
{
gint op_code, event, error;
@@ -154,6 +160,66 @@ supports_xinput2_devices (int *opcode)
return TRUE;
}
+gboolean
+xdevice_is_synaptics (XDevice *xdevice)
+{
+ Atom realtype, prop;
+ int realformat;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ /* we don't check on the type being XI_TOUCHPAD here,
+ * but having a "Synaptics Off" property should be enough */
+
+ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Off", False);
+ if (!prop)
+ return FALSE;
+
+ gdk_error_trap_push ();
+ if ((XGetDeviceProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), xdevice, prop, 0, 1, False,
+ XA_INTEGER, &realtype, &realformat, &nitems,
+ &bytes_after, &data) == Success) && (realtype != None)) {
+ gdk_error_trap_pop_ignored ();
+ XFree (data);
+ return TRUE;
+ }
+ gdk_error_trap_pop_ignored ();
+
+ return FALSE;
+}
+
+gboolean
+synaptics_is_present (void)
+{
+ XDeviceInfo *device_info;
+ gint n_devices;
+ guint i;
+ gboolean retval;
+
+ retval = FALSE;
+
+ device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), &n_devices);
+ if (device_info == NULL)
+ return FALSE;
+
+ for (i = 0; i < n_devices; i++) {
+ XDevice *device;
+
+ gdk_error_trap_push ();
+ device = XOpenDevice (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), device_info[i].id);
+ if (gdk_error_trap_pop () || (device == NULL))
+ continue;
+
+ retval = xdevice_is_synaptics (device);
+ xdevice_close (device);
+ if (retval)
+ break;
+ }
+ XFreeDeviceList (device_info);
+
+ return retval;
+}
+
static gboolean
device_type_is_present (GsdDeviceType type)
{
@@ -181,6 +247,29 @@ mouse_is_present (void)
return device_type_is_present (GSD_DEVICE_TYPE_MOUSE);
}
+gboolean
+trackball_is_present (void)
+{
+ gboolean retval = FALSE;
+ GList *l, *mice = gsd_device_manager_list_devices (gsd_device_manager_get (),
+ GSD_DEVICE_TYPE_MOUSE);
+ if (mice == NULL)
+ return FALSE;
+
+ for (l = mice; l != NULL; l = l->next) {
+ gchar *lowercase;
+ const gchar *name = gsd_device_get_name (l->data);
+ if (!name)
+ continue;
+ lowercase = g_ascii_strdown (name, -1);
+ retval = strstr (lowercase, "trackball") != NULL;
+ g_free (lowercase);
+ }
+
+ g_list_free (mice);
+ return retval;
+}
+
char *
xdevice_get_device_node (int deviceid)
{
@@ -339,6 +428,172 @@ set_device_enabled (int device_id,
return TRUE;
}
+gboolean
+set_synaptics_device_enabled (int device_id,
+ gboolean enabled)
+{
+ Atom prop;
+ guchar value;
+
+ prop = XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "Synaptics Off", False);
+ if (!prop)
+ return FALSE;
+
+ gdk_error_trap_push ();
+
+ value = enabled ? 0 : 1;
+ XIChangeProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
+ device_id, prop, XA_INTEGER, 8, PropModeReplace, &value, 1);
+
+ if (gdk_error_trap_pop ())
+ return FALSE;
+
+ return TRUE;
+}
+
+static const char *
+custom_command_to_string (CustomCommand command)
+{
+ switch (command) {
+ case COMMAND_DEVICE_ADDED:
+ return "added";
+ case COMMAND_DEVICE_REMOVED:
+ return "removed";
+ case COMMAND_DEVICE_PRESENT:
+ return "present";
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+/* Run a custom command on device presence events. Parameters passed into
+ * the custom command are:
+ * command -t [added|removed|present] -i <device ID> <device name>
+ * Type 'added' and 'removed' signal 'device added' and 'device removed',
+ * respectively. Type 'present' signals 'device present at
+ * gnome-settings-daemon init'.
+ *
+ * The script is expected to run synchronously, and an exit value
+ * of "1" means that no other settings will be applied to this
+ * particular device.
+ *
+ * More options may be added in the future.
+ *
+ * This function returns TRUE if we should not apply any more settings
+ * to the device.
+ */
+gboolean
+run_custom_command (GdkDevice *device,
+ CustomCommand command)
+{
+ GSettings *settings;
+ GError *error = NULL;
+ char *cmd;
+ char *argv[7];
+ int exit_status;
+ gboolean rc;
+ int id;
+ char *out;
+
+ settings = g_settings_new (INPUT_DEVICES_SCHEMA);
+ cmd = g_settings_get_string (settings, KEY_HOTPLUG_COMMAND);
+ g_object_unref (settings);
+
+ if (!cmd || cmd[0] == '\0') {
+ g_free (cmd);
+ return FALSE;
+ }
+
+ /* Easter egg! */
+ g_object_get (device, "device-id", &id, NULL);
+
+ argv[0] = cmd;
+ argv[1] = "-t";
+ argv[2] = (char *) custom_command_to_string (command);
+ argv[3] = "-i";
+ argv[4] = g_strdup_printf ("%d", id);
+ argv[5] = (char*) gdk_device_get_name (device);
+ argv[6] = NULL;
+
+ out = g_strjoinv (" ", argv);
+ g_debug ("About to launch command: %s", out);
+ g_free (out);
+
+ rc = g_spawn_sync (g_get_home_dir (), argv, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, NULL, &exit_status, &error);
+
+ if (rc == FALSE) {
+ g_warning ("Couldn't execute command '%s', verify that this is a valid command: %s", cmd, error->message);
+ g_clear_error (&error);
+ }
+
+ g_free (argv[0]);
+ g_free (argv[4]);
+
+ if (!g_spawn_check_exit_status (exit_status, &error)) {
+ if (g_error_matches (error, G_SPAWN_EXIT_ERROR, 1)) {
+ g_clear_error (&error);
+ return TRUE;
+ }
+ g_clear_error (&error);
+ }
+
+ return FALSE;
+}
+
+GList *
+get_disabled_synaptics (void)
+{
+ GdkDisplay *display;
+ XDeviceInfo *device_info;
+ gint n_devices, act_format, rc;
+ guint i;
+ GList *ret;
+ Atom prop, act_type;
+ unsigned long nitems, bytes_after;
+ unsigned char *data;
+
+ ret = NULL;
+
+ display = gdk_display_get_default ();
+ prop = gdk_x11_get_xatom_by_name ("Synaptics Off");
+
+ gdk_error_trap_push ();
+
+ device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY (display), &n_devices);
+ if (device_info == NULL) {
+ gdk_error_trap_pop_ignored ();
+
+ return ret;
+ }
+
+ for (i = 0; i < n_devices; i++) {
+ rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display),
+ device_info[i].id, prop, 0, 1, False,
+ XA_INTEGER, &act_type, &act_format,
+ &nitems, &bytes_after, &data);
+
+ if (rc != Success || act_type != XA_INTEGER ||
+ act_format != 8 || nitems < 1)
+ continue;
+
+ if (!(data[0])) {
+ XFree (data);
+ continue;
+ }
+
+ XFree (data);
+
+ ret = g_list_prepend (ret, GINT_TO_POINTER (device_info[i].id));
+ }
+
+ gdk_error_trap_pop_ignored ();
+
+ XFreeDeviceList (device_info);
+
+ return ret;
+}
+
const char *
xdevice_get_wacom_tool_type (int deviceid)
{
@@ -425,3 +680,27 @@ xdevice_get_dimensions (int deviceid,
return (w != 0 && h != 0);
}
+
+gboolean
+xdevice_is_libinput (gint deviceid)
+{
+ GdkDisplay *display = gdk_display_get_default ();
+ gulong nitems, bytes_after;
+ gint rc, format;
+ guchar *data;
+ Atom type;
+
+ gdk_error_trap_push ();
+
+ /* Lookup a libinput driver specific property */
+ rc = XIGetProperty (GDK_DISPLAY_XDISPLAY (display), deviceid,
+ gdk_x11_get_xatom_by_name ("libinput Send Events Mode Enabled"),
+ 0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, &data);
+
+ if (rc == Success)
+ XFree (data);
+
+ gdk_error_trap_pop_ignored ();
+
+ return rc == Success && nitems > 0;
+}
diff --git a/plugins/common/gsd-input-helper.h b/plugins/common/gsd-input-helper.h
index 31e2e47..85e6d07 100644
--- a/plugins/common/gsd-input-helper.h
+++ b/plugins/common/gsd-input-helper.h
@@ -44,20 +44,32 @@ typedef struct {
} data;
} PropertyHelper;
+gboolean supports_xinput_devices (void);
gboolean supports_xinput2_devices (int *opcode);
gboolean supports_xtest (void);
gboolean set_device_enabled (int device_id,
gboolean enabled);
+gboolean set_synaptics_device_enabled (int device_id,
+ gboolean enabled);
+
+gboolean xdevice_is_synaptics (XDevice *xdevice);
+
+gboolean synaptics_is_present (void);
gboolean touchpad_is_present (void);
gboolean touchscreen_is_present (void);
gboolean mouse_is_present (void);
+gboolean trackball_is_present (void);
gboolean device_set_property (XDevice *xdevice,
const char *device_name,
PropertyHelper *property);
+gboolean run_custom_command (GdkDevice *device,
+ CustomCommand command);
+
+GList * get_disabled_synaptics (void);
char * xdevice_get_device_node (int deviceid);
int xdevice_get_last_tool_id (int deviceid);
gboolean xdevice_get_dimensions (int deviceid,
@@ -67,6 +79,9 @@ void xdevice_close (XDevice *xdevice);
const char * xdevice_get_wacom_tool_type (int deviceid);
+gboolean xdevice_is_libinput (gint deviceid);
+
+
G_END_DECLS
#endif /* __GSD_INPUT_HELPER_H */
diff --git a/plugins/common/test-input-helper.c b/plugins/common/test-input-helper.c
index 954ac30..e78f463 100644
--- a/plugins/common/test-input-helper.c
+++ b/plugins/common/test-input-helper.c
@@ -32,7 +32,7 @@
int main (int argc, char **argv)
{
GList *devices, *l;
- gboolean has_touchpad, has_touchscreen;
+ gboolean has_touchpad, has_touchscreen, has_trackball;
2016-09-18 21:58:23 +02:00
gboolean has_mouse;
2016-03-08 16:53:53 +01:00
gtk_init (&argc, &argv);
@@ -42,6 +42,9 @@ int main (int argc, char **argv)
has_touchscreen = touchscreen_is_present ();
g_print ("Has touchscreen:\t\t\t%s\n", has_touchscreen ? "yes" : "no");
+ has_trackball = trackball_is_present ();
+ g_print ("Has trackball:\t\t\t\t%s\n", has_trackball ? "yes" : "no");
+
devices = gsd_device_manager_list_devices (gsd_device_manager_get (), GSD_DEVICE_TYPE_MOUSE);
for (l = devices; l != NULL; l = l->next)
g_print ("Device '%s' is a mouse\n", gsd_device_get_name (l->data));
--
2.6.2
From 2e27aeec3c5bcb9fe398bd3835226f261b60baf1 Mon Sep 17 00:00:00 2001
From: Frederic Crozat <fcrozat@suse.com>
Date: Fri, 4 Mar 2016 15:51:23 +0100
Subject: [PATCH 3/3] Revert "common: Remove "hotplug-command" helper"
This reverts commit c50ceb880e928506d987747f9e554079ad3d9826.
---
...e.settings-daemon.peripherals.gschema.xml.in.in | 8 +++
plugins/common/Makefile.am | 3 +
plugins/common/gsd-input-helper.c | 3 +
plugins/common/gsd-input-helper.h | 6 ++
plugins/common/input-device-example.sh | 69 ++++++++++++++++++++++
plugins/keyboard/gsd-keyboard-manager.c | 18 ++++++
6 files changed, 107 insertions(+)
create mode 100644 plugins/common/input-device-example.sh
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 f4120e9..6a65f08 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="keyboard" schema="org.gnome.settings-daemon.peripherals.keyboard"/>
<child name="mouse" schema="org.gnome.settings-daemon.peripherals.mouse"/>
<child name="touchscreen" schema="org.gnome.settings-daemon.peripherals.touchscreen"/>
+ <child name="input-devices" schema="org.gnome.settings-daemon.peripherals.input-devices"/>
</schema>
<schema gettext-domain="@GETTEXT_PACKAGE@" id="org.gnome.settings-daemon.peripherals.smartcard" path="/org/gnome/settings-daemon/peripherals/smartcard/">
<key name="removal-action" enum="org.gnome.settings-daemon.GsdSmartcardRemovalAction">
@@ -67,6 +68,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.input-devices" path="/org/gnome/settings-daemon/peripherals/input-devices/">
+ <key name="hotplug-command" type="s">
+ <default>''</default>
+ <_summary>Device hotplug custom command</_summary>
+ <_description>Command to be run when a device is added or removed. An exit value of 1 means that the device will not be handled further by gnome-settings-daemon.</_description>
+ </key>
+ </schema>
<!-- Deprecated schemas/keys -->
<schema id="org.gnome.settings-daemon.peripherals.mouse.deprecated">
diff --git a/plugins/common/Makefile.am b/plugins/common/Makefile.am
index 0331226..0f85351 100644
--- a/plugins/common/Makefile.am
+++ b/plugins/common/Makefile.am
@@ -82,6 +82,9 @@ test_egg_key_parsing_SOURCES = test-egg-key-parsing.c
test_egg_key_parsing_LDADD = libcommon.la $(COMMON_LIBS)
test_egg_key_parsing_CFLAGS = $(libcommon_la_CFLAGS)
+scriptsdir = $(datadir)/gnome-settings-daemon-@GSD_API_VERSION@
+scripts_DATA = input-device-example.sh
+
EXTRA_DIST = $(scripts_DATA) test-plugin.h
CLEANFILES = \
diff --git a/plugins/common/gsd-input-helper.c b/plugins/common/gsd-input-helper.c
index 4dca795..e3fe2d8 100644
--- a/plugins/common/gsd-input-helper.c
+++ b/plugins/common/gsd-input-helper.c
@@ -31,6 +31,9 @@
#include "gsd-input-helper.h"
#include "gsd-device-manager.h"
+#define INPUT_DEVICES_SCHEMA "org.gnome.settings-daemon.peripherals.input-devices"
+#define KEY_HOTPLUG_COMMAND "hotplug-command"
+
#define ABS_MT_X "Abs MT Position X"
#define ABS_MT_Y "Abs MT Position Y"
#define ABS_X "Abs X"
diff --git a/plugins/common/gsd-input-helper.h b/plugins/common/gsd-input-helper.h
index 85e6d07..aadd790 100644
--- a/plugins/common/gsd-input-helper.h
+++ b/plugins/common/gsd-input-helper.h
@@ -28,6 +28,12 @@ G_BEGIN_DECLS
#define WACOM_SERIAL_IDS_PROP "Wacom Serial IDs"
+typedef enum {
+ COMMAND_DEVICE_ADDED,
+ COMMAND_DEVICE_REMOVED,
+ COMMAND_DEVICE_PRESENT
+} CustomCommand;
+
/* Generic property setting code. Fill up the struct property with the property
* data and pass it into device_set_property together with the device to be
* changed. Note: doesn't cater for non-zero offsets yet, but we don't have
diff --git a/plugins/common/input-device-example.sh b/plugins/common/input-device-example.sh
new file mode 100644
index 0000000..235cdc4
--- /dev/null
+++ b/plugins/common/input-device-example.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+#
+# This script is an example hotplug script for use with the various
+# input devices plugins.
+#
+# The script is called with the arguments:
+# -t [added|present|removed] <device name>
+# added ... device was just plugged in
+# present.. device was present at gnome-settings-daemon startup
+# removed.. device was just removed
+# -i <device ID>
+# device ID being the XInput device ID
+# <device name> The name of the device
+#
+# The script should return 1 if the device is to be
+# ignored from future configuration.
+#
+# Set the script to be used with:
+# gsettings set org.gnome.settings-daemon.peripherals.input-devices hotplug-command /path/to/script/input-devices.sh
+#
+
+args=`getopt "t:i:" $*`
+
+set -- $args
+
+while [ $# -gt 0 ]
+do
+ case $1 in
+ -t)
+ shift;
+ type="$1"
+ ;;
+ -i)
+ shift;
+ id="$1"
+ ;;
+ --)
+ shift;
+ device="$@"
+ break;
+ ;;
+ *)
+ echo "Unknown option $1";
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+retval=0
+
+case $type in
+ added)
+ echo "Device '$device' (ID=$id) was added"
+ ;;
+ present)
+ echo "Device '$device' (ID=$id) was already present at startup"
+ ;;
+ removed)
+ echo "Device '$device' (ID=$id) was removed"
+ ;;
+ *)
+ echo "Unknown operation"
+ retval=1
+ ;;
+esac
+
+# All further processing will be disabled if $retval == 1
+exit $retval
diff --git a/plugins/keyboard/gsd-keyboard-manager.c b/plugins/keyboard/gsd-keyboard-manager.c
index ddeeee6..eb75c92 100644
--- a/plugins/keyboard/gsd-keyboard-manager.c
+++ b/plugins/keyboard/gsd-keyboard-manager.c
@@ -90,6 +90,7 @@ struct GsdKeyboardManagerPrivate
GsdNumLockState old_state;
GdkDeviceManager *device_manager;
guint device_added_id;
+ guint device_removed_id;
};
static void gsd_keyboard_manager_class_init (GsdKeyboardManagerClass *klass);
@@ -363,6 +364,20 @@ device_added_cb (GdkDeviceManager *device_manager,
if (source == GDK_SOURCE_KEYBOARD) {
g_debug ("New keyboard plugged in, applying all settings");
apply_numlock (manager);
+ run_custom_command (device, COMMAND_DEVICE_ADDED);
+ }
+}
+
+static void
+device_removed_cb (GdkDeviceManager *device_manager,
+ GdkDevice *device,
+ GsdKeyboardManager *manager)
+{
+ GdkInputSource source;
+
+ source = gdk_device_get_source (device);
+ if (source == GDK_SOURCE_KEYBOARD) {
+ run_custom_command (device, COMMAND_DEVICE_REMOVED);
}
}
@@ -378,6 +393,8 @@ set_devicepresence_handler (GsdKeyboardManager *manager)
manager->priv->device_added_id = g_signal_connect (G_OBJECT (device_manager), "device-added",
G_CALLBACK (device_added_cb), manager);
+ manager->priv->device_removed_id = g_signal_connect (G_OBJECT (device_manager), "device-removed",
+ G_CALLBACK (device_removed_cb), manager);
manager->priv->device_manager = device_manager;
}
@@ -758,6 +775,7 @@ gsd_keyboard_manager_stop (GsdKeyboardManager *manager)
if (p->device_manager != NULL) {
g_signal_handler_disconnect (p->device_manager, p->device_added_id);
+ g_signal_handler_disconnect (p->device_manager, p->device_removed_id);
p->device_manager = NULL;
}
--
2.6.2