eis/client: Add touch support

Touch devices are handled very similarly to how absolute pointer
devices, by creating either shared or standalone devices depending on
what kind of monitor it's associated with.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/4064>
This commit is contained in:
Jonas Ådahl 2024-09-13 14:22:52 +02:00
parent 6a1e1d74e5
commit c46db0a020

View File

@ -29,6 +29,7 @@
#define MAX_BUTTON 128
#define MAX_KEY 0x2ff /* KEY_MAX as of 5.13 */
#define MAX_SLOTS 64
typedef struct _MetaEisDevice MetaEisDevice;
@ -41,6 +42,12 @@ struct _MetaEisDevice
guchar button_state[(MAX_BUTTON + 7) / 8];
guchar key_state[(MAX_KEY + 7) / 8];
struct {
gboolean is_acquired;
uint32_t eis_touch_id;
} slots[MAX_SLOTS];
GHashTable *slot_map;
};
struct _MetaEisClient
@ -56,6 +63,7 @@ struct _MetaEisClient
MetaEisDevice *keyboard_device;
gulong keymap_changed_handler_id;
gboolean have_abs_pointer_devices;
gboolean have_touch_devices;
gulong viewports_changed_handler_id;
};
@ -178,10 +186,38 @@ drop_abs_devices (gpointer key,
return drop_device (key, value, data);
}
static gboolean
drop_touch_devices (gpointer key,
gpointer value,
gpointer data)
{
struct eis_device *eis_device = key;
if (!eis_device_has_capability (eis_device, EIS_DEVICE_CAP_TOUCH))
return FALSE;
return drop_device (key, value, data);
}
static gboolean
drop_viewport_devices (gpointer key,
gpointer value,
gpointer data)
{
struct eis_device *eis_device = key;
if (!eis_device_has_capability (eis_device, EIS_DEVICE_CAP_TOUCH) &&
!eis_device_has_capability (eis_device, EIS_DEVICE_CAP_POINTER_ABSOLUTE))
return FALSE;
return drop_device (key, value, data);
}
static void
meta_eis_device_free (MetaEisDevice *device)
{
eis_device_unref (device->eis_device);
g_hash_table_unref (device->slot_map);
free (device);
}
@ -310,6 +346,18 @@ configure_abs (MetaEisClient *client,
add_viewport_region (eis_device, viewport);
}
static void
configure_touch (MetaEisClient *client,
struct eis_device *eis_device,
gpointer user_data)
{
MetaEisViewport *viewport = META_EIS_VIEWPORT (user_data);
eis_device_configure_capability (eis_device, EIS_DEVICE_CAP_TOUCH);
add_viewport_region (eis_device, viewport);
}
static MetaEisDevice *
create_device (MetaEisClient *client,
struct eis_seat *eis_seat,
@ -340,6 +388,7 @@ create_device (MetaEisClient *client,
device = g_new0 (MetaEisDevice, 1);
device->eis_device = eis_device_ref (eis_device);
device->device = virtual_device;
device->slot_map = g_hash_table_new (NULL, NULL);
eis_device_set_user_data (eis_device, device);
g_hash_table_insert (client->eis_devices,
@ -395,19 +444,16 @@ handle_motion_relative (MetaEisClient *client,
}
static MetaEisViewport *
find_viewport (struct eis_event *event)
find_viewport (MetaEisDevice *device,
double x,
double y)
{
struct eis_device *eis_device = eis_event_get_device (event);
MetaEisDevice *device = eis_device_get_user_data (eis_device);
double x, y;
struct eis_region *region;
if (device->viewport)
return device->viewport;
x = eis_event_pointer_get_absolute_x (event);
y = eis_event_pointer_get_absolute_y (event);
region = eis_device_get_region_at (eis_device, x, y);
region = eis_device_get_region_at (device->eis_device, x, y);
if (!region)
return NULL;
@ -423,12 +469,13 @@ handle_motion_absolute (MetaEisClient *client,
MetaEisViewport *viewport;
double x, y;
viewport = find_viewport (event);
x = eis_event_pointer_get_absolute_x (event);
y = eis_event_pointer_get_absolute_y (event);
viewport = find_viewport (device, x, y);
if (!viewport)
return;
x = eis_event_pointer_get_absolute_x (event);
y = eis_event_pointer_get_absolute_y (event);
if (!meta_eis_viewport_transform_coordinate (viewport, x, y, &x, &y))
return;
@ -603,6 +650,119 @@ handle_key (MetaEisClient *client,
notify_key (device, key, is_press);
}
static int
acquire_slot (MetaEisDevice *device,
uint32_t eis_touch_id)
{
int slot;
for (slot = 0; slot < MAX_SLOTS; slot++)
{
if (device->slots[slot].is_acquired)
continue;
device->slots[slot].is_acquired = TRUE;
device->slots[slot].eis_touch_id = eis_touch_id;
g_hash_table_insert (device->slot_map,
GUINT_TO_POINTER (eis_touch_id),
GINT_TO_POINTER (slot));
return slot;
}
return -1;
}
static int
get_slot (MetaEisDevice *device,
uint32_t touch_id)
{
return GPOINTER_TO_INT (g_hash_table_lookup (device->slot_map,
GUINT_TO_POINTER (touch_id)));
}
static void
release_slot (MetaEisDevice *device,
int slot)
{
g_assert (device->slots[slot].is_acquired);
device->slots[slot].is_acquired = FALSE;
g_hash_table_remove (device->slot_map,
GUINT_TO_POINTER (device->slots[slot].eis_touch_id));
device->slots[slot].eis_touch_id = 0;
}
static void
handle_touch_down (MetaEisClient *client,
struct eis_event *event)
{
struct eis_device *eis_device = eis_event_get_device (event);
MetaEisDevice *device = eis_device_get_user_data (eis_device);
MetaEisViewport *viewport;
double x, y;
int slot;
x = eis_event_touch_get_x (event);
y = eis_event_touch_get_y (event);
viewport = find_viewport (device, x, y);
if (!viewport)
return;
if (!meta_eis_viewport_transform_coordinate (viewport, x, y, &x, &y))
return;
slot = acquire_slot (device, eis_event_touch_get_id (event));
clutter_virtual_input_device_notify_touch_down (device->device,
g_get_monotonic_time (),
slot,
x, y);
}
static void
handle_touch_motion (MetaEisClient *client,
struct eis_event *event)
{
struct eis_device *eis_device = eis_event_get_device (event);
MetaEisDevice *device = eis_device_get_user_data (eis_device);
MetaEisViewport *viewport;
double x, y;
int slot;
x = eis_event_touch_get_x (event);
y = eis_event_touch_get_y (event);
viewport = find_viewport (device, x, y);
if (!viewport)
return;
if (!meta_eis_viewport_transform_coordinate (viewport, x, y, &x, &y))
return;
slot = get_slot (device, eis_event_touch_get_id (event));
clutter_virtual_input_device_notify_touch_motion (device->device,
g_get_monotonic_time (),
slot,
x, y);
}
static void
handle_touch_up (MetaEisClient *client,
struct eis_event *event)
{
struct eis_device *eis_device = eis_event_get_device (event);
MetaEisDevice *device = eis_device_get_user_data (eis_device);
int slot;
slot = get_slot (device, eis_event_touch_get_id (event));
release_slot (device, slot);
clutter_virtual_input_device_notify_touch_up (device->device,
g_get_monotonic_time (),
slot);
}
static void
on_keymap_changed (MetaBackend *backend,
gpointer data)
@ -697,6 +857,15 @@ add_abs_pointer_devices (MetaEisClient *client)
configure_abs);
}
static void
add_touch_devices (MetaEisClient *client)
{
add_viewport_devices (client,
CLUTTER_TOUCHSCREEN_DEVICE,
"virtual touch screen",
configure_touch);
}
gboolean
meta_eis_client_process_event (MetaEisClient *client,
struct eis_event *event)
@ -711,6 +880,7 @@ meta_eis_client_process_event (MetaEisClient *client,
gboolean wants_pointer_device;
gboolean wants_keyboard_device;
gboolean wants_abs_pointer_devices;
gboolean wants_touch_devices;
eis_seat = eis_event_get_seat (event);
@ -720,6 +890,8 @@ meta_eis_client_process_event (MetaEisClient *client,
eis_event_seat_has_capability (event, EIS_DEVICE_CAP_KEYBOARD);
wants_abs_pointer_devices =
eis_event_seat_has_capability (event, EIS_DEVICE_CAP_POINTER_ABSOLUTE);
wants_touch_devices =
eis_event_seat_has_capability (event, EIS_DEVICE_CAP_TOUCH);
if (wants_pointer_device && !client->pointer_device)
{
@ -795,6 +967,25 @@ meta_eis_client_process_event (MetaEisClient *client,
client);
client->have_abs_pointer_devices = FALSE;
}
if (wants_touch_devices && !client->have_touch_devices)
{
meta_topic (META_DEBUG_EIS,
"Seat %s bindings updated, enabling touch devices",
eis_seat_get_name (eis_seat));
add_touch_devices (client);
client->have_touch_devices = TRUE;
}
else if (!wants_touch_devices && client->have_touch_devices)
{
meta_topic (META_DEBUG_EIS,
"Seat %s bindings updated, destroying touch devices",
eis_seat_get_name (eis_seat));
g_hash_table_foreach_remove (client->eis_devices,
drop_touch_devices,
client);
client->have_touch_devices = FALSE;
}
break;
}
case EIS_EVENT_DEVICE_CLOSED:
@ -834,6 +1025,15 @@ meta_eis_client_process_event (MetaEisClient *client,
case EIS_EVENT_KEYBOARD_KEY:
handle_key (client, event);
break;
case EIS_EVENT_TOUCH_DOWN:
handle_touch_down (client, event);
break;
case EIS_EVENT_TOUCH_MOTION:
handle_touch_motion (client, event);
break;
case EIS_EVENT_TOUCH_UP:
handle_touch_up (client, event);
break;
case EIS_EVENT_FRAME:
/* FIXME: we should be accumulating the above events */
break;
@ -855,11 +1055,14 @@ update_viewports (MetaEisClient *client)
meta_topic (META_DEBUG_EIS, "Updating viewports");
g_hash_table_foreach_remove (client->eis_devices,
drop_abs_devices,
drop_viewport_devices,
client);
if (client->have_abs_pointer_devices)
add_abs_pointer_devices (client);
if (client->have_touch_devices)
add_touch_devices (client);
}
static void
@ -916,6 +1119,11 @@ meta_eis_client_new (MetaEis *eis,
eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_SCROLL);
}
if (meta_eis_get_device_types (eis) & META_EIS_DEVICE_TYPE_TOUCHSCREEN)
{
eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_TOUCH);
}
eis_seat_add (eis_seat);
eis_seat_unref (eis_seat);
client->eis_seat = eis_seat_ref (eis_seat);