From f5aa66f135c9e7e9c57363c8933e6a87d0900112 Mon Sep 17 00:00:00 2001 From: Frederic Crozat 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 +#include +#include +#include +#include +#include +#include +#include +#ifdef __linux +#include +#endif + #include #include #include #include +#include +#include +#include +#include +#include +#include #include +#include +#include + #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 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 + * 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; gboolean has_mouse; 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 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 @@ + @@ -67,6 +68,13 @@ <_summary>Whether the tablet's orientation is locked, or rotated automatically. + + + '' + <_summary>Device hotplug custom command + <_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. + + 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] +# added ... device was just plugged in +# present.. device was present at gnome-settings-daemon startup +# removed.. device was just removed +# -i +# device ID being the XInput device ID +# 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