xi2: Improve device hierarchy handling

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.
This commit is contained in:
Carlos Garnacho
2010-12-13 12:20:34 +01:00
parent 9f41101ccc
commit be7de347bf
6 changed files with 224 additions and 37 deletions

View File

@ -59,7 +59,13 @@ struct _GdkDevicePrivate
GdkDeviceKey *keys; GdkDeviceKey *keys;
GdkDeviceManager *device_manager; GdkDeviceManager *device_manager;
GdkDisplay *display; GdkDisplay *display;
/* Paired master for master,
* associated master for slaves
*/
GdkDevice *associated; GdkDevice *associated;
GList *slaves;
GdkDeviceType type; GdkDeviceType type;
GArray *axes; GArray *axes;
}; };
@ -290,6 +296,9 @@ gdk_device_dispose (GObject *object)
device = GDK_DEVICE (object); device = GDK_DEVICE (object);
priv = device->priv; priv = device->priv;
if (priv->type == GDK_DEVICE_TYPE_SLAVE)
_gdk_device_remove_slave (priv->associated, device);
if (priv->associated) if (priv->associated)
{ {
_gdk_device_set_associated_device (priv->associated, NULL); _gdk_device_set_associated_device (priv->associated, NULL);
@ -820,6 +829,24 @@ gdk_device_get_associated_device (GdkDevice *device)
return priv->associated; 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 void
_gdk_device_set_associated_device (GdkDevice *device, _gdk_device_set_associated_device (GdkDevice *device,
GdkDevice *associated) GdkDevice *associated)
@ -842,6 +869,81 @@ _gdk_device_set_associated_device (GdkDevice *device,
if (associated) if (associated)
priv->associated = g_object_ref (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);
} }
/** /**

View File

@ -222,6 +222,7 @@ gboolean gdk_device_get_axis (GdkDevice *device,
GdkDisplay * gdk_device_get_display (GdkDevice *device); GdkDisplay * gdk_device_get_display (GdkDevice *device);
GdkDevice * gdk_device_get_associated_device (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); GdkDeviceType gdk_device_get_device_type (GdkDevice *device);

View File

@ -183,12 +183,16 @@ gdk_device_manager_class_init (GdkDeviceManagerClass *klass)
* @device_manager: the object on which the signal is emitted * @device_manager: the object on which the signal is emitted
* @device: the #GdkDevice that changed. * @device: the #GdkDevice that changed.
* *
* The ::device-changed signal is emitted either when some * The ::device-changed signal is emitted whenever a device
* #GdkDevice has changed the number of either axes or keys. * has changed in the hierarchy, either slave devices being
* For example In X this will normally happen when the slave * disconnected from their master device or connected to
* device routing events through the master device changes, * another one, or master devices being added or removed
* in that case the master device will change to reflect the * a slave device.
* new slave device axes and keys. *
* 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] = signals [DEVICE_CHANGED] =
g_signal_new (g_intern_static_string ("device-changed"), g_signal_new (g_intern_static_string ("device-changed"),

View File

@ -125,6 +125,10 @@ GdkTimeCoord ** _gdk_device_allocate_history (GdkDevice *device,
void _gdk_input_check_extension_events (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 G_END_DECLS

View File

@ -263,15 +263,29 @@ add_device (GdkDeviceManagerXI2 *device_manager,
if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard) if (dev->use == XIMasterPointer || dev->use == XIMasterKeyboard)
device_manager->master_devices = g_list_append (device_manager->master_devices, device); 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); 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 else
g_warning ("Unhandled device: %s\n", gdk_device_get_name (device)); g_warning ("Unhandled device: %s\n", gdk_device_get_name (device));
if (emit_signal) 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; 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->master_devices = g_list_remove (device_manager->master_devices, device);
device_manager->slave_devices = g_list_remove (device_manager->slave_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); g_signal_emit_by_name (device_manager, "device-removed", device);
@ -301,7 +314,7 @@ remove_device (GdkDeviceManagerXI2 *device_manager,
} }
static void static void
relate_devices (gpointer key, relate_masters (gpointer key,
gpointer value, gpointer value,
gpointer user_data) gpointer user_data)
{ {
@ -316,13 +329,29 @@ relate_devices (gpointer key,
_gdk_device_set_associated_device (relative, device); _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 static void
gdk_device_manager_xi2_constructed (GObject *object) gdk_device_manager_xi2_constructed (GObject *object)
{ {
GdkDeviceManagerXI2 *device_manager_xi2; GdkDeviceManagerXI2 *device_manager_xi2;
GdkDisplay *display; GdkDisplay *display;
GdkScreen *screen; GdkScreen *screen;
GHashTable *relations; GHashTable *masters, *slaves;
Display *xdisplay; Display *xdisplay;
XIDeviceInfo *info, *dev; XIDeviceInfo *info, *dev;
int ndevices, i; int ndevices, i;
@ -332,7 +361,9 @@ gdk_device_manager_xi2_constructed (GObject *object)
device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (object); device_manager_xi2 = GDK_DEVICE_MANAGER_XI2 (object);
display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object)); display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (object));
xdisplay = GDK_DISPLAY_XDISPLAY (display); 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); info = XIQueryDevice(xdisplay, XIAllDevices, &ndevices);
@ -347,7 +378,14 @@ gdk_device_manager_xi2_constructed (GObject *object)
if (dev->use == XIMasterPointer || if (dev->use == XIMasterPointer ||
dev->use == XIMasterKeyboard) 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->deviceid),
GINT_TO_POINTER (dev->attachment)); GINT_TO_POINTER (dev->attachment));
} }
@ -356,8 +394,11 @@ gdk_device_manager_xi2_constructed (GObject *object)
XIFreeDeviceInfo(info); XIFreeDeviceInfo(info);
/* Stablish relationships between devices */ /* Stablish relationships between devices */
g_hash_table_foreach (relations, relate_devices, object); g_hash_table_foreach (masters, relate_masters, object);
g_hash_table_destroy (relations); g_hash_table_destroy (masters);
g_hash_table_foreach (slaves, relate_slaves, object);
g_hash_table_destroy (slaves);
/* Connect to hierarchy change events */ /* Connect to hierarchy change events */
screen = gdk_display_get_default_screen (display); 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); g_list_free (device_manager_xi2->slave_devices);
device_manager_xi2->slave_devices = NULL; 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) if (device_manager_xi2->id_table)
{ {
g_hash_table_destroy (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; list = device_manager_xi2->master_devices;
break; break;
case GDK_DEVICE_TYPE_SLAVE: case GDK_DEVICE_TYPE_SLAVE:
list = device_manager_xi2->slave_devices;
break;
case GDK_DEVICE_TYPE_FLOATING: 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; break;
default: default:
g_assert_not_reached (); g_assert_not_reached ();
@ -457,32 +505,61 @@ static void
handle_hierarchy_changed (GdkDeviceManagerXI2 *device_manager, handle_hierarchy_changed (GdkDeviceManagerXI2 *device_manager,
XIHierarchyEvent *ev) XIHierarchyEvent *ev)
{ {
GdkDisplay *display;
Display *xdisplay;
GdkDevice *device; GdkDevice *device;
XIDeviceInfo *info;
int ndevices;
gint i; gint i;
/* We only care about enabled devices */ display = gdk_device_manager_get_display (GDK_DEVICE_MANAGER (device_manager));
if (!(ev->flags & XIDeviceEnabled) && xdisplay = GDK_DISPLAY_XDISPLAY (display);
!(ev->flags & XIDeviceDisabled))
return;
for (i = 0; i < ev->num_info; i++) for (i = 0; i < ev->num_info; i++)
{ {
if (ev->info[i].flags & XIDeviceEnabled) 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); info = XIQueryDevice(xdisplay, ev->info[i].deviceid, &ndevices);
device = add_device (device_manager, &info[0], TRUE); device = add_device (device_manager, &info[0], TRUE);
XIFreeDeviceInfo(info); XIFreeDeviceInfo(info);
} }
else if (ev->info[i].flags & XIDeviceDisabled) else if (ev->info[i].flags & XIDeviceDisabled)
remove_device (device_manager, ev->info[i].deviceid); 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);
}
} }
} }

View File

@ -43,7 +43,6 @@ struct _GdkDeviceManagerXI2
GList *master_devices; GList *master_devices;
GList *slave_devices; GList *slave_devices;
GList *floating_devices;
GdkDevice *client_pointer; GdkDevice *client_pointer;