diff options
Diffstat (limited to 'plugins/mouse/msd-mouse-manager.c')
-rw-r--r-- | plugins/mouse/msd-mouse-manager.c | 1124 |
1 files changed, 1124 insertions, 0 deletions
diff --git a/plugins/mouse/msd-mouse-manager.c b/plugins/mouse/msd-mouse-manager.c new file mode 100644 index 0000000..d7cb8e4 --- /dev/null +++ b/plugins/mouse/msd-mouse-manager.c @@ -0,0 +1,1124 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#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> + +#include <locale.h> + +#include <glib.h> +#include <glib/gi18n.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> + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +#include <X11/extensions/XInput.h> +#include <X11/extensions/XIproto.h> +#endif +#include <mateconf/mateconf.h> +#include <mateconf/mateconf-client.h> + +#include "mate-settings-profile.h" +#include "msd-mouse-manager.h" + +#define MSD_MOUSE_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MSD_TYPE_MOUSE_MANAGER, MsdMouseManagerPrivate)) + +#define MATECONF_MOUSE_DIR "/desktop/mate/peripherals/mouse" +#define MATECONF_MOUSE_A11Y_DIR "/desktop/mate/accessibility/mouse" +#define MATECONF_TOUCHPAD_DIR "/desktop/mate/peripherals/touchpad" + +#define KEY_LEFT_HANDED MATECONF_MOUSE_DIR "/left_handed" +#define KEY_MOTION_ACCELERATION MATECONF_MOUSE_DIR "/motion_acceleration" +#define KEY_MOTION_THRESHOLD MATECONF_MOUSE_DIR "/motion_threshold" +#define KEY_LOCATE_POINTER MATECONF_MOUSE_DIR "/locate_pointer" +#define KEY_DWELL_ENABLE MATECONF_MOUSE_A11Y_DIR "/dwell_enable" +#define KEY_DELAY_ENABLE MATECONF_MOUSE_A11Y_DIR "/delay_enable" +#define KEY_TOUCHPAD_DISABLE_W_TYPING MATECONF_TOUCHPAD_DIR "/disable_while_typing" +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +#define KEY_TAP_TO_CLICK MATECONF_TOUCHPAD_DIR "/tap_to_click" +#define KEY_SCROLL_METHOD MATECONF_TOUCHPAD_DIR "/scroll_method" +#define KEY_PAD_HORIZ_SCROLL MATECONF_TOUCHPAD_DIR "/horiz_scroll_enabled" +#define KEY_TOUCHPAD_ENABLED MATECONF_TOUCHPAD_DIR "/touchpad_enabled" +#endif + +struct MsdMouseManagerPrivate +{ + guint notify; + guint notify_a11y; + guint notify_touchpad; + + gboolean mousetweaks_daemon_running; + gboolean syndaemon_spawned; + GPid syndaemon_pid; + gboolean locate_pointer_spawned; + GPid locate_pointer_pid; +}; + +static void msd_mouse_manager_class_init (MsdMouseManagerClass *klass); +static void msd_mouse_manager_init (MsdMouseManager *mouse_manager); +static void msd_mouse_manager_finalize (GObject *object); +static void set_mouse_settings (MsdMouseManager *manager); +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +static int set_tap_to_click (gboolean state, gboolean left_handed); +static XDevice* device_is_touchpad (XDeviceInfo deviceinfo); +#endif + +G_DEFINE_TYPE (MsdMouseManager, msd_mouse_manager, G_TYPE_OBJECT) + +static gpointer manager_object = NULL; + +static void +msd_mouse_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MsdMouseManager *self; + + self = MSD_MOUSE_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +msd_mouse_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MsdMouseManager *self; + + self = MSD_MOUSE_MANAGER (object); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +msd_mouse_manager_constructor (GType type, + guint n_construct_properties, + GObjectConstructParam *construct_properties) +{ + MsdMouseManager *mouse_manager; + MsdMouseManagerClass *klass; + + klass = MSD_MOUSE_MANAGER_CLASS (g_type_class_peek (MSD_TYPE_MOUSE_MANAGER)); + + mouse_manager = MSD_MOUSE_MANAGER (G_OBJECT_CLASS (msd_mouse_manager_parent_class)->constructor (type, + n_construct_properties, + construct_properties)); + + return G_OBJECT (mouse_manager); +} + +static void +msd_mouse_manager_dispose (GObject *object) +{ + MsdMouseManager *mouse_manager; + + mouse_manager = MSD_MOUSE_MANAGER (object); + + G_OBJECT_CLASS (msd_mouse_manager_parent_class)->dispose (object); +} + +static void +msd_mouse_manager_class_init (MsdMouseManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->get_property = msd_mouse_manager_get_property; + object_class->set_property = msd_mouse_manager_set_property; + object_class->constructor = msd_mouse_manager_constructor; + object_class->dispose = msd_mouse_manager_dispose; + object_class->finalize = msd_mouse_manager_finalize; + + g_type_class_add_private (klass, sizeof (MsdMouseManagerPrivate)); +} + + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +static gboolean +supports_xinput_devices (void) +{ + gint op_code, event, error; + + return XQueryExtension (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), + "XInputExtension", + &op_code, + &event, + &error); +} +#endif + +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; + } +} + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +static gboolean +xinput_device_has_buttons (XDeviceInfo *device_info) +{ + int i; + XAnyClassInfo *class_info; + + class_info = device_info->inputclassinfo; + for (i = 0; i < device_info->num_classes; i++) { + if (class_info->class == ButtonClass) { + XButtonInfo *button_info; + + button_info = (XButtonInfo *) class_info; + if (button_info->num_buttons > 0) + return TRUE; + } + + class_info = (XAnyClassInfo *) (((guchar *) class_info) + + class_info->length); + } + 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 (); + + return is_single_button; +} + + +static void +set_xinput_devices_left_handed (gboolean left_handed) +{ + XDeviceInfo *device_info; + gint n_devices; + guchar *buttons; + gsize buttons_capacity = 16; + gint n_buttons; + gint i; + + device_info = XListInputDevices (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), &n_devices); + + if (n_devices > 0) + buttons = g_new (guchar, buttons_capacity); + else + buttons = NULL; + + for (i = 0; i < n_devices; i++) { + XDevice *device = NULL; + + if ((device_info[i].use == IsXPointer) || + (device_info[i].use == IsXKeyboard) || + (!xinput_device_has_buttons (&device_info[i]))) + continue; + + /* If the device is a touchpad, swap tap buttons + * around too, otherwise a tap would be a right-click */ + device = device_is_touchpad (device_info[i]); + if (device != NULL) { + MateConfClient *client = mateconf_client_get_default (); + gboolean tap = mateconf_client_get_bool (client, KEY_TAP_TO_CLICK, NULL); + gboolean single_button = touchpad_has_single_button (device); + + if (tap && !single_button) + set_tap_to_click (tap, left_handed); + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + g_object_unref (client); + + if (single_button) + continue; + } + + gdk_error_trap_push (); + + device = XOpenDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device_info[i].id); + + if ((gdk_error_trap_pop () != 0) || + (device == NULL)) + continue; + + n_buttons = XGetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + 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()), device, + buttons, + buttons_capacity); + } + + configure_button_layout (buttons, n_buttons, left_handed); + + XSetDeviceButtonMapping (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, buttons, n_buttons); + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + } + g_free (buttons); + + if (device_info != NULL) + XFreeDeviceList (device_info); +} + +static GdkFilterReturn +devicepresence_filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + XEvent *xev = (XEvent *) xevent; + XEventClass class_presence; + int xi_presence; + + DevicePresence (gdk_x11_get_default_xdisplay (), xi_presence, class_presence); + + if (xev->type == xi_presence) + { + XDevicePresenceNotifyEvent *dpn = (XDevicePresenceNotifyEvent *) xev; + if (dpn->devchange == DeviceEnabled) + set_mouse_settings ((MsdMouseManager *) data); + } + return GDK_FILTER_CONTINUE; +} + +static void +set_devicepresence_handler (MsdMouseManager *manager) +{ + Display *display; + XEventClass class_presence; + int xi_presence; + + if (!supports_xinput_devices ()) + return; + + display = gdk_x11_get_default_xdisplay (); + + gdk_error_trap_push (); + DevicePresence (display, xi_presence, class_presence); + XSelectExtensionEvent (display, + RootWindow (display, DefaultScreen (display)), + &class_presence, 1); + + gdk_flush (); + if (!gdk_error_trap_pop ()) + gdk_window_add_filter (NULL, devicepresence_filter, manager); +} +#endif + +static void +set_left_handed (MsdMouseManager *manager, + gboolean left_handed) +{ + guchar *buttons ; + gsize buttons_capacity = 16; + gint n_buttons, i; + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + if (supports_xinput_devices ()) { + /* When XInput support is available, never set the + * button ordering on the core pointer as that would + * revert the changes we make on the devices themselves */ + set_xinput_devices_left_handed (left_handed); + return; + } +#endif + + buttons = g_new (guchar, buttons_capacity); + n_buttons = XGetPointerMapping (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), + buttons, + (gint) buttons_capacity); + while (n_buttons > buttons_capacity) { + buttons_capacity = n_buttons; + buttons = (guchar *) g_realloc (buttons, + buttons_capacity * sizeof (guchar)); + + n_buttons = XGetPointerMapping (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), + buttons, + (gint) buttons_capacity); + } + + configure_button_layout (buttons, n_buttons, left_handed); + + /* X refuses to change the mapping while buttons are engaged, + * so if this is the case we'll retry a few times + */ + for (i = 0; + i < 20 && XSetPointerMapping (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), buttons, n_buttons) == MappingBusy; + ++i) { + g_usleep (300); + } + + g_free (buttons); +} + +static void +set_motion_acceleration (MsdMouseManager *manager, + gfloat motion_acceleration) +{ + gint numerator, denominator; + + 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; + } + + XChangePointerControl (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), True, False, + numerator, denominator, + 0); +} + +static void +set_motion_threshold (MsdMouseManager *manager, + int motion_threshold) +{ + XChangePointerControl (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), False, True, + 0, 0, motion_threshold); +} + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +static XDevice* +device_is_touchpad (XDeviceInfo deviceinfo) +{ + XDevice *device; + Atom realtype, prop; + int realformat; + unsigned long nitems, bytes_after; + unsigned char *data; + + if (deviceinfo.type != XInternAtom (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), XI_TOUCHPAD, False)) + return NULL; + + prop = XInternAtom (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), "Synaptics Off", False); + if (!prop) + return NULL; + + gdk_error_trap_push (); + device = XOpenDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), deviceinfo.id); + if (gdk_error_trap_pop () || (device == NULL)) + return NULL; + + gdk_error_trap_push (); + if ((XGetDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, prop, 0, 1, False, + XA_INTEGER, &realtype, &realformat, &nitems, + &bytes_after, &data) == Success) && (realtype != None)) { + gdk_error_trap_pop (); + XFree (data); + return device; + } + gdk_error_trap_pop (); + + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + return NULL; +} +#endif + +static int +set_disable_w_typing (MsdMouseManager *manager, gboolean state) +{ + + if (state) { + GError *error = NULL; + char *args[5]; + + if (manager->priv->syndaemon_spawned) + return 0; + + args[0] = "syndaemon"; + args[1] = "-i"; + args[2] = "0.5"; + args[3] = "-k"; + args[4] = NULL; + + if (!g_find_program_in_path (args[0])) + return 0; + + g_spawn_async (g_get_home_dir (), args, NULL, + G_SPAWN_SEARCH_PATH, NULL, NULL, + &manager->priv->syndaemon_pid, &error); + + manager->priv->syndaemon_spawned = (error == NULL); + + if (error) { + MateConfClient *client; + client = mateconf_client_get_default (); + mateconf_client_set_bool (client, KEY_TOUCHPAD_DISABLE_W_TYPING, FALSE, NULL); + g_object_unref (client); + g_error_free (error); + } + + } 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; + } + + return 0; +} + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H +static int +set_tap_to_click (gboolean state, gboolean left_handed) +{ + int numdevices, i, format, rc; + unsigned long nitems, bytes_after; + XDeviceInfo *devicelist = XListInputDevices (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), &numdevices); + XDevice * device; + unsigned char* data; + Atom prop, type; + + if (devicelist == NULL) + return 0; + + prop = XInternAtom (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), "Synaptics Tap Action", False); + + if (!prop) + return 0; + + for (i = 0; i < numdevices; i++) { + if ((device = device_is_touchpad (devicelist[i]))) { + gdk_error_trap_push (); + rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, 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()), device, prop, XA_INTEGER, 8, + PropModeReplace, data, nitems); + } + + if (rc == Success) + XFree (data); + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + if (gdk_error_trap_pop ()) { + g_warning ("Error in setting tap to click on \"%s\"", devicelist[i].name); + continue; + } + } + } + + XFreeDeviceList (devicelist); + return 0; +} + +static int +set_horiz_scroll (gboolean state) +{ + int numdevices, i, rc; + XDeviceInfo *devicelist = XListInputDevices (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), &numdevices); + XDevice *device; + Atom act_type, prop_edge, prop_twofinger; + int act_format; + unsigned long nitems, bytes_after; + unsigned char *data; + + if (devicelist == NULL) + return 0; + + 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 0; + + for (i = 0; i < numdevices; i++) { + if ((device = device_is_touchpad (devicelist[i]))) { + gdk_error_trap_push (); + rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + 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()), device, + prop_edge, XA_INTEGER, 8, + PropModeReplace, data, nitems); + } + + XFree (data); + + rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + 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()), device, + prop_twofinger, XA_INTEGER, 8, + PropModeReplace, data, nitems); + } + + XFree (data); + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + if (gdk_error_trap_pop ()) { + g_warning ("Error in setting horiz scroll on \"%s\"", devicelist[i].name); + continue; + } + } + } + + XFreeDeviceList (devicelist); + return 0; +} + + +/** + * Scroll methods are: 0 - disabled, 1 - edge scrolling, 2 - twofinger + * scrolling + */ +static int +set_edge_scroll (int method) +{ + int numdevices, i, rc; + XDeviceInfo *devicelist = XListInputDevices (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), &numdevices); + XDevice *device; + Atom act_type, prop_edge, prop_twofinger; + int act_format; + unsigned long nitems, bytes_after; + unsigned char *data; + + if (devicelist == NULL) + return 0; + + 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 0; + + for (i = 0; i < numdevices; i++) { + if ((device = device_is_touchpad (devicelist[i]))) { + gdk_error_trap_push (); + rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + 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 == 1) ? 1 : 0; + XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + prop_edge, XA_INTEGER, 8, + PropModeReplace, data, nitems); + } + + XFree (data); + + rc = XGetDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + 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 == 2) ? 1 : 0; + XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + prop_twofinger, XA_INTEGER, 8, + PropModeReplace, data, nitems); + } + + XFree (data); + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + if (gdk_error_trap_pop ()) { + g_warning ("Error in setting edge scroll on \"%s\"", devicelist[i].name); + continue; + } + } + } + + XFreeDeviceList (devicelist); + return 0; +} + +static int +set_touchpad_enabled (gboolean state) +{ + int numdevices, i; + XDeviceInfo *devicelist = XListInputDevices (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), &numdevices); + XDevice *device; + Atom prop_enabled; + + if (devicelist == NULL) + return 0; + + prop_enabled = XInternAtom (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), "Device Enabled", False); + + if (!prop_enabled) + return 0; + + for (i = 0; i < numdevices; i++) { + if ((device = device_is_touchpad (devicelist[i]))) { + unsigned char data = state; + gdk_error_trap_push (); + XChangeDeviceProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device, + prop_enabled, XA_INTEGER, 8, + PropModeReplace, &data, 1); + XCloseDevice (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), device); + gdk_flush (); + if (gdk_error_trap_pop ()) { + g_warning ("Error %s device \"%s\"", + (state) ? "enabling" : "disabling", + devicelist[i].name); + continue; + } + } + } + + XFreeDeviceList (devicelist); + return 0; +} +#endif + +static void +set_locate_pointer (MsdMouseManager *manager, + gboolean state) +{ + if (state) { + GError *error = NULL; + char *args[2]; + + if (manager->priv->locate_pointer_spawned) + return; + + args[0] = LIBEXECDIR "/msd-locate-pointer"; + args[1] = NULL; + + g_spawn_async (NULL, args, NULL, + 0, NULL, NULL, + &manager->priv->locate_pointer_pid, &error); + + manager->priv->locate_pointer_spawned = (error == NULL); + + if (error) { + MateConfClient *client; + client = mateconf_client_get_default (); + mateconf_client_set_bool (client, KEY_LOCATE_POINTER, FALSE, NULL); + g_object_unref (client); + g_error_free (error); + } + + } + else if (manager->priv->locate_pointer_spawned) { + kill (manager->priv->locate_pointer_pid, SIGHUP); + g_spawn_close_pid (manager->priv->locate_pointer_pid); + manager->priv->locate_pointer_spawned = FALSE; + } +} + +static void +set_mousetweaks_daemon (MsdMouseManager *manager, + gboolean dwell_enable, + gboolean delay_enable) +{ + GError *error = NULL; + gchar *comm; + gboolean run_daemon = dwell_enable || delay_enable; + + if (run_daemon || manager->priv->mousetweaks_daemon_running) + comm = g_strdup_printf ("mousetweaks %s", + run_daemon ? "" : "-s"); + else + return; + + if (run_daemon) + manager->priv->mousetweaks_daemon_running = TRUE; + + + if (! g_spawn_command_line_async (comm, &error)) { + if (error->code == G_SPAWN_ERROR_NOENT && + (dwell_enable || delay_enable)) { + GtkWidget *dialog; + MateConfClient *client; + + client = mateconf_client_get_default (); + if (dwell_enable) + mateconf_client_set_bool (client, + KEY_DWELL_ENABLE, + FALSE, NULL); + else if (delay_enable) + mateconf_client_set_bool (client, + KEY_DELAY_ENABLE, + FALSE, NULL); + g_object_unref (client); + + dialog = gtk_message_dialog_new (NULL, 0, + GTK_MESSAGE_WARNING, + GTK_BUTTONS_OK, + _("Could not enable mouse accessibility features")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), + _("Mouse accessibility requires Mousetweaks " + "to be installed on your system.")); + gtk_window_set_title (GTK_WINDOW (dialog), + _("Mouse Preferences")); + gtk_window_set_icon_name (GTK_WINDOW (dialog), + "input-mouse"); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } + g_error_free (error); + } + g_free (comm); +} + +static void +set_mouse_settings (MsdMouseManager *manager) +{ + MateConfClient *client = mateconf_client_get_default (); + gboolean left_handed = mateconf_client_get_bool (client, KEY_LEFT_HANDED, NULL); + + set_left_handed (manager, left_handed); + set_motion_acceleration (manager, mateconf_client_get_float (client, KEY_MOTION_ACCELERATION , NULL)); + set_motion_threshold (manager, mateconf_client_get_int (client, KEY_MOTION_THRESHOLD, NULL)); + + set_disable_w_typing (manager, mateconf_client_get_bool (client, KEY_TOUCHPAD_DISABLE_W_TYPING, NULL)); +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + set_tap_to_click (mateconf_client_get_bool (client, KEY_TAP_TO_CLICK, NULL), left_handed); + set_edge_scroll (mateconf_client_get_int (client, KEY_SCROLL_METHOD, NULL)); + set_horiz_scroll (mateconf_client_get_bool (client, KEY_PAD_HORIZ_SCROLL, NULL)); + set_touchpad_enabled (mateconf_client_get_bool (client, KEY_TOUCHPAD_ENABLED, NULL)); +#endif + + g_object_unref (client); +} + +static void +mouse_callback (MateConfClient *client, + guint cnxn_id, + MateConfEntry *entry, + MsdMouseManager *manager) +{ + if (! strcmp (entry->key, KEY_LEFT_HANDED)) { + if (entry->value->type == MATECONF_VALUE_BOOL) { + set_left_handed (manager, mateconf_value_get_bool (entry->value)); + } + } else if (! strcmp (entry->key, KEY_MOTION_ACCELERATION)) { + if (entry->value->type == MATECONF_VALUE_FLOAT) { + set_motion_acceleration (manager, mateconf_value_get_float (entry->value)); + } + } else if (! strcmp (entry->key, KEY_MOTION_THRESHOLD)) { + if (entry->value->type == MATECONF_VALUE_INT) { + set_motion_threshold (manager, mateconf_value_get_int (entry->value)); + } + } else if (! strcmp (entry->key, KEY_TOUCHPAD_DISABLE_W_TYPING)) { + if (entry->value->type == MATECONF_VALUE_BOOL) + set_disable_w_typing (manager, mateconf_value_get_bool (entry->value)); +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + } else if (! strcmp (entry->key, KEY_TAP_TO_CLICK)) { + if (entry->value->type == MATECONF_VALUE_BOOL) { + set_tap_to_click (mateconf_value_get_bool (entry->value), + mateconf_client_get_bool (client, KEY_LEFT_HANDED, NULL)); + } + } else if (! strcmp (entry->key, KEY_SCROLL_METHOD)) { + if (entry->value->type == MATECONF_VALUE_INT) { + set_edge_scroll (mateconf_value_get_int (entry->value)); + set_horiz_scroll (mateconf_client_get_bool (client, KEY_PAD_HORIZ_SCROLL, NULL)); + } + } else if (! strcmp (entry->key, KEY_PAD_HORIZ_SCROLL)) { + if (entry->value->type == MATECONF_VALUE_BOOL) + set_horiz_scroll (mateconf_value_get_bool (entry->value)); +#endif + } else if (! strcmp (entry->key, KEY_LOCATE_POINTER)) { + if (entry->value->type == MATECONF_VALUE_BOOL) { + set_locate_pointer (manager, mateconf_value_get_bool (entry->value)); + } +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + } else if (! strcmp (entry->key, KEY_TOUCHPAD_ENABLED)) { + if (entry->value->type == MATECONF_VALUE_BOOL) { + set_touchpad_enabled (mateconf_value_get_bool (entry->value)); + } +#endif + } else if (! strcmp (entry->key, KEY_DWELL_ENABLE)) { + if (entry->value->type == MATECONF_VALUE_BOOL) { + set_mousetweaks_daemon (manager, + mateconf_value_get_bool (entry->value), + mateconf_client_get_bool (client, KEY_DELAY_ENABLE, NULL)); + } + } else if (! strcmp (entry->key, KEY_DELAY_ENABLE)) { + if (entry->value->type == MATECONF_VALUE_BOOL) { + set_mousetweaks_daemon (manager, + mateconf_client_get_bool (client, KEY_DWELL_ENABLE, NULL), + mateconf_value_get_bool (entry->value)); + } + } +} + +static guint +register_config_callback (MsdMouseManager *manager, + MateConfClient *client, + const char *path, + MateConfClientNotifyFunc func) +{ + mateconf_client_add_dir (client, path, MATECONF_CLIENT_PRELOAD_ONELEVEL, NULL); + return mateconf_client_notify_add (client, path, func, manager, NULL, NULL); +} + +static void +msd_mouse_manager_init (MsdMouseManager *manager) +{ + manager->priv = MSD_MOUSE_MANAGER_GET_PRIVATE (manager); +} + +static gboolean +msd_mouse_manager_idle_cb (MsdMouseManager *manager) +{ + MateConfClient *client; + + mate_settings_profile_start (NULL); + + client = mateconf_client_get_default (); + + manager->priv->notify = + register_config_callback (manager, + client, + MATECONF_MOUSE_DIR, + (MateConfClientNotifyFunc) mouse_callback); + manager->priv->notify_a11y = + register_config_callback (manager, + client, + MATECONF_MOUSE_A11Y_DIR, + (MateConfClientNotifyFunc) mouse_callback); + manager->priv->notify_touchpad = + register_config_callback (manager, + client, + MATECONF_TOUCHPAD_DIR, + (MateConfClientNotifyFunc) mouse_callback); + manager->priv->syndaemon_spawned = FALSE; + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + set_devicepresence_handler (manager); +#endif + set_mouse_settings (manager); + set_locate_pointer (manager, mateconf_client_get_bool (client, KEY_LOCATE_POINTER, NULL)); + set_mousetweaks_daemon (manager, + mateconf_client_get_bool (client, KEY_DWELL_ENABLE, NULL), + mateconf_client_get_bool (client, KEY_DELAY_ENABLE, NULL)); + + set_disable_w_typing (manager, mateconf_client_get_bool (client, KEY_TOUCHPAD_DISABLE_W_TYPING, NULL)); +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + set_tap_to_click (mateconf_client_get_bool (client, KEY_TAP_TO_CLICK, NULL), + mateconf_client_get_bool (client, KEY_LEFT_HANDED, NULL)); + set_edge_scroll (mateconf_client_get_int (client, KEY_SCROLL_METHOD, NULL)); + set_horiz_scroll (mateconf_client_get_bool (client, KEY_PAD_HORIZ_SCROLL, NULL)); + set_touchpad_enabled (mateconf_client_get_bool (client, KEY_TOUCHPAD_ENABLED, NULL)); +#endif + + g_object_unref (client); + + mate_settings_profile_end (NULL); + + return FALSE; +} + +gboolean +msd_mouse_manager_start (MsdMouseManager *manager, + GError **error) +{ + mate_settings_profile_start (NULL); + + g_idle_add ((GSourceFunc) msd_mouse_manager_idle_cb, manager); + + mate_settings_profile_end (NULL); + + return TRUE; +} + +void +msd_mouse_manager_stop (MsdMouseManager *manager) +{ + MsdMouseManagerPrivate *p = manager->priv; + MateConfClient *client; + + g_debug ("Stopping mouse manager"); + + client = mateconf_client_get_default (); + + if (p->notify != 0) { + mateconf_client_remove_dir (client, MATECONF_MOUSE_DIR, NULL); + mateconf_client_notify_remove (client, p->notify); + p->notify = 0; + } + + if (p->notify_a11y != 0) { + mateconf_client_remove_dir (client, MATECONF_MOUSE_A11Y_DIR, NULL); + mateconf_client_notify_remove (client, p->notify_a11y); + p->notify_a11y = 0; + } + + if (p->notify_touchpad != 0) { + mateconf_client_remove_dir (client, MATECONF_TOUCHPAD_DIR, NULL); + mateconf_client_notify_remove (client, p->notify_touchpad); + p->notify_touchpad = 0; + } + + g_object_unref (client); + + set_locate_pointer (manager, FALSE); + +#ifdef HAVE_X11_EXTENSIONS_XINPUT_H + gdk_window_remove_filter (NULL, devicepresence_filter, manager); +#endif +} + +static void +msd_mouse_manager_finalize (GObject *object) +{ + MsdMouseManager *mouse_manager; + + g_return_if_fail (object != NULL); + g_return_if_fail (MSD_IS_MOUSE_MANAGER (object)); + + mouse_manager = MSD_MOUSE_MANAGER (object); + + g_return_if_fail (mouse_manager->priv != NULL); + + G_OBJECT_CLASS (msd_mouse_manager_parent_class)->finalize (object); +} + +MsdMouseManager * +msd_mouse_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (MSD_TYPE_MOUSE_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return MSD_MOUSE_MANAGER (manager_object); +} |