/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2007 William Jon McCann * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_X11_EXTENSIONS_XINPUT_H #include #include #endif #include #include #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); }