]> Pileus Git - ~andy/gtk/commitdiff
xi2: Improve device hierarchy handling
authorCarlos Garnacho <carlosg@gnome.org>
Mon, 13 Dec 2010 11:20:34 +0000 (12:20 +0100)
committerCarlos Garnacho <carlosg@gnome.org>
Wed, 15 Dec 2010 02:17:59 +0000 (03:17 +0100)
The xi2 device manager now handles slaves being detached and/or
attached to a master.

gdk_device_list_slaves() has been added so it is possible to
know how slaves relate with masters. The other backends (X11 and not)
don't neeed to to anything special here since their hierarchy is
fully flat.

gdk/gdkdevice.c
gdk/gdkdevice.h
gdk/gdkdevicemanager.c
gdk/gdkdeviceprivate.h
gdk/x11/gdkdevicemanager-xi2.c
gdk/x11/gdkdevicemanager-xi2.h

index dc66f3bdddc70bd2d550865335915d9ba0c08852..1330d81bf3e7f524ec35f08b66bca1eb7ed94f97 100644 (file)
@@ -59,7 +59,13 @@ struct _GdkDevicePrivate
   GdkDeviceKey *keys;
   GdkDeviceManager *device_manager;
   GdkDisplay *display;
+
+  /* Paired master for master,
+   * associated master for slaves
+   */
   GdkDevice *associated;
+
+  GList *slaves;
   GdkDeviceType type;
   GArray *axes;
 };
@@ -290,6 +296,9 @@ gdk_device_dispose (GObject *object)
   device = GDK_DEVICE (object);
   priv = device->priv;
 
+  if (priv->type == GDK_DEVICE_TYPE_SLAVE)
+    _gdk_device_remove_slave (priv->associated, device);
+
   if (priv->associated)
     {
       _gdk_device_set_associated_device (priv->associated, NULL);
@@ -820,6 +829,24 @@ gdk_device_get_associated_device (GdkDevice *device)
   return priv->associated;
 }
 
+static void
+_gdk_device_set_device_type (GdkDevice     *device,
+                             GdkDeviceType  type)
+{
+  GdkDevicePrivate *priv;
+
+  priv = device->priv;
+
+  if (priv->type != type)
+    {
+      priv->type = type;
+
+      g_print ("Setting device type to %d\n", type);
+
+      g_object_notify (G_OBJECT (device), "type");
+    }
+}
+
 void
 _gdk_device_set_associated_device (GdkDevice *device,
                                    GdkDevice *associated)
@@ -842,6 +869,81 @@ _gdk_device_set_associated_device (GdkDevice *device,
 
   if (associated)
     priv->associated = g_object_ref (associated);
+
+  if (priv->type != GDK_DEVICE_TYPE_MASTER)
+    {
+      if (priv->associated)
+        _gdk_device_set_device_type (device, GDK_DEVICE_TYPE_SLAVE);
+      else
+        _gdk_device_set_device_type (device, GDK_DEVICE_TYPE_FLOATING);
+    }
+}
+
+/**
+ * gdk_device_list_slave_devices:
+ * @device: a #GdkDevice
+ *
+ * If the device if of type %GDK_DEVICE_TYPE_MASTER, it will return
+ * the list of slave devices attached to it, otherwise it will return
+ * %NULL
+ *
+ * Returns: (transfer container): the list of slave devices, or %NULL. The
+ *          list must be freed with g_list_free(), the contents of the list
+ *          are owned by GTK+ and should not be freed.
+ **/
+GList *
+gdk_device_list_slave_devices (GdkDevice *device)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_val_if_fail (GDK_IS_DEVICE (device), NULL);
+  g_return_val_if_fail (gdk_device_get_device_type (device) != GDK_DEVICE_TYPE_MASTER, NULL);
+
+  priv = device->priv;
+
+  return g_list_copy (priv->slaves);
+}
+
+void
+_gdk_device_add_slave (GdkDevice *device,
+                       GdkDevice *slave)
+{
+  GdkDevicePrivate *priv;
+
+  g_return_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER);
+  g_return_if_fail (gdk_device_get_device_type (slave) != GDK_DEVICE_TYPE_MASTER);
+
+  priv = device->priv;
+
+  g_print ("Adding %s ---> %s\n",
+           gdk_device_get_name (slave),
+           gdk_device_get_name (device));
+
+  if (!g_list_find (priv->slaves, slave))
+    priv->slaves = g_list_prepend (priv->slaves, slave);
+}
+
+void
+_gdk_device_remove_slave (GdkDevice *device,
+                          GdkDevice *slave)
+{
+  GdkDevicePrivate *priv;
+  GList *elem;
+
+  g_return_if_fail (gdk_device_get_device_type (device) == GDK_DEVICE_TYPE_MASTER);
+  g_return_if_fail (gdk_device_get_device_type (slave) != GDK_DEVICE_TYPE_MASTER);
+
+  priv = device->priv;
+  elem = g_list_find (priv->slaves, slave);
+
+  if (!elem)
+    return;
+
+  g_print ("Removing %s ---> %s\n",
+           gdk_device_get_name (slave),
+           gdk_device_get_name (device));
+
+  priv->slaves = g_list_delete_link (priv->slaves, elem);
 }
 
 /**
index 27375f7621579bf21f5db94e1197bc91d37cd006..2c797a9fc820343f870b019429ddecd756c6b30f 100644 (file)
@@ -222,6 +222,7 @@ gboolean gdk_device_get_axis     (GdkDevice         *device,
 GdkDisplay * gdk_device_get_display (GdkDevice      *device);
 
 GdkDevice  * gdk_device_get_associated_device (GdkDevice     *device);
+GList *      gdk_device_list_slave_devices    (GdkDevice     *device);
 
 GdkDeviceType gdk_device_get_device_type (GdkDevice *device);
 
index fced01ff491d20722cdaa6a0dac5cffbbaa55ab4..e282036424933383352421e561642158dd9d63c2 100644 (file)
@@ -183,12 +183,16 @@ gdk_device_manager_class_init (GdkDeviceManagerClass *klass)
    * @device_manager: the object on which the signal is emitted
    * @device: the #GdkDevice that changed.
    *
-   * The ::device-changed signal is emitted either when some
-   * #GdkDevice has changed the number of either axes or keys.
-   * For example In X this will normally happen when the slave
-   * device routing events through the master device changes,
-   * in that case the master device will change to reflect the
-   * new slave device axes and keys.
+   * The ::device-changed signal is emitted whenever a device
+   * has changed in the hierarchy, either slave devices being
+   * disconnected from their master device or connected to
+   * another one, or master devices being added or removed
+   * a slave device.
+   *
+   * If a slave device is detached from all master devices
+   * (gdk_device_get_associated_device() returns %NULL), its
+   * #GdkDeviceType will change to %GDK_DEVICE_TYPE_FLOATING,
+   * if it's attached, it will change to %GDK_DEVICE_TYPE_SLAVE.
    */
   signals [DEVICE_CHANGED] =
     g_signal_new (g_intern_static_string ("device-changed"),
index d878ae634d991c83f9d5a5b480ea9ed5b8c4f28a..f7318620d8371b4da3e8a5ac05e62bf78a013504 100644 (file)
@@ -125,6 +125,10 @@ GdkTimeCoord ** _gdk_device_allocate_history  (GdkDevice *device,
 
 void _gdk_input_check_extension_events (GdkDevice *device);
 
+void _gdk_device_add_slave (GdkDevice *device,
+                            GdkDevice *slave);
+void _gdk_device_remove_slave (GdkDevice *device,
+                               GdkDevice *slave);
 
 G_END_DECLS
 
index bdfeac9953426e07e18a862d63460cd77835e6ee..ee3461c3fe88c2e1792dfb9e63ca4fbba62defa4 100644 (file)
@@ -263,15 +263,29 @@ add_device (GdkDeviceManagerXI2 *device_manager,
 
   if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard)
     device_manager->master_devices = g_list_append (device_manager->master_devices, device);
-  else if (dev->use == XISlavePointer || dev->use == XISlaveKeyboard)
+  else if (dev->use == XISlavePointer || dev->use == XISlaveKeyboard || dev->use == XIFloatingSlave)
     device_manager->slave_devices = g_list_append (device_manager->slave_devices, device);
-  else if (dev->use == XIFloatingSlave)
-    device_manager->floating_devices = g_list_append (device_manager->floating_devices, device);
   else
     g_warning ("Unhandled device: %s\n", gdk_device_get_name (device));
 
   if (emit_signal)
-    g_signal_emit_by_name (device_manager, "device-added", device);
+    {
+      if (dev->use == XISlavePointer || dev->use == XISlaveKeyboard)
+        {
+          GdkDevice *master;
+
+          /* The device manager is already constructed, then
+           * keep the hierarchy coherent for the added device.
+           */
+          master = g_hash_table_lookup (device_manager->id_table,
+                                        GINT_TO_POINTER (dev->attachment));
+
+          _gdk_device_set_associated_device (device, master);
+          _gdk_device_add_slave (master, device);
+        }
+
+      g_signal_emit_by_name (device_manager, "device-added", device);
+    }
 
   return device;
 }
@@ -289,7 +303,6 @@ remove_device (GdkDeviceManagerXI2 *device_manager,
     {
       device_manager->master_devices = g_list_remove (device_manager->master_devices, device);
       device_manager->slave_devices = g_list_remove (device_manager->slave_devices, device);
-      device_manager->floating_devices = g_list_remove (device_manager->floating_devices, device);
 
       g_signal_emit_by_name (device_manager, "device-removed", device);
 
@@ -301,7 +314,7 @@ remove_device (GdkDeviceManagerXI2 *device_manager,
 }
 
 static void
-relate_devices (gpointer key,
+relate_masters (gpointer key,
                 gpointer value,
                 gpointer user_data)
 {
@@ -316,13 +329,29 @@ relate_devices (gpointer key,
   _gdk_device_set_associated_device (relative, device);
 }
 
+static void
+relate_slaves (gpointer key,
+               gpointer value,
+               gpointer user_data)
+{
+  GdkDeviceManagerXI2 *device_manager;
+  GdkDevice *slave, *master;
+
+  device_manager = user_data;
+  slave = g_hash_table_lookup (device_manager->id_table, key);
+  master = g_hash_table_lookup (device_manager->id_table, value);
+
+  _gdk_device_set_associated_device (slave, master);
+  _gdk_device_add_slave (master, slave);
+}
+
 static void
 gdk_device_manager_xi2_constructed (GObject *object)
 {
   GdkDeviceManagerXI2 *device_manager_xi2;
   GdkDisplay *display;
   GdkScreen *screen;
-  GHashTable *relations;
+  GHashTable *masters, *slaves;
   Display *xdisplay;
   XIDeviceInfo *info, *dev;
   int ndevices, i;
@@ -332,7 +361,9 @@ gdk_device_manager_xi2_constructed (GObject *object)
   device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (object);
   display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
   xdisplay = GDK_DISPLAY_XDISPLAY (display);
-  relations = g_hash_table_new (NULL, NULL);
+
+  masters = g_hash_table_new (NULL, NULL);
+  slaves = g_hash_table_new (NULL, NULL);
 
   info = XIQueryDevice(xdisplay, XIAllDevices, &ndevices);
 
@@ -347,7 +378,14 @@ gdk_device_manager_xi2_constructed (GObject *object)
       if (dev->use == XIMasterPointer ||
           dev->use == XIMasterKeyboard)
         {
-          g_hash_table_insert (relations,
+          g_hash_table_insert (masters,
+                               GINT_TO_POINTER (dev->deviceid),
+                               GINT_TO_POINTER (dev->attachment));
+        }
+      else if (dev->use == XISlavePointer ||
+               dev->use == XISlaveKeyboard)
+        {
+          g_hash_table_insert (slaves,
                                GINT_TO_POINTER (dev->deviceid),
                                GINT_TO_POINTER (dev->attachment));
         }
@@ -356,8 +394,11 @@ gdk_device_manager_xi2_constructed (GObject *object)
   XIFreeDeviceInfo(info);
 
   /* Stablish relationships between devices */
-  g_hash_table_foreach (relations, relate_devices, object);
-  g_hash_table_destroy (relations);
+  g_hash_table_foreach (masters, relate_masters, object);
+  g_hash_table_destroy (masters);
+
+  g_hash_table_foreach (slaves, relate_slaves, object);
+  g_hash_table_destroy (slaves);
 
   /* Connect to hierarchy change events */
   screen = gdk_display_get_default_screen (display);
@@ -388,10 +429,6 @@ gdk_device_manager_xi2_dispose (GObject *object)
   g_list_free (device_manager_xi2->slave_devices);
   device_manager_xi2->slave_devices = NULL;
 
-  g_list_foreach (device_manager_xi2->floating_devices, (GFunc) g_object_unref, NULL);
-  g_list_free (device_manager_xi2->floating_devices);
-  device_manager_xi2->floating_devices = NULL;
-
   if (device_manager_xi2->id_table)
     {
       g_hash_table_destroy (device_manager_xi2->id_table);
@@ -416,10 +453,21 @@ gdk_device_manager_xi2_list_devices (GdkDeviceManager *device_manager,
       list = device_manager_xi2->master_devices;
       break;
     case GDK_DEVICE_TYPE_SLAVE:
-      list = device_manager_xi2->slave_devices;
-      break;
     case GDK_DEVICE_TYPE_FLOATING:
-      list = device_manager_xi2->floating_devices;
+      {
+        GList *devs = device_manager_xi2->slave_devices;
+
+        while (devs)
+          {
+            GdkDevice *dev;
+
+            dev = devs->data;
+            devs = devs->next;
+
+            if (type == gdk_device_get_device_type (dev))
+              list = g_list_prepend (list, dev);
+          }
+      }
       break;
     default:
       g_assert_not_reached ();
@@ -457,32 +505,61 @@ static void
 handle_hierarchy_changed (GdkDeviceManagerXI2 *device_manager,
                           XIHierarchyEvent    *ev)
 {
+  GdkDisplay *display;
+  Display *xdisplay;
   GdkDevice *device;
+  XIDeviceInfo *info;
+  int ndevices;
   gint i;
 
-  /* We only care about enabled devices */
-  if (!(ev->flags & XIDeviceEnabled) &&
-      !(ev->flags & XIDeviceDisabled))
-    return;
+  display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager));
+  xdisplay = GDK_DISPLAY_XDISPLAY (display);
 
   for (i = 0; i < ev->num_info; i++)
     {
       if (ev->info[i].flags & XIDeviceEnabled)
         {
-          GdkDisplay *display;
-          Display *xdisplay;
-          XIDeviceInfo *info;
-          int ndevices;
-
-          display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager));
-          xdisplay = GDK_DISPLAY_XDISPLAY (display);
-
           info = XIQueryDevice(xdisplay, ev->info[i].deviceid, &ndevices);
           device = add_device (device_manager, &info[0], TRUE);
           XIFreeDeviceInfo(info);
         }
       else if (ev->info[i].flags & XIDeviceDisabled)
         remove_device (device_manager, ev->info[i].deviceid);
+      else if (ev->info[i].flags & XISlaveAttached ||
+               ev->info[i].flags & XISlaveDetached)
+        {
+          GdkDevice *master, *slave;
+
+          slave = g_hash_table_lookup (device_manager->id_table,
+                                       GINT_TO_POINTER (ev->info[i].deviceid));
+
+          /* Remove old master info */
+          master = gdk_device_get_associated_device (slave);
+
+          if (master)
+            {
+              _gdk_device_remove_slave (master, slave);
+              _gdk_device_set_associated_device (slave, NULL);
+
+              g_signal_emit_by_name (device_manager, "device-changed", master);
+            }
+
+          /* Add new master if it's an attachment event */
+          if (ev->info[i].flags & XISlaveAttached)
+            {
+              info = XIQueryDevice(xdisplay, ev->info[i].deviceid, &ndevices);
+
+              master = g_hash_table_lookup (device_manager->id_table,
+                                            GINT_TO_POINTER (info->attachment));
+
+              _gdk_device_set_associated_device (slave, master);
+              _gdk_device_add_slave (master, slave);
+
+              g_signal_emit_by_name (device_manager, "device-changed", master);
+            }
+
+          g_signal_emit_by_name (device_manager, "device-changed", slave);
+        }
     }
 }
 
index 20054c160c66459d8468ce25f76bba13a21521db..e16a6f7bf8b919af543031a493127a34a2279669 100644 (file)
@@ -43,7 +43,6 @@ struct _GdkDeviceManagerXI2
 
   GList *master_devices;
   GList *slave_devices;
-  GList *floating_devices;
 
   GdkDevice *client_pointer;