From 69fad154b9b853d3a2787034a001989efcd108df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 28 Aug 2023 23:13:16 +0200 Subject: [PATCH] eis: Use either standalone or shared devices for absolute input Sometimes it makes no sense to have a shared pointer device, for example when they have no set region occupying the global stage coordinate space. This applies to for example window screen cast based pointer device regions - they are always local to the window, and have no position. We do need shared absolute devices in some cases though, primarily multi-head remote desktop, where it must be possible to keep a button reliably pressed when crossing monitors that have their own corresponding regions. To handle this, outsource all this policy to the one who drives the emulated input devices. Remote desktop sessions where the screen casts correspond to specific monitors (physical or virtual), we need to make sure they map to the stage coordinate space, while for window screencast or area screencasts, we create standalone absolute pointer devices with a single region each. Part-of: --- src/backends/meta-eis-client.c | 237 +++++++++++++++------ src/backends/meta-eis.c | 42 ++++ src/backends/meta-eis.h | 8 + src/backends/meta-remote-desktop-session.c | 57 +++++ src/backends/meta-screen-cast-session.c | 48 ++++- src/backends/meta-screen-cast-session.h | 2 + 6 files changed, 324 insertions(+), 70 deletions(-) diff --git a/src/backends/meta-eis-client.c b/src/backends/meta-eis-client.c index 1459d9281..59ed2082a 100644 --- a/src/backends/meta-eis-client.c +++ b/src/backends/meta-eis-client.c @@ -24,7 +24,6 @@ #include "backends/meta-backend-private.h" #include "backends/meta-monitor-manager-private.h" -#include "backends/meta-viewport-info.h" #include "clutter/clutter-mutter.h" #include "core/meta-anonymous-file.h" @@ -38,6 +37,8 @@ struct _MetaEisDevice struct eis_device *eis_device; ClutterVirtualInputDevice *device; + MetaEisViewport *viewport; + guchar button_state[(MAX_BUTTON + 7) / 8]; guchar key_state[(MAX_KEY + 7) / 8]; }; @@ -47,11 +48,12 @@ struct _MetaEisClient GObject parent_instance; MetaEis *eis; - MetaViewportInfo *viewports; struct eis_client *eis_client; struct eis_seat *eis_seat; GHashTable *eis_devices; /* eis_device => MetaEisDevice*/ + + gulong viewports_changed_handler_id; }; G_DEFINE_TYPE (MetaEisClient, meta_eis_client, G_TYPE_OBJECT) @@ -219,34 +221,94 @@ configure_keyboard (MetaEisClient *client, } } -static void -configure_abs (MetaEisClient *client, - struct eis_device *eis_device, - gpointer user_data) +static gboolean +has_region (struct eis_device *eis_device, + int x, + int y, + int width, + int height) { - int idx = 0; - MtkRectangle rect; - float scale; + int i = 0; - if (!client->viewports) - return; /* FIXME: should be an error */ + while (TRUE) + { + struct eis_region *region; + + region = eis_device_get_region (eis_device, i++); + if (!region) + return FALSE; + + if (eis_region_get_x (region) == x && + eis_region_get_y (region) == y && + eis_region_get_width (region) == width && + eis_region_get_height (region) == height) + return TRUE; + } +} + +static void +add_viewport_region (struct eis_device *eis_device, + MetaEisViewport *viewport) +{ + gboolean has_position; + int x, y; + int width, height; + double scale; + const char *mapping_id; + struct eis_region *eis_region; + + if (meta_eis_viewport_get_position (viewport, &x, &y)) + has_position = TRUE; + meta_eis_viewport_get_size (viewport, &width, &height); + scale = meta_eis_viewport_get_physical_scale (viewport); + + if (has_region (eis_device, x, y, width, height)) + return; + + eis_region = eis_device_new_region (eis_device); + if (has_position) + eis_region_set_offset (eis_region, x, y); + eis_region_set_size (eis_region, width, height); + eis_region_set_physical_scale (eis_region, scale); + + mapping_id = meta_eis_viewport_get_mapping_id (viewport); + g_warn_if_fail (mapping_id); + eis_region_set_mapping_id (eis_region, mapping_id); + + eis_region_set_user_data (eis_region, viewport); + eis_region_add (eis_region); + eis_region_unref (eis_region); +} + +static void +configure_abs_shared (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_POINTER_ABSOLUTE); eis_device_configure_capability (eis_device, EIS_DEVICE_CAP_BUTTON); eis_device_configure_capability (eis_device, EIS_DEVICE_CAP_SCROLL); - while (meta_viewport_info_get_view_info (client->viewports, idx++, &rect, &scale)) - { - struct eis_region *r = eis_device_new_region (eis_device); - eis_region_set_offset (r, rect.x, rect.y); - eis_region_set_size (r, rect.width, rect.height); - eis_region_set_physical_scale (r, scale); - eis_region_add (r); - eis_region_unref (r); - } + add_viewport_region (eis_device, viewport); } static void +configure_abs_standalone (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_POINTER_ABSOLUTE); + eis_device_configure_capability (eis_device, EIS_DEVICE_CAP_BUTTON); + eis_device_configure_capability (eis_device, EIS_DEVICE_CAP_SCROLL); + + add_viewport_region (eis_device, viewport); +} + +static MetaEisDevice * add_device (MetaEisClient *client, struct eis_seat *eis_seat, ClutterInputDeviceType type, @@ -281,6 +343,8 @@ add_device (MetaEisClient *client, eis_device_add (eis_device); eis_device_resume (eis_device); g_free (name); + + return device; } static void @@ -299,16 +363,43 @@ handle_motion_relative (MetaEisClient *client, dx, dy); } +static MetaEisViewport * +find_viewport (struct eis_event *event) +{ + 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); + if (!region) + return NULL; + + return META_EIS_VIEWPORT (eis_region_get_user_data (region)); +} + static void handle_motion_absolute (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; + viewport = find_viewport (event); + 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; clutter_virtual_input_device_notify_absolute_motion (device->device, g_get_monotonic_time (), @@ -529,6 +620,52 @@ on_keymap_changed (MetaBackend *backend, configure_keyboard, NULL); } +static void +add_abs_pointer_devices (MetaEisClient *client) +{ + MetaEisDevice *shared_device = NULL; + GList *viewports; + GList *l; + + viewports = meta_eis_peek_viewports (client->eis); + if (!viewports) + return; /* FIXME: should be an error */ + + for (l = viewports; l; l = l->next) + { + MetaEisViewport *viewport = l->data; + + if (meta_eis_viewport_is_standalone (viewport)) + { + MetaEisDevice *device; + + device = add_device (client, + client->eis_seat, + CLUTTER_POINTER_DEVICE, + "standalone virtual absolute pointer", + configure_abs_standalone, + viewport); + device->viewport = viewport; + } + else + { + if (!shared_device) + { + shared_device = add_device (client, + client->eis_seat, + CLUTTER_POINTER_DEVICE, + "shared virtual absolute pointer", + configure_abs_shared, + viewport); + } + else + { + add_viewport_region (shared_device->eis_device, viewport); + } + } + } +} + gboolean meta_eis_client_process_event (MetaEisClient *client, struct eis_event *event) @@ -561,15 +698,8 @@ meta_eis_client_process_event (MetaEisClient *client, G_CALLBACK (on_keymap_changed), client); } - if (eis_event_seat_has_capability (event, EIS_DEVICE_CAP_POINTER_ABSOLUTE)) - { - add_device (client, - eis_seat, - CLUTTER_POINTER_DEVICE, - "virtual absolute pointer", - configure_abs, - NULL); - } + + add_abs_pointer_devices (client); break; /* We only have one seat, so if the client unbinds from that @@ -630,52 +760,30 @@ drop_abs_devices (gpointer key, } static void -meta_eis_client_set_viewports (MetaEisClient *client, - MetaViewportInfo *viewports) +update_viewports (MetaEisClient *client) { - /* Updating viewports means we have to recreate our absolute pointer - * devices. */ - g_hash_table_foreach_remove (client->eis_devices, drop_abs_devices, client); - - g_clear_object (&client->viewports); - client->viewports = g_object_ref (viewports); - - add_device (client, - client->eis_seat, - CLUTTER_POINTER_DEVICE, - "virtual absolute pointer", - configure_abs, - NULL); + add_abs_pointer_devices (client); } static void -on_monitors_changed (MetaMonitorManager *monitor_manager, - MetaEisClient *client) +on_viewports_changed (MetaEis *eis, + MetaEisClient *client) { - MetaViewportInfo *viewports; - - viewports = meta_monitor_manager_get_viewports (monitor_manager); - meta_eis_client_set_viewports (client, viewports); + update_viewports (client); } static void meta_eis_client_disconnect (MetaEisClient *client) { - MetaBackend *backend = meta_eis_get_backend (client->eis); - MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); - - g_signal_handlers_disconnect_by_func (monitor_manager, - on_monitors_changed, - client); + g_clear_signal_handler (&client->viewports_changed_handler_id, client->eis); g_hash_table_foreach_remove (client->eis_devices, drop_device, client); g_clear_pointer (&client->eis_seat, eis_seat_unref); if (client->eis_client) eis_client_disconnect (client->eis_client); g_clear_pointer (&client->eis_client, eis_client_unref); - g_clear_object (&client->viewports); } MetaEisClient * @@ -683,9 +791,6 @@ meta_eis_client_new (MetaEis *eis, struct eis_client *eis_client) { MetaEisClient *client; - MetaBackend *backend; - MetaMonitorManager *monitor_manager; - MetaViewportInfo *viewports; struct eis_seat *eis_seat; client = g_object_new (META_TYPE_EIS_CLIENT, NULL); @@ -725,13 +830,11 @@ meta_eis_client_new (MetaEis *eis, (GDestroyNotify) eis_device_unref, (GDestroyNotify) meta_eis_device_free); - backend = meta_eis_get_backend (eis); - monitor_manager = meta_backend_get_monitor_manager (backend); - viewports = meta_monitor_manager_get_viewports (monitor_manager); - meta_eis_client_set_viewports (client, viewports); - g_signal_connect (monitor_manager, "monitors-changed", - G_CALLBACK (on_monitors_changed), - client); + client->viewports_changed_handler_id = + g_signal_connect (eis, "viewports-changed", + G_CALLBACK (on_viewports_changed), + client); + update_viewports (client); return client; } diff --git a/src/backends/meta-eis.c b/src/backends/meta-eis.c index ca7f17f2f..ceab95e17 100644 --- a/src/backends/meta-eis.c +++ b/src/backends/meta-eis.c @@ -27,6 +27,15 @@ #include "clutter/clutter-mutter.h" #include "meta/util.h" +enum +{ + VIEWPORTS_CHANGED, + + N_SIGNALS +}; + +static int signals[N_SIGNALS]; + typedef struct _MetaEventSource MetaEventSource; struct _MetaEventSource @@ -47,6 +56,8 @@ struct _MetaEis MetaEisDeviceTypes device_types; + GList *viewports; + GHashTable *eis_clients; /* eis_client => MetaEisClient */ }; @@ -274,6 +285,7 @@ meta_eis_dispose (GObject *object) { MetaEis *eis = META_EIS (object); + g_clear_pointer (&eis->viewports, g_list_free); g_clear_pointer (&eis->event_source, meta_event_source_free); g_clear_pointer (&eis->eis, eis_unref); g_clear_pointer (&eis->eis_clients, g_hash_table_destroy); @@ -287,6 +299,14 @@ meta_eis_class_init (MetaEisClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_eis_dispose; + + signals[VIEWPORTS_CHANGED] = + g_signal_new ("viewports-changed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } MetaEisDeviceTypes @@ -294,3 +314,25 @@ meta_eis_get_device_types (MetaEis *eis) { return eis->device_types; } + +void +meta_eis_add_viewport (MetaEis *eis, + MetaEisViewport *viewport) +{ + eis->viewports = g_list_append (eis->viewports, viewport); + g_signal_emit (eis, signals[VIEWPORTS_CHANGED], 0); +} + +void +meta_eis_remove_viewport (MetaEis *eis, + MetaEisViewport *viewport) +{ + eis->viewports = g_list_remove (eis->viewports, viewport); + g_signal_emit (eis, signals[VIEWPORTS_CHANGED], 0); +} + +GList * +meta_eis_peek_viewports (MetaEis *eis) +{ + return eis->viewports; +} diff --git a/src/backends/meta-eis.h b/src/backends/meta-eis.h index 897f9f019..e915c576e 100644 --- a/src/backends/meta-eis.h +++ b/src/backends/meta-eis.h @@ -44,4 +44,12 @@ MetaBackend * meta_eis_get_backend (MetaEis *eis); int meta_eis_add_client_get_fd (MetaEis *eis); +void meta_eis_add_viewport (MetaEis *eis, + MetaEisViewport *viewport); + +void meta_eis_remove_viewport (MetaEis *eis, + MetaEisViewport *viewport); + +GList * meta_eis_peek_viewports (MetaEis *eis); + MetaEisDeviceTypes meta_eis_get_device_types (MetaEis *eis); diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c index 60b34fade..ec35e719f 100644 --- a/src/backends/meta-remote-desktop-session.c +++ b/src/backends/meta-remote-desktop-session.c @@ -203,6 +203,50 @@ ensure_virtual_device (MetaRemoteDesktopSession *session, *virtual_device_ptr = clutter_seat_create_virtual_device (seat, device_type); } +static void +on_stream_added (MetaScreenCastSession *screen_cast_session, + MetaScreenCastStream *stream, + MetaRemoteDesktopSession *session) +{ + meta_eis_add_viewport (session->eis, META_EIS_VIEWPORT (stream)); +} + +static void +on_stream_removed (MetaScreenCastSession *screen_cast_session, + MetaScreenCastStream *stream, + MetaRemoteDesktopSession *session) +{ + meta_eis_remove_viewport (session->eis, META_EIS_VIEWPORT (stream)); +} + +static void +initialize_viewports (MetaRemoteDesktopSession *session) +{ + if (session->screen_cast_session) + { + GList *streams; + GList *l; + + streams = + meta_screen_cast_session_peek_streams (session->screen_cast_session); + for (l = streams; l; l = l->next) + { + MetaScreenCastStream *stream = META_SCREEN_CAST_STREAM (l->data); + + meta_eis_add_viewport (session->eis, META_EIS_VIEWPORT (stream)); + } + + g_signal_connect (session->screen_cast_session, + "stream-added", + G_CALLBACK (on_stream_added), + session); + g_signal_connect (session->screen_cast_session, + "stream-removed", + G_CALLBACK (on_stream_removed), + session); + } +} + static gboolean meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, GError **error) @@ -215,6 +259,9 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, return FALSE; } + if (session->eis) + initialize_viewports (session); + init_remote_access_handle (session); session->started = TRUE; @@ -281,6 +328,13 @@ meta_remote_desktop_session_register_screen_cast (MetaRemoteDesktopSession *ses MetaScreenCastSession *screen_cast_session, GError **error) { + if (session->started) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Remote desktop session already started"); + return FALSE; + } + if (session->screen_cast_session) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, @@ -1696,6 +1750,9 @@ handle_connect_to_eis (MetaDBusRemoteDesktopSession *skeleton, eis_device_types = device_types_to_eis_device_types (device_types); session->eis = meta_eis_new (backend, eis_device_types); + + if (session->started) + initialize_viewports (session); } fd = meta_eis_add_client_get_fd (session->eis); diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c index 275c02824..ca0921d68 100644 --- a/src/backends/meta-screen-cast-session.c +++ b/src/backends/meta-screen-cast-session.c @@ -38,6 +38,16 @@ #define META_SCREEN_CAST_SESSION_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Session" +enum +{ + STREAM_ADDED, + STREAM_REMOVED, + + N_SIGNALS, +}; + +static int signals[N_SIGNALS]; + enum { PROP_0, @@ -184,6 +194,12 @@ meta_screen_cast_session_close (MetaDbusSession *dbus_session) g_object_unref (session); } +GList * +meta_screen_cast_session_peek_streams (MetaScreenCastSession *session) +{ + return session->streams; +} + MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path) @@ -366,6 +382,7 @@ on_stream_closed (MetaScreenCastStream *stream, MetaScreenCastSession *session) { session->streams = g_list_remove (session->streams, stream); + g_signal_emit (session, signals[STREAM_REMOVED], 0, stream); g_object_unref (stream); switch (session->session_type) @@ -392,6 +409,16 @@ is_valid_cursor_mode (MetaScreenCastCursorMode cursor_mode) return FALSE; } +static void +add_stream (MetaScreenCastSession *session, + MetaScreenCastStream *stream) +{ + session->streams = g_list_append (session->streams, stream); + g_signal_emit (session, signals[STREAM_ADDED], 0, stream); + + g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session); +} + static gboolean handle_record_monitor (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation, @@ -484,9 +511,7 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton, stream = META_SCREEN_CAST_STREAM (monitor_stream); stream_path = meta_screen_cast_stream_get_object_path (stream); - session->streams = g_list_append (session->streams, stream); - - g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session); + add_stream (session, stream); meta_dbus_screen_cast_session_complete_record_monitor (skeleton, invocation, @@ -890,6 +915,23 @@ meta_screen_cast_session_class_init (MetaScreenCastSessionClass *klass) G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); meta_dbus_session_install_properties (object_class, N_PROPS); + + signals[STREAM_ADDED] = + g_signal_new ("stream-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + META_TYPE_SCREEN_CAST_STREAM); + signals[STREAM_REMOVED] = + g_signal_new ("stream-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 1, + META_TYPE_SCREEN_CAST_STREAM); } static gboolean diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h index 765fc3cb9..b3be279ea 100644 --- a/src/backends/meta-screen-cast-session.h +++ b/src/backends/meta-screen-cast-session.h @@ -56,6 +56,8 @@ gboolean meta_screen_cast_session_start (MetaScreenCastSession *session, gboolean meta_screen_cast_session_is_active (MetaScreenCastSession *session); +GList * meta_screen_cast_session_peek_streams (MetaScreenCastSession *session); + MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path);