From 78353322ce1070ee1fd7bb5367e2c4dd43928f42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Andreas=20M=C3=BCller?= <schnitzeltony@googlemail.com>
Date: Thu, 20 Dec 2012 16:58:19 +0100
Subject: [PATCH 5/5] pointers: detect a change of pointer-device used and set
 "touchscreen-pointer" as found in xfconf
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

To detect pointer device in use the DevicePresence-, DeviceMotionNotify- and
DeviceButtonPress-events are handled. Tests showed that by repopenning and
closing the pointer devices (e.g when another setting was modified), the events
stopped working. Therefore all pointer devices are opened only once and managed
in a hash table.

Signed-off-by: Andreas Müller <schnitzeltony@googlemail.com>
Upstream-Status: Submitted [1]
[1] https://bugzilla.xfce.org/show_bug.cgi?id=9474
---
 xfsettingsd/pointers.c | 342 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 225 insertions(+), 117 deletions(-)

diff --git a/xfsettingsd/pointers.c b/xfsettingsd/pointers.c
index cfa56e2..41c8bba 100644
--- a/xfsettingsd/pointers.c
+++ b/xfsettingsd/pointers.c
@@ -66,11 +66,9 @@ static void             xfce_pointers_helper_channel_property_changed (XfconfCha
                                                                        const gchar        *property_name,
                                                                        const GValue       *value,
                                                                        XfcePointersHelper *helper);
-#ifdef DEVICE_HOTPLUGGING
 static GdkFilterReturn  xfce_pointers_helper_event_filter             (GdkXEvent          *xevent,
                                                                        GdkEvent           *gdk_event,
                                                                        gpointer            user_data);
-#endif
 static void             xfce_pointers_helper_set_property             (GObject              *object,
                                                                        guint                 prop_id,
                                                                        const GValue         *value,
@@ -96,8 +94,11 @@ struct _XfcePointersHelper
     GPid           syndaemon_pid;
 #endif
 
+    GHashTable    *pointer_devices;
+    XID            last_pointer_active;
+    gint           device_motion_event_type;
+    gint           device_button_press_event_type;
 #ifdef DEVICE_HOTPLUGGING
-    /* device presence event type */
     gint           device_presence_event_type;
 #endif
 };
@@ -117,6 +118,15 @@ G_DEFINE_TYPE (XfcePointersHelper, xfce_pointers_helper, G_TYPE_OBJECT);
 
 
 
+typedef struct
+{
+    XDevice     *device;
+    gchar*      name;
+}
+XfcePointerDeviceData;
+
+
+
 static void
 xfce_pointers_helper_class_init (XfcePointersHelperClass *klass)
 {
@@ -147,6 +157,18 @@ xfce_pointers_helper_init (XfcePointersHelper *helper)
 
 
 static void
+xfce_pointers_device_close (gpointer data)
+{
+    XfcePointerDeviceData *device_data = data;
+
+    XCloseDevice (GDK_DISPLAY (), device_data->device);
+    g_free (device_data->name);
+    g_free (device_data);
+}
+
+
+
+static void
 xfce_pointers_helper_constructed (GObject *object)
 {
     XfcePointersHelper *helper = XFCE_POINTERS_HELPER (object);
@@ -185,6 +207,10 @@ xfce_pointers_helper_constructed (GObject *object)
         helper->channel = xfconf_channel_get ("pointers");
 
         /* restore the pointer devices */
+        helper->pointer_devices = g_hash_table_new_full (g_int_hash,
+                                                         g_int_equal,
+                                                         g_free,
+                                                         xfce_pointers_device_close);
         xfce_pointers_helper_restore_devices (helper, NULL);
 
         /* monitor the channel */
@@ -194,21 +220,20 @@ xfce_pointers_helper_constructed (GObject *object)
         /* launch syndaemon if required */
         xfce_pointers_helper_syndaemon_check (helper);
 
-#ifdef DEVICE_HOTPLUGGING
         if (G_LIKELY (xdisplay != NULL))
         {
+#ifdef DEVICE_HOTPLUGGING
             /* monitor device changes */
             gdk_error_trap_push ();
             DevicePresence (xdisplay, helper->device_presence_event_type, event_class);
             XSelectExtensionEvent (xdisplay, RootWindow (xdisplay, DefaultScreen (xdisplay)), &event_class, 1);
 
             /* add an event filter */
-            if (gdk_error_trap_pop () == 0)
-                gdk_window_add_filter (NULL, xfce_pointers_helper_event_filter, helper);
-            else
+            if (gdk_error_trap_pop () != 0)
                 g_warning ("Failed to create device filter");
-        }
 #endif
+            gdk_window_add_filter (NULL, xfce_pointers_helper_event_filter, helper);
+        }
     }
 }
 
@@ -238,7 +263,10 @@ xfce_pointers_helper_set_property (GObject      *object,
 static void
 xfce_pointers_helper_finalize (GObject *object)
 {
-    xfce_pointers_helper_syndaemon_stop (XFCE_POINTERS_HELPER (object));
+    XfcePointersHelper* helper = XFCE_POINTERS_HELPER (object);
+
+    xfce_pointers_helper_syndaemon_stop (helper);
+    g_hash_table_destroy (helper->pointer_devices);
 
     (*G_OBJECT_CLASS (xfce_pointers_helper_parent_class)->finalize) (object);
 }
@@ -267,19 +295,19 @@ static void
 xfce_pointers_helper_syndaemon_check (XfcePointersHelper *helper)
 {
 #ifdef DEVICE_PROPERTIES
-    Display     *xdisplay = GDK_DISPLAY ();
-    XDeviceInfo *device_list;
-    XDevice     *device;
-    gint         n, ndevices;
-    Atom         touchpad_type;
-    Atom         touchpad_off_prop;
-    Atom        *props;
-    gint         i, nprops;
-    gboolean     have_synaptics = FALSE;
-    gdouble      disable_duration;
-    gchar        disable_duration_string[64];
-    gchar       *args[] = { "syndaemon", "-i", disable_duration_string, "-K", "-R", NULL };
-    GError      *error = NULL;
+    Display               *xdisplay = GDK_DISPLAY ();
+    XDeviceInfo           *device_list;
+    XfcePointerDeviceData *device_data;
+    gint                   n, ndevices;
+    Atom                   touchpad_type;
+    Atom                   touchpad_off_prop;
+    Atom                  *props;
+    gint                   i, nprops;
+    gboolean               have_synaptics = FALSE;
+    gdouble                disable_duration;
+    gchar                  disable_duration_string[64];
+    gchar                 *args[] = { "syndaemon", "-i", disable_duration_string, "-K", "-R", NULL };
+    GError                *error = NULL;
 
     /* only stop a running daemon */
     if (!xfconf_channel_get_bool (helper->channel, "/DisableTouchpadWhileTyping", FALSE))
@@ -299,17 +327,16 @@ xfce_pointers_helper_syndaemon_check (XfcePointersHelper *helper)
         if (device_list[n].type != touchpad_type)
             continue;
 
-        gdk_error_trap_push ();
-        device = XOpenDevice (xdisplay, device_list[n].id);
-        if (gdk_error_trap_pop () != 0 || device == NULL)
+        device_data = g_hash_table_lookup (helper->pointer_devices, &device_list[n].id);
+        if (device_data == NULL)
         {
-            g_critical ("Unable to open device %s", device_list[n].name);
+            g_critical ("xfce_pointers_helper_syndaemon_check: Unable to find device %s / ID %i in hash table", device_list[n].name, device_list[n].id);
             break;
         }
 
         /* look for the Synaptics Off property */
         gdk_error_trap_push ();
-        props = XListDeviceProperties (xdisplay, device, &nprops);
+        props = XListDeviceProperties (xdisplay, device_data->device, &nprops);
         if (gdk_error_trap_pop () == 0
             && props != NULL)
         {
@@ -319,8 +346,6 @@ xfce_pointers_helper_syndaemon_check (XfcePointersHelper *helper)
             XFree (props);
         }
 
-        XCloseDevice (xdisplay, device);
-
         if (have_synaptics)
             break;
     }
@@ -847,24 +872,56 @@ xfce_pointers_helper_change_properties (gpointer key,
 
 
 static void
+xfce_pointers_helper_change_current_device (XfcePointersHelper *helper,
+                                            XID *xid)
+{
+    XfcePointerDeviceData *device_data;
+    gchar*                 prop;
+    GValue                 bool_val = { 0, };
+
+    helper->last_pointer_active = *xid;
+    device_data = g_hash_table_lookup (helper->pointer_devices, xid);
+    if (device_data == NULL)
+    {
+        g_critical ("Unable to find device ID %i in hash table", *xid);
+        return;
+    }
+    if (G_LIKELY (G_IS_OBJECT (helper->xsettings_helper)))
+    {
+        prop = g_strconcat ("/", device_data->name, "/TouchscreenPointer", NULL);
+        g_value_init (&bool_val, G_TYPE_BOOLEAN);
+        g_value_set_boolean (&bool_val,
+                             xfconf_channel_get_bool (helper->channel, prop, FALSE));
+        g_object_set_property (helper->xsettings_helper, "touchscreen-pointer", &bool_val);
+        g_value_unset (&bool_val);
+        g_free (prop);
+    }
+    else
+        g_critical ("xsettings_helper was not properly set");
+}
+
+
+static void
 xfce_pointers_helper_restore_devices (XfcePointersHelper *helper,
                                       XID                *xid)
 {
-    Display         *xdisplay = GDK_DISPLAY ();
-    XDeviceInfo     *device_list, *device_info;
-    gint             n, ndevices;
-    XDevice         *device;
-    gchar           *device_name;
-    gchar            prop[256];
-    gboolean         right_handed;
-    gboolean         reverse_scrolling;
-    gint             threshold;
-    gdouble          acceleration;
+    Display               *xdisplay = GDK_DISPLAY ();
+    XDeviceInfo           *device_list, *device_info;
+    gint                   n, ndevices;
+    XDevice               *device;
+    XfcePointerDeviceData *device_data;
+    gchar                 *device_name;
+    gchar                  prop[256];
+    gboolean               right_handed;
+    gboolean               reverse_scrolling;
+    gint                   threshold;
+    gdouble                acceleration;
+    XEventClass            event_classes[2];
 #ifdef DEVICE_PROPERTIES
-    GHashTable      *props;
-    XfcePointerData  pointer_data;
+    GHashTable            *props;
+    XfcePointerData        pointer_data;
 #endif
-    const gchar     *mode;
+    const gchar           *mode;
 
     gdk_error_trap_push ();
     device_list = XListInputDevices (xdisplay, &ndevices);
@@ -874,86 +931,110 @@ xfce_pointers_helper_restore_devices (XfcePointersHelper *helper,
         return;
     }
 
+    device_data = NULL;
     for (n = 0; n < ndevices; n++)
     {
-        /* filter the pointer devices */
+        /* filter the physical pointer devices */
         device_info = &device_list[n];
         if (device_info->use != IsXExtensionPointer
-            || device_info->name == NULL)
+            || device_info->name == NULL
+            || g_str_has_prefix (device_info->name, "Virtual core XTEST"))
             continue;
 
         /* filter out the device if one is set */
         if (xid != NULL && device_info->id != *xid)
             continue;
 
-        /* open the device */
-        gdk_error_trap_push ();
-        device = XOpenDevice (xdisplay, device_info->id);
-        if (gdk_error_trap_pop () != 0 || device == NULL)
+        device_data = g_hash_table_lookup (helper->pointer_devices, &device_info->id);
+        if (device_data == NULL)
         {
-            g_critical ("Unable to open device %s", device_info->name);
-            continue;
-        }
+            /* open the device and insert to hash */
+            gdk_error_trap_push ();
+            device = XOpenDevice (xdisplay, device_info->id);
+            if (G_UNLIKELY(gdk_error_trap_pop () != 0 || device == NULL))
+            {
+                g_critical ("Unable to open device %s / ID: %i", device_info->name, device_info->id);
+                continue;
+            }
 
-        /* create a valid xfconf property name for the device */
-        device_name = xfce_pointers_helper_device_xfconf_name (device_info->name);
+            /* create a valid xfconf property name for the device */
+            device_name = xfce_pointers_helper_device_xfconf_name (device_info->name);
 
-        /* read buttonmap properties */
-        g_snprintf (prop, sizeof (prop), "/%s/RightHanded", device_name);
-        right_handed = xfconf_channel_get_bool (helper->channel, prop, -1);
+            /* add device to our list */
+            device_data = g_new (XfcePointerDeviceData, 1);
+            device_data->device = device;
+            device_data->name = device_name;
+            g_hash_table_insert (helper->pointer_devices, g_memdup (&device_info->id, sizeof(device_info->id)), device_data);
 
-        g_snprintf (prop, sizeof (prop), "/%s/ReverseScrolling", device_name);
-        reverse_scrolling = xfconf_channel_get_bool (helper->channel, prop, -1);
+            /* catch motion event / button-press for new device */
+            gdk_error_trap_push ();
+            DeviceMotionNotify (device, helper->device_motion_event_type, event_classes[0]);
+            DeviceButtonPress (device, helper->device_button_press_event_type, event_classes[1]);
+            XSelectExtensionEvent (xdisplay, RootWindow (xdisplay, DefaultScreen (xdisplay)), event_classes, 2);
+            if (G_UNLIKELY (gdk_error_trap_pop () != 0))
+               g_critical ("Unable to register DeviceButtonPress/DeviceMotionNotify for %i", device_info->id);
 
-        if (right_handed != -1 || reverse_scrolling != -1)
-        {
-            xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay,
+
+            /* read buttonmap properties */
+            g_snprintf (prop, sizeof (prop), "/%s/RightHanded", device_name);
+            right_handed = xfconf_channel_get_bool (helper->channel, prop, -1);
+
+            g_snprintf (prop, sizeof (prop), "/%s/ReverseScrolling", device_name);
+            reverse_scrolling = xfconf_channel_get_bool (helper->channel, prop, -1);
+
+            if (right_handed != -1 || reverse_scrolling != -1)
+            {
+                xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay,
                                                         right_handed, reverse_scrolling);
-        }
+            }
 
-        /* read feedback settings */
-        g_snprintf (prop, sizeof (prop), "/%s/Threshold", device_name);
-        threshold = xfconf_channel_get_int (helper->channel, prop, -1);
+            /* read feedback settings */
+            g_snprintf (prop, sizeof (prop), "/%s/Threshold", device_name);
+            threshold = xfconf_channel_get_int (helper->channel, prop, -1);
 
-        g_snprintf (prop, sizeof (prop), "/%s/Acceleration", device_name);
-        acceleration = xfconf_channel_get_double (helper->channel, prop, -1.00);
+            g_snprintf (prop, sizeof (prop), "/%s/Acceleration", device_name);
+            acceleration = xfconf_channel_get_double (helper->channel, prop, -1.00);
 
-        if (threshold != -1 || acceleration != -1.00)
-        {
-            xfce_pointers_helper_change_feedback (device_info, device, xdisplay,
-                                                  threshold, acceleration);
-        }
+            if (threshold != -1 || acceleration != -1.00)
+            {
+                xfce_pointers_helper_change_feedback (device_info, device, xdisplay,
+                                                      threshold, acceleration);
+            }
 
-        /* read mode settings */
-        g_snprintf (prop, sizeof (prop), "/%s/Mode", device_name);
-        mode =  xfconf_channel_get_string  (helper->channel, prop, NULL);
+            /* read mode settings */
+            g_snprintf (prop, sizeof (prop), "/%s/Mode", device_name);
+            mode =  xfconf_channel_get_string  (helper->channel, prop, NULL);
 
-        if (mode != NULL)
-            xfce_pointers_helper_change_mode (device_info, device, xdisplay, mode);
+            if (mode != NULL)
+                xfce_pointers_helper_change_mode (device_info, device, xdisplay, mode);
 
 #ifdef DEVICE_PROPERTIES
-        /* set device properties */
-        g_snprintf (prop, sizeof (prop), "/%s/Properties", device_name);
-        props = xfconf_channel_get_properties (helper->channel, prop);
+            /* set device properties */
+            g_snprintf (prop, sizeof (prop), "/%s/Properties", device_name);
+            props = xfconf_channel_get_properties (helper->channel, prop);
 
-        if (props != NULL)
-        {
-            pointer_data.xdisplay = xdisplay;
-            pointer_data.device = device;
-            pointer_data.device_info = device_info;
-            pointer_data.prop_name_len = strlen (prop) + 1;
+            if (props != NULL)
+            {
+                pointer_data.xdisplay = xdisplay;
+                pointer_data.device = device;
+                pointer_data.device_info = device_info;
+                pointer_data.prop_name_len = strlen (prop) + 1;
 
-            g_hash_table_foreach (props, xfce_pointers_helper_change_properties, &pointer_data);
+                g_hash_table_foreach (props, xfce_pointers_helper_change_properties, &pointer_data);
 
-            g_hash_table_destroy (props);
-        }
+                g_hash_table_destroy (props);
+            }
 #endif
-
-        g_free (device_name);
-        XCloseDevice (xdisplay, device);
+        }
     }
-
     XFreeDeviceList (device_list);
+    if (G_LIKELY (device_data != NULL))
+    {
+        if (helper->last_pointer_active != device_data->device->device_id)
+            xfce_pointers_helper_change_current_device (helper, &device_data->device->device_id);
+    }
+    else
+        g_critical("no device selected in xfce_pointers_helper_restore_devices");
 }
 
 
@@ -964,12 +1045,12 @@ xfce_pointers_helper_channel_property_changed (XfconfChannel      *channel,
                                                const GValue       *value,
                                                XfcePointersHelper *helper)
 {
-    Display      *xdisplay = GDK_DISPLAY ();
-    XDeviceInfo  *device_list, *device_info;
-    XDevice      *device;
-    gint          n, ndevices;
-    gchar       **names;
-    gchar        *device_name;
+    Display               *xdisplay = GDK_DISPLAY ();
+    XDeviceInfo           *device_list, *device_info;
+    XfcePointerDeviceData *device_data;
+    gint                   n, ndevices;
+    gchar                **names;
+    gchar                 *device_name;
 
     if (G_UNLIKELY (property_name == NULL))
          return;
@@ -1000,63 +1081,72 @@ xfce_pointers_helper_channel_property_changed (XfconfChannel      *channel,
             /* filter the pointer devices */
             device_info = &device_list[n];
             if (device_info->use != IsXExtensionPointer
-                || device_info->name == NULL)
+                || device_info->name == NULL
+                || g_str_has_prefix (device_info->name, "Virtual core XTEST"))
                 continue;
 
             /* search the device name */
             device_name = xfce_pointers_helper_device_xfconf_name (device_info->name);
             if (strcmp (names[0], device_name) == 0)
             {
-                /* open the device */
-                gdk_error_trap_push ();
-                device = XOpenDevice (xdisplay, device_info->id);
-                if (gdk_error_trap_pop () != 0 || device == NULL)
+                /* find the device */
+                device_data = g_hash_table_lookup (helper->pointer_devices, &device_info->id);
+                if (device_data == NULL)
                 {
-                    g_critical ("Unable to open device %s", device_info->name);
+                    g_critical ("xfce_pointers_helper_channel_property_changed: Unable to find device %s / ID %i in hash table", device_info->name, device_info->id);
                     continue;
                 }
 
                 /* check the property that requires updating */
                 if (strcmp (names[1], "RightHanded") == 0)
                 {
-                    xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay,
+                    xfce_pointers_helper_change_button_mapping (device_info, device_data->device, xdisplay,
                                                                 g_value_get_boolean (value), -1);
                 }
                 else if (strcmp (names[1], "ReverseScrolling") == 0)
                 {
-                    xfce_pointers_helper_change_button_mapping (device_info, device, xdisplay,
+                    xfce_pointers_helper_change_button_mapping (device_info, device_data->device, xdisplay,
                                                                 -1, g_value_get_boolean (value));
                 }
                 else if (strcmp (names[1], "Threshold") == 0)
                 {
-                    xfce_pointers_helper_change_feedback (device_info, device, xdisplay,
+                    xfce_pointers_helper_change_feedback (device_info, device_data->device, xdisplay,
                                                           g_value_get_int (value), -2.00);
                 }
                 else if (strcmp (names[1], "Acceleration") == 0)
                 {
-                    xfce_pointers_helper_change_feedback (device_info, device, xdisplay,
+                    xfce_pointers_helper_change_feedback (device_info, device_data->device, xdisplay,
                                                           -2, g_value_get_double (value));
                 }
 #ifdef DEVICE_PROPERTIES
                 else if (strcmp (names[1], "Properties") == 0)
                 {
-                    xfce_pointers_helper_change_property (device_info, device, xdisplay,
+                    xfce_pointers_helper_change_property (device_info, device_data->device, xdisplay,
                                                           names[2], value);
                 }
 #endif
                 else if (strcmp (names[1], "Mode") == 0)
                 {
-                    xfce_pointers_helper_change_mode (device_info, device, xdisplay,
+                    xfce_pointers_helper_change_mode (device_info, device_data->device, xdisplay,
                                                       g_value_get_string (value));
                 }
+                else if (strcmp (names[1], "TouchscreenPointer") == 0)
+                {
+                   /* only hide if the current device's property was changed */
+                   if (device_info->id == helper->last_pointer_active)
+                   {
+                     if (G_LIKELY (G_IS_OBJECT (helper->xsettings_helper)))
+                         g_object_set_property (helper->xsettings_helper, "touchscreen-pointer", value);
+                     else
+                         g_critical ("xsettings_helper was not properly set");
+                   }
+                }
                 else
                 {
                     g_warning ("Unknown property %s set for device %s",
                                property_name, device_info->name);
                 }
 
-                XCloseDevice (xdisplay, device);
-
                 /* stop searching */
                 n = ndevices;
             }
@@ -1072,26 +1162,44 @@ xfce_pointers_helper_channel_property_changed (XfconfChannel      *channel,
 
 
 
-#ifdef DEVICE_HOTPLUGGING
 static GdkFilterReturn
 xfce_pointers_helper_event_filter (GdkXEvent *xevent,
                                    GdkEvent  *gdk_event,
                                    gpointer   user_data)
 {
+    XDevicePresenceNotifyEvent *dpn_event;
     XEvent                     *event = xevent;
-    XDevicePresenceNotifyEvent *dpn_event = xevent;
     XfcePointersHelper         *helper = XFCE_POINTERS_HELPER (user_data);
 
-    if (event->type == helper->device_presence_event_type)
+    /* Comparison for device changed is done here redundantly to prevent call
+     * on every mouse move.
+     */
+    if (event->type == helper->device_motion_event_type
+             && helper->last_pointer_active != ((XDeviceMotionEvent*)xevent)->deviceid)
+        xfce_pointers_helper_change_current_device (helper, &((XDeviceMotionEvent*)xevent)->deviceid);
+    else if (event->type == helper->device_button_press_event_type
+             && helper->last_pointer_active != ((XDeviceButtonEvent*)xevent)->deviceid)
+        xfce_pointers_helper_change_current_device (helper, &((XDeviceButtonEvent*)xevent)->deviceid);
+
+#ifdef DEVICE_HOTPLUGGING
+    /* handle device add/remove */
+    else if (event->type == helper->device_presence_event_type)
     {
+        dpn_event = xevent;
         /* restore device settings */
         if (dpn_event->devchange == DeviceAdded)
             xfce_pointers_helper_restore_devices (helper, &dpn_event->deviceid);
+        else if(dpn_event->devchange == DeviceRemoved)
+            /* we could try to find a remaining pointer to set that as active
+             * one but that might not fit and as soon as the user works with
+             * another pointer we are changing to correct one.
+             */
+            g_hash_table_remove (helper->pointer_devices, &dpn_event->deviceid);
 
         /* check if we need to launch syndaemon */
         xfce_pointers_helper_syndaemon_check (helper);
     }
+#endif
 
     return GDK_FILTER_CONTINUE;
 }
-#endif
-- 
1.8.3.1

