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: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3228>
This commit is contained in:
Jonas Ådahl 2023-08-28 23:13:16 +02:00 committed by Robert Mader
parent d4559a9ca4
commit 69fad154b9
6 changed files with 324 additions and 70 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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);