diff --git a/gdk/gdkdevice.c b/gdk/gdkdevice.c index dc66f3bddd..1330d81bf3 100644 --- a/gdk/gdkdevice.c +++ b/gdk/gdkdevice.c @@ -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); } /** diff --git a/gdk/gdkdevice.h b/gdk/gdkdevice.h index 27375f7621..2c797a9fc8 100644 --- a/gdk/gdkdevice.h +++ b/gdk/gdkdevice.h @@ -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); diff --git a/gdk/gdkdevicemanager.c b/gdk/gdkdevicemanager.c index fced01ff49..e282036424 100644 --- a/gdk/gdkdevicemanager.c +++ b/gdk/gdkdevicemanager.c @@ -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"), diff --git a/gdk/gdkdeviceprivate.h b/gdk/gdkdeviceprivate.h index d878ae634d..f7318620d8 100644 --- a/gdk/gdkdeviceprivate.h +++ b/gdk/gdkdeviceprivate.h @@ -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 diff --git a/gdk/x11/gdkdevicemanager-xi2.c b/gdk/x11/gdkdevicemanager-xi2.c index bdfeac9953..ee3461c3fe 100644 --- a/gdk/x11/gdkdevicemanager-xi2.c +++ b/gdk/x11/gdkdevicemanager-xi2.c @@ -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); + } } } diff --git a/gdk/x11/gdkdevicemanager-xi2.h b/gdk/x11/gdkdevicemanager-xi2.h index 20054c160c..e16a6f7bf8 100644 --- a/gdk/x11/gdkdevicemanager-xi2.h +++ b/gdk/x11/gdkdevicemanager-xi2.h @@ -43,7 +43,6 @@ struct _GdkDeviceManagerXI2 GList *master_devices; GList *slave_devices; - GList *floating_devices; GdkDevice *client_pointer;