From 6f63bd31b5dba5a5e4a4ad53487dccdd55989fa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 16:09:11 +0200 Subject: [PATCH 01/40] build: Make each executable/library have their own log domain This helps reading log output during, as it's otherwise often unclear whether a log entry came from a test client or mutter itself. --- src/meson.build | 16 ++++++++++++---- src/tests/meson.build | 30 ++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/meson.build b/src/meson.build index 7b456f524fb..13a69c1a631 100644 --- a/src/meson.build +++ b/src/meson.build @@ -163,7 +163,6 @@ mutter_c_args = [ '-DCOGL_ENABLE_MUTTER_API', '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', - '-DG_LOG_DOMAIN="mutter"', '-DSN_API_NOT_YET_FROZEN=1', '-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), ] @@ -1023,7 +1022,10 @@ libmutter = shared_library(libmutter_name, soversion: 0, gnu_symbol_visibility: 'hidden', include_directories: mutter_includes, - c_args: mutter_c_args, + c_args: [ + mutter_c_args, + '-DG_LOG_DOMAIN="libmutter"', + ], dependencies: [ libmutter_cogl_dep, libmutter_clutter_dep, @@ -1049,7 +1051,10 @@ executable('mutter', files('core/mutter.c'), ], include_directories: mutter_includes, - c_args: mutter_c_args, + c_args: [ + mutter_c_args, + '-DG_LOG_DOMAIN="mutter"', + ], dependencies: [libmutter_dep], install_dir: bindir, install: true, @@ -1062,7 +1067,10 @@ executable('mutter-restart-helper', include_directories: [ top_includepath, ], - c_args: mutter_c_args, + c_args: [ + mutter_c_args, + '-DG_LOG_DOMAIN="mutter-restart-helper"', + ], dependencies: [ x11_dep, xcomposite_dep, diff --git a/src/tests/meson.build b/src/tests/meson.build index 2cb05d9668e..cada89d7220 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -38,7 +38,10 @@ libmutter_test = shared_library(libmutter_test_name, mutter_test_sources, gnu_symbol_visibility: 'hidden', include_directories: tests_includes, - c_args: tests_c_args, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="libmutter-test"', + ], dependencies: tests_deps, install_rpath: pkglibdir, install_dir: libdir, @@ -104,7 +107,10 @@ endforeach test_client = executable('mutter-test-client', sources: ['test-client.c'], include_directories: tests_includes, - c_args: tests_c_args, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-test-client"', + ], dependencies: [ gtk3_dep, gio_unix_dep, @@ -120,7 +126,10 @@ test_runner = executable('mutter-test-runner', 'test-runner.c', ], include_directories: tests_includes, - c_args: tests_c_args, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-test-runner"', + ], dependencies: libmutter_test_dep, install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, @@ -202,7 +211,10 @@ if have_native_tests dbus_screen_cast_built_sources, ], include_directories: tests_includes, - c_args: tests_c_args, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-screen-cast-client"', + ], dependencies: [ gio_dep, libpipewire_dep, @@ -289,7 +301,10 @@ if have_native_tests test_executable = executable('mutter-' + test_case['name'], sources: test_case['sources'], include_directories: tests_includes, - c_args: tests_c_args, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-@0@-test"'.format(test_case['name']), + ], dependencies: libmutter_test_dep, install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, @@ -352,7 +367,10 @@ if have_kvm_tests or have_tty_tests test_executable = executable('mutter-' + test_case['name'], sources: test_case['sources'], include_directories: tests_includes, - c_args: tests_c_args, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-@0@-test"'.format(test_case['name']), + ], dependencies: libmutter_test_dep, install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, -- GitLab From a892fbab52edf3d30358a5659934093f0a717272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 16:10:13 +0200 Subject: [PATCH 02/40] util: Avoid overriding 'message' in meta_topic() We'd put the message in a variable called `message`. If something passed to meta_topic() was called `message`, it'd end up being `NULL` in the log entry. Avoid this by making the local message variable a bit more "on topic". --- src/meta/util.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/meta/util.h b/src/meta/util.h index c44a63fc873..c8628bcf9ce 100644 --- a/src/meta/util.h +++ b/src/meta/util.h @@ -175,10 +175,11 @@ const char * meta_topic_to_string (MetaDebugTopic topic); { \ if (meta_is_topic_enabled (debug_topic)) \ { \ - g_autofree char *message = NULL; \ + g_autofree char *_topic_message = NULL; \ \ - message = g_strdup_printf (__VA_ARGS__); \ - g_message ("%s: %s", meta_topic_to_string (debug_topic), message); \ + _topic_message = g_strdup_printf (__VA_ARGS__); \ + g_message ("%s: %s", meta_topic_to_string (debug_topic), \ + _topic_message); \ } \ } \ G_STMT_END -- GitLab From 39c93b3df939436d32f65e6933f36263ac188e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Apr 2022 12:12:33 +0200 Subject: [PATCH 03/40] display: Get the backend from the context --- .../clutter-input-pointer-a11y-private.h | 3 +- clutter/clutter/clutter-input-pointer-a11y.c | 6 ++-- clutter/clutter/clutter-main.c | 1 - src/core/display.c | 2 +- src/core/events.c | 30 +++++++++++++------ 5 files changed, 28 insertions(+), 14 deletions(-) diff --git a/clutter/clutter/clutter-input-pointer-a11y-private.h b/clutter/clutter/clutter-input-pointer-a11y-private.h index a66ddeac9d7..d7f4655012c 100644 --- a/clutter/clutter/clutter-input-pointer-a11y-private.h +++ b/clutter/clutter/clutter-input-pointer-a11y-private.h @@ -43,7 +43,8 @@ CLUTTER_EXPORT gboolean _clutter_is_input_pointer_a11y_enabled (ClutterInputDevice *device); CLUTTER_EXPORT -void _clutter_input_pointer_a11y_maybe_handle_event (ClutterEvent *event); +void clutter_input_pointer_a11y_update (ClutterInputDevice *device, + const ClutterEvent *event); G_END_DECLS diff --git a/clutter/clutter/clutter-input-pointer-a11y.c b/clutter/clutter/clutter-input-pointer-a11y.c index cfee58e55f5..a74aa4cc223 100644 --- a/clutter/clutter/clutter-input-pointer-a11y.c +++ b/clutter/clutter/clutter-input-pointer-a11y.c @@ -730,13 +730,15 @@ _clutter_is_input_pointer_a11y_enabled (ClutterInputDevice *device) } void -_clutter_input_pointer_a11y_maybe_handle_event (ClutterEvent *event) +clutter_input_pointer_a11y_update (ClutterInputDevice *device, + const ClutterEvent *event) { - ClutterInputDevice *device = clutter_event_get_device (event); ClutterMainContext *clutter_context; ClutterBackend *backend; + g_return_if_fail (clutter_event_get_device (event) == device); + if (!_clutter_is_input_pointer_a11y_enabled (device)) return; diff --git a/clutter/clutter/clutter-main.c b/clutter/clutter/clutter-main.c index a2ee338b66a..5b01783e5e9 100644 --- a/clutter/clutter/clutter-main.c +++ b/clutter/clutter/clutter-main.c @@ -802,7 +802,6 @@ clutter_do_event (ClutterEvent *event) context->current_event = g_slist_prepend (context->current_event, event); - _clutter_input_pointer_a11y_maybe_handle_event (event); if (_clutter_event_process_filters (event, event_actor)) { context->current_event = diff --git a/src/core/display.c b/src/core/display.c index a1c5c2d9121..058d09f2a5e 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -829,12 +829,12 @@ MetaDisplay * meta_display_new (MetaContext *context, GError **error) { + MetaBackend *backend = meta_context_get_backend (context); MetaDisplay *display; MetaDisplayPrivate *priv; int i; guint32 timestamp; Window old_active_xwindow = None; - MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager; MetaSettings *settings; diff --git a/src/core/events.c b/src/core/events.c index 0dc3a73222c..d5a9a93da34 100644 --- a/src/core/events.c +++ b/src/core/events.c @@ -61,9 +61,10 @@ typedef enum } EventsUnfreezeMethod; static gboolean -stage_has_key_focus (void) +stage_has_key_focus (MetaDisplay *display) { - MetaBackend *backend = meta_get_backend (); + MetaContext *context = meta_display_get_context (display); + MetaBackend *backend = meta_context_get_backend (context); ClutterActor *stage = meta_backend_get_stage (backend); return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == stage; @@ -92,7 +93,10 @@ get_window_for_event (MetaDisplay *display, /* Always use the key focused window for key events. */ if (IS_KEY_EVENT (event)) - return stage_has_key_focus () ? display->focus_window : NULL; + { + return stage_has_key_focus (display) ? display->focus_window + : NULL; + } window_actor = meta_window_actor_from_actor (event_actor); if (window_actor) @@ -112,9 +116,11 @@ get_window_for_event (MetaDisplay *display, } static void -handle_idletime_for_event (const ClutterEvent *event) +handle_idletime_for_event (MetaDisplay *display, + const ClutterEvent *event) { - MetaBackend *backend = meta_get_backend (); + MetaContext *context = meta_display_get_context (display); + MetaBackend *backend = meta_context_get_backend (context); MetaIdleManager *idle_manager; if (clutter_event_get_device (event) == NULL) @@ -144,7 +150,8 @@ sequence_is_pointer_emulated (MetaDisplay *display, return TRUE; #ifdef HAVE_NATIVE_BACKEND - MetaBackend *backend = meta_get_backend (); + MetaContext *context = meta_display_get_context (display); + MetaBackend *backend = meta_context_get_backend (context); /* When using Clutter's native input backend there is no concept of * pointer emulating sequence, we still must make up our own to be @@ -213,7 +220,9 @@ meta_display_handle_event (MetaDisplay *display, const ClutterEvent *event, ClutterActor *event_actor) { - MetaBackend *backend = meta_get_backend (); + MetaContext *context = meta_display_get_context (display); + MetaBackend *backend = meta_context_get_backend (context); + ClutterInputDevice *device; MetaWindow *window = NULL; gboolean bypass_clutter = FALSE; G_GNUC_UNUSED gboolean bypass_wayland = FALSE; @@ -251,6 +260,9 @@ meta_display_handle_event (MetaDisplay *display, } } + device = clutter_event_get_device (event); + clutter_input_pointer_a11y_update (device, event); + sequence = clutter_event_get_event_sequence (event); /* Set the pointer emulating sequence on touch begin, if eligible */ @@ -312,7 +324,7 @@ meta_display_handle_event (MetaDisplay *display, if (event->type != CLUTTER_DEVICE_ADDED && event->type != CLUTTER_DEVICE_REMOVED) - handle_idletime_for_event (event); + handle_idletime_for_event (display, event); #ifdef HAVE_WAYLAND if (wayland_compositor && event->type == CLUTTER_MOTION) @@ -398,7 +410,7 @@ meta_display_handle_event (MetaDisplay *display, */ if (display->event_route == META_EVENT_ROUTE_NORMAL) { - if (IS_KEY_EVENT (event) && !stage_has_key_focus ()) + if (IS_KEY_EVENT (event) && !stage_has_key_focus (display)) { bypass_wayland = TRUE; goto out; -- GitLab From d04c1c4ec685360069f786c436c18db6e9e70c3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Mar 2022 11:54:30 +0100 Subject: [PATCH 04/40] barrier: Some coding style cleanup --- src/backends/meta-barrier.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index 94a4b7964e8..ca4f9607f1b 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -227,35 +227,45 @@ meta_barrier_class_init (MetaBarrierClass *klass) "Display", "The display to construct the pointer barrier on", META_TYPE_DISPLAY, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_props[PROP_X1] = g_param_spec_int ("x1", "X1", "The first X coordinate of the barrier", 0, G_MAXSHORT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_props[PROP_Y1] = g_param_spec_int ("y1", "Y1", "The first Y coordinate of the barrier", 0, G_MAXSHORT, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_props[PROP_X2] = g_param_spec_int ("x2", "X2", "The second X coordinate of the barrier", 0, G_MAXSHORT, G_MAXSHORT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_props[PROP_Y2] = g_param_spec_int ("y2", "Y2", "The second Y coordinate of the barrier", 0, G_MAXSHORT, G_MAXSHORT, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_props[PROP_DIRECTIONS] = g_param_spec_flags ("directions", @@ -263,7 +273,9 @@ meta_barrier_class_init (MetaBarrierClass *klass) "A set of directions to let the pointer through", META_TYPE_BARRIER_DIRECTION, 0, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); -- GitLab From 4092b6f9babc4fd1644fdddd3ef70c321e781c27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Mar 2022 22:41:40 +0100 Subject: [PATCH 05/40] barrier: Remove _ prefix in various places It's not a convention used anywhere else anymore, lets remove the prefixes. --- src/backends/meta-barrier-private.h | 6 +++--- src/backends/meta-barrier.c | 8 ++++---- src/backends/native/meta-barrier-native.c | 16 ++++++++-------- src/backends/x11/meta-barrier-x11.c | 18 +++++++++--------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/backends/meta-barrier-private.h b/src/backends/meta-barrier-private.h index d0483e43c6a..dd68c4f1df6 100644 --- a/src/backends/meta-barrier-private.h +++ b/src/backends/meta-barrier-private.h @@ -47,10 +47,10 @@ struct _MetaBarrierImplClass void (*destroy) (MetaBarrierImpl *barrier); }; -void _meta_barrier_emit_hit_signal (MetaBarrier *barrier, +void meta_barrier_emit_hit_signal (MetaBarrier *barrier, + MetaBarrierEvent *event); +void meta_barrier_emit_left_signal (MetaBarrier *barrier, MetaBarrierEvent *event); -void _meta_barrier_emit_left_signal (MetaBarrier *barrier, - MetaBarrierEvent *event); void meta_barrier_event_unref (MetaBarrierEvent *event); diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index ca4f9607f1b..2f892f4e3ad 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -334,15 +334,15 @@ meta_barrier_init (MetaBarrier *barrier) } void -_meta_barrier_emit_hit_signal (MetaBarrier *barrier, - MetaBarrierEvent *event) +meta_barrier_emit_hit_signal (MetaBarrier *barrier, + MetaBarrierEvent *event) { g_signal_emit (barrier, obj_signals[HIT], 0, event); } void -_meta_barrier_emit_left_signal (MetaBarrier *barrier, - MetaBarrierEvent *event) +meta_barrier_emit_left_signal (MetaBarrier *barrier, + MetaBarrierEvent *event) { g_signal_emit (barrier, obj_signals[LEFT], 0, event); } diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 2e17c49c73c..47463e4aa06 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -356,9 +356,9 @@ static gboolean emit_event_idle (MetaBarrierIdleData *idle_data) { if (idle_data->state == META_BARRIER_STATE_HELD) - _meta_barrier_emit_hit_signal (idle_data->barrier, idle_data->event); + meta_barrier_emit_hit_signal (idle_data->barrier, idle_data->event); else - _meta_barrier_emit_left_signal (idle_data->barrier, idle_data->event); + meta_barrier_emit_left_signal (idle_data->barrier, idle_data->event); meta_barrier_event_unref (idle_data->event); @@ -573,7 +573,7 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager, } static gboolean -_meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) +meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); @@ -581,7 +581,7 @@ _meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) } static void -_meta_barrier_impl_native_release (MetaBarrierImpl *impl, +meta_barrier_impl_native_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); @@ -592,7 +592,7 @@ _meta_barrier_impl_native_release (MetaBarrierImpl *impl, } static void -_meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) +meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); @@ -631,9 +631,9 @@ meta_barrier_impl_native_class_init (MetaBarrierImplNativeClass *klass) { MetaBarrierImplClass *impl_class = META_BARRIER_IMPL_CLASS (klass); - impl_class->is_active = _meta_barrier_impl_native_is_active; - impl_class->release = _meta_barrier_impl_native_release; - impl_class->destroy = _meta_barrier_impl_native_destroy; + impl_class->is_active = meta_barrier_impl_native_is_active; + impl_class->release = meta_barrier_impl_native_release; + impl_class->destroy = meta_barrier_impl_native_destroy; } static void diff --git a/src/backends/x11/meta-barrier-x11.c b/src/backends/x11/meta-barrier-x11.c index 998aefb38f0..ebbf1dabb01 100644 --- a/src/backends/x11/meta-barrier-x11.c +++ b/src/backends/x11/meta-barrier-x11.c @@ -53,7 +53,7 @@ G_DEFINE_TYPE (MetaBarrierImplX11, META_TYPE_BARRIER_IMPL) static gboolean -_meta_barrier_impl_x11_is_active (MetaBarrierImpl *impl) +meta_barrier_impl_x11_is_active (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); @@ -61,8 +61,8 @@ _meta_barrier_impl_x11_is_active (MetaBarrierImpl *impl) } static void -_meta_barrier_impl_x11_release (MetaBarrierImpl *impl, - MetaBarrierEvent *event) +meta_barrier_impl_x11_release (MetaBarrierImpl *impl, + MetaBarrierEvent *event) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); MetaDisplay *display = self->barrier->priv->display; @@ -77,7 +77,7 @@ _meta_barrier_impl_x11_release (MetaBarrierImpl *impl, } static void -_meta_barrier_impl_x11_destroy (MetaBarrierImpl *impl) +meta_barrier_impl_x11_destroy (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); MetaDisplay *display = self->barrier->priv->display; @@ -154,10 +154,10 @@ meta_barrier_fire_xevent (MetaBarrier *barrier, switch (xevent->evtype) { case XI_BarrierHit: - _meta_barrier_emit_hit_signal (barrier, event); + meta_barrier_emit_hit_signal (barrier, event); break; case XI_BarrierLeave: - _meta_barrier_emit_left_signal (barrier, event); + meta_barrier_emit_left_signal (barrier, event); break; default: g_assert_not_reached (); @@ -201,9 +201,9 @@ meta_barrier_impl_x11_class_init (MetaBarrierImplX11Class *klass) { MetaBarrierImplClass *impl_class = META_BARRIER_IMPL_CLASS (klass); - impl_class->is_active = _meta_barrier_impl_x11_is_active; - impl_class->release = _meta_barrier_impl_x11_release; - impl_class->destroy = _meta_barrier_impl_x11_destroy; + impl_class->is_active = meta_barrier_impl_x11_is_active; + impl_class->release = meta_barrier_impl_x11_release; + impl_class->destroy = meta_barrier_impl_x11_destroy; } static void -- GitLab From 63ba7650699e2ee04efaba07022b3d8566b4edcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Mar 2022 00:39:53 +0100 Subject: [PATCH 06/40] screen-cast: Remove stray newline --- src/backends/meta-screen-cast.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c index 6162b8d2769..119c8712fc2 100644 --- a/src/backends/meta-screen-cast.c +++ b/src/backends/meta-screen-cast.c @@ -359,7 +359,6 @@ meta_screen_cast_new (MetaBackend *backend, return screen_cast; } - static void meta_screen_cast_init (MetaScreenCast *screen_cast) { -- GitLab From 54c0002b9c3301b5edf04db5320c613a6fb6a5ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Mar 2022 22:19:19 +0100 Subject: [PATCH 07/40] compositor/dnd: Fix indentation --- src/compositor/meta-dnd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compositor/meta-dnd.c b/src/compositor/meta-dnd.c index 0edc797e70e..4053ccaeaf9 100644 --- a/src/compositor/meta-dnd.c +++ b/src/compositor/meta-dnd.c @@ -222,7 +222,7 @@ meta_dnd_handle_xdnd_event (MetaBackend *backend, return TRUE; } - return FALSE; + return FALSE; } #ifdef HAVE_WAYLAND -- GitLab From 46b3c33c4b74ceb5a5a0ee427acf3f42293a9a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Mar 2022 00:35:14 +0100 Subject: [PATCH 08/40] dbus-session: Rename client_vanished() to close() This means the MetaDbusSession interface takes a more active role instead of being something that more or less sends signals to the interface implementor. This will allow better control when using MetaDbusSession to manage these sessions, instead of their non-abstract variants. --- src/backends/meta-dbus-session-watcher.c | 18 ++++++--------- src/backends/meta-dbus-session-watcher.h | 4 +++- src/backends/meta-remote-desktop-session.c | 27 +++++++++++----------- src/backends/meta-remote-desktop-session.h | 2 -- src/backends/meta-remote-desktop.c | 4 ++-- src/backends/meta-screen-cast-session.c | 19 ++++++--------- src/backends/meta-screen-cast-session.h | 2 -- src/backends/meta-screen-cast.c | 4 ++-- 8 files changed, 34 insertions(+), 46 deletions(-) diff --git a/src/backends/meta-dbus-session-watcher.c b/src/backends/meta-dbus-session-watcher.c index a885b423be5..9ebcb3335e8 100644 --- a/src/backends/meta-dbus-session-watcher.c +++ b/src/backends/meta-dbus-session-watcher.c @@ -57,12 +57,6 @@ typedef struct _MetaDbusSessionClient GList *sessions; } MetaDbusSessionClient; -static void -meta_dbus_session_client_vanished (MetaDbusSession *session) -{ - META_DBUS_SESSION_GET_IFACE (session)->client_vanished (session); -} - static void meta_dbus_session_client_destroy (MetaDbusSessionClient *client) { @@ -77,11 +71,7 @@ meta_dbus_session_client_destroy (MetaDbusSessionClient *client) session = l->data; - /* - * This will invoke on_session_closed which removes the session from the - * list. - */ - meta_dbus_session_client_vanished (session); + meta_dbus_session_close (session); } if (client->name_watcher_id) @@ -235,3 +225,9 @@ meta_dbus_session_watcher_class_init (MetaDbusSessionWatcherClass *klass) object_class->finalize = meta_dbus_session_watcher_finalize; } + +void +meta_dbus_session_close (MetaDbusSession *session) +{ + META_DBUS_SESSION_GET_IFACE (session)->close (session); +} diff --git a/src/backends/meta-dbus-session-watcher.h b/src/backends/meta-dbus-session-watcher.h index 06d3e1ff9a3..03cd7b33e54 100644 --- a/src/backends/meta-dbus-session-watcher.h +++ b/src/backends/meta-dbus-session-watcher.h @@ -34,7 +34,7 @@ struct _MetaDbusSessionInterface { GTypeInterface parent_iface; - void (* client_vanished) (MetaDbusSession *session); + void (* close) (MetaDbusSession *session); }; #define META_TYPE_DBUS_SESSION_WATCHER (meta_dbus_session_watcher_get_type ()) @@ -49,4 +49,6 @@ void meta_dbus_session_watcher_watch_session (MetaDbusSessionWatcher *session_wa void meta_dbus_session_notify_closed (MetaDbusSession *session); +void meta_dbus_session_close (MetaDbusSession *session); + #endif /* META_DBUS_SESSION_WATCHER_H */ diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c index caee7aa6a7a..def441c01e0 100644 --- a/src/backends/meta-remote-desktop-session.c +++ b/src/backends/meta-remote-desktop-session.c @@ -196,9 +196,11 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, return TRUE; } -void -meta_remote_desktop_session_close (MetaRemoteDesktopSession *session) +static void +meta_remote_desktop_session_close (MetaDbusSession *dbus_session) { + MetaRemoteDesktopSession *session = + META_REMOTE_DESKTOP_SESSION (dbus_session); MetaDBusRemoteDesktopSession *skeleton = META_DBUS_REMOTE_DESKTOP_SESSION (session); @@ -206,9 +208,12 @@ meta_remote_desktop_session_close (MetaRemoteDesktopSession *session) if (session->screen_cast_session) { + MetaDbusSession *screen_cast_session = + META_DBUS_SESSION (session->screen_cast_session); + g_clear_signal_handler (&session->screen_cast_session_closed_handler_id, session->screen_cast_session); - meta_screen_cast_session_close (session->screen_cast_session); + meta_dbus_session_close (screen_cast_session); session->screen_cast_session = NULL; } @@ -248,7 +253,7 @@ on_screen_cast_session_closed (MetaScreenCastSession *screen_cast_session, MetaRemoteDesktopSession *session) { session->screen_cast_session = NULL; - meta_remote_desktop_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } gboolean @@ -373,7 +378,7 @@ handle_start (MetaDBusRemoteDesktopSession *skeleton, error->message); g_error_free (error); - meta_remote_desktop_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); return TRUE; } @@ -405,7 +410,7 @@ handle_stop (MetaDBusRemoteDesktopSession *skeleton, return TRUE; } - meta_remote_desktop_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); meta_dbus_remote_desktop_session_complete_stop (skeleton, invocation); @@ -1664,16 +1669,10 @@ meta_remote_desktop_session_init_iface (MetaDBusRemoteDesktopSessionIface *iface iface->handle_selection_read = handle_selection_read; } -static void -meta_remote_desktop_session_client_vanished (MetaDbusSession *dbus_session) -{ - meta_remote_desktop_session_close (META_REMOTE_DESKTOP_SESSION (dbus_session)); -} - static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { - iface->client_vanished = meta_remote_desktop_session_client_vanished; + iface->close = meta_remote_desktop_session_close; } static void @@ -1748,7 +1747,7 @@ meta_remote_desktop_session_handle_stop (MetaRemoteAccessHandle *handle) if (!session) return; - meta_remote_desktop_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } static void diff --git a/src/backends/meta-remote-desktop-session.h b/src/backends/meta-remote-desktop-session.h index 7af9c48977a..dec331bd210 100644 --- a/src/backends/meta-remote-desktop-session.h +++ b/src/backends/meta-remote-desktop-session.h @@ -51,8 +51,6 @@ void meta_remote_desktop_session_request_transfer (MetaRemoteDesktopSession *se const char *mime_type, GTask *task); -void meta_remote_desktop_session_close (MetaRemoteDesktopSession *session); - MetaRemoteDesktopSession * meta_remote_desktop_session_new (MetaRemoteDesktop *remote_desktop, const char *peer_name, GError **error); diff --git a/src/backends/meta-remote-desktop.c b/src/backends/meta-remote-desktop.c index f0a49981842..cb4af4e000f 100644 --- a/src/backends/meta-remote-desktop.c +++ b/src/backends/meta-remote-desktop.c @@ -88,7 +88,7 @@ meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop) MetaRemoteDesktopSession *session = value; g_hash_table_iter_steal (&iter); - meta_remote_desktop_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } } } @@ -262,7 +262,7 @@ on_prepare_shutdown (MetaBackend *backend, MetaRemoteDesktopSession *session = value; g_hash_table_iter_steal (&iter); - meta_remote_desktop_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } } diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c index d96fb6709f4..ea091966df1 100644 --- a/src/backends/meta-screen-cast-session.c +++ b/src/backends/meta-screen-cast-session.c @@ -129,9 +129,10 @@ meta_screen_cast_session_is_active (MetaScreenCastSession *session) return session->is_active; } -void -meta_screen_cast_session_close (MetaScreenCastSession *session) +static void +meta_screen_cast_session_close (MetaDbusSession *dbus_session) { + MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (dbus_session); MetaDBusScreenCastSession *skeleton = META_DBUS_SCREEN_CAST_SESSION (session); session->is_active = FALSE; @@ -286,7 +287,7 @@ handle_stop (MetaDBusScreenCastSession *skeleton, return TRUE; } - meta_screen_cast_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); meta_dbus_screen_cast_session_complete_stop (skeleton, invocation); @@ -297,7 +298,7 @@ static void on_stream_closed (MetaScreenCastStream *stream, MetaScreenCastSession *session) { - meta_screen_cast_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } static gboolean @@ -706,16 +707,10 @@ meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface) iface->handle_record_virtual = handle_record_virtual; } -static void -meta_screen_cast_session_client_vanished (MetaDbusSession *dbus_session) -{ - meta_screen_cast_session_close (META_SCREEN_CAST_SESSION (dbus_session)); -} - static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { - iface->client_vanished = meta_screen_cast_session_client_vanished; + iface->close = meta_screen_cast_session_close; } MetaScreenCastSession * @@ -818,7 +813,7 @@ meta_screen_cast_session_handle_stop (MetaRemoteAccessHandle *handle) if (!session) return; - meta_screen_cast_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } static void diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h index d9c3d0eccac..e65bad7e644 100644 --- a/src/backends/meta-screen-cast-session.h +++ b/src/backends/meta-screen-cast-session.h @@ -61,8 +61,6 @@ gboolean meta_screen_cast_session_start (MetaScreenCastSession *session, gboolean meta_screen_cast_session_is_active (MetaScreenCastSession *session); -void meta_screen_cast_session_close (MetaScreenCastSession *session); - MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path); diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c index 119c8712fc2..5c25ed28b00 100644 --- a/src/backends/meta-screen-cast.c +++ b/src/backends/meta-screen-cast.c @@ -68,7 +68,7 @@ meta_screen_cast_inhibit (MetaScreenCast *screen_cast) { MetaScreenCastSession *session = screen_cast->sessions->data; - meta_screen_cast_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } } } @@ -338,7 +338,7 @@ on_prepare_shutdown (MetaBackend *backend, if (meta_screen_cast_session_get_session_type (session) != META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP) - meta_screen_cast_session_close (session); + meta_dbus_session_close (META_DBUS_SESSION (session)); } } -- GitLab From edda3c75ac6cf7c583827d3ee288f7c46f610d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Mar 2022 00:37:11 +0100 Subject: [PATCH 09/40] dbus-session: Add get_id() vfunc It's currently not set by anything, and will only be used by non-abstract implementations of a future D-Bus interface session manager. When interface implementations gets ported to this new type, their MetaDbusSession implementations will set this vfunc. --- src/backends/meta-dbus-session-watcher.c | 6 ++++++ src/backends/meta-dbus-session-watcher.h | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/backends/meta-dbus-session-watcher.c b/src/backends/meta-dbus-session-watcher.c index 9ebcb3335e8..79f07560cc2 100644 --- a/src/backends/meta-dbus-session-watcher.c +++ b/src/backends/meta-dbus-session-watcher.c @@ -231,3 +231,9 @@ meta_dbus_session_close (MetaDbusSession *session) { META_DBUS_SESSION_GET_IFACE (session)->close (session); } + +const char * +meta_dbus_session_get_id (MetaDbusSession *session) +{ + return META_DBUS_SESSION_GET_IFACE (session)->get_id (session); +} diff --git a/src/backends/meta-dbus-session-watcher.h b/src/backends/meta-dbus-session-watcher.h index 03cd7b33e54..98aee63e4ea 100644 --- a/src/backends/meta-dbus-session-watcher.h +++ b/src/backends/meta-dbus-session-watcher.h @@ -35,6 +35,7 @@ struct _MetaDbusSessionInterface GTypeInterface parent_iface; void (* close) (MetaDbusSession *session); + const char * (* get_id) (MetaDbusSession *session); }; #define META_TYPE_DBUS_SESSION_WATCHER (meta_dbus_session_watcher_get_type ()) @@ -51,4 +52,6 @@ void meta_dbus_session_notify_closed (MetaDbusSession *session); void meta_dbus_session_close (MetaDbusSession *session); +const char * meta_dbus_session_get_id (MetaDbusSession *session); + #endif /* META_DBUS_SESSION_WATCHER_H */ -- GitLab From d7d920740a9eeba8f214eb7f20d80c0e3226ec33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Mar 2022 00:20:27 +0100 Subject: [PATCH 10/40] backends: Add generic D-Bus session manager helper class This class is intended to be used as a base class for D-bus interface implementations that deal with "session" objects, i.e. a D-Bus object representing a certain session of some kind, e.g. a screen cast session. It handles things such as hooking up to the D-Bus client watcher, generates IDs, handles shutdown procedures. --- src/backends/meta-backend-private.h | 2 + src/backends/meta-backend-types.h | 3 + src/backends/meta-backend.c | 10 +- src/backends/meta-dbus-session-manager.c | 507 +++++++++++++++++++++++ src/backends/meta-dbus-session-manager.h | 58 +++ src/meson.build | 6 +- 6 files changed, 583 insertions(+), 3 deletions(-) create mode 100644 src/backends/meta-dbus-session-manager.c create mode 100644 src/backends/meta-dbus-session-manager.h diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h index e9e94e044df..8500b292121 100644 --- a/src/backends/meta-backend-private.h +++ b/src/backends/meta-backend-private.h @@ -136,6 +136,8 @@ META_EXPORT_TEST MetaRenderer * meta_backend_get_renderer (MetaBackend *backend); MetaEgl * meta_backend_get_egl (MetaBackend *backend); +MetaDbusSessionWatcher * meta_backend_get_dbus_session_watcher (MetaBackend *backend); + #ifdef HAVE_REMOTE_DESKTOP MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend); diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h index 6febe4dfa37..0a71c8eff43 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -65,6 +65,9 @@ typedef struct _MetaVirtualModeInfo MetaVirtualModeInfo; typedef struct _MetaIdleManager MetaIdleManager; +typedef struct _MetaDbusSession MetaDbusSession; +typedef struct _MetaDbusSessionWatcher MetaDbusSessionWatcher; + #ifdef HAVE_REMOTE_DESKTOP typedef struct _MetaRemoteDesktop MetaRemoteDesktop; #endif diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index cf6a04943e8..789404700e7 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -149,9 +149,9 @@ struct _MetaBackendPrivate MetaEgl *egl; #endif MetaSettings *settings; + MetaDbusSessionWatcher *dbus_session_watcher; #ifdef HAVE_REMOTE_DESKTOP MetaRemoteAccessController *remote_access_controller; - MetaDbusSessionWatcher *dbus_session_watcher; MetaScreenCast *screen_cast; MetaRemoteDesktop *remote_desktop; #endif @@ -1348,6 +1348,14 @@ meta_backend_get_settings (MetaBackend *backend) return priv->settings; } +MetaDbusSessionWatcher * +meta_backend_get_dbus_session_watcher (MetaBackend *backend) +{ + MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); + + return priv->dbus_session_watcher; +} + #ifdef HAVE_REMOTE_DESKTOP /** * meta_backend_get_remote_desktop: (skip) diff --git a/src/backends/meta-dbus-session-manager.c b/src/backends/meta-dbus-session-manager.c new file mode 100644 index 00000000000..72e4e26dc8e --- /dev/null +++ b/src/backends/meta-dbus-session-manager.c @@ -0,0 +1,507 @@ +/* + * Copyright (C) 2015-2017, 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "backends/meta-dbus-session-manager.h" + +#include + +#include "backends/meta-backend-private.h" +#include "backends/meta-dbus-session-watcher.h" + +enum +{ + PROP_0, + + PROP_BACKEND, + PROP_SERVICE_NAME, + PROP_SERVICE_PATH, + PROP_SESSION_GTYPE, + PROP_INTERFACE_SKELETON, + + N_PROPS +}; + +static GParamSpec *obj_props[N_PROPS]; + +typedef struct _MetaDbusSessionManagerPrivate +{ + GObject parent; + + MetaBackend *backend; + char *service_name; + char *service_path; + + GType session_gtype; + + guint dbus_name_id; + GDBusInterfaceSkeleton *interface_skeleton; + + int inhibit_count; + + GHashTable *sessions; +} MetaDbusSessionManagerPrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (MetaDbusSessionManager, + meta_dbus_session_manager, + G_TYPE_OBJECT) + +static void +on_prepare_shutdown (MetaBackend *backend, + MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + GHashTableIter iter; + gpointer value; + + g_hash_table_iter_init (&iter, priv->sessions); + while (g_hash_table_iter_next (&iter, NULL, &value)) + { + MetaDbusSession *session = META_DBUS_SESSION (value); + + g_hash_table_iter_steal (&iter); + meta_dbus_session_close (session); + } +} + +static void +meta_dbus_session_manager_finalize (GObject *object) +{ + MetaDbusSessionManager *session_manager = META_DBUS_SESSION_MANAGER (object); + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + g_clear_handle_id (&priv->dbus_name_id, g_bus_unown_name); + + g_assert (g_hash_table_size (priv->sessions) == 0); + g_hash_table_destroy (priv->sessions); + + g_clear_pointer (&priv->service_name, g_free); + g_clear_pointer (&priv->service_path, g_free); + + g_clear_object (&priv->interface_skeleton); + + G_OBJECT_CLASS (meta_dbus_session_manager_parent_class)->finalize (object); +} + +static void +on_bus_acquired (GDBusConnection *connection, + const char *name, + gpointer user_data) +{ + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (user_data); + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + g_autoptr (GError) error = NULL; + + meta_topic (META_DEBUG_BACKEND, + "Acquired D-Bus name '%s', exporting service on '%s'", + priv->service_name, priv->service_path); + + if (!g_dbus_interface_skeleton_export (priv->interface_skeleton, + connection, + priv->service_path, + &error)) + { + g_warning ("Failed to export '%s' object on '%s': %s", + priv->service_name, + priv->service_path, + error->message); + } +} + +static void +meta_dbus_session_manager_constructed (GObject *object) +{ + MetaDbusSessionManager *session_manager = META_DBUS_SESSION_MANAGER (object); + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + priv->dbus_name_id = + g_bus_own_name (G_BUS_TYPE_SESSION, + priv->service_name, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + NULL, + NULL, + session_manager, + NULL); + + g_signal_connect (priv->backend, "prepare-shutdown", + G_CALLBACK (on_prepare_shutdown), + session_manager); + + G_OBJECT_CLASS (meta_dbus_session_manager_parent_class)->constructed (object); +} + +static void +meta_dbus_session_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaDbusSessionManager *session_manager = META_DBUS_SESSION_MANAGER (object); + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + switch (prop_id) + { + case PROP_BACKEND: + priv->backend = g_value_get_object (value); + break; + case PROP_SERVICE_NAME: + priv->service_name = g_value_dup_string (value); + break; + case PROP_SERVICE_PATH: + priv->service_path = g_value_dup_string (value); + break; + case PROP_SESSION_GTYPE: + priv->session_gtype = g_value_get_gtype (value); + break; + case PROP_INTERFACE_SKELETON: + priv->interface_skeleton = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_dbus_session_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaDbusSessionManager *session_manager = META_DBUS_SESSION_MANAGER (object); + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + switch (prop_id) + { + case PROP_BACKEND: + g_value_set_object (value, priv->backend); + break; + case PROP_SERVICE_NAME: + g_value_set_string (value, priv->service_name); + break; + case PROP_SERVICE_PATH: + g_value_set_string (value, priv->service_path); + break; + case PROP_SESSION_GTYPE: + g_value_set_gtype (value, priv->session_gtype); + break; + case PROP_INTERFACE_SKELETON: + g_value_set_object (value, priv->interface_skeleton); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_dbus_session_manager_class_init (MetaDbusSessionManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_dbus_session_manager_finalize; + object_class->constructed = meta_dbus_session_manager_constructed; + object_class->set_property = meta_dbus_session_manager_set_property; + object_class->get_property = meta_dbus_session_manager_get_property; + + obj_props[PROP_BACKEND] = + g_param_spec_object ("backend", + "backend", + "MetaBackend", + META_TYPE_BACKEND, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_SERVICE_NAME] = + g_param_spec_string ("service-name", + "service name", + "Service name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_SERVICE_PATH] = + g_param_spec_string ("service-path", + "service path", + "Service path", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_SESSION_GTYPE] = + g_param_spec_gtype ("session-gtype", + "session gtype", + "GType to construct for a session", + G_TYPE_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_INTERFACE_SKELETON] = + g_param_spec_object ("interface-skeleton", + "interface skeleton", + "GDBusInterfaceSkeleton", + G_TYPE_DBUS_INTERFACE_SKELETON, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPS, obj_props); +} + +static void +meta_dbus_session_manager_init (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + priv->sessions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); +} + +static void +on_session_closed (MetaDbusSession *session, + MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + const char *session_id; + + session_id = meta_dbus_session_get_id (session); + g_hash_table_remove (priv->sessions, session_id); +} + +static char * +generate_session_id (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + g_autoptr (GRand) rand = NULL; + char *session_id; + + rand = g_rand_new (); + + while (TRUE) + { + session_id = meta_generate_random_id (rand, 32); + if (g_hash_table_lookup (priv->sessions, session_id)) + g_free (session_id); + else + return session_id; + } +} + +static void +append_property (GObjectClass *object_class, + GArray *names, + GArray *values, + const char *name, + ...) +{ + va_list var_args; + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + GType ptype; + char *error = NULL; + + va_start (var_args, name); + + pspec = g_object_class_find_property (object_class, name); + g_assert (pspec); + + ptype = G_PARAM_SPEC_VALUE_TYPE (pspec); + G_VALUE_COLLECT_INIT (&value, ptype, var_args, 0, &error); + g_assert (!error); + + g_array_append_val (names, name); + g_array_append_val (values, value); + va_end (var_args); +} + +MetaDbusSession * +meta_dbus_session_manager_create_session (MetaDbusSessionManager *session_manager, + GDBusMethodInvocation *invocation, + GError **error, + ...) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + MetaDbusSessionWatcher *session_watcher = + meta_backend_get_dbus_session_watcher (priv->backend); + GObject *session; + va_list var_args; + GObjectClass *object_class; + g_autoptr (GArray) names = NULL; + g_autoptr (GArray) values = NULL; + const char *property_name; + const char *peer_name; + g_autofree char *session_id = NULL; + const char *client_dbus_name; + + if (priv->inhibit_count > 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Session creation inhibited"); + return NULL; + } + + peer_name = g_dbus_method_invocation_get_sender (invocation); + object_class = g_type_class_ref (priv->session_gtype); + + va_start (var_args, error); + names = g_array_new (FALSE, FALSE, sizeof (const char *)); + values = g_array_new (FALSE, FALSE, sizeof (GValue)); + g_array_set_clear_func (values, (GDestroyNotify) g_value_unset); + + property_name = va_arg (var_args, const char *); + while (property_name) + { + GValue value = G_VALUE_INIT; + GParamSpec *pspec; + GType ptype; + gchar *error = NULL; + + pspec = g_object_class_find_property (object_class, + property_name); + g_assert (pspec); + + ptype = G_PARAM_SPEC_VALUE_TYPE (pspec); + G_VALUE_COLLECT_INIT (&value, ptype, var_args, 0, &error); + g_assert (!error); + + g_array_append_val (names, property_name); + g_array_append_val (values, value); + + property_name = va_arg (var_args, const char *); + } + + va_end (var_args); + + append_property (object_class, names, values, "session-manager", + session_manager); + append_property (object_class, names, values, "peer-name", peer_name); + + session_id = generate_session_id (session_manager); + append_property (object_class, names, values, "id", session_id); + + g_type_class_unref (object_class); + + session = g_object_new_with_properties (priv->session_gtype, + values->len, + (const char **) names->data, + (const GValue *) values->data); + if (!g_initable_init (G_INITABLE (session), NULL, error)) + { + g_object_unref (session); + return NULL; + } + + g_hash_table_insert (priv->sessions, + g_strdup (session_id), + session); + + client_dbus_name = g_dbus_method_invocation_get_sender (invocation); + meta_dbus_session_watcher_watch_session (session_watcher, + client_dbus_name, + META_DBUS_SESSION (session)); + + g_signal_connect (session, "session-closed", + G_CALLBACK (on_session_closed), + session_manager); + + return META_DBUS_SESSION (session); +} + +MetaDbusSession * +meta_dbus_session_manager_get_session (MetaDbusSessionManager *session_manager, + const char *session_id) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + return g_hash_table_lookup (priv->sessions, session_id); +} + +void +meta_dbus_session_manager_inhibit (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + priv->inhibit_count++; + if (priv->inhibit_count == 1) + { + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, priv->sessions); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + MetaDbusSession *session = META_DBUS_SESSION (value); + + g_hash_table_iter_steal (&iter); + meta_dbus_session_close (session); + } + } +} + +void +meta_dbus_session_manager_uninhibit (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + g_return_if_fail (priv->inhibit_count > 0); + + priv->inhibit_count--; +} + +MetaBackend * +meta_dbus_session_manager_get_backend (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + return priv->backend; +} + +GDBusConnection * +meta_dbus_session_manager_get_connection (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + return g_dbus_interface_skeleton_get_connection (priv->interface_skeleton); +} + +GDBusInterfaceSkeleton * +meta_dbus_session_manager_get_interface_skeleton (MetaDbusSessionManager *session_manager) +{ + MetaDbusSessionManagerPrivate *priv = + meta_dbus_session_manager_get_instance_private (session_manager); + + return priv->interface_skeleton; +} diff --git a/src/backends/meta-dbus-session-manager.h b/src/backends/meta-dbus-session-manager.h new file mode 100644 index 00000000000..da2f027d20a --- /dev/null +++ b/src/backends/meta-dbus-session-manager.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015-2017, 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_DBUS_SERVICE_MANAGER_H +#define META_DBUS_SERVICE_MANAGER_H + +#include +#include + +#include "backends/meta-backend-types.h" + +#define META_TYPE_DBUS_SESSION_MANAGER (meta_dbus_session_manager_get_type ()) +G_DECLARE_DERIVABLE_TYPE (MetaDbusSessionManager, + meta_dbus_session_manager, + META, DBUS_SESSION_MANAGER, + GObject) + +struct _MetaDbusSessionManagerClass +{ + GObjectClass parent_class; +}; + +MetaDbusSession * meta_dbus_session_manager_create_session (MetaDbusSessionManager *session_manager, + GDBusMethodInvocation *invocation, + GError **error, + ...); + +MetaDbusSession * meta_dbus_session_manager_get_session (MetaDbusSessionManager *session_manager, + const char *session_id); + +void meta_dbus_session_manager_inhibit (MetaDbusSessionManager *session_manager); + +void meta_dbus_session_manager_uninhibit (MetaDbusSessionManager *session_manager); + +MetaBackend * meta_dbus_session_manager_get_backend (MetaDbusSessionManager *session_manager); + +GDBusConnection * meta_dbus_session_manager_get_connection (MetaDbusSessionManager *session_manager); + +GDBusInterfaceSkeleton * meta_dbus_session_manager_get_interface_skeleton (MetaDbusSessionManager *session_manager); + +#endif /* META_DBUS_SERVICE_MANAGER_H */ diff --git a/src/meson.build b/src/meson.build index 13a69c1a631..d36ec5774da 100644 --- a/src/meson.build +++ b/src/meson.build @@ -193,6 +193,10 @@ mutter_sources = [ 'backends/meta-cursor-sprite-xcursor.h', 'backends/meta-cursor-tracker.c', 'backends/meta-cursor-tracker-private.h', + 'backends/meta-dbus-session-manager.c', + 'backends/meta-dbus-session-manager.h', + 'backends/meta-dbus-session-watcher.c', + 'backends/meta-dbus-session-watcher.h', 'backends/meta-display-config-shared.h', 'backends/meta-dnd-private.h', 'backends/meta-gpu.c', @@ -489,8 +493,6 @@ endif if have_remote_desktop mutter_sources += [ - 'backends/meta-dbus-session-watcher.c', - 'backends/meta-dbus-session-watcher.h', 'backends/meta-remote-desktop.c', 'backends/meta-remote-desktop.h', 'backends/meta-remote-desktop-session.c', -- GitLab From f0210ba9413fbe86b1ec60b08bd4165219b2c1b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 11 Mar 2022 09:39:07 +0100 Subject: [PATCH 11/40] Port screen cast and remote desktop to MetaDbusSessionManager This eliminates some code duplication related to managing D-Bus session objects. --- src/backends/meta-backend-types.h | 1 + src/backends/meta-backend.c | 22 +- .../meta-remote-access-controller-private.h | 6 +- src/backends/meta-remote-access-controller.c | 47 +-- src/backends/meta-remote-desktop-session.c | 202 ++++++++---- src/backends/meta-remote-desktop-session.h | 6 - src/backends/meta-remote-desktop.c | 300 ++++-------------- src/backends/meta-remote-desktop.h | 17 +- .../meta-screen-cast-area-stream-src.c | 1 + src/backends/meta-screen-cast-session.c | 196 ++++++++++-- src/backends/meta-screen-cast-session.h | 5 - src/backends/meta-screen-cast.c | 268 ++++------------ src/backends/meta-screen-cast.h | 12 +- src/meson.build | 1 + 14 files changed, 497 insertions(+), 587 deletions(-) diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h index 0a71c8eff43..127ee251931 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -66,6 +66,7 @@ typedef struct _MetaVirtualModeInfo MetaVirtualModeInfo; typedef struct _MetaIdleManager MetaIdleManager; typedef struct _MetaDbusSession MetaDbusSession; +typedef struct _MetaDbusSessionManager MetaDbusSessionManager; typedef struct _MetaDbusSessionWatcher MetaDbusSessionWatcher; #ifdef HAVE_REMOTE_DESKTOP diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 789404700e7..1169ba29e52 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -224,9 +224,9 @@ meta_backend_dispose (GObject *object) #ifdef HAVE_REMOTE_DESKTOP g_clear_object (&priv->remote_desktop); g_clear_object (&priv->screen_cast); +#endif g_clear_object (&priv->dbus_session_watcher); g_clear_object (&priv->remote_access_controller); -#endif #ifdef HAVE_LIBWACOM g_clear_pointer (&priv->wacom_db, libwacom_database_destroy); @@ -566,14 +566,20 @@ meta_backend_real_post_init (MetaBackend *backend) input_settings); } -#ifdef HAVE_REMOTE_DESKTOP - priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL); - priv->screen_cast = meta_screen_cast_new (backend, - priv->dbus_session_watcher); - priv->remote_desktop = meta_remote_desktop_new (backend, - priv->dbus_session_watcher); priv->remote_access_controller = - meta_remote_access_controller_new (priv->remote_desktop, priv->screen_cast); + meta_remote_access_controller_new (); + priv->dbus_session_watcher = + g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL); + +#ifdef HAVE_REMOTE_DESKTOP + priv->screen_cast = meta_screen_cast_new (backend); + meta_remote_access_controller_add ( + priv->remote_access_controller, + META_DBUS_SESSION_MANAGER (priv->screen_cast)); + priv->remote_desktop = meta_remote_desktop_new (backend); + meta_remote_access_controller_add ( + priv->remote_access_controller, + META_DBUS_SESSION_MANAGER (priv->remote_desktop)); #endif /* HAVE_REMOTE_DESKTOP */ if (!meta_monitor_manager_is_headless (priv->monitor_manager)) diff --git a/src/backends/meta-remote-access-controller-private.h b/src/backends/meta-remote-access-controller-private.h index 8c8a319c405..be301a5e997 100644 --- a/src/backends/meta-remote-access-controller-private.h +++ b/src/backends/meta-remote-access-controller-private.h @@ -24,8 +24,10 @@ #include "backends/meta-backend-types.h" #include "meta/meta-remote-access-controller.h" -MetaRemoteAccessController * meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop, - MetaScreenCast *screen_cast); +MetaRemoteAccessController * meta_remote_access_controller_new (void); + +void meta_remote_access_controller_add (MetaRemoteAccessController *controller, + MetaDbusSessionManager *session_manager); void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller, MetaRemoteAccessHandle *handle); diff --git a/src/backends/meta-remote-access-controller.c b/src/backends/meta-remote-access-controller.c index b2ac93d888c..db0d1bd5d30 100644 --- a/src/backends/meta-remote-access-controller.c +++ b/src/backends/meta-remote-access-controller.c @@ -22,6 +22,8 @@ #include "backends/meta-remote-access-controller-private.h" +#include "backends/meta-dbus-session-manager.h" + #ifdef HAVE_REMOTE_DESKTOP #include "backends/meta-remote-desktop.h" #include "backends/meta-screen-cast.h" @@ -73,8 +75,7 @@ struct _MetaRemoteAccessController { GObject parent; - MetaRemoteDesktop *remote_desktop; - MetaScreenCast *screen_cast; + GList *session_managers; }; G_DEFINE_TYPE (MetaRemoteAccessController, @@ -153,10 +154,14 @@ meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *con void meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller) { -#ifdef HAVE_REMOTE_DESKTOP - meta_remote_desktop_inhibit (controller->remote_desktop); - meta_screen_cast_inhibit (controller->screen_cast); -#endif + GList *l; + + for (l = controller->session_managers; l; l = l->next) + { + MetaDbusSessionManager *session_manager = l->data; + + meta_dbus_session_manager_inhibit (session_manager); + } } /** @@ -170,24 +175,28 @@ meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController void meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller) { -#ifdef HAVE_REMOTE_DESKTOP - meta_screen_cast_uninhibit (controller->screen_cast); - meta_remote_desktop_uninhibit (controller->remote_desktop); -#endif + GList *l; + + for (l = controller->session_managers; l; l = l->next) + { + MetaDbusSessionManager *session_manager = l->data; + + meta_dbus_session_manager_uninhibit (session_manager); + } } MetaRemoteAccessController * -meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop, - MetaScreenCast *screen_cast) +meta_remote_access_controller_new (void) { - MetaRemoteAccessController *remote_access_controller; - - remote_access_controller = g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, - NULL); - remote_access_controller->remote_desktop = remote_desktop; - remote_access_controller->screen_cast = screen_cast; + return g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL); +} - return remote_access_controller; +void +meta_remote_access_controller_add (MetaRemoteAccessController *controller, + MetaDbusSessionManager *session_manager) +{ + controller->session_managers = g_list_append (controller->session_managers, + session_manager); } static void diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c index def441c01e0..d80493e5e91 100644 --- a/src/backends/meta-remote-desktop-session.c +++ b/src/backends/meta-remote-desktop-session.c @@ -34,6 +34,7 @@ #include #include "backends/meta-dbus-session-watcher.h" +#include "backends/meta-dbus-session-manager.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-remote-access-controller-private.h" #include "backends/x11/meta-backend-x11.h" @@ -49,6 +50,19 @@ #define TRANSFER_REQUEST_CLEANUP_TIMEOUT_MS (s2ms (15)) +enum +{ + PROP_0, + + PROP_SESSION_MANAGER, + PROP_PEER_NAME, + PROP_ID, + + N_PROPS +}; + +static GParamSpec *obj_props[N_PROPS]; + typedef enum _MetaRemoteDesktopNotifyAxisFlags { META_REMOTE_DESKTOP_NOTIFY_AXIS_FLAGS_NONE = 0, @@ -69,7 +83,7 @@ struct _MetaRemoteDesktopSession { MetaDBusRemoteDesktopSessionSkeleton parent; - MetaRemoteDesktop *remote_desktop; + MetaDbusSessionManager *session_manager; GDBusConnection *connection; char *peer_name; @@ -96,6 +110,8 @@ struct _MetaRemoteDesktopSession guint transfer_request_timeout_id; }; +static void initable_init_iface (GInitableIface *iface); + static void meta_remote_desktop_session_init_iface (MetaDBusRemoteDesktopSessionIface *iface); @@ -105,6 +121,8 @@ meta_dbus_session_init_iface (MetaDbusSessionInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktopSession, meta_remote_desktop_session, META_DBUS_TYPE_REMOTE_DESKTOP_SESSION_SKELETON, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_init_iface) G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP_SESSION, meta_remote_desktop_session_init_iface) G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION, @@ -149,8 +167,8 @@ static void ensure_virtual_device (MetaRemoteDesktopSession *session, ClutterInputDeviceType device_type) { - MetaRemoteDesktop *remote_desktop = session->remote_desktop; - MetaBackend *backend = meta_remote_desktop_get_backend (remote_desktop); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend); ClutterVirtualInputDevice **virtual_device_ptr = NULL; @@ -236,16 +254,18 @@ meta_remote_desktop_session_close (MetaDbusSession *dbus_session) g_object_unref (session); } -char * -meta_remote_desktop_session_get_object_path (MetaRemoteDesktopSession *session) +static const char * +meta_remote_desktop_session_get_id (MetaDbusSession *dbus_session) { - return session->object_path; + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (dbus_session); + + return session->session_id; } char * -meta_remote_desktop_session_get_session_id (MetaRemoteDesktopSession *session) +meta_remote_desktop_session_get_object_path (MetaRemoteDesktopSession *session) { - return session->session_id; + return session->object_path; } static void @@ -278,44 +298,6 @@ meta_remote_desktop_session_register_screen_cast (MetaRemoteDesktopSession *ses return TRUE; } -MetaRemoteDesktopSession * -meta_remote_desktop_session_new (MetaRemoteDesktop *remote_desktop, - const char *peer_name, - GError **error) -{ - MetaBackend *backend = meta_remote_desktop_get_backend (remote_desktop); - ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); - ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend); - ClutterKeymap *keymap = clutter_seat_get_keymap (seat); - GDBusInterfaceSkeleton *interface_skeleton; - MetaRemoteDesktopSession *session; - - session = g_object_new (META_TYPE_REMOTE_DESKTOP_SESSION, NULL); - - session->remote_desktop = remote_desktop; - session->peer_name = g_strdup (peer_name); - - interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); - session->connection = meta_remote_desktop_get_connection (remote_desktop); - if (!g_dbus_interface_skeleton_export (interface_skeleton, - session->connection, - session->object_path, - error)) - { - g_object_unref (session); - return NULL; - } - - g_object_bind_property (keymap, "caps-lock-state", - session, "caps-lock-state", - G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); - g_object_bind_property (keymap, "num-lock-state", - session, "num-lock-state", - G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); - - return session; -} - static gboolean check_permission (MetaRemoteDesktopSession *session, GDBusMethodInvocation *invocation) @@ -1646,6 +1628,46 @@ handle_selection_read (MetaDBusRemoteDesktopSession *skeleton, return TRUE; } +static gboolean +meta_remote_desktop_session_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (initable); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + ClutterKeymap *keymap = clutter_seat_get_keymap (seat); + GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); + MetaDBusRemoteDesktopSession *skeleton = + META_DBUS_REMOTE_DESKTOP_SESSION (interface_skeleton); + + meta_dbus_remote_desktop_session_set_session_id (skeleton, session->session_id); + + session->connection = + meta_dbus_session_manager_get_connection (session->session_manager); + if (!g_dbus_interface_skeleton_export (interface_skeleton, + session->connection, + session->object_path, + error)) + return FALSE; + + g_object_bind_property (keymap, "caps-lock-state", + session, "caps-lock-state", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + g_object_bind_property (keymap, "num-lock-state", + session, "num-lock-state", + G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); + + return TRUE; +} + +static void +initable_init_iface (GInitableIface *iface) +{ + iface->init = meta_remote_desktop_session_initable_init; +} + static void meta_remote_desktop_session_init_iface (MetaDBusRemoteDesktopSessionIface *iface) { @@ -1673,6 +1695,7 @@ static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { iface->close = meta_remote_desktop_session_close; + iface->get_id = meta_remote_desktop_session_get_id; } static void @@ -1698,18 +1721,59 @@ meta_remote_desktop_session_finalize (GObject *object) } static void -meta_remote_desktop_session_init (MetaRemoteDesktopSession *session) +meta_remote_desktop_session_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - MetaDBusRemoteDesktopSession *skeleton = - META_DBUS_REMOTE_DESKTOP_SESSION (session); - GRand *rand; - static unsigned int global_session_number = 0; + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (object); - rand = g_rand_new (); - session->session_id = meta_generate_random_id (rand, 32); - g_rand_free (rand); + switch (prop_id) + { + case PROP_SESSION_MANAGER: + session->session_manager = g_value_get_object (value); + break; + case PROP_PEER_NAME: + session->peer_name = g_value_dup_string (value); + break; + case PROP_ID: + session->session_id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} - meta_dbus_remote_desktop_session_set_session_id (skeleton, session->session_id); +static void +meta_remote_desktop_session_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (object); + + switch (prop_id) + { + case PROP_SESSION_MANAGER: + g_value_set_object (value, session->session_manager); + break; + case PROP_PEER_NAME: + g_value_set_string (value, session->peer_name); + break; + case PROP_ID: + g_value_set_string (value, session->session_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_remote_desktop_session_init (MetaRemoteDesktopSession *session) +{ + static unsigned int global_session_number = 0; session->object_path = g_strdup_printf (META_REMOTE_DESKTOP_SESSION_DBUS_PATH "/u%u", @@ -1725,6 +1789,34 @@ meta_remote_desktop_session_class_init (MetaRemoteDesktopSessionClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_remote_desktop_session_finalize; + object_class->set_property = meta_remote_desktop_session_set_property; + object_class->get_property = meta_remote_desktop_session_get_property; + + obj_props[PROP_SESSION_MANAGER] = + g_param_spec_object ("session-manager", + "session manager", + "D-Bus session manager", + META_TYPE_DBUS_SESSION_MANAGER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_PEER_NAME] = + g_param_spec_string ("peer-name", + "peer name", + "D-Bus peer name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_ID] = + g_param_spec_string ("id", + "session id", + "Unique ID of the session", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPS, obj_props); } static MetaRemoteDesktopSessionHandle * diff --git a/src/backends/meta-remote-desktop-session.h b/src/backends/meta-remote-desktop-session.h index dec331bd210..f3aa2a428d1 100644 --- a/src/backends/meta-remote-desktop-session.h +++ b/src/backends/meta-remote-desktop-session.h @@ -41,8 +41,6 @@ G_DECLARE_FINAL_TYPE (MetaRemoteDesktopSessionHandle, char * meta_remote_desktop_session_get_object_path (MetaRemoteDesktopSession *session); -char * meta_remote_desktop_session_get_session_id (MetaRemoteDesktopSession *session); - gboolean meta_remote_desktop_session_register_screen_cast (MetaRemoteDesktopSession *session, MetaScreenCastSession *screen_cast_session, GError **error); @@ -51,8 +49,4 @@ void meta_remote_desktop_session_request_transfer (MetaRemoteDesktopSession *se const char *mime_type, GTask *task); -MetaRemoteDesktopSession * meta_remote_desktop_session_new (MetaRemoteDesktop *remote_desktop, - const char *peer_name, - GError **error); - #endif /* META_REMOTE_DESKTOP_SESSION_H */ diff --git a/src/backends/meta-remote-desktop.c b/src/backends/meta-remote-desktop.c index cb4af4e000f..3e03dc712f9 100644 --- a/src/backends/meta-remote-desktop.c +++ b/src/backends/meta-remote-desktop.c @@ -52,256 +52,49 @@ typedef enum _MetaRemoteDesktopDeviceTypes struct _MetaRemoteDesktop { - MetaDBusRemoteDesktopSkeleton parent; - - MetaBackend *backend; - int dbus_name_id; - - int inhibit_count; - - GHashTable *sessions; - - MetaDbusSessionWatcher *session_watcher; + MetaDbusSessionManager parent; }; -static void -meta_remote_desktop_init_iface (MetaDBusRemoteDesktopIface *iface); - -G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktop, - meta_remote_desktop, - META_DBUS_TYPE_REMOTE_DESKTOP_SKELETON, - G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP, - meta_remote_desktop_init_iface)); - -void -meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop) -{ - remote_desktop->inhibit_count++; - if (remote_desktop->inhibit_count == 1) - { - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init (&iter, remote_desktop->sessions); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - MetaRemoteDesktopSession *session = value; - - g_hash_table_iter_steal (&iter); - meta_dbus_session_close (META_DBUS_SESSION (session)); - } - } -} - -void -meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop) -{ - g_return_if_fail (remote_desktop->inhibit_count > 0); - - remote_desktop->inhibit_count--; -} - -MetaBackend * -meta_remote_desktop_get_backend (MetaRemoteDesktop *remote_desktop) -{ - return remote_desktop->backend; -} - -GDBusConnection * -meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop) -{ - GDBusInterfaceSkeleton *interface_skeleton = - G_DBUS_INTERFACE_SKELETON (remote_desktop); - - return g_dbus_interface_skeleton_get_connection (interface_skeleton); -} - -MetaRemoteDesktopSession * -meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop, - const char *session_id) -{ - return g_hash_table_lookup (remote_desktop->sessions, session_id); -} - -static void -on_session_closed (MetaRemoteDesktopSession *session, - MetaRemoteDesktop *remote_desktop) -{ - char *session_id; - - session_id = meta_remote_desktop_session_get_session_id (session); - g_hash_table_remove (remote_desktop->sessions, session_id); -} +G_DEFINE_TYPE (MetaRemoteDesktop, meta_remote_desktop, + META_TYPE_DBUS_SESSION_MANAGER) static gboolean handle_create_session (MetaDBusRemoteDesktop *skeleton, - GDBusMethodInvocation *invocation) + GDBusMethodInvocation *invocation, + MetaRemoteDesktop *remote_desktop) { - MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (skeleton); - const char *peer_name; + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (remote_desktop); + MetaDbusSession *dbus_session; MetaRemoteDesktopSession *session; - GError *error = NULL; - char *session_id; + g_autoptr (GError) error = NULL; char *session_path; - const char *client_dbus_name; - - if (remote_desktop->inhibit_count > 0) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Session creation inhibited"); - return TRUE; - } - - peer_name = g_dbus_method_invocation_get_sender (invocation); - session = meta_remote_desktop_session_new (remote_desktop, - peer_name, - &error); - if (!session) + dbus_session = + meta_dbus_session_manager_create_session (session_manager, + invocation, + &error, + NULL); + if (!dbus_session) { - g_warning ("Failed to create remote desktop session: %s", - error->message); - - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_FAILED, - "Failed to create session: %s", - error->message); - g_error_free (error); - - return TRUE; + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + error->message); + return G_DBUS_METHOD_INVOCATION_HANDLED; } - session_id = meta_remote_desktop_session_get_session_id (session); - g_hash_table_insert (remote_desktop->sessions, - session_id, - session); - - client_dbus_name = g_dbus_method_invocation_get_sender (invocation); - meta_dbus_session_watcher_watch_session (remote_desktop->session_watcher, - client_dbus_name, - META_DBUS_SESSION (session)); - + session = META_REMOTE_DESKTOP_SESSION (dbus_session); session_path = meta_remote_desktop_session_get_object_path (session); meta_dbus_remote_desktop_complete_create_session (skeleton, invocation, session_path); - - g_signal_connect (session, "session-closed", - G_CALLBACK (on_session_closed), - remote_desktop); - - return TRUE; -} - -static void -meta_remote_desktop_init_iface (MetaDBusRemoteDesktopIface *iface) -{ - iface->handle_create_session = handle_create_session; -} - -static void -on_bus_acquired (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - MetaRemoteDesktop *remote_desktop = user_data; - GDBusInterfaceSkeleton *interface_skeleton = - G_DBUS_INTERFACE_SKELETON (remote_desktop); - GError *error = NULL; - - if (!g_dbus_interface_skeleton_export (interface_skeleton, - connection, - META_REMOTE_DESKTOP_DBUS_PATH, - &error)) - g_warning ("Failed to export remote desktop object: %s", error->message); -} - -static void -on_name_acquired (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - g_info ("Acquired name %s", name); -} - -static void -on_name_lost (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - g_warning ("Lost or failed to acquire name %s", name); -} - -static void -meta_remote_desktop_constructed (GObject *object) -{ - MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (object); - - remote_desktop->dbus_name_id = - g_bus_own_name (G_BUS_TYPE_SESSION, - META_REMOTE_DESKTOP_DBUS_SERVICE, - G_BUS_NAME_OWNER_FLAGS_NONE, - on_bus_acquired, - on_name_acquired, - on_name_lost, - remote_desktop, - NULL); -} - -static void -on_prepare_shutdown (MetaBackend *backend, - MetaRemoteDesktop *remote_desktop) -{ - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, remote_desktop->sessions); - while (g_hash_table_iter_next (&iter, NULL, &value)) - { - MetaRemoteDesktopSession *session = value; - - g_hash_table_iter_steal (&iter); - meta_dbus_session_close (META_DBUS_SESSION (session)); - } -} - -static void -meta_remote_desktop_finalize (GObject *object) -{ - MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (object); - - if (remote_desktop->dbus_name_id != 0) - g_bus_unown_name (remote_desktop->dbus_name_id); - - g_assert (g_hash_table_size (remote_desktop->sessions) == 0); - g_hash_table_destroy (remote_desktop->sessions); - - G_OBJECT_CLASS (meta_remote_desktop_parent_class)->finalize (object); -} - -MetaRemoteDesktop * -meta_remote_desktop_new (MetaBackend *backend, - MetaDbusSessionWatcher *session_watcher) -{ - MetaRemoteDesktop *remote_desktop; - - remote_desktop = g_object_new (META_TYPE_REMOTE_DESKTOP, NULL); - remote_desktop->backend = backend; - remote_desktop->session_watcher = session_watcher; - - g_signal_connect (backend, "prepare-shutdown", - G_CALLBACK (on_prepare_shutdown), - remote_desktop); - - return remote_desktop; + return G_DBUS_METHOD_INVOCATION_HANDLED; } static MetaRemoteDesktopDeviceTypes -calculate_supported_device_types (void) +calculate_supported_device_types (MetaBackend *backend) { - ClutterBackend *backend = clutter_get_default_backend (); - ClutterSeat *seat = clutter_backend_get_default_seat (backend); + ClutterSeat *seat = meta_backend_get_default_seat (backend); ClutterVirtualDeviceType device_types; MetaRemoteDesktopDeviceTypes supported_devices = META_REMOTE_DESKTOP_DEVICE_TYPE_NONE; @@ -320,16 +113,52 @@ calculate_supported_device_types (void) } static void -meta_remote_desktop_init (MetaRemoteDesktop *remote_desktop) +meta_remote_desktop_constructed (GObject *object) { - remote_desktop->sessions = g_hash_table_new (g_str_hash, g_str_equal); + MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (object); + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (remote_desktop); + GDBusInterfaceSkeleton *interface_skeleton = + meta_dbus_session_manager_get_interface_skeleton (session_manager); + MetaDBusRemoteDesktop *interface = + META_DBUS_REMOTE_DESKTOP (interface_skeleton); + MetaBackend *backend = meta_dbus_session_manager_get_backend (session_manager); + + g_signal_connect (interface, "handle-create-session", + G_CALLBACK (handle_create_session), remote_desktop); meta_dbus_remote_desktop_set_supported_device_types ( - META_DBUS_REMOTE_DESKTOP (remote_desktop), - calculate_supported_device_types ()); + interface, + calculate_supported_device_types (backend)); meta_dbus_remote_desktop_set_version ( - META_DBUS_REMOTE_DESKTOP (remote_desktop), + interface, META_REMOTE_DESKTOP_API_VERSION); + + G_OBJECT_CLASS (meta_remote_desktop_parent_class)->constructed (object); +} + +MetaRemoteDesktop * +meta_remote_desktop_new (MetaBackend *backend) +{ + MetaRemoteDesktop *remote_desktop; + g_autoptr (MetaDBusRemoteDesktop) skeleton = NULL; + + skeleton = meta_dbus_remote_desktop_skeleton_new (); + remote_desktop = + g_object_new (META_TYPE_REMOTE_DESKTOP, + "backend", backend, + "service-name", META_REMOTE_DESKTOP_DBUS_SERVICE, + "service-path", META_REMOTE_DESKTOP_DBUS_PATH, + "session-gtype", META_TYPE_REMOTE_DESKTOP_SESSION, + "interface-skeleton", skeleton, + NULL); + + return remote_desktop; +} + +static void +meta_remote_desktop_init (MetaRemoteDesktop *remote_desktop) +{ } static void @@ -338,5 +167,4 @@ meta_remote_desktop_class_init (MetaRemoteDesktopClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_remote_desktop_constructed; - object_class->finalize = meta_remote_desktop_finalize; } diff --git a/src/backends/meta-remote-desktop.h b/src/backends/meta-remote-desktop.h index 3a7f3856324..98bbb57c400 100644 --- a/src/backends/meta-remote-desktop.h +++ b/src/backends/meta-remote-desktop.h @@ -26,6 +26,7 @@ #include #include "backends/meta-backend-types.h" +#include "backends/meta-dbus-session-manager.h" #include "backends/meta-dbus-session-watcher.h" #include "meta-dbus-remote-desktop.h" @@ -35,20 +36,8 @@ typedef struct _MetaRemoteDesktopSession MetaRemoteDesktopSession; #define META_TYPE_REMOTE_DESKTOP (meta_remote_desktop_get_type ()) G_DECLARE_FINAL_TYPE (MetaRemoteDesktop, meta_remote_desktop, META, REMOTE_DESKTOP, - MetaDBusRemoteDesktopSkeleton) + MetaDbusSessionManager) -void meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop); - -void meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop); - -MetaBackend * meta_remote_desktop_get_backend (MetaRemoteDesktop *remote_desktop); - -MetaRemoteDesktopSession * meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop, - const char *session_id); - -GDBusConnection * meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop); - -MetaRemoteDesktop * meta_remote_desktop_new (MetaBackend *backend, - MetaDbusSessionWatcher *session_watcher); +MetaRemoteDesktop * meta_remote_desktop_new (MetaBackend *backend); #endif /* META_REMOTE_DESKTOP_H */ diff --git a/src/backends/meta-screen-cast-area-stream-src.c b/src/backends/meta-screen-cast-area-stream-src.c index aa15481010f..ac6e8910501 100644 --- a/src/backends/meta-screen-cast-area-stream-src.c +++ b/src/backends/meta-screen-cast-area-stream-src.c @@ -26,6 +26,7 @@ #include "backends/meta-backend-private.h" #include "backends/meta-cursor-tracker-private.h" +#include "backends/meta-dbus-session-manager.h" #include "backends/meta-screen-cast-area-stream.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-stage-private.h" diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c index ea091966df1..f5d93ff58a3 100644 --- a/src/backends/meta-screen-cast-session.c +++ b/src/backends/meta-screen-cast-session.c @@ -25,6 +25,7 @@ #include "backends/meta-screen-cast-session.h" #include "backends/meta-backend-private.h" +#include "backends/meta-dbus-session-manager.h" #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-remote-access-controller-private.h" #include "backends/meta-screen-cast-area-stream.h" @@ -34,18 +35,35 @@ #include "backends/meta-screen-cast-window-stream.h" #include "core/display-private.h" +#include "meta-private-enum-types.h" + #define META_SCREEN_CAST_SESSION_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Session" +enum +{ + PROP_0, + + PROP_SESSION_MANAGER, + PROP_PEER_NAME, + PROP_ID, + PROP_SESSION_TYPE, + + N_PROPS +}; + +static GParamSpec *obj_props[N_PROPS]; + struct _MetaScreenCastSession { MetaDBusScreenCastSessionSkeleton parent; - MetaScreenCast *screen_cast; + MetaDbusSessionManager *session_manager; char *peer_name; MetaScreenCastSessionType session_type; char *object_path; + char *session_id; GList *streams; @@ -55,6 +73,8 @@ struct _MetaScreenCastSession gboolean disable_animations; }; +static void initable_init_iface (GInitableIface *iface); + static void meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface); @@ -64,6 +84,8 @@ meta_dbus_session_init_iface (MetaDbusSessionInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCastSession, meta_screen_cast_session, META_DBUS_TYPE_SCREEN_CAST_SESSION_SKELETON, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_init_iface) G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST_SESSION, meta_screen_cast_session_init_iface) G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION, @@ -86,7 +108,8 @@ meta_screen_cast_session_handle_new (MetaScreenCastSession *session); static void init_remote_access_handle (MetaScreenCastSession *session) { - MetaBackend *backend = meta_get_backend (); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); MetaRemoteAccessController *remote_access_controller; MetaRemoteAccessHandle *remote_access_handle; @@ -163,6 +186,14 @@ meta_screen_cast_session_close (MetaDbusSession *dbus_session) g_object_unref (session); } +static const char * +meta_screen_cast_session_get_id (MetaDbusSession *dbus_session) +{ + MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (dbus_session); + + return session->session_id; +} + MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path) @@ -184,7 +215,7 @@ meta_screen_cast_session_get_stream (MetaScreenCastSession *session, MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session) { - return session->screen_cast; + return META_SCREEN_CAST (session->session_manager); } void @@ -220,6 +251,38 @@ check_permission (MetaScreenCastSession *session, g_dbus_method_invocation_get_sender (invocation)) == 0; } +static gboolean +meta_screen_cast_session_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (initable); + GDBusInterfaceSkeleton *interface_skeleton; + GDBusConnection *connection; + static unsigned int global_session_number = 0; + + session->object_path = + g_strdup_printf (META_SCREEN_CAST_SESSION_DBUS_PATH "/u%u", + ++global_session_number); + + interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); + connection = + meta_dbus_session_manager_get_connection (session->session_manager); + if (!g_dbus_interface_skeleton_export (interface_skeleton, + connection, + session->object_path, + error)) + return FALSE; + + return TRUE; +} + +static void +initable_init_iface (GInitableIface *iface) +{ + iface->init = meta_screen_cast_session_initable_init; +} + static gboolean handle_start (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation) @@ -576,7 +639,7 @@ handle_record_area (MetaDBusScreenCastSession *skeleton, interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton); connection = g_dbus_interface_skeleton_get_connection (interface_skeleton); - backend = meta_screen_cast_get_backend (session->screen_cast); + backend = meta_dbus_session_manager_get_backend (session->session_manager); stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); flags = META_SCREEN_CAST_FLAG_NONE; @@ -711,36 +774,7 @@ static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { iface->close = meta_screen_cast_session_close; -} - -MetaScreenCastSession * -meta_screen_cast_session_new (MetaScreenCast *screen_cast, - MetaScreenCastSessionType session_type, - const char *peer_name, - GError **error) -{ - GDBusInterfaceSkeleton *interface_skeleton; - MetaScreenCastSession *session; - GDBusConnection *connection; - static unsigned int global_session_number = 0; - - session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL); - session->screen_cast = screen_cast; - session->session_type = session_type; - session->peer_name = g_strdup (peer_name); - session->object_path = - g_strdup_printf (META_SCREEN_CAST_SESSION_DBUS_PATH "/u%u", - ++global_session_number); - - interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); - connection = meta_screen_cast_get_connection (screen_cast); - if (!g_dbus_interface_skeleton_export (interface_skeleton, - connection, - session->object_path, - error)) - return NULL; - - return session; + iface->get_id = meta_screen_cast_session_get_id; } static void @@ -751,10 +785,67 @@ meta_screen_cast_session_finalize (GObject *object) g_clear_object (&session->handle); g_free (session->peer_name); g_free (session->object_path); + g_free (session->session_id); G_OBJECT_CLASS (meta_screen_cast_session_parent_class)->finalize (object); } +static void +meta_screen_cast_session_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (object); + + switch (prop_id) + { + case PROP_SESSION_MANAGER: + session->session_manager = g_value_get_object (value); + break; + case PROP_PEER_NAME: + session->peer_name = g_value_dup_string (value); + break; + case PROP_ID: + session->session_id = g_value_dup_string (value); + break; + case PROP_SESSION_TYPE: + session->session_type = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_screen_cast_session_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (object); + + switch (prop_id) + { + case PROP_SESSION_MANAGER: + g_value_set_object (value, session->session_manager); + break; + case PROP_PEER_NAME: + g_value_set_string (value, session->peer_name); + break; + case PROP_ID: + g_value_set_string (value, session->session_id); + break; + case PROP_SESSION_TYPE: + g_value_set_enum (value, session->session_type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + static void meta_screen_cast_session_init (MetaScreenCastSession *session) { @@ -766,6 +857,43 @@ meta_screen_cast_session_class_init (MetaScreenCastSessionClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_screen_cast_session_finalize; + object_class->set_property = meta_screen_cast_session_set_property; + object_class->get_property = meta_screen_cast_session_get_property; + + obj_props[PROP_SESSION_MANAGER] = + g_param_spec_object ("session-manager", + "session manager", + "D-Bus session manager", + META_TYPE_DBUS_SESSION_MANAGER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_PEER_NAME] = + g_param_spec_string ("peer-name", + "peer name", + "D-Bus peer name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_ID] = + g_param_spec_string ("id", + "session id", + "Unique ID of the session", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_SESSION_TYPE] = + g_param_spec_enum ("session-type", + "session type", + "The type of screen cast session", + META_TYPE_SCREEN_CAST_SESSION_TYPE, + META_SCREEN_CAST_SESSION_TYPE_NORMAL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPS, obj_props); } static gboolean diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h index e65bad7e644..599f6d25b93 100644 --- a/src/backends/meta-screen-cast-session.h +++ b/src/backends/meta-screen-cast-session.h @@ -51,11 +51,6 @@ char * meta_screen_cast_session_get_peer_name (MetaScreenCastSession *session); MetaScreenCastSessionType meta_screen_cast_session_get_session_type (MetaScreenCastSession *session); -MetaScreenCastSession * meta_screen_cast_session_new (MetaScreenCast *screen_cast, - MetaScreenCastSessionType session_type, - const char *peer_name, - GError **error); - gboolean meta_screen_cast_session_start (MetaScreenCastSession *session, GError **error); diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c index 5c25ed28b00..76e8b35ca12 100644 --- a/src/backends/meta-screen-cast.c +++ b/src/backends/meta-screen-cast.c @@ -36,64 +36,20 @@ struct _MetaScreenCast { - MetaDBusScreenCastSkeleton parent; - - int dbus_name_id; - - int inhibit_count; - - GList *sessions; - - MetaDbusSessionWatcher *session_watcher; - MetaBackend *backend; + MetaDbusSessionManager parent; gboolean disable_dma_bufs; }; -static void -meta_screen_cast_init_iface (MetaDBusScreenCastIface *iface); - -G_DEFINE_TYPE_WITH_CODE (MetaScreenCast, meta_screen_cast, - META_DBUS_TYPE_SCREEN_CAST_SKELETON, - G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST, - meta_screen_cast_init_iface)) - -void -meta_screen_cast_inhibit (MetaScreenCast *screen_cast) -{ - screen_cast->inhibit_count++; - if (screen_cast->inhibit_count == 1) - { - while (screen_cast->sessions) - { - MetaScreenCastSession *session = screen_cast->sessions->data; - - meta_dbus_session_close (META_DBUS_SESSION (session)); - } - } -} - -void -meta_screen_cast_uninhibit (MetaScreenCast *screen_cast) -{ - g_return_if_fail (screen_cast->inhibit_count > 0); - - screen_cast->inhibit_count--; -} - -GDBusConnection * -meta_screen_cast_get_connection (MetaScreenCast *screen_cast) -{ - GDBusInterfaceSkeleton *interface_skeleton = - G_DBUS_INTERFACE_SKELETON (screen_cast); - - return g_dbus_interface_skeleton_get_connection (interface_skeleton); -} +G_DEFINE_TYPE (MetaScreenCast, meta_screen_cast, + META_TYPE_DBUS_SESSION_MANAGER) MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast) { - return screen_cast->backend; + MetaDbusSessionManager *session_manager = META_DBUS_SESSION_MANAGER (screen_cast); + + return meta_dbus_session_manager_get_backend (session_manager); } void @@ -107,8 +63,12 @@ meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, int width, int height) { + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (screen_cast); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session_manager); ClutterBackend *clutter_backend = - meta_backend_get_clutter_backend (screen_cast->backend); + meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); @@ -140,19 +100,28 @@ register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, { MetaScreenCast *screen_cast = meta_screen_cast_session_get_screen_cast (session); - MetaBackend *backend = meta_screen_cast_get_backend (screen_cast); + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (screen_cast); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session_manager); MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend); + MetaDbusSessionManager *remote_desktop_session_manager = + META_DBUS_SESSION_MANAGER (remote_desktop); + MetaDbusSession *remote_desktop_dbus_session; MetaRemoteDesktopSession *remote_desktop_session; - remote_desktop_session = - meta_remote_desktop_get_session (remote_desktop, remote_desktop_session_id); - if (!remote_desktop_session) + remote_desktop_dbus_session = + meta_dbus_session_manager_get_session (remote_desktop_session_manager, + remote_desktop_session_id); + if (!remote_desktop_dbus_session) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No remote desktop session found"); return FALSE; } + remote_desktop_session = + META_REMOTE_DESKTOP_SESSION (remote_desktop_dbus_session); if (!meta_remote_desktop_session_register_screen_cast (remote_desktop_session, session, error)) @@ -161,36 +130,21 @@ register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, return TRUE; } -static void -on_session_closed (MetaScreenCastSession *session, - MetaScreenCast *screen_cast) -{ - screen_cast->sessions = g_list_remove (screen_cast->sessions, session); -} - static gboolean handle_create_session (MetaDBusScreenCast *skeleton, GDBusMethodInvocation *invocation, - GVariant *properties) + GVariant *properties, + MetaScreenCast *screen_cast) { - MetaScreenCast *screen_cast = META_SCREEN_CAST (skeleton); - const char *peer_name; - MetaScreenCastSession *session; - GError *error = NULL; - const char *session_path; - const char *client_dbus_name; + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (screen_cast); char *remote_desktop_session_id = NULL; - gboolean disable_animations; MetaScreenCastSessionType session_type; - - if (screen_cast->inhibit_count > 0) - { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Session creation inhibited"); - - return TRUE; - } + MetaDbusSession *dbus_session; + MetaScreenCastSession *session; + g_autoptr (GError) error = NULL; + gboolean disable_animations; + const char *session_path; g_variant_lookup (properties, "remote-desktop-session-id", "s", &remote_desktop_session_id); @@ -200,24 +154,20 @@ handle_create_session (MetaDBusScreenCast *skeleton, else session_type = META_SCREEN_CAST_SESSION_TYPE_NORMAL; - peer_name = g_dbus_method_invocation_get_sender (invocation); - session = meta_screen_cast_session_new (screen_cast, - session_type, - peer_name, - &error); - if (!session) + dbus_session = + meta_dbus_session_manager_create_session (session_manager, + invocation, + &error, + "session-type", session_type, + NULL); + if (!dbus_session) { - g_warning ("Failed to create screen cast session: %s", - error->message); - - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_FAILED, - "Failed to create session: %s", - error->message); - g_error_free (error); - - return TRUE; + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + error->message); + return G_DBUS_METHOD_INVOCATION_HANDLED; } + session = META_SCREEN_CAST_SESSION (dbus_session); if (remote_desktop_session_id) { @@ -228,9 +178,8 @@ handle_create_session (MetaDBusScreenCast *skeleton, g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s", error->message); - g_error_free (error); - g_object_unref (session); - return TRUE; + meta_dbus_session_close (dbus_session); + return G_DBUS_METHOD_INVOCATION_HANDLED; } } @@ -240,121 +189,46 @@ handle_create_session (MetaDBusScreenCast *skeleton, meta_screen_cast_session_set_disable_animations (session, disable_animations); } - - client_dbus_name = g_dbus_method_invocation_get_sender (invocation); - meta_dbus_session_watcher_watch_session (screen_cast->session_watcher, - client_dbus_name, - META_DBUS_SESSION (session)); - session_path = meta_screen_cast_session_get_object_path (session); meta_dbus_screen_cast_complete_create_session (skeleton, invocation, session_path); - - screen_cast->sessions = g_list_append (screen_cast->sessions, session); - - g_signal_connect (session, "session-closed", - G_CALLBACK (on_session_closed), - screen_cast); - - return TRUE; -} - -static void -meta_screen_cast_init_iface (MetaDBusScreenCastIface *iface) -{ - iface->handle_create_session = handle_create_session; -} - -static void -on_bus_acquired (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - MetaScreenCast *screen_cast = user_data; - GDBusInterfaceSkeleton *interface_skeleton = - G_DBUS_INTERFACE_SKELETON (screen_cast); - GError *error = NULL; - - if (!g_dbus_interface_skeleton_export (interface_skeleton, - connection, - META_SCREEN_CAST_DBUS_PATH, - &error)) - g_warning ("Failed to export remote desktop object: %s", error->message); -} - -static void -on_name_acquired (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - g_info ("Acquired name %s", name); -} - -static void -on_name_lost (GDBusConnection *connection, - const char *name, - gpointer user_data) -{ - g_warning ("Lost or failed to acquire name %s", name); + return G_DBUS_METHOD_INVOCATION_HANDLED; } static void meta_screen_cast_constructed (GObject *object) { MetaScreenCast *screen_cast = META_SCREEN_CAST (object); + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (screen_cast); + GDBusInterfaceSkeleton *interface_skeleton = + meta_dbus_session_manager_get_interface_skeleton (session_manager); + MetaDBusScreenCast *skeleton = META_DBUS_SCREEN_CAST (interface_skeleton); - screen_cast->dbus_name_id = - g_bus_own_name (G_BUS_TYPE_SESSION, - META_SCREEN_CAST_DBUS_SERVICE, - G_BUS_NAME_OWNER_FLAGS_NONE, - on_bus_acquired, - on_name_acquired, - on_name_lost, - screen_cast, - NULL); -} - -static void -meta_screen_cast_finalize (GObject *object) -{ - MetaScreenCast *screen_cast = META_SCREEN_CAST (object); - - if (screen_cast->dbus_name_id) - g_bus_unown_name (screen_cast->dbus_name_id); - - g_assert (!screen_cast->sessions); - - G_OBJECT_CLASS (meta_screen_cast_parent_class)->finalize (object); -} + g_signal_connect (interface_skeleton, "handle-create-session", + G_CALLBACK (handle_create_session), screen_cast); -static void -on_prepare_shutdown (MetaBackend *backend, - MetaScreenCast *screen_cast) -{ - while (screen_cast->sessions) - { - MetaScreenCastSession *session = screen_cast->sessions->data; + meta_dbus_screen_cast_set_version (skeleton, META_SCREEN_CAST_API_VERSION); - if (meta_screen_cast_session_get_session_type (session) != - META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP) - meta_dbus_session_close (META_DBUS_SESSION (session)); - } + G_OBJECT_CLASS (meta_screen_cast_parent_class)->constructed (object); } MetaScreenCast * -meta_screen_cast_new (MetaBackend *backend, - MetaDbusSessionWatcher *session_watcher) +meta_screen_cast_new (MetaBackend *backend) { MetaScreenCast *screen_cast; - - screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL); - screen_cast->backend = backend; - screen_cast->session_watcher = session_watcher; - - g_signal_connect (backend, "prepare-shutdown", - G_CALLBACK (on_prepare_shutdown), - screen_cast); + g_autoptr (MetaDBusScreenCast) skeleton = NULL; + + skeleton = meta_dbus_screen_cast_skeleton_new (); + screen_cast = + g_object_new (META_TYPE_SCREEN_CAST, + "backend", backend, + "service-name", META_SCREEN_CAST_DBUS_SERVICE, + "service-path", META_SCREEN_CAST_DBUS_PATH, + "session-gtype", META_TYPE_SCREEN_CAST_SESSION, + "interface-skeleton", skeleton, + NULL); return screen_cast; } @@ -369,9 +243,6 @@ meta_screen_cast_init (MetaScreenCast *screen_cast) pw_init (NULL, NULL); is_pipewire_initialized = TRUE; } - - meta_dbus_screen_cast_set_version (META_DBUS_SCREEN_CAST (screen_cast), - META_SCREEN_CAST_API_VERSION); } static void @@ -380,5 +251,4 @@ meta_screen_cast_class_init (MetaScreenCastClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_screen_cast_constructed; - object_class->finalize = meta_screen_cast_finalize; } diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h index be297e28da5..a68c9d175f3 100644 --- a/src/backends/meta-screen-cast.h +++ b/src/backends/meta-screen-cast.h @@ -26,6 +26,7 @@ #include #include "backends/meta-backend-private.h" +#include "backends/meta-dbus-session-manager.h" #include "backends/meta-dbus-session-watcher.h" #include "meta-dbus-screen-cast.h" @@ -47,13 +48,7 @@ typedef enum _MetaScreenCastFlag #define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, META, SCREEN_CAST, - MetaDBusScreenCastSkeleton) - -void meta_screen_cast_inhibit (MetaScreenCast *screen_cast); - -void meta_screen_cast_uninhibit (MetaScreenCast *screen_cast); - -GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); + MetaDbusSessionManager) MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); @@ -63,7 +58,6 @@ CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *scree int width, int height); -MetaScreenCast * meta_screen_cast_new (MetaBackend *backend, - MetaDbusSessionWatcher *session_watcher); +MetaScreenCast * meta_screen_cast_new (MetaBackend *backend); #endif /* META_SCREEN_CAST_H */ diff --git a/src/meson.build b/src/meson.build index d36ec5774da..fff4a715aee 100644 --- a/src/meson.build +++ b/src/meson.build @@ -802,6 +802,7 @@ mutter_private_enum_sources = [] if have_remote_desktop mutter_private_enum_sources += [ 'backends/meta-screen-cast.h', + 'backends/meta-screen-cast-session.h', ] endif -- GitLab From 990f3062719bc9519bd6fa8cc0a16ea2d5a0cf15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Mar 2022 22:15:30 +0100 Subject: [PATCH 12/40] x11/cm: Stop using intermediate bypass_clutter variable There is no need to use the 'bypass-*' method of event processing in the changed function since in all cases the 'bypass-*' variable was set, any following event processing functions would ignore the event anyway. Simplify things a bit by just returning TRUE if the event is consumed. --- src/backends/x11/cm/meta-backend-x11-cm.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/backends/x11/cm/meta-backend-x11-cm.c b/src/backends/x11/cm/meta-backend-x11-cm.c index ed2b7bb8bba..d66ae8342fc 100644 --- a/src/backends/x11/cm/meta-backend-x11-cm.c +++ b/src/backends/x11/cm/meta-backend-x11-cm.c @@ -364,21 +364,20 @@ meta_backend_x11_cm_lock_layout_group (MetaBackend *backend, } static gboolean -meta_backend_x11_cm_handle_host_xevent (MetaBackendX11 *backend_x11, +meta_backend_x11_cm_handle_host_xevent (MetaBackendX11 *x11, XEvent *event) { - MetaBackend *backend = META_BACKEND (backend_x11); - MetaBackendX11 *x11 = META_BACKEND_X11 (backend); + MetaBackend *backend = META_BACKEND (x11); + MetaContext *context = meta_backend_get_context (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); - gboolean bypass_clutter = FALSE; MetaDisplay *display; - display = meta_get_display (); + display = meta_context_get_display (context); if (display) { MetaCompositor *compositor = display->compositor; @@ -387,7 +386,7 @@ meta_backend_x11_cm_handle_host_xevent (MetaBackendX11 *backend_x11, if (meta_dnd_handle_xdnd_event (backend, compositor_x11, xdisplay, event)) - bypass_clutter = TRUE; + return TRUE; } if (event->type == meta_backend_x11_get_xkb_event_base (x11)) @@ -412,10 +411,10 @@ meta_backend_x11_cm_handle_host_xevent (MetaBackendX11 *backend_x11, } } - bypass_clutter |= - meta_monitor_manager_xrandr_handle_xevent (monitor_manager_xrandr, event); + if (meta_monitor_manager_xrandr_handle_xevent (monitor_manager_xrandr, event)) + return TRUE; - return bypass_clutter; + return FALSE; } static void -- GitLab From 7e4532e9d0db9f5a34ad37721705484c9a4a3b71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 6 Apr 2022 11:32:51 +0200 Subject: [PATCH 13/40] x11-display: Use autoptr during construction This simplifies error paths, as they don't need to unref. --- src/x11/meta-x11-display.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c index 5343633ce4c..f21ad5ff701 100644 --- a/src/x11/meta-x11-display.c +++ b/src/x11/meta-x11-display.c @@ -1134,9 +1134,10 @@ on_window_visibility_updated (MetaDisplay *display, * it already has a window manager, and sets the error appropriately. */ MetaX11Display * -meta_x11_display_new (MetaDisplay *display, GError **error) +meta_x11_display_new (MetaDisplay *display, + GError **error) { - MetaX11Display *x11_display; + g_autoptr (MetaX11Display) x11_display = NULL; Display *xdisplay; Screen *xscreen; Window xroot; @@ -1413,7 +1414,6 @@ meta_x11_display_new (MetaDisplay *display, GError **error) "Failed to acquire window manager ownership"); g_object_run_dispose (G_OBJECT (x11_display)); - g_clear_object (&x11_display); return NULL; } @@ -1422,7 +1422,7 @@ meta_x11_display_new (MetaDisplay *display, GError **error) x11_display->wm_sn_atom = wm_sn_atom; x11_display->wm_sn_timestamp = timestamp; - return x11_display; + return g_steal_pointer (&x11_display); } void -- GitLab From f58c52dc3ef0141b18cdf07633ac38844f6e7545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 6 Apr 2022 11:34:09 +0200 Subject: [PATCH 14/40] x11-display: Use proper error reporting instead of meta_fatal() There are two hard requirements regarding X11 extensions: XFixes 5.0 and XInput 2.2. When these were not met, meta_fatal() was called, instead of the error reporting we already have in place. Lets get rid of the meta_fatal()'s. --- src/x11/meta-x11-display.c | 39 ++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c index f21ad5ff701..ccdf0208be6 100644 --- a/src/x11/meta-x11-display.c +++ b/src/x11/meta-x11-display.c @@ -390,8 +390,9 @@ query_xdamage_extension (MetaX11Display *x11_display) x11_display->damage_event_base); } -static void -query_xfixes_extension (MetaX11Display *x11_display) +static gboolean +query_xfixes_extension (MetaX11Display *x11_display, + GError **error) { x11_display->xfixes_error_base = 0; x11_display->xfixes_event_base = 0; @@ -405,20 +406,28 @@ query_xfixes_extension (MetaX11Display *x11_display) XFixesQueryVersion (x11_display->xdisplay, &xfixes_major, &xfixes_minor); if (xfixes_major * 100 + xfixes_minor < 500) - meta_fatal ("Mutter requires XFixes 5.0"); + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "XFixes version 5.0 required"); + return FALSE; + } } else { - meta_fatal ("Mutter requires XFixes 5.0"); + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "XFixes version 5.0 required"); + return FALSE; } meta_verbose ("Attempted to init XFixes, found error base %d event base %d", x11_display->xfixes_error_base, x11_display->xfixes_event_base); + return TRUE; } -static void -query_xi_extension (MetaX11Display *x11_display) +static gboolean +query_xi_extension (MetaX11Display *x11_display, + GError **error) { int major = 2, minor = 3; gboolean has_xi = FALSE; @@ -429,7 +438,7 @@ query_xi_extension (MetaX11Display *x11_display) &x11_display->xinput_error_base, &x11_display->xinput_event_base)) { - if (XIQueryVersion (x11_display->xdisplay, &major, &minor) == Success) + if (XIQueryVersion (x11_display->xdisplay, &major, &minor) == Success) { int version = (major * 10) + minor; if (version >= 22) @@ -441,7 +450,13 @@ query_xi_extension (MetaX11Display *x11_display) } if (!has_xi) - meta_fatal ("X server doesn't have the XInput extension, version 2.2 or newer"); + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "XInput version 2.2 required"); + return FALSE; + } + + return TRUE; } /* @@ -1248,8 +1263,12 @@ meta_x11_display_new (MetaDisplay *display, query_xshape_extension (x11_display); query_xcomposite_extension (x11_display); query_xdamage_extension (x11_display); - query_xfixes_extension (x11_display); - query_xi_extension (x11_display); + + if (!query_xfixes_extension (x11_display, error)) + return NULL; + + if (!query_xi_extension (x11_display, error)) + return NULL; g_signal_connect_object (display, "cursor-updated", -- GitLab From 7ff51259bf37539436c77803e0cca11ccb18cea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Mon, 14 Mar 2022 22:45:34 +0100 Subject: [PATCH 15/40] barriers: Make barriers fully part of the backend Prior to this commit, barriers were created with a MetaDisplay pointer, despite being entities related and owned by the backend. In the X11 case, it was also not hooked up to the backend X11 connection, but the clutter one, meaning for example that the logic was active (but dormant) also for the Xwayland connection. Fix this by moving X11 barrier management and event processing fully to the backend. Also replace passing a display pointer with passing a backend pointer. Keep the display pointer around for a release, but mark it as deprecated. --- src/backends/meta-backend-private.h | 8 ++ src/backends/meta-backend-types.h | 3 + src/backends/meta-backend.c | 27 ++++++ src/backends/meta-barrier-private.h | 4 +- src/backends/meta-barrier.c | 69 +++++++++++-- src/backends/native/meta-backend-native.c | 7 ++ src/backends/native/meta-barrier-native.c | 4 +- src/backends/x11/cm/meta-backend-x11-cm.c | 16 +++ src/backends/x11/meta-backend-x11.c | 94 ++++++++++++++---- src/backends/x11/meta-backend-x11.h | 3 + src/backends/x11/meta-barrier-x11.c | 97 ++++++++++++------- src/backends/x11/meta-barrier-x11.h | 8 ++ .../x11/nested/meta-backend-x11-nested.c | 7 ++ src/core/display.c | 17 +--- src/meta/display.h | 1 + src/meta/meta-backend.h | 9 ++ src/x11/events.c | 6 -- src/x11/meta-x11-display-private.h | 5 - src/x11/meta-x11-display.c | 10 +- 19 files changed, 293 insertions(+), 102 deletions(-) diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h index 8500b292121..82343eaf072 100644 --- a/src/backends/meta-backend-private.h +++ b/src/backends/meta-backend-private.h @@ -63,6 +63,8 @@ struct _MetaBackendClass void (* post_init) (MetaBackend *backend); + MetaBackendCapabilities (* get_capabilities) (MetaBackend *backend); + MetaMonitorManager * (* create_monitor_manager) (MetaBackend *backend, GError **error); MetaCursorRenderer * (* get_cursor_renderer) (MetaBackend *backend, @@ -104,6 +106,9 @@ struct _MetaBackendClass void (* update_screen_size) (MetaBackend *backend, int width, int height); void (* select_stage_events) (MetaBackend *backend); + MetaBarrierImpl * (* create_barrier_impl) (MetaBackend *backend, + MetaBarrier *barrier); + void (* set_pointer_constraint) (MetaBackend *backend, MetaPointerConstraint *constraint); @@ -164,6 +169,9 @@ xkb_layout_index_t meta_backend_get_keymap_layout_group (MetaBackend *backend); gboolean meta_backend_is_lid_closed (MetaBackend *backend); +MetaBarrierImpl * meta_backend_create_barrier_impl (MetaBackend *backend, + MetaBarrier *barrier); + MetaPointerConstraint * meta_backend_get_client_pointer_constraint (MetaBackend *backend); void meta_backend_set_client_pointer_constraint (MetaBackend *backend, MetaPointerConstraint *constraint); diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h index 127ee251931..4640ff3614d 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -63,6 +63,9 @@ typedef struct _MetaVirtualMonitor MetaVirtualMonitor; typedef struct _MetaVirtualMonitorInfo MetaVirtualMonitorInfo; typedef struct _MetaVirtualModeInfo MetaVirtualModeInfo; +typedef struct _MetaBarrier MetaBarrier; +typedef struct _MetaBarrierImpl MetaBarrierImpl; + typedef struct _MetaIdleManager MetaIdleManager; typedef struct _MetaDbusSession MetaDbusSession; diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 1169ba29e52..b24226cb049 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -53,6 +53,7 @@ #include +#include "backends/meta-barrier-private.h" #include "backends/meta-cursor-renderer.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-idle-manager.h" @@ -96,6 +97,7 @@ enum PROP_0, PROP_CONTEXT, + PROP_CAPABILITIES, N_PROPS }; @@ -846,6 +848,9 @@ meta_backend_get_property (GObject *object, case PROP_CONTEXT: g_value_set_object (value, priv->context); break; + case PROP_CAPABILITIES: + g_value_set_flags (value, meta_backend_get_capabilities (backend)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -879,6 +884,14 @@ meta_backend_class_init (MetaBackendClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_props[PROP_CAPABILITIES] = + g_param_spec_flags ("capabilities", + "capabilities", + "Backend capabilities", + META_TYPE_BACKEND_CAPABILITIES, + META_BACKEND_CAPABILITY_NONE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); signals[KEYMAP_CHANGED] = @@ -1557,6 +1570,14 @@ meta_backend_set_client_pointer_constraint (MetaBackend *backend, g_set_object (&priv->client_pointer_constraint, constraint); } +MetaBarrierImpl * +meta_backend_create_barrier_impl (MetaBackend *backend, + MetaBarrier *barrier) +{ + return META_BACKEND_GET_CLASS (backend)->create_barrier_impl (backend, + barrier); +} + ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend) { @@ -1576,6 +1597,12 @@ meta_backend_prepare_shutdown (MetaBackend *backend) g_signal_emit (backend, signals[PREPARE_SHUTDOWN], 0); } +MetaBackendCapabilities +meta_backend_get_capabilities (MetaBackend *backend) +{ + return META_BACKEND_GET_CLASS (backend)->get_capabilities (backend); +} + /** * meta_is_stage_views_enabled: * diff --git a/src/backends/meta-barrier-private.h b/src/backends/meta-barrier-private.h index dd68c4f1df6..16bfcdcd0da 100644 --- a/src/backends/meta-barrier-private.h +++ b/src/backends/meta-barrier-private.h @@ -54,11 +54,13 @@ void meta_barrier_emit_left_signal (MetaBarrier *barrier, void meta_barrier_event_unref (MetaBarrierEvent *event); +MetaBackend * meta_barrier_get_backend (MetaBarrier *barrier); + G_END_DECLS struct _MetaBarrierPrivate { - MetaDisplay *display; + MetaBackend *backend; MetaBorder border; MetaBarrierImpl *impl; }; diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index 2f892f4e3ad..777287cc999 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -39,6 +39,7 @@ enum { PROP_0, + PROP_BACKEND, PROP_DISPLAY, PROP_X1, @@ -62,6 +63,21 @@ enum static guint obj_signals[LAST_SIGNAL]; +static MetaBackend * +backend_from_display (MetaDisplay *display) +{ + MetaContext *context = meta_display_get_context (display); + + return meta_context_get_backend (context); +} + +static MetaDisplay * +display_from_backend (MetaBackend *backend) +{ + MetaContext *context = meta_backend_get_context (backend); + + return meta_context_get_display (context); +} static void meta_barrier_get_property (GObject *object, @@ -71,10 +87,14 @@ meta_barrier_get_property (GObject *object, { MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; + switch (prop_id) { + case PROP_BACKEND: + g_value_set_object (value, priv->backend); + break; case PROP_DISPLAY: - g_value_set_object (value, priv->display); + g_value_set_object (value, display_from_backend (priv->backend)); break; case PROP_X1: g_value_set_int (value, priv->border.line.a.x); @@ -108,9 +128,18 @@ meta_barrier_set_property (GObject *object, MetaBarrierPrivate *priv = barrier->priv; switch (prop_id) { - case PROP_DISPLAY: - priv->display = g_value_get_object (value); + case PROP_BACKEND: + priv->backend = g_value_get_object (value); break; + case PROP_DISPLAY: + { + MetaDisplay *display; + + display = g_value_get_object (value); + if (display) + priv->backend = backend_from_display (g_value_get_object (value)); + break; + } case PROP_X1: priv->border.line.a.x = g_value_get_int (value); break; @@ -182,11 +211,11 @@ meta_barrier_release (MetaBarrier *barrier, } static void -meta_barrier_constructed (GObject *object) +init_barrier_impl (MetaBarrier *barrier) { - MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; + g_return_if_fail (priv->backend); g_return_if_fail (priv->border.line.a.x == priv->border.line.b.x || priv->border.line.a.y == priv->border.line.b.y); g_return_if_fail (priv->border.line.a.x >= 0); @@ -195,15 +224,22 @@ meta_barrier_constructed (GObject *object) g_return_if_fail (priv->border.line.b.y >= 0); #if defined(HAVE_NATIVE_BACKEND) - if (META_IS_BACKEND_NATIVE (meta_get_backend ())) + if (META_IS_BACKEND_NATIVE (priv->backend)) priv->impl = meta_barrier_impl_native_new (barrier); #endif - if (META_IS_BACKEND_X11 (meta_get_backend ()) && + if (META_IS_BACKEND_X11 (priv->backend) && !meta_is_wayland_compositor ()) priv->impl = meta_barrier_impl_x11_new (barrier); - if (priv->impl == NULL) - g_warning ("Created a non-working barrier"); + g_warn_if_fail (priv->impl); +} + +static void +meta_barrier_constructed (GObject *object) +{ + MetaBarrier *barrier = META_BARRIER (object); + + init_barrier_impl (barrier); /* Take a ref that we'll release in destroy() so that the object stays * alive while active. */ @@ -222,11 +258,20 @@ meta_barrier_class_init (MetaBarrierClass *klass) object_class->dispose = meta_barrier_dispose; object_class->constructed = meta_barrier_constructed; + obj_props[PROP_BACKEND] = + g_param_spec_object ("backend", + "backend", + "The backend", + META_TYPE_BACKEND, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); obj_props[PROP_DISPLAY] = g_param_spec_object ("display", "Display", "The display to construct the pointer barrier on", META_TYPE_DISPLAY, + G_PARAM_DEPRECATED | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); @@ -347,6 +392,12 @@ meta_barrier_emit_left_signal (MetaBarrier *barrier, g_signal_emit (barrier, obj_signals[LEFT], 0, event); } +MetaBackend * +meta_barrier_get_backend (MetaBarrier *barrier) +{ + return barrier->priv->backend; +} + static void meta_barrier_impl_class_init (MetaBarrierImplClass *klass) { diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index 22453878766..3ff78f39f7c 100644 --- a/src/backends/native/meta-backend-native.c +++ b/src/backends/native/meta-backend-native.c @@ -251,6 +251,12 @@ meta_backend_native_post_init (MetaBackend *backend) update_viewports (backend); } +static MetaBackendCapabilities +meta_backend_native_get_capabilities (MetaBackend *backend) +{ + return META_BACKEND_CAPABILITY_BARRIERS; +} + static MetaMonitorManager * meta_backend_native_create_monitor_manager (MetaBackend *backend, GError **error) @@ -675,6 +681,7 @@ meta_backend_native_class_init (MetaBackendNativeClass *klass) backend_class->create_default_seat = meta_backend_native_create_default_seat; backend_class->post_init = meta_backend_native_post_init; + backend_class->get_capabilities = meta_backend_native_get_capabilities; backend_class->create_monitor_manager = meta_backend_native_create_monitor_manager; backend_class->get_cursor_renderer = meta_backend_native_get_cursor_renderer; diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 47463e4aa06..63acb6a7961 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -606,10 +606,10 @@ meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) MetaBarrierImpl * meta_barrier_impl_native_new (MetaBarrier *barrier) { + MetaBackend *backend = meta_barrier_get_backend (barrier); + ClutterSeat *seat = meta_backend_get_default_seat (backend); MetaBarrierImplNative *self; MetaBarrierManagerNative *manager; - ClutterBackend *backend = clutter_get_default_backend (); - ClutterSeat *seat = clutter_backend_get_default_seat (backend); self = g_object_new (META_TYPE_BARRIER_IMPL_NATIVE, NULL); diff --git a/src/backends/x11/cm/meta-backend-x11-cm.c b/src/backends/x11/cm/meta-backend-x11-cm.c index d66ae8342fc..1d0d2b0d321 100644 --- a/src/backends/x11/cm/meta-backend-x11-cm.c +++ b/src/backends/x11/cm/meta-backend-x11-cm.c @@ -28,6 +28,7 @@ #include "backends/meta-backend-private.h" #include "backends/meta-dnd-private.h" +#include "backends/x11/meta-barrier-x11.h" #include "backends/x11/meta-cursor-renderer-x11.h" #include "backends/x11/meta-cursor-tracker-x11.h" #include "backends/x11/meta-gpu-xrandr.h" @@ -115,6 +116,20 @@ meta_backend_x11_cm_post_init (MetaBackend *backend) take_touch_grab (backend); } +static MetaBackendCapabilities +meta_backend_x11_cm_get_capabilities (MetaBackend *backend) +{ + MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); + MetaX11Barriers *barriers; + MetaBackendCapabilities capabilities = META_BACKEND_CAPABILITY_NONE; + + barriers = meta_backend_x11_get_barriers (backend_x11); + if (barriers) + capabilities |= META_BACKEND_CAPABILITY_BARRIERS; + + return capabilities; +} + static MetaRenderer * meta_backend_x11_cm_create_renderer (MetaBackend *backend, GError **error) @@ -522,6 +537,7 @@ meta_backend_x11_cm_class_init (MetaBackendX11CmClass *klass) object_class->constructed = meta_backend_x11_cm_constructed; backend_class->post_init = meta_backend_x11_cm_post_init; + backend_class->get_capabilities = meta_backend_x11_cm_get_capabilities; backend_class->create_renderer = meta_backend_x11_cm_create_renderer; backend_class->create_monitor_manager = meta_backend_x11_cm_create_monitor_manager; backend_class->get_cursor_renderer = meta_backend_x11_cm_get_cursor_renderer; diff --git a/src/backends/x11/meta-backend-x11.c b/src/backends/x11/meta-backend-x11.c index 056a16ad71c..b9640ece6ab 100644 --- a/src/backends/x11/meta-backend-x11.c +++ b/src/backends/x11/meta-backend-x11.c @@ -45,6 +45,7 @@ #include "backends/meta-idle-monitor-private.h" #include "backends/meta-keymap-utils.h" #include "backends/meta-stage-private.h" +#include "backends/x11/meta-barrier-x11.h" #include "backends/x11/meta-clutter-backend-x11.h" #include "backends/x11/meta-event-x11.h" #include "backends/x11/meta-seat-x11.h" @@ -79,6 +80,7 @@ struct _MetaBackendX11Private int xinput_event_base; int xinput_error_base; Time latest_evtime; + gboolean have_xinput_23; uint8_t xkb_event_base; uint8_t xkb_error_base; @@ -89,6 +91,8 @@ struct _MetaBackendX11Private xkb_layout_index_t keymap_layout_group; MetaLogicalMonitor *cached_current_logical_monitor; + + MetaX11Barriers *barriers; }; typedef struct _MetaBackendX11Private MetaBackendX11Private; @@ -286,7 +290,7 @@ maybe_spoof_event_as_stage_event (MetaBackendX11 *x11, } } -static void +static gboolean handle_input_event (MetaBackendX11 *x11, XEvent *event) { @@ -296,9 +300,17 @@ handle_input_event (MetaBackendX11 *x11, event->xcookie.extension == priv->xinput_opcode) { XIEvent *input_event = (XIEvent *) event->xcookie.data; + MetaX11Barriers *barriers; + + barriers = meta_backend_x11_get_barriers (x11); + if (barriers && + meta_x11_barriers_process_xevent (barriers, input_event)) + return TRUE; maybe_spoof_event_as_stage_event (x11, input_event); } + + return FALSE; } static void @@ -401,10 +413,13 @@ handle_host_xevent (MetaBackend *backend, if (!bypass_clutter) { - handle_input_event (x11, event); + if (handle_input_event (x11, event)) + goto done; + meta_x11_handle_event (backend, event); } +done: XFreeEventData (priv->xdisplay, &event->xcookie); } @@ -518,7 +533,6 @@ meta_backend_x11_post_init (MetaBackend *backend) ClutterSeat *seat; MetaInputSettings *input_settings; int major, minor; - gboolean has_xi = FALSE; priv->source = x_event_source_new (backend); @@ -532,24 +546,6 @@ meta_backend_x11_post_init (MetaBackend *backend) priv->user_active_alarm = xsync_user_active_alarm_set (priv); - if (XQueryExtension (priv->xdisplay, - "XInputExtension", - &priv->xinput_opcode, - &priv->xinput_error_base, - &priv->xinput_event_base)) - { - major = 2; minor = 3; - if (XIQueryVersion (priv->xdisplay, &major, &minor) == Success) - { - int version = (major * 10) + minor; - if (version >= 22) - has_xi = TRUE; - } - } - - if (!has_xi) - meta_fatal ("X server doesn't have the XInput extension, version 2.2 or newer"); - if (!xkb_x11_setup_xkb_extension (priv->xcb, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, @@ -816,6 +812,47 @@ init_xkb_state (MetaBackendX11 *x11) xkb_state_unref (state); } +static gboolean +init_xinput (MetaBackendX11 *backend_x11, + GError **error) +{ + MetaBackendX11Private *priv = + meta_backend_x11_get_instance_private (backend_x11); + gboolean has_xi = FALSE; + + if (XQueryExtension (priv->xdisplay, + "XInputExtension", + &priv->xinput_opcode, + &priv->xinput_error_base, + &priv->xinput_event_base)) + { + int major, minor; + + major = 2; minor = 3; + if (XIQueryVersion (priv->xdisplay, &major, &minor) == Success) + { + int version; + + version = (major * 10) + minor; + if (version >= 22) + has_xi = TRUE; + + if (version >= 23) + priv->have_xinput_23 = TRUE; + } + } + + if (!has_xi) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "X server doesn't have the XInput extension, " + "version 2.2 or newer"); + return FALSE; + } + + return TRUE; +} + static gboolean meta_backend_x11_initable_init (GInitable *initable, GCancellable *cancellable, @@ -849,6 +886,12 @@ meta_backend_x11_initable_init (GInitable *initable, init_xkb_state (x11); + if (!init_xinput (x11, error)) + return FALSE; + + if (priv->have_xinput_23) + priv->barriers = meta_x11_barriers_new (x11); + return initable_parent_iface->init (initable, cancellable, error); } @@ -989,3 +1032,12 @@ meta_backend_x11_sync_pointer (MetaBackendX11 *backend_x11) clutter_event_put (event); clutter_event_free (event); } + +MetaX11Barriers * +meta_backend_x11_get_barriers (MetaBackendX11 *backend_x11) +{ + MetaBackendX11Private *priv = + meta_backend_x11_get_instance_private (backend_x11); + + return priv->barriers; +} diff --git a/src/backends/x11/meta-backend-x11.h b/src/backends/x11/meta-backend-x11.h index 515cde91b49..748ab3c119a 100644 --- a/src/backends/x11/meta-backend-x11.h +++ b/src/backends/x11/meta-backend-x11.h @@ -29,6 +29,7 @@ #include #include "backends/meta-backend-private.h" +#include "backends/x11/meta-backend-x11-types.h" #include "backends/x11/meta-clutter-backend-x11.h" #define META_TYPE_BACKEND_X11 (meta_backend_x11_get_type ()) @@ -64,4 +65,6 @@ void meta_backend_x11_reload_cursor (MetaBackendX11 *x11); void meta_backend_x11_sync_pointer (MetaBackendX11 *backend_x11); +MetaX11Barriers * meta_backend_x11_get_barriers (MetaBackendX11 *backend_x11); + #endif /* META_BACKEND_X11_H */ diff --git a/src/backends/x11/meta-barrier-x11.c b/src/backends/x11/meta-barrier-x11.c index ebbf1dabb01..8be0c971882 100644 --- a/src/backends/x11/meta-barrier-x11.c +++ b/src/backends/x11/meta-barrier-x11.c @@ -35,11 +35,17 @@ #include #include +#include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-barrier-x11.h" #include "core/display-private.h" #include "meta/barrier.h" #include "x11/meta-x11-display-private.h" +struct _MetaX11Barriers +{ + GHashTable *barriers; +}; + struct _MetaBarrierImplX11 { MetaBarrierImpl parent; @@ -65,34 +71,29 @@ meta_barrier_impl_x11_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); - MetaDisplay *display = self->barrier->priv->display; - Display *dpy = meta_x11_display_get_xdisplay (display->x11_display); + MetaBackend *backend = self->barrier->priv->backend; + MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); + Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); - if (META_X11_DISPLAY_HAS_XINPUT_23 (display->x11_display)) - { - XIBarrierReleasePointer (dpy, - META_VIRTUAL_CORE_POINTER_ID, - self->xbarrier, event->event_id); - } + XIBarrierReleasePointer (xdisplay, + META_VIRTUAL_CORE_POINTER_ID, + self->xbarrier, event->event_id); } static void meta_barrier_impl_x11_destroy (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); - MetaDisplay *display = self->barrier->priv->display; - Display *dpy; - - if (display == NULL) - return; - - dpy = meta_x11_display_get_xdisplay (display->x11_display); + MetaBackend *backend = self->barrier->priv->backend; + MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); + MetaX11Barriers *barriers = meta_backend_x11_get_barriers (backend_x11); + Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); if (!meta_barrier_is_active (self->barrier)) return; - XFixesDestroyPointerBarrier (dpy, self->xbarrier); - g_hash_table_remove (display->x11_display->xids, &self->xbarrier); + XFixesDestroyPointerBarrier (xdisplay, self->xbarrier); + g_hash_table_remove (barriers->barriers, &self->xbarrier); self->xbarrier = 0; } @@ -100,26 +101,24 @@ MetaBarrierImpl * meta_barrier_impl_x11_new (MetaBarrier *barrier) { MetaBarrierImplX11 *self; - MetaDisplay *display = barrier->priv->display; - Display *dpy; + MetaBackend *backend; + MetaBackendX11 *backend_x11; + MetaX11Barriers *barriers; + Display *xdisplay; Window root; unsigned int allowed_motion_dirs; - if (display == NULL) - { - g_warning ("A display must be provided when constructing a barrier."); - return NULL; - } - self = g_object_new (META_TYPE_BARRIER_IMPL_X11, NULL); self->barrier = barrier; - dpy = meta_x11_display_get_xdisplay (display->x11_display); - root = DefaultRootWindow (dpy); + backend = self->barrier->priv->backend; + backend_x11 = META_BACKEND_X11 (backend); + xdisplay = meta_backend_x11_get_xdisplay (backend_x11); + root = DefaultRootWindow (xdisplay); allowed_motion_dirs = meta_border_get_allows_directions (&barrier->priv->border); - self->xbarrier = XFixesCreatePointerBarrier (dpy, root, + self->xbarrier = XFixesCreatePointerBarrier (xdisplay, root, barrier->priv->border.line.a.x, barrier->priv->border.line.a.y, barrier->priv->border.line.b.x, @@ -127,7 +126,8 @@ meta_barrier_impl_x11_new (MetaBarrier *barrier) allowed_motion_dirs, 0, NULL); - g_hash_table_insert (display->x11_display->xids, &self->xbarrier, barrier); + barriers = meta_backend_x11_get_barriers (backend_x11); + g_hash_table_insert (barriers->barriers, &self->xbarrier, barrier); return META_BARRIER_IMPL (self); } @@ -167,15 +167,12 @@ meta_barrier_fire_xevent (MetaBarrier *barrier, } gboolean -meta_x11_display_process_barrier_xevent (MetaX11Display *x11_display, - XIEvent *event) +meta_x11_barriers_process_xevent (MetaX11Barriers *barriers, + XIEvent *event) { MetaBarrier *barrier; XIBarrierEvent *xev; - if (event == NULL) - return FALSE; - switch (event->evtype) { case XI_BarrierHit: @@ -186,8 +183,8 @@ meta_x11_display_process_barrier_xevent (MetaX11Display *x11_display, } xev = (XIBarrierEvent *) event; - barrier = g_hash_table_lookup (x11_display->xids, &xev->barrier); - if (barrier != NULL) + barrier = g_hash_table_lookup (barriers->barriers, &xev->barrier); + if (barrier) { meta_barrier_fire_xevent (barrier, xev); return TRUE; @@ -210,3 +207,31 @@ static void meta_barrier_impl_x11_init (MetaBarrierImplX11 *self) { } + +MetaX11Barriers * +meta_x11_barriers_new (MetaBackendX11 *backend_x11) +{ + Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); + Window root = meta_backend_x11_get_root_xwindow (backend_x11); + MetaX11Barriers *x11_barriers; + unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = {}; + XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; + + x11_barriers = g_new0 (MetaX11Barriers, 1); + x11_barriers->barriers = g_hash_table_new (meta_unsigned_long_hash, + meta_unsigned_long_equal); + + XISetMask (mask.mask, XI_BarrierHit); + XISetMask (mask.mask, XI_BarrierLeave); + XISelectEvents (xdisplay, root, &mask, 1); + + return x11_barriers; +} + +void +meta_x11_barriers_free (MetaX11Barriers *x11_barriers) +{ + g_assert (g_hash_table_size (x11_barriers->barriers) == 0); + g_hash_table_unref (x11_barriers->barriers); + g_free (x11_barriers); +} diff --git a/src/backends/x11/meta-barrier-x11.h b/src/backends/x11/meta-barrier-x11.h index 3562d106f54..ae50fdccab2 100644 --- a/src/backends/x11/meta-barrier-x11.h +++ b/src/backends/x11/meta-barrier-x11.h @@ -26,6 +26,7 @@ #define META_BARRIER_X11_H #include "backends/meta-barrier-private.h" +#include "backends/x11/meta-backend-x11-types.h" G_BEGIN_DECLS @@ -37,6 +38,13 @@ G_DECLARE_FINAL_TYPE (MetaBarrierImplX11, MetaBarrierImpl *meta_barrier_impl_x11_new (MetaBarrier *barrier); +MetaX11Barriers * meta_x11_barriers_new (MetaBackendX11 *backend_x11); + +void meta_x11_barriers_free (MetaX11Barriers *x11_barriers); + +gboolean meta_x11_barriers_process_xevent (MetaX11Barriers *barriers, + XIEvent *event); + G_END_DECLS #endif /* META_BARRIER_X11_H1 */ diff --git a/src/backends/x11/nested/meta-backend-x11-nested.c b/src/backends/x11/nested/meta-backend-x11-nested.c index 041b42860b3..0f38f4ad210 100644 --- a/src/backends/x11/nested/meta-backend-x11-nested.c +++ b/src/backends/x11/nested/meta-backend-x11-nested.c @@ -245,6 +245,12 @@ meta_backend_x11_nested_post_init (MetaBackend *backend) backend_class->post_init (backend); } +static MetaBackendCapabilities +meta_backend_x11_nested_get_capabilities (MetaBackend *backend) +{ + return META_BACKEND_CAPABILITY_NONE; +} + static gboolean meta_backend_x11_nested_initable_init (GInitable *initable, GCancellable *cancellable, @@ -303,6 +309,7 @@ meta_backend_x11_nested_class_init (MetaBackendX11NestedClass *klass) object_class->dispose = meta_backend_x11_nested_dispose; backend_class->post_init = meta_backend_x11_nested_post_init; + backend_class->get_capabilities = meta_backend_x11_nested_get_capabilities; backend_class->create_renderer = meta_backend_x11_nested_create_renderer; backend_class->create_monitor_manager = meta_backend_x11_nested_create_monitor_manager; backend_class->get_cursor_renderer = meta_backend_x11_nested_get_cursor_renderer; diff --git a/src/core/display.c b/src/core/display.c index 058d09f2a5e..7573956fa05 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2863,20 +2863,11 @@ meta_display_modifiers_accelerator_activate (MetaDisplay *display) gboolean meta_display_supports_extended_barriers (MetaDisplay *display) { -#ifdef HAVE_NATIVE_BACKEND - if (META_IS_BACKEND_NATIVE (meta_get_backend ())) - return TRUE; -#endif - - if (META_IS_BACKEND_X11_CM (meta_get_backend ())) - { - if (meta_is_wayland_compositor()) - return FALSE; - - return META_X11_DISPLAY_HAS_XINPUT_23 (display->x11_display); - } + MetaContext *context = meta_display_get_context (display); + MetaBackend *backend = meta_context_get_backend (context); - return FALSE; + return !!(meta_backend_get_capabilities (backend) & + META_BACKEND_CAPABILITY_BARRIERS); } /** diff --git a/src/meta/display.h b/src/meta/display.h index e59bd039392..206e08c0c0d 100644 --- a/src/meta/display.h +++ b/src/meta/display.h @@ -78,6 +78,7 @@ GType meta_display_get_type (void) G_GNUC_CONST; #define meta_XFree(p) do { if ((p)) XFree ((p)); } while (0) META_EXPORT +G_DEPRECATED_FOR (meta_backend_get_capabilities) gboolean meta_display_supports_extended_barriers (MetaDisplay *display); META_EXPORT diff --git a/src/meta/meta-backend.h b/src/meta/meta-backend.h index cfb042a726d..05fb47234f6 100644 --- a/src/meta/meta-backend.h +++ b/src/meta/meta-backend.h @@ -33,6 +33,12 @@ #include "meta/meta-monitor-manager.h" #include "meta/meta-remote-access-controller.h" +typedef enum _MetaBackendCapabilities +{ + META_BACKEND_CAPABILITY_NONE = 0, + META_BACKEND_CAPABILITY_BARRIERS = 1 << 0, +} MetaBackendCapabilities; + #define META_TYPE_BACKEND (meta_backend_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaBackend, meta_backend, META, BACKEND, GObject) @@ -77,6 +83,9 @@ gboolean meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend); META_EXPORT gboolean meta_backend_is_headless (MetaBackend *backend); +META_EXPORT +MetaBackendCapabilities meta_backend_get_capabilities (MetaBackend *backend); + META_EXPORT void meta_clutter_init (void); diff --git a/src/x11/events.c b/src/x11/events.c index 8882baac4ec..677d99c2bf9 100644 --- a/src/x11/events.c +++ b/src/x11/events.c @@ -1956,12 +1956,6 @@ meta_x11_display_handle_xevent (MetaX11Display *x11_display, } } - if (meta_x11_display_process_barrier_xevent (x11_display, input_event)) - { - bypass_gtk = bypass_compositor = TRUE; - goto out; - } - if (handle_input_xevent (x11_display, input_event, event->xany.serial)) { bypass_gtk = bypass_compositor = TRUE; diff --git a/src/x11/meta-x11-display-private.h b/src/x11/meta-x11-display-private.h index d53073e1110..9158387d695 100644 --- a/src/x11/meta-x11-display-private.h +++ b/src/x11/meta-x11-display-private.h @@ -175,8 +175,6 @@ struct _MetaX11Display unsigned int have_damage : 1; #define META_X11_DISPLAY_HAS_COMPOSITE(x11_display) ((x11_display)->have_composite) #define META_X11_DISPLAY_HAS_DAMAGE(x11_display) ((x11_display)->have_damage) - gboolean have_xinput_23 : 1; -#define META_X11_DISPLAY_HAS_XINPUT_23(x11_display) ((x11_display)->have_xinput_23) MetaX11StartupNotification *startup_notification; MetaX11Stack *x11_stack; @@ -213,9 +211,6 @@ void meta_x11_display_register_sync_alarm (MetaX11Display *x11_display, void meta_x11_display_unregister_sync_alarm (MetaX11Display *x11_display, XSyncAlarm alarm); -gboolean meta_x11_display_process_barrier_xevent (MetaX11Display *x11_display, - XIEvent *event); - META_EXPORT void meta_x11_display_set_alarm_filter (MetaX11Display *x11_display, MetaAlarmFilter filter, diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c index ccdf0208be6..4d1cfc4f2d5 100644 --- a/src/x11/meta-x11-display.c +++ b/src/x11/meta-x11-display.c @@ -429,7 +429,7 @@ static gboolean query_xi_extension (MetaX11Display *x11_display, GError **error) { - int major = 2, minor = 3; + int major = 2, minor = 2; gboolean has_xi = FALSE; if (XQueryExtension (x11_display->xdisplay, @@ -443,9 +443,6 @@ query_xi_extension (MetaX11Display *x11_display, int version = (major * 10) + minor; if (version >= 22) has_xi = TRUE; - - if (version >= 23) - x11_display->have_xinput_23 = TRUE; } } @@ -807,11 +804,6 @@ init_event_masks (MetaX11Display *x11_display) XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusOut); - if (META_X11_DISPLAY_HAS_XINPUT_23 (x11_display)) - { - XISetMask (mask.mask, XI_BarrierHit); - XISetMask (mask.mask, XI_BarrierLeave); - } XISelectEvents (x11_display->xdisplay, x11_display->xroot, &mask, 1); event_mask = (SubstructureRedirectMask | SubstructureNotifyMask | -- GitLab From c68ab9304c335f2317826040b32a64180fdd8855 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Tue, 15 Mar 2022 23:13:44 +0100 Subject: [PATCH 16/40] x11-display: Stop special casing barriers when iterating window table In the past, barries were added to the window management X11 display instance window table, and then special cased when iterating over the list. Since then, barriers, which are really part of the backend, has stopped being added to the window hash table, instead being managed by the backend. Lets clean up the left-over special casing that is no longer needed. --- src/x11/meta-x11-display.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/x11/meta-x11-display.c b/src/x11/meta-x11-display.c index 4d1cfc4f2d5..55b79c1621a 100644 --- a/src/x11/meta-x11-display.c +++ b/src/x11/meta-x11-display.c @@ -107,17 +107,10 @@ meta_x11_display_unmanage_windows (MetaX11Display *x11_display) for (l = windows; l; l = l->next) { - if (META_IS_WINDOW (l->data)) - { - MetaWindow *window = l->data; + MetaWindow *window = META_WINDOW (l->data); - if (!window->unmanaging) - meta_window_unmanage (window, META_CURRENT_TIME); - } - else if (META_IS_BARRIER (l->data)) - meta_barrier_destroy (META_BARRIER (l->data)); - else - g_assert_not_reached (); + if (!window->unmanaging) + meta_window_unmanage (window, META_CURRENT_TIME); } g_list_free_full (windows, g_object_unref); } -- GitLab From 40cb9360429544ac83d01ea2862d565f75386ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 18 Mar 2022 15:48:23 +0100 Subject: [PATCH 17/40] barrier: Modernize type implementation This means using G_DECLARE*() to declare the type, using `_get_private_instance()` everywhere, and other smaller cleanups. --- src/backends/meta-barrier-private.h | 9 +-- src/backends/meta-barrier.c | 46 +++++++++++---- src/backends/native/meta-barrier-native.c | 36 ++++++------ src/backends/x11/meta-barrier-x11.c | 19 ++++--- src/meta/barrier.h | 69 ++++++----------------- 5 files changed, 82 insertions(+), 97 deletions(-) diff --git a/src/backends/meta-barrier-private.h b/src/backends/meta-barrier-private.h index 16bfcdcd0da..ebe22cb54f6 100644 --- a/src/backends/meta-barrier-private.h +++ b/src/backends/meta-barrier-private.h @@ -56,13 +56,8 @@ void meta_barrier_event_unref (MetaBarrierEvent *event); MetaBackend * meta_barrier_get_backend (MetaBarrier *barrier); -G_END_DECLS +MetaBorder * meta_barrier_get_border (MetaBarrier *barrier); -struct _MetaBarrierPrivate -{ - MetaBackend *backend; - MetaBorder border; - MetaBarrierImpl *impl; -}; +G_END_DECLS #endif /* META_BARRIER_PRIVATE_H */ diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index 777287cc999..45ccd99f9fa 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -23,6 +23,18 @@ #include "backends/native/meta-barrier-native.h" #endif +struct _MetaBarrier +{ + GObject parent; +}; + +typedef struct _MetaBarrierPrivate +{ + MetaBackend *backend; + MetaBorder border; + MetaBarrierImpl *impl; +} MetaBarrierPrivate; + G_DEFINE_TYPE_WITH_PRIVATE (MetaBarrier, meta_barrier, G_TYPE_OBJECT) G_DEFINE_TYPE (MetaBarrierImpl, meta_barrier_impl, G_TYPE_OBJECT) @@ -86,7 +98,7 @@ meta_barrier_get_property (GObject *object, GParamSpec *pspec) { MetaBarrier *barrier = META_BARRIER (object); - MetaBarrierPrivate *priv = barrier->priv; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); switch (prop_id) { @@ -125,7 +137,8 @@ meta_barrier_set_property (GObject *object, GParamSpec *pspec) { MetaBarrier *barrier = META_BARRIER (object); - MetaBarrierPrivate *priv = barrier->priv; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + switch (prop_id) { case PROP_BACKEND: @@ -166,7 +179,7 @@ static void meta_barrier_dispose (GObject *object) { MetaBarrier *barrier = META_BARRIER (object); - MetaBarrierPrivate *priv = barrier->priv; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); if (meta_barrier_is_active (barrier)) { @@ -182,7 +195,8 @@ meta_barrier_dispose (GObject *object) gboolean meta_barrier_is_active (MetaBarrier *barrier) { - MetaBarrierImpl *impl = barrier->priv->impl; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + MetaBarrierImpl *impl = priv->impl; if (impl) return META_BARRIER_IMPL_GET_CLASS (impl)->is_active (impl); @@ -204,7 +218,8 @@ void meta_barrier_release (MetaBarrier *barrier, MetaBarrierEvent *event) { - MetaBarrierImpl *impl = barrier->priv->impl; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + MetaBarrierImpl *impl = priv->impl; if (impl) META_BARRIER_IMPL_GET_CLASS (impl)->release (impl, event); @@ -213,7 +228,7 @@ meta_barrier_release (MetaBarrier *barrier, static void init_barrier_impl (MetaBarrier *barrier) { - MetaBarrierPrivate *priv = barrier->priv; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); g_return_if_fail (priv->backend); g_return_if_fail (priv->border.line.a.x == priv->border.line.b.x || @@ -364,7 +379,8 @@ meta_barrier_class_init (MetaBarrierClass *klass) void meta_barrier_destroy (MetaBarrier *barrier) { - MetaBarrierImpl *impl = barrier->priv->impl; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + MetaBarrierImpl *impl = priv->impl; if (impl) META_BARRIER_IMPL_GET_CLASS (impl)->destroy (impl); @@ -375,7 +391,6 @@ meta_barrier_destroy (MetaBarrier *barrier) static void meta_barrier_init (MetaBarrier *barrier) { - barrier->priv = meta_barrier_get_instance_private (barrier); } void @@ -395,15 +410,22 @@ meta_barrier_emit_left_signal (MetaBarrier *barrier, MetaBackend * meta_barrier_get_backend (MetaBarrier *barrier) { - return barrier->priv->backend; + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + + return priv->backend; +} + +MetaBorder * +meta_barrier_get_border (MetaBarrier *barrier) +{ + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + + return &priv->border; } static void meta_barrier_impl_class_init (MetaBarrierImplClass *klass) { - klass->is_active = NULL; - klass->release = NULL; - klass->destroy = NULL; } static void diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 63acb6a7961..0b325b846d1 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -102,18 +102,20 @@ next_serial (void) static gboolean is_barrier_horizontal (MetaBarrier *barrier) { - return meta_border_is_horizontal (&barrier->priv->border); + MetaBorder *border = meta_barrier_get_border (barrier); + + return meta_border_is_horizontal (border); } static gboolean is_barrier_blocking_directions (MetaBarrier *barrier, MetaBarrierDirection directions) { + MetaBorder *border = meta_barrier_get_border (barrier); MetaBorderMotionDirection border_motion_directions = (MetaBorderMotionDirection) directions; - return meta_border_is_blocking_directions (&barrier->priv->border, - border_motion_directions); + return meta_border_is_blocking_directions (border, border_motion_directions); } static void @@ -131,7 +133,8 @@ dismiss_pointer (MetaBarrierImplNative *self) static MetaLine2 calculate_barrier_hit_box (MetaBarrier *barrier) { - MetaLine2 hit_box = barrier->priv->border.line; + MetaBorder *border = meta_barrier_get_border (barrier); + MetaLine2 hit_box = border->line; if (is_barrier_horizontal (barrier)) { @@ -170,6 +173,7 @@ maybe_release_barrier (gpointer key, { MetaBarrierImplNative *self = key; MetaBarrier *barrier = self->barrier; + MetaBorder *border = meta_barrier_get_border (barrier); MetaLine2 *motion = user_data; MetaLine2 hit_box; @@ -179,10 +183,8 @@ maybe_release_barrier (gpointer key, /* Release if we end up outside barrier end points. */ if (is_barrier_horizontal (barrier)) { - if (motion->b.x > MAX (barrier->priv->border.line.a.x, - barrier->priv->border.line.b.x) || - motion->b.x < MIN (barrier->priv->border.line.a.x, - barrier->priv->border.line.b.x)) + if (motion->b.x > MAX (border->line.a.x, border->line.b.x) || + motion->b.x < MIN (border->line.a.x, border->line.b.x)) { dismiss_pointer (self); return; @@ -190,10 +192,8 @@ maybe_release_barrier (gpointer key, } else { - if (motion->b.y > MAX (barrier->priv->border.line.a.y, - barrier->priv->border.line.b.y) || - motion->b.y < MIN (barrier->priv->border.line.a.y, - barrier->priv->border.line.b.y)) + if (motion->b.y > MAX (border->line.a.y, border->line.b.y) || + motion->b.y < MIN (border->line.a.y, border->line.b.y)) { dismiss_pointer (self); return; @@ -254,6 +254,7 @@ update_closest_barrier (gpointer key, { MetaBarrierImplNative *self = key; MetaBarrier *barrier = self->barrier; + MetaBorder *border = meta_barrier_get_border (barrier); MetaClosestBarrierData *data = user_data; MetaVector2 intersection; float dx, dy; @@ -274,7 +275,7 @@ update_closest_barrier (gpointer key, /* Check if the motion intersects with the barrier, and retrieve the * intersection point if any. */ - if (!meta_line2_intersects_with (&barrier->priv->border.line, + if (!meta_line2_intersects_with (&border->line, &data->in.motion, &intersection)) return; @@ -473,13 +474,14 @@ clamp_to_barrier (MetaBarrierImplNative *self, float *y) { MetaBarrier *barrier = self->barrier; + MetaBorder *border = meta_barrier_get_border (barrier); if (is_barrier_horizontal (barrier)) { if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_Y) - *y = barrier->priv->border.line.a.y; + *y = border->line.a.y; else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_Y) - *y = barrier->priv->border.line.a.y; + *y = border->line.a.y; self->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_Y | META_BARRIER_DIRECTION_NEGATIVE_Y); @@ -489,9 +491,9 @@ clamp_to_barrier (MetaBarrierImplNative *self, else { if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_X) - *x = barrier->priv->border.line.a.x; + *x = border->line.a.x; else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_X) - *x = barrier->priv->border.line.a.x; + *x = border->line.a.x; self->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_X | META_BARRIER_DIRECTION_NEGATIVE_X); diff --git a/src/backends/x11/meta-barrier-x11.c b/src/backends/x11/meta-barrier-x11.c index 8be0c971882..45ea1421b62 100644 --- a/src/backends/x11/meta-barrier-x11.c +++ b/src/backends/x11/meta-barrier-x11.c @@ -71,7 +71,7 @@ meta_barrier_impl_x11_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); - MetaBackend *backend = self->barrier->priv->backend; + MetaBackend *backend = meta_barrier_get_backend (self->barrier); MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); @@ -84,7 +84,7 @@ static void meta_barrier_impl_x11_destroy (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); - MetaBackend *backend = self->barrier->priv->backend; + MetaBackend *backend = meta_barrier_get_backend (self->barrier); MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); MetaX11Barriers *barriers = meta_backend_x11_get_barriers (backend_x11); Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); @@ -106,23 +106,24 @@ meta_barrier_impl_x11_new (MetaBarrier *barrier) MetaX11Barriers *barriers; Display *xdisplay; Window root; + MetaBorder *border; unsigned int allowed_motion_dirs; self = g_object_new (META_TYPE_BARRIER_IMPL_X11, NULL); self->barrier = barrier; - backend = self->barrier->priv->backend; + backend = meta_barrier_get_backend (self->barrier); backend_x11 = META_BACKEND_X11 (backend); xdisplay = meta_backend_x11_get_xdisplay (backend_x11); root = DefaultRootWindow (xdisplay); - allowed_motion_dirs = - meta_border_get_allows_directions (&barrier->priv->border); + border = meta_barrier_get_border (barrier); + allowed_motion_dirs = meta_border_get_allows_directions (border); self->xbarrier = XFixesCreatePointerBarrier (xdisplay, root, - barrier->priv->border.line.a.x, - barrier->priv->border.line.a.y, - barrier->priv->border.line.b.x, - barrier->priv->border.line.b.y, + border->line.a.x, + border->line.a.y, + border->line.b.x, + border->line.b.y, allowed_motion_dirs, 0, NULL); diff --git a/src/meta/barrier.h b/src/meta/barrier.h index 72ed33cfc7a..a73bcd25bc8 100644 --- a/src/meta/barrier.h +++ b/src/meta/barrier.h @@ -9,58 +9,6 @@ G_BEGIN_DECLS -#define META_TYPE_BARRIER (meta_barrier_get_type ()) -#define META_BARRIER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BARRIER, MetaBarrier)) -#define META_BARRIER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BARRIER, MetaBarrierClass)) -#define META_IS_BARRIER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BARRIER)) -#define META_IS_BARRIER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BARRIER)) -#define META_BARRIER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BARRIER, MetaBarrierClass)) - -typedef struct _MetaBarrier MetaBarrier; -typedef struct _MetaBarrierClass MetaBarrierClass; -typedef struct _MetaBarrierPrivate MetaBarrierPrivate; - -typedef struct _MetaBarrierEvent MetaBarrierEvent; - -/** - * MetaBarrier: - * - * The MetaBarrier structure contains - * only private data and should be accessed using the provided API - * - **/ -struct _MetaBarrier -{ - GObject parent; - - MetaBarrierPrivate *priv; -}; - -/** - * MetaBarrierClass: - * - * The MetaBarrierClass structure contains only - * private data. - */ -struct _MetaBarrierClass -{ - /*< private >*/ - GObjectClass parent_class; -}; - -META_EXPORT -GType meta_barrier_get_type (void) G_GNUC_CONST; - -META_EXPORT -gboolean meta_barrier_is_active (MetaBarrier *barrier); - -META_EXPORT -void meta_barrier_destroy (MetaBarrier *barrier); - -META_EXPORT -void meta_barrier_release (MetaBarrier *barrier, - MetaBarrierEvent *event); - /** * MetaBarrierDirection: * @META_BARRIER_DIRECTION_POSITIVE_X: Positive direction in the X axis @@ -78,6 +26,23 @@ typedef enum META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, } MetaBarrierDirection; +#define META_TYPE_BARRIER (meta_barrier_get_type ()) +META_EXPORT +G_DECLARE_FINAL_TYPE (MetaBarrier, meta_barrier, + META, BARRIER, GObject) + +typedef struct _MetaBarrierEvent MetaBarrierEvent; + +META_EXPORT +gboolean meta_barrier_is_active (MetaBarrier *barrier); + +META_EXPORT +void meta_barrier_destroy (MetaBarrier *barrier); + +META_EXPORT +void meta_barrier_release (MetaBarrier *barrier, + MetaBarrierEvent *event); + /** * MetaBarrierEvent: * @event_id: A unique integer ID identifying a -- GitLab From 1678c36c7312af1ae8862e8bc082ebad39871820 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 18 Mar 2022 17:31:25 +0100 Subject: [PATCH 18/40] barrier: Add constructor method This one does proper error reporting. Via Javascript, barriers are constructed directly via GObject construction, which currently can't handle error reporting, but when calling from C we can. --- src/backends/meta-barrier.c | 32 ++++++++++++++++++++++++++++++++ src/meta/barrier.h | 9 +++++++++ 2 files changed, 41 insertions(+) diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index 45ccd99f9fa..c57a95d296e 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -393,6 +393,38 @@ meta_barrier_init (MetaBarrier *barrier) { } +MetaBarrier * +meta_barrier_new (MetaBackend *backend, + int x1, + int y1, + int x2, + int y2, + MetaBarrierDirection directions, + GError **error) +{ + MetaBarrier *barrier; + MetaBarrierPrivate *priv; + + barrier = g_object_new (META_TYPE_BARRIER, + "backend", backend, + "x1", x1, + "y1", y1, + "x2", x2, + "y2", y2, + "directions", directions, + NULL); + priv = meta_barrier_get_instance_private (barrier); + if (!priv->impl) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Failed to create barrier"); + g_object_unref (barrier); + return NULL; + } + + return barrier; +} + void meta_barrier_emit_hit_signal (MetaBarrier *barrier, MetaBarrierEvent *event) diff --git a/src/meta/barrier.h b/src/meta/barrier.h index a73bcd25bc8..374c42f0b7f 100644 --- a/src/meta/barrier.h +++ b/src/meta/barrier.h @@ -33,6 +33,15 @@ G_DECLARE_FINAL_TYPE (MetaBarrier, meta_barrier, typedef struct _MetaBarrierEvent MetaBarrierEvent; +META_EXPORT +MetaBarrier * meta_barrier_new (MetaBackend *backend, + int x1, + int y1, + int x2, + int y2, + MetaBarrierDirection directions, + GError **error); + META_EXPORT gboolean meta_barrier_is_active (MetaBarrier *barrier); -- GitLab From caf111c4ba09ddc49b098a902007f4805cb8caaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 30 Mar 2022 17:27:51 +0200 Subject: [PATCH 19/40] Add beginning of input capture API This API aims to provide a way for users to capture input devices under certain conditions, for example when a pointer crosses a specified barrier. So far only part of the API is implemented, specifially the session management as well as zone advertisement, where a zone refers to a region in the compositor which edges will eventually be made available for barrier placement. So far the remote access handle is created while the session is enable, despite the input capturing isn't actually active yet. This will change in the future once it can actually become active. --- .../org.gnome.Mutter.InputCapture.xml | 68 +++ src/backends/meta-backend.c | 8 + src/backends/meta-input-capture-session.c | 539 ++++++++++++++++++ src/backends/meta-input-capture-session.h | 42 ++ src/backends/meta-input-capture.c | 162 ++++++ src/backends/meta-input-capture.h | 35 ++ src/meson.build | 11 + src/tests/input-capture-test-client.c | 342 +++++++++++ src/tests/input-capture-tests.c | 209 +++++++ src/tests/meson.build | 22 + src/tests/meta-test-utils.h | 1 + 11 files changed, 1439 insertions(+) create mode 100644 data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml create mode 100644 src/backends/meta-input-capture-session.c create mode 100644 src/backends/meta-input-capture-session.h create mode 100644 src/backends/meta-input-capture.c create mode 100644 src/backends/meta-input-capture.h create mode 100644 src/tests/input-capture-test-client.c create mode 100644 src/tests/input-capture-tests.c diff --git a/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml new file mode 100644 index 00000000000..44edacd8554 --- /dev/null +++ b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index b24226cb049..5c924dc539b 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -58,6 +58,7 @@ #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-idle-manager.h" #include "backends/meta-idle-monitor-private.h" +#include "backends/meta-input-capture.h" #include "backends/meta-input-mapper-private.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-logical-monitor.h" @@ -157,6 +158,7 @@ struct _MetaBackendPrivate MetaScreenCast *screen_cast; MetaRemoteDesktop *remote_desktop; #endif + MetaInputCapture *input_capture; #ifdef HAVE_PROFILER MetaProfiler *profiler; @@ -227,6 +229,7 @@ meta_backend_dispose (GObject *object) g_clear_object (&priv->remote_desktop); g_clear_object (&priv->screen_cast); #endif + g_clear_object (&priv->input_capture); g_clear_object (&priv->dbus_session_watcher); g_clear_object (&priv->remote_access_controller); @@ -584,6 +587,11 @@ meta_backend_real_post_init (MetaBackend *backend) META_DBUS_SESSION_MANAGER (priv->remote_desktop)); #endif /* HAVE_REMOTE_DESKTOP */ + priv->input_capture = meta_input_capture_new (backend); + meta_remote_access_controller_add ( + priv->remote_access_controller, + META_DBUS_SESSION_MANAGER (priv->input_capture)); + if (!meta_monitor_manager_is_headless (priv->monitor_manager)) { reset_pointer_position (backend); diff --git a/src/backends/meta-input-capture-session.c b/src/backends/meta-input-capture-session.c new file mode 100644 index 00000000000..079e073399c --- /dev/null +++ b/src/backends/meta-input-capture-session.c @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "backends/meta-input-capture-session.h" + +#include + +#include "backends/meta-dbus-session-watcher.h" +#include "backends/meta-dbus-session-manager.h" +#include "backends/meta-monitor-manager-private.h" +#include "backends/meta-logical-monitor.h" +#include "backends/meta-remote-access-controller-private.h" +#include "meta/boxes.h" +#include "meta/meta-backend.h" + +#include "meta-dbus-input-capture.h" + +#define META_INPUT_CAPTURE_SESSION_DBUS_PATH "/org/gnome/Mutter/InputCapture/Session" + +enum +{ + PROP_0, + + PROP_SESSION_MANAGER, + PROP_PEER_NAME, + PROP_ID, + + N_PROPS +}; + +static GParamSpec *obj_props[N_PROPS]; + +struct _MetaInputCaptureSession +{ + MetaDBusInputCaptureSessionSkeleton parent; + + MetaDbusSessionManager *session_manager; + + GDBusConnection *connection; + char *peer_name; + + char *session_id; + char *object_path; + + gboolean enabled; + + uint32_t serial; + + MetaInputCaptureSessionHandle *handle; +}; + +static void initable_init_iface (GInitableIface *iface); + +static void meta_input_capture_session_init_iface (MetaDBusInputCaptureSessionIface *iface); + +static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface); + +static MetaInputCaptureSessionHandle * meta_input_capture_session_handle_new (MetaInputCaptureSession *session); + +G_DEFINE_TYPE_WITH_CODE (MetaInputCaptureSession, + meta_input_capture_session, + META_DBUS_TYPE_INPUT_CAPTURE_SESSION_SKELETON, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + initable_init_iface) + G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_INPUT_CAPTURE_SESSION, + meta_input_capture_session_init_iface) + G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION, + meta_dbus_session_init_iface)) + +struct _MetaInputCaptureSessionHandle +{ + MetaRemoteAccessHandle parent; + + MetaInputCaptureSession *session; +}; + +G_DEFINE_TYPE (MetaInputCaptureSessionHandle, + meta_input_capture_session_handle, + META_TYPE_REMOTE_ACCESS_HANDLE) + +static void +init_remote_access_handle (MetaInputCaptureSession *session) +{ + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + MetaRemoteAccessController *remote_access_controller; + MetaRemoteAccessHandle *remote_access_handle; + + session->handle = meta_input_capture_session_handle_new (session); + + remote_access_controller = meta_backend_get_remote_access_controller (backend); + remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle); + meta_remote_access_controller_notify_new_handle (remote_access_controller, + remote_access_handle); +} + +static gboolean +meta_input_capture_session_enable (MetaInputCaptureSession *session, + GError **error) +{ + g_assert (!session->enabled); + + session->enabled = TRUE; + + init_remote_access_handle (session); + + return TRUE; +} + +static void +meta_input_capture_session_disable (MetaInputCaptureSession *session) +{ + session->enabled = FALSE; + + if (session->handle) + { + MetaRemoteAccessHandle *remote_access_handle = + META_REMOTE_ACCESS_HANDLE (session->handle); + + meta_remote_access_handle_notify_stopped (remote_access_handle); + } +} + +static void +meta_input_capture_session_close (MetaDbusSession *dbus_session) +{ + MetaInputCaptureSession *session = + META_INPUT_CAPTURE_SESSION (dbus_session); + MetaDBusInputCaptureSession *skeleton = + META_DBUS_INPUT_CAPTURE_SESSION (session); + + if (session->enabled) + meta_input_capture_session_disable (session); + + meta_dbus_session_notify_closed (META_DBUS_SESSION (session)); + meta_dbus_input_capture_session_emit_closed (skeleton); + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (session)); + + g_object_unref (session); +} + +static const char * +meta_input_capture_session_get_id (MetaDbusSession *dbus_session) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (dbus_session); + + return session->session_id; +} + +static gboolean +check_permission (MetaInputCaptureSession *session, + GDBusMethodInvocation *invocation) +{ + return g_strcmp0 (session->peer_name, + g_dbus_method_invocation_get_sender (invocation)) == 0; +} + +static gboolean +handle_add_barrier (MetaDBusInputCaptureSession *object, + GDBusMethodInvocation *invocation, + unsigned int serial, + GVariant *position) +{ + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Not implemented"); + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static gboolean +handle_get_zones (MetaDBusInputCaptureSession *object, + GDBusMethodInvocation *invocation) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + GVariant *zones_variant; + GVariantBuilder zones_builder; + GList *logical_monitors; + GList *l; + + g_variant_builder_init (&zones_builder, G_VARIANT_TYPE ("a(uuii)")); + logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); + for (l = logical_monitors; l; l = l->next) + { + MetaLogicalMonitor *logical_monitor = l->data; + MetaRectangle layout; + + layout = meta_logical_monitor_get_layout (logical_monitor); + g_variant_builder_add (&zones_builder, "(uuii)", + layout.width, + layout.height, + layout.x, + layout.y); + } + + zones_variant = g_variant_builder_end (&zones_builder); + + meta_dbus_input_capture_session_complete_get_zones (object, invocation, + session->serial, + zones_variant); + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static gboolean +handle_enable (MetaDBusInputCaptureSession *skeleton, + GDBusMethodInvocation *invocation) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (skeleton); + g_autoptr (GError) error = NULL; + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + if (session->enabled) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Already enabled"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + if (!meta_input_capture_session_enable (session, &error)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Failed to enable input capture: %s", + error->message); + + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + meta_dbus_input_capture_session_complete_enable (skeleton, invocation); + + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static gboolean +handle_disable (MetaDBusInputCaptureSession *skeleton, + GDBusMethodInvocation *invocation) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (skeleton); + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + if (!session->enabled) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Session not enabled"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + meta_input_capture_session_disable (session); + + meta_dbus_input_capture_session_complete_disable (skeleton, invocation); + + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static gboolean +handle_release (MetaDBusInputCaptureSession *object, + GDBusMethodInvocation *invocation, + GVariant *position) +{ + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Not implemented"); + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static gboolean +handle_close (MetaDBusInputCaptureSession *object, + GDBusMethodInvocation *invocation) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + meta_dbus_session_close (META_DBUS_SESSION (session)); + + meta_dbus_input_capture_session_complete_close (object, invocation); + + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static void +on_monitors_changed (MetaMonitorManager *monitor_manager, + MetaInputCaptureSession *session) +{ + MetaDBusInputCaptureSession *skeleton = + META_DBUS_INPUT_CAPTURE_SESSION (session); + + session->serial++; + meta_input_capture_session_disable (session); + meta_dbus_input_capture_session_emit_zones_changed (skeleton); +} + +static gboolean +meta_input_capture_session_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (initable); + GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + + session->connection = + meta_dbus_session_manager_get_connection (session->session_manager); + if (!g_dbus_interface_skeleton_export (interface_skeleton, + session->connection, + session->object_path, + error)) + return FALSE; + + g_signal_connect_object (monitor_manager, "monitors-changed", + G_CALLBACK (on_monitors_changed), + session, 0); + + return TRUE; +} + +static void +initable_init_iface (GInitableIface *iface) +{ + iface->init = meta_input_capture_session_initable_init; +} + +static void +meta_input_capture_session_init_iface (MetaDBusInputCaptureSessionIface *iface) +{ + iface->handle_add_barrier = handle_add_barrier; + iface->handle_enable = handle_enable; + iface->handle_disable = handle_disable; + iface->handle_release = handle_release; + iface->handle_close = handle_close; + iface->handle_get_zones = handle_get_zones; +} + +static void +meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) +{ + iface->close = meta_input_capture_session_close; + iface->get_id = meta_input_capture_session_get_id; +} + +static void +meta_input_capture_session_finalize (GObject *object) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + + g_clear_object (&session->handle); + g_free (session->peer_name); + g_free (session->session_id); + g_free (session->object_path); + + G_OBJECT_CLASS (meta_input_capture_session_parent_class)->finalize (object); +} + +static void +meta_input_capture_session_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + + switch (prop_id) + { + case PROP_SESSION_MANAGER: + session->session_manager = g_value_get_object (value); + break; + case PROP_PEER_NAME: + session->peer_name = g_value_dup_string (value); + break; + case PROP_ID: + session->session_id = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_input_capture_session_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + + switch (prop_id) + { + case PROP_SESSION_MANAGER: + g_value_set_object (value, session->session_manager); + break; + case PROP_PEER_NAME: + g_value_set_string (value, session->peer_name); + break; + case PROP_ID: + g_value_set_string (value, session->session_id); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +meta_input_capture_session_class_init (MetaInputCaptureSessionClass *klass) +{ + + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = meta_input_capture_session_finalize; + object_class->set_property = meta_input_capture_session_set_property; + object_class->get_property = meta_input_capture_session_get_property; + + obj_props[PROP_SESSION_MANAGER] = + g_param_spec_object ("session-manager", + "session manager", + "D-Bus session manager", + META_TYPE_DBUS_SESSION_MANAGER, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_PEER_NAME] = + g_param_spec_string ("peer-name", + "peer name", + "D-Bus peer name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + obj_props[PROP_ID] = + g_param_spec_string ("id", + "session id", + "Unique ID of the session", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); + g_object_class_install_properties (object_class, N_PROPS, obj_props); +} + +static void +meta_input_capture_session_init (MetaInputCaptureSession *session) +{ + static unsigned int global_session_number = 0; + + session->object_path = + g_strdup_printf (META_INPUT_CAPTURE_SESSION_DBUS_PATH "/u%u", + ++global_session_number); +} + +char * +meta_input_capture_session_get_object_path (MetaInputCaptureSession *session) +{ + return session->object_path; +} + +static MetaInputCaptureSessionHandle * +meta_input_capture_session_handle_new (MetaInputCaptureSession *session) +{ + MetaInputCaptureSessionHandle *handle; + + handle = g_object_new (META_TYPE_INPUT_CAPTURE_SESSION_HANDLE, NULL); + handle->session = session; + + return handle; +} + +static void +meta_input_capture_session_handle_stop (MetaRemoteAccessHandle *handle) +{ + MetaInputCaptureSession *session; + + session = META_INPUT_CAPTURE_SESSION_HANDLE (handle)->session; + if (!session) + return; + + meta_dbus_session_close (META_DBUS_SESSION (session)); +} + +static void +meta_input_capture_session_handle_class_init (MetaInputCaptureSessionHandleClass *klass) +{ + MetaRemoteAccessHandleClass *remote_access_handle_class = + META_REMOTE_ACCESS_HANDLE_CLASS (klass); + + remote_access_handle_class->stop = meta_input_capture_session_handle_stop; +} + +static void +meta_input_capture_session_handle_init (MetaInputCaptureSessionHandle *handle) +{ +} diff --git a/src/backends/meta-input-capture-session.h b/src/backends/meta-input-capture-session.h new file mode 100644 index 00000000000..1a52085b024 --- /dev/null +++ b/src/backends/meta-input-capture-session.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_INPUT_CAPTURE_SESSION_H +#define META_INPUT_CAPTURE_SESSION_H + +#include + +#include "backends/meta-input-capture.h" +#include "meta/meta-remote-access-controller.h" + +#define META_TYPE_INPUT_CAPTURE_SESSION (meta_input_capture_session_get_type ()) +G_DECLARE_FINAL_TYPE (MetaInputCaptureSession, meta_input_capture_session, + META, INPUT_CAPTURE_SESSION, + MetaDBusInputCaptureSessionSkeleton) + +#define META_TYPE_INPUT_CAPTURE_SESSION_HANDLE (meta_input_capture_session_handle_get_type ()) +G_DECLARE_FINAL_TYPE (MetaInputCaptureSessionHandle, + meta_input_capture_session_handle, + META, INPUT_CAPTURE_SESSION_HANDLE, + MetaRemoteAccessHandle) + +char * meta_input_capture_session_get_object_path (MetaInputCaptureSession *session); + +#endif /* META_INPUT_CAPTURE_SESSION_H */ diff --git a/src/backends/meta-input-capture.c b/src/backends/meta-input-capture.c new file mode 100644 index 00000000000..30c796b65cf --- /dev/null +++ b/src/backends/meta-input-capture.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "backends/meta-input-capture.h" + +#include "backends/meta-input-capture-session.h" +#include "backends/meta-backend-private.h" +#include "clutter/clutter.h" + +#include "meta-dbus-capture-input.h" + +#define META_INPUT_CAPTURE_DBUS_SERVICE "org.gnome.Mutter.InputCapture" +#define META_INPUT_CAPTURE_DBUS_PATH "/org/gnome/Mutter/InputCapture" + +typedef enum _MetaInputCaptureCapabilities +{ + META_INPUT_CAPTURE_CAPABILITY_NONE = 0, + META_INPUT_CAPTURE_CAPABILITY_RELATIVE_POINTER = 1, + META_INPUT_CAPTURE_CAPABILITY_ABSOLUTE_POINTER = 2, + META_INPUT_CAPTURE_CAPABILITY_KEYBOARD = 4, + META_INPUT_CAPTURE_CAPABILITY_TOUCH = 8, +} MetaInputCaptureCapabilities; + +struct _MetaInputCapture +{ + MetaDbusSessionManager parent; +}; + +G_DEFINE_TYPE (MetaInputCapture, meta_input_capture, + META_TYPE_DBUS_SESSION_MANAGER) + +static gboolean +handle_create_session (MetaDBusInputCapture *skeleton, + GDBusMethodInvocation *invocation, + uint32_t capabilities, + MetaInputCapture *input_capture) +{ + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (input_capture); + MetaDbusSession *dbus_session; + MetaInputCaptureSession *session; + g_autoptr (GError) error = NULL; + char *session_path; + + dbus_session = + meta_dbus_session_manager_create_session (session_manager, + invocation, + &error, + NULL); + if (!dbus_session) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + error->message); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + session = META_INPUT_CAPTURE_SESSION (dbus_session); + + session_path = meta_input_capture_session_get_object_path (session); + meta_dbus_input_capture_complete_create_session (skeleton, + invocation, + session_path); + + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + +static void +meta_input_capture_constructed (GObject *object) +{ + MetaInputCapture *input_capture = META_INPUT_CAPTURE (object); + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (input_capture); + GDBusInterfaceSkeleton *interface_skeleton = + meta_dbus_session_manager_get_interface_skeleton (session_manager); + + g_signal_connect (interface_skeleton, "handle-create-session", + G_CALLBACK (handle_create_session), input_capture); + + G_OBJECT_CLASS (meta_input_capture_parent_class)->constructed (object); +} + +static void +meta_input_capture_class_init (MetaInputCaptureClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructed = meta_input_capture_constructed; +} + +static void +meta_input_capture_init (MetaInputCapture *input_capture) +{ +} + +static MetaInputCaptureCapabilities +calculate_supported_capabilities (MetaInputCapture *input_capture) +{ + MetaDbusSessionManager *session_manager = + META_DBUS_SESSION_MANAGER (input_capture); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session_manager); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + ClutterVirtualDeviceType device_types; + MetaInputCaptureCapabilities supported_capabilities = + META_INPUT_CAPTURE_CAPABILITY_NONE; + + device_types = + clutter_seat_get_supported_virtual_device_types (seat); + + if (device_types & CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD) + supported_capabilities |= META_INPUT_CAPTURE_CAPABILITY_KEYBOARD; + if (device_types & CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER) + { + supported_capabilities |= META_INPUT_CAPTURE_CAPABILITY_RELATIVE_POINTER; + supported_capabilities |= META_INPUT_CAPTURE_CAPABILITY_ABSOLUTE_POINTER; + } + if (device_types & CLUTTER_VIRTUAL_DEVICE_TYPE_TOUCHSCREEN) + supported_capabilities |= META_INPUT_CAPTURE_CAPABILITY_TOUCH; + + return supported_capabilities; +} + +MetaInputCapture * +meta_input_capture_new (MetaBackend *backend) +{ + MetaInputCapture *input_capture; + g_autoptr (MetaDBusInputCapture) skeleton = NULL; + + skeleton = meta_dbus_input_capture_skeleton_new (); + input_capture = g_object_new (META_TYPE_INPUT_CAPTURE, + "backend", backend, + "service-name", META_INPUT_CAPTURE_DBUS_SERVICE, + "service-path", META_INPUT_CAPTURE_DBUS_PATH, + "session-gtype", META_TYPE_INPUT_CAPTURE_SESSION, + "interface-skeleton", skeleton, + NULL); + + meta_dbus_input_capture_set_supported_capabilities ( + META_DBUS_INPUT_CAPTURE (skeleton), + calculate_supported_capabilities (input_capture)); + + return input_capture; +} diff --git a/src/backends/meta-input-capture.h b/src/backends/meta-input-capture.h new file mode 100644 index 00000000000..d651e77b346 --- /dev/null +++ b/src/backends/meta-input-capture.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_INPUT_CAPTURE_H +#define META_INPUT_CAPTURE_H + +#include "backends/meta-dbus-session-manager.h" + +#include "meta-dbus-input-capture.h" + +#define META_TYPE_INPUT_CAPTURE (meta_input_capture_get_type ()) +G_DECLARE_FINAL_TYPE (MetaInputCapture, meta_input_capture, + META, INPUT_CAPTURE, + MetaDbusSessionManager) + +MetaInputCapture * meta_input_capture_new (MetaBackend *backend); + +#endif /* META_INPUT_CAPTURE_H */ diff --git a/src/meson.build b/src/meson.build index fff4a715aee..c6b30307c5d 100644 --- a/src/meson.build +++ b/src/meson.build @@ -205,6 +205,10 @@ mutter_sources = [ 'backends/meta-idle-manager.c', 'backends/meta-idle-manager.h', 'backends/meta-idle-monitor-private.h', + 'backends/meta-input-capture.c', + 'backends/meta-input-capture.h', + 'backends/meta-input-capture-session.c', + 'backends/meta-input-capture-session.h', 'backends/meta-input-device.c', 'backends/meta-input-mapper.c', 'backends/meta-input-mapper-private.h', @@ -903,6 +907,13 @@ dbus_rtkit_built_sources = gnome.gdbus_codegen('meta-dbus-rtkit1', ) mutter_built_sources += dbus_rtkit_built_sources +dbus_input_capture_built_sources = gnome.gdbus_codegen('meta-dbus-input-capture', + join_paths(dbus_interfaces_dir, 'org.gnome.Mutter.InputCapture.xml'), + interface_prefix: 'org.gnome.Mutter', + namespace: 'MetaDBus', +) +mutter_built_sources += dbus_input_capture_built_sources + wayland_protocol_server_headers = [] wayland_protocol_client_headers = [] wayland_protocol_sources = [] diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c new file mode 100644 index 00000000000..c4ca7b8a0ed --- /dev/null +++ b/src/tests/input-capture-test-client.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include +#include + +#include "meta-dbus-input-capture.h" + +typedef struct +{ + unsigned int width; + unsigned int height; + int x; + int y; +} Zone; + +G_DEFINE_AUTOPTR_CLEANUP_FUNC (Zone, g_free) + +typedef enum _Capabilities +{ + CAPABILITY_NONE = 0, + CAPABILITY_RELATIVE_POINTER = 1, + CAPABILITY_ABSOLUTE_POINTER = 2, + CAPABILITY_KEYBOARD = 4, + CAPABILITY_TOUCH = 8, +} Capabilities; + +typedef struct _InputCapture +{ + MetaDBusInputCapture *proxy; +} InputCapture; + +typedef struct _InputCaptureSession +{ + MetaDBusInputCaptureSession *proxy; + unsigned int serial; +} InputCaptureSession; + +static void +ping_mutter (InputCaptureSession *session) +{ + GDBusProxy *proxy = G_DBUS_PROXY (session->proxy); + GError *error = NULL; + + if (!g_dbus_connection_call_sync (g_dbus_proxy_get_connection (proxy), + "org.gnome.Mutter.InputCapture", + g_dbus_proxy_get_object_path (proxy), + "org.freedesktop.DBus.Peer", + "Ping", + NULL, + NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, + NULL, &error)) + g_error ("Failed to ping D-Bus peer: %s", error->message); +} + +static void +write_state (InputCaptureSession *session, + const char *state) +{ + ping_mutter (session); + fprintf (stdout, "%s\n", state); + fflush (stdout); +} + +static InputCapture * +input_capture_new (void) +{ + InputCapture *input_capture; + GError *error = NULL; + + input_capture = g_new0 (InputCapture, 1); + input_capture->proxy = meta_dbus_input_capture_proxy_new_for_bus_sync ( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "org.gnome.Mutter.InputCapture", + "/org/gnome/Mutter/InputCapture", + NULL, + &error); + if (!input_capture->proxy) + g_error ("Failed to acquire proxy: %s", error->message); + + return input_capture; +} + +static InputCaptureSession * +input_capture_create_session (InputCapture *input_capture) +{ + GError *error = NULL; + InputCaptureSession *session; + g_autofree char *session_path = NULL; + + if (!meta_dbus_input_capture_call_create_session_sync (input_capture->proxy, + CAPABILITY_RELATIVE_POINTER | + CAPABILITY_ABSOLUTE_POINTER, + &session_path, + NULL, + &error)) + g_error ("Failed to create input capture session: %s", error->message); + + session = g_new0 (InputCaptureSession, 1); + session->proxy = meta_dbus_input_capture_session_proxy_new_for_bus_sync ( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "org.gnome.Mutter.InputCapture", + session_path, + NULL, &error); + if (!session->proxy) + g_error ("Failed to acquire proxy: %s", error->message); + + return session; +} + +static void +input_capture_session_close (InputCaptureSession *session) +{ + GError *error = NULL; + + if (!meta_dbus_input_capture_session_call_close_sync (session->proxy, + NULL, &error)) + g_error ("Failed to close session: %s", error->message); + + g_object_unref (session->proxy); + g_free (session); +} + +static GList * +input_capture_session_get_zones (InputCaptureSession *session) +{ + GError *error = NULL; + g_autoptr (GVariant) zones_variant = NULL; + GVariantIter iter; + GList *zones = NULL; + unsigned int width, height; + int x, y; + + if (!meta_dbus_input_capture_session_call_get_zones_sync (session->proxy, + &session->serial, + &zones_variant, + NULL, &error)) + g_error ("Failed to get zones: %s", error->message); + + g_variant_iter_init (&iter, zones_variant); + while (g_variant_iter_next (&iter, "(uuii)", &width, &height, &x, &y)) + { + Zone *zone; + + zone = g_new0 (Zone, 1); + *zone = (Zone) { + .width = width, + .height = height, + .x = x, + .y = y, + }; + zones = g_list_append (zones, zone); + } + + return zones; +} + +static void +input_capture_session_enable (InputCaptureSession *session) +{ + g_autoptr (GError) error = NULL; + + if (!meta_dbus_input_capture_session_call_enable_sync (session->proxy, + NULL, &error)) + g_warning ("Failed to enable session: %s", error->message); +} + +static void +input_capture_session_disable (InputCaptureSession *session) +{ + g_autoptr (GError) error = NULL; + + if (!meta_dbus_input_capture_session_call_disable_sync (session->proxy, + NULL, &error)) + g_warning ("Failed to disable session: %s", error->message); +} + +static void +test_sanity (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*org.freedesktop.DBus.Error.Failed: Session not enabled*"); + input_capture_session_disable (session); + g_test_assert_expected_messages (); + + input_capture_session_enable (session); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*org.freedesktop.DBus.Error.Failed: Already enabled*"); + input_capture_session_enable (session); + g_test_assert_expected_messages (); + + input_capture_session_disable (session); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, + "*org.freedesktop.DBus.Error.Failed: Session not enabled*"); + input_capture_session_disable (session); + g_test_assert_expected_messages (); + + input_capture_session_close (session); +} + +static void +on_zones_changed (MetaDBusInputCaptureSession *proxy, + int *zones_changed_count) +{ + *zones_changed_count += 1; +} + +static void +assert_zones (GList *zones, + const Zone *expected_zones, + int n_expected_zones) +{ + GList *l; + int i; + + g_assert_cmpuint (g_list_length (zones), ==, n_expected_zones); + + for (l = zones, i = 0; l; l = l->next, i++) + { + Zone *zone = l->data; + + g_assert_cmpint (zone->width, ==, expected_zones[i].width); + g_assert_cmpint (zone->height, ==, expected_zones[i].height); + g_assert_cmpint (zone->x, ==, expected_zones[i].x); + g_assert_cmpint (zone->y, ==, expected_zones[i].y); + } +} + +static void +test_zones (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + static const Zone expected_zones1[] = { + { .width = 800, .height = 600, .x = 0, .y = 0 }, + { .width = 1024, .height = 768, .x = 800, .y = 0 }, + }; + static const Zone expected_zones2[] = { + { .width = 1024, .height = 768, .x = 0, .y = 0 }, + }; + GList *zones; + int zones_changed_count = 0; + unsigned int serial; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + g_signal_connect (session->proxy, "zones-changed", + G_CALLBACK (on_zones_changed), + &zones_changed_count); + + zones = input_capture_session_get_zones (session); + assert_zones (zones, expected_zones1, G_N_ELEMENTS (expected_zones1)); + g_clear_list (&zones, g_free); + + write_state (session, "1"); + + while (zones_changed_count == 0) + g_main_context_iteration (NULL, TRUE); + + serial = session->serial; + g_clear_list (&zones, g_free); + + zones = input_capture_session_get_zones (session); + g_assert_cmpuint (session->serial, >, serial); + assert_zones (zones, expected_zones2, G_N_ELEMENTS (expected_zones2)); + g_clear_list (&zones, g_free); + + input_capture_session_close (session); +} + +static const struct +{ + const char *name; + void (* func) (void); +} test_cases[] = { + { "sanity", test_sanity, }, + { "zones", test_zones, }, +}; + +static void +print_to_stderr (const char *text) +{ + fputs (text, stderr); + fflush (stderr); +} + +int +main (int argc, + char **argv) +{ + const char *test_case; + int i; + + g_assert_cmpint (argc, ==, 2); + + test_case = argv[1]; + + g_set_print_handler (print_to_stderr); + g_test_init (&argc, &argv, NULL); + + for (i = 0; i < G_N_ELEMENTS (test_cases); i++) + { + if (g_strcmp0 (test_cases[i].name, test_case) == 0) + { + test_cases[i].func (); + return EXIT_SUCCESS; + } + } + + g_warning ("Invalid test case '%s'", test_case); + return EXIT_FAILURE; +} diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c new file mode 100644 index 00000000000..cb319aff033 --- /dev/null +++ b/src/tests/input-capture-tests.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include + +#include "meta-test/meta-context-test.h" +#include "tests/meta-test-utils.h" + +typedef struct _InputCaptureTestClient +{ + GSubprocess *subprocess; + char *path; + GMainLoop *main_loop; + GDataInputStream *line_reader; +} InputCaptureTestClient; + +static MetaContext *test_context; + +static InputCaptureTestClient * +input_capture_test_client_new (const char *test_case) +{ + g_autofree char *test_client_path = NULL; + g_autoptr (GSubprocessLauncher) launcher = NULL; + GSubprocess *subprocess; + GError *error = NULL; + InputCaptureTestClient *test_client; + GInputStream *stdout_stream; + GDataInputStream *line_reader; + + test_client_path = g_test_build_filename (G_TEST_BUILT, + "src", + "tests", + "mutter-input-capture-test-client", + NULL); + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE); + subprocess = g_subprocess_launcher_spawn (launcher, + &error, + test_client_path, + test_case, + NULL); + if (!subprocess) + g_error ("Failed to launch input capture test client: %s", error->message); + + stdout_stream = g_subprocess_get_stdout_pipe (subprocess); + line_reader = g_data_input_stream_new (stdout_stream); + + test_client = g_new0 (InputCaptureTestClient, 1); + test_client->subprocess = subprocess; + test_client->main_loop = g_main_loop_new (NULL, FALSE); + test_client->line_reader = line_reader; + + return test_client; +} + +typedef struct +{ + GMainLoop *loop; + const char *expected_state; +} WaitData; + +static void +on_line_read (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + WaitData *data = user_data; + g_autofree char *line = NULL; + g_autoptr (GError) error = NULL; + + line = + g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (source_object), + res, NULL, &error); + if (error) + g_error ("Failed to read line from test client: %s", error->message); + if (!line) + g_error ("Unexpected EOF"); + + g_assert_cmpstr (data->expected_state, ==, line); + + g_main_loop_quit (data->loop); +} + +static void +input_capture_test_client_wait_for_state (InputCaptureTestClient *test_client, + const char *expected_state) +{ + WaitData data; + + data.loop = g_main_loop_new (NULL, FALSE); + data.expected_state = expected_state; + + g_data_input_stream_read_line_async (test_client->line_reader, + G_PRIORITY_DEFAULT, + NULL, + on_line_read, + &data); + + g_main_loop_run (data.loop); + g_main_loop_unref (data.loop); +} + +static void +input_capture_test_client_finished (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + InputCaptureTestClient *test_client = user_data; + GError *error = NULL; + + if (!g_subprocess_wait_finish (test_client->subprocess, + res, + &error)) + { + g_error ("Failed to wait for input capture test client: %s", + error->message); + } + + g_main_loop_quit (test_client->main_loop); +} + +static void +input_capture_test_client_finish (InputCaptureTestClient *test_client) +{ + g_subprocess_wait_async (test_client->subprocess, NULL, + input_capture_test_client_finished, test_client); + + g_main_loop_run (test_client->main_loop); + + g_assert_true (g_subprocess_get_successful (test_client->subprocess)); + + g_main_loop_unref (test_client->main_loop); + g_object_unref (test_client->line_reader); + g_object_unref (test_client->subprocess); + g_free (test_client); +} + +static void +meta_test_input_capture_sanity (void) +{ + InputCaptureTestClient *test_client; + + test_client = input_capture_test_client_new ("sanity"); + input_capture_test_client_finish (test_client); +} + +static void +meta_test_input_capture_zones (void) +{ + g_autoptr (MetaVirtualMonitor) virtual_monitor1 = NULL; + g_autoptr (MetaVirtualMonitor) virtual_monitor2 = NULL; + InputCaptureTestClient *test_client; + + virtual_monitor1 = meta_create_test_monitor (test_context, 800, 600, 20.0); + virtual_monitor2 = meta_create_test_monitor (test_context, 1024, 768, 20.0); + + test_client = input_capture_test_client_new ("zones"); + + input_capture_test_client_wait_for_state (test_client, "1"); + + g_clear_object (&virtual_monitor1); + + input_capture_test_client_finish (test_client); +} + +static void +init_tests (void) +{ + g_test_add_func ("/backends/native/input-capture/sanity", + meta_test_input_capture_sanity); + g_test_add_func ("/backends/native/input-capture/zones", + meta_test_input_capture_zones); +} + +int +main (int argc, + char **argv) +{ + g_autoptr (MetaContext) context = NULL; + g_autoptr (GError) error = NULL; + + context = test_context = + meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS, + META_CONTEXT_TEST_FLAG_NO_X11); + g_assert (meta_context_configure (context, &argc, &argv, NULL)); + + init_tests (); + + return meta_context_test_run_tests (META_CONTEXT_TEST (context), + META_TEST_RUN_FLAG_NONE); +} diff --git a/src/tests/meson.build b/src/tests/meson.build index cada89d7220..1029371c4cb 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -223,6 +223,23 @@ if have_native_tests install_dir: mutter_installed_tests_libexecdir, ) + input_capture_client = executable('mutter-input-capture-test-client', + sources: [ + 'input-capture-test-client.c', + dbus_input_capture_built_sources, + ], + include_directories: tests_includes, + c_args: [ + tests_c_args, + '-DG_LOG_DOMAIN="mutter-input-capture-test-client"', + ], + dependencies: [ + gio_unix_dep, + ], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, + ) + # Native backend tests test_cases += [ { @@ -256,6 +273,11 @@ if have_native_tests 'suite': 'backends/native', 'sources': [ 'native-persistent-virtual-monitor.c' ], }, + { + 'name': 'input-capture', + 'suite': 'backends/native', + 'sources': [ 'input-capture-tests.c' ], + }, ] # Privileged tests diff --git a/src/tests/meta-test-utils.h b/src/tests/meta-test-utils.h index fe0bde766fa..7af0b67598d 100644 --- a/src/tests/meta-test-utils.h +++ b/src/tests/meta-test-utils.h @@ -25,6 +25,7 @@ #include #include "backends/meta-backend-types.h" +#include "backends/meta-virtual-monitor.h" #include "meta/window.h" #define META_TEST_CLIENT_ERROR meta_test_client_error_quark () -- GitLab From 20d5eba927c67cd7f447261c328f93aee96f34da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 31 Mar 2022 21:24:46 +0200 Subject: [PATCH 20/40] barrier: Add ways to make barriers sticky A sticky barrier means that a pointer in motion intersecting a barrier doesn't move once having hit it. The intention with this is to allow an input capture clients to continue a motion once a barrier is hit. --- src/backends/meta-barrier-private.h | 2 + src/backends/meta-barrier.c | 28 ++++++++++ src/backends/native/meta-barrier-native.c | 63 ++++++++++++++++++++++- src/meta/barrier.h | 7 +++ 4 files changed, 98 insertions(+), 2 deletions(-) diff --git a/src/backends/meta-barrier-private.h b/src/backends/meta-barrier-private.h index ebe22cb54f6..38b1bfa0724 100644 --- a/src/backends/meta-barrier-private.h +++ b/src/backends/meta-barrier-private.h @@ -58,6 +58,8 @@ MetaBackend * meta_barrier_get_backend (MetaBarrier *barrier); MetaBorder * meta_barrier_get_border (MetaBarrier *barrier); +MetaBarrierFlags meta_barrier_get_flags (MetaBarrier *barrier); + G_END_DECLS #endif /* META_BARRIER_PRIVATE_H */ diff --git a/src/backends/meta-barrier.c b/src/backends/meta-barrier.c index c57a95d296e..103c39e1c56 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -33,6 +33,8 @@ typedef struct _MetaBarrierPrivate MetaBackend *backend; MetaBorder border; MetaBarrierImpl *impl; + + MetaBarrierFlags flags; } MetaBarrierPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaBarrier, meta_barrier, G_TYPE_OBJECT) @@ -59,6 +61,7 @@ enum PROP_X2, PROP_Y2, PROP_DIRECTIONS, + PROP_FLAGS, PROP_LAST, }; @@ -124,6 +127,9 @@ meta_barrier_get_property (GObject *object, g_value_set_flags (value, meta_border_get_allows_directions (&priv->border)); break; + case PROP_FLAGS: + g_value_set_flags (value, priv->flags); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -169,6 +175,9 @@ meta_barrier_set_property (GObject *object, meta_border_set_allows_directions (&priv->border, g_value_get_flags (value)); break; + case PROP_FLAGS: + priv->flags = g_value_get_flags (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -336,6 +345,15 @@ meta_barrier_class_init (MetaBarrierClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + obj_props[PROP_FLAGS] = + g_param_spec_flags ("flags", + "Flags", + "Flags for manipulating barrier behavior", + META_TYPE_BARRIER_FLAGS, + META_BARRIER_FLAG_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); @@ -400,6 +418,7 @@ meta_barrier_new (MetaBackend *backend, int x2, int y2, MetaBarrierDirection directions, + MetaBarrierFlags flags, GError **error) { MetaBarrier *barrier; @@ -412,6 +431,7 @@ meta_barrier_new (MetaBackend *backend, "x2", x2, "y2", y2, "directions", directions, + "flags", flags, NULL); priv = meta_barrier_get_instance_private (barrier); if (!priv->impl) @@ -455,6 +475,14 @@ meta_barrier_get_border (MetaBarrier *barrier) return &priv->border; } +MetaBarrierFlags +meta_barrier_get_flags (MetaBarrier *barrier) +{ + MetaBarrierPrivate *priv = meta_barrier_get_instance_private (barrier); + + return priv->flags; +} + static void meta_barrier_impl_class_init (MetaBarrierImplClass *klass) { diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 0b325b846d1..c03942bc95e 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -44,6 +44,7 @@ struct _MetaBarrierManagerNative { GHashTable *barriers; GMutex mutex; + MetaBarrierImplNative *stickied_barrier; }; typedef enum @@ -504,6 +505,38 @@ clamp_to_barrier (MetaBarrierImplNative *self, self->state = META_BARRIER_STATE_HIT; } +static gboolean +stick_to_barrier (MetaBarrierImplNative *self, + MetaBarrierDirection motion_dir, + float prev_x, + float prev_y, + float *x, + float *y) +{ + MetaLine2 motion = { + .a = { .x = prev_x, .y = prev_y }, + .b = { .x = *x, .y = *y }, + }; + MetaBorder *border = meta_barrier_get_border (self->barrier); + MetaVector2 intersection; + + if (meta_line2_intersects_with (&motion, &border->line, + &intersection)) + { + *x = intersection.x; + *y = intersection.y; + + self->blocked_dir = motion_dir; + self->state = META_BARRIER_STATE_HIT; + self->manager->stickied_barrier = self; + return TRUE; + } + else + { + return FALSE; + } +} + void meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager, ClutterInputDevice *device, @@ -524,6 +557,16 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager, device, NULL, &prev_pos, NULL)) return; + prev_x = prev_pos.x; + prev_y = prev_pos.y; + + if (manager->stickied_barrier) + { + *x = prev_pos.x; + *y = prev_pos.y; + return; + } + g_mutex_lock (&manager->mutex); prev_x = prev_pos.x; @@ -548,7 +591,18 @@ meta_barrier_manager_native_process_in_impl (MetaBarrierManagerNative *manager, *x, *y, motion_dir, &barrier_impl)) - clamp_to_barrier (barrier_impl, &motion_dir, x, y); + { + MetaBarrier *barrier = barrier_impl->barrier; + + if (meta_barrier_get_flags (barrier) & META_BARRIER_FLAG_STICKY) + { + if (stick_to_barrier (barrier_impl, motion_dir, + prev_x, prev_y, x, y)) + break; + } + + clamp_to_barrier (barrier_impl, &motion_dir, x, y); + } else break; } @@ -590,7 +644,10 @@ meta_barrier_impl_native_release (MetaBarrierImpl *impl, if (self->state == META_BARRIER_STATE_HELD && event->event_id == self->trigger_serial) - self->state = META_BARRIER_STATE_RELEASE; + { + self->state = META_BARRIER_STATE_RELEASE; + self->manager->stickied_barrier = NULL; + } } static void @@ -599,6 +656,8 @@ meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); g_mutex_lock (&self->manager->mutex); + if (self->manager->stickied_barrier == self) + self->manager->stickied_barrier = NULL; g_hash_table_remove (self->manager->barriers, self); g_mutex_unlock (&self->manager->mutex); g_main_context_unref (self->main_context); diff --git a/src/meta/barrier.h b/src/meta/barrier.h index 374c42f0b7f..412b25d5242 100644 --- a/src/meta/barrier.h +++ b/src/meta/barrier.h @@ -26,6 +26,12 @@ typedef enum META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, } MetaBarrierDirection; +typedef enum +{ + META_BARRIER_FLAG_NONE = 1 << 0, + META_BARRIER_FLAG_STICKY = 1 << 1, +} MetaBarrierFlags; + #define META_TYPE_BARRIER (meta_barrier_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaBarrier, meta_barrier, @@ -40,6 +46,7 @@ MetaBarrier * meta_barrier_new (MetaBackend *backend, int x2, int y2, MetaBarrierDirection directions, + MetaBarrierFlags flags, GError **error); META_EXPORT -- GitLab From 721741e8d7f9ec1c4a37b5fb1f5feb89520873e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 1 Apr 2022 08:34:46 +0200 Subject: [PATCH 21/40] tests/utils: Add helper to flush the input thread Add a helper function that ensures any queued virtual input events have been flushed from the input thread. This works by posting a task to the input thread, which will itself queue another callback back to the main thread. Once the main thread callback is invoked, the flush call is unblocked and the function returns. Upon this, any previously emitted virtual input event should have already passed through the input thread back into the main thread, however not necessarily fully processed. For making sure it has been processed, one also have to make sure the stage has been updated, e.g. via `meta_wait_for_paint()`. --- src/backends/native/meta-seat-impl.h | 1 + src/backends/native/meta-seat-native.h | 2 ++ src/tests/meta-test-utils.c | 47 ++++++++++++++++++++++++++ src/tests/meta-test-utils.h | 3 ++ 4 files changed, 53 insertions(+) diff --git a/src/backends/native/meta-seat-impl.h b/src/backends/native/meta-seat-impl.h index d3e2ab86079..5ca31e10f5d 100644 --- a/src/backends/native/meta-seat-impl.h +++ b/src/backends/native/meta-seat-impl.h @@ -133,6 +133,7 @@ MetaSeatImpl * meta_seat_impl_new (MetaSeatNative *seat_native, void meta_seat_impl_destroy (MetaSeatImpl *seat_impl); +META_EXPORT_TEST void meta_seat_impl_run_input_task (MetaSeatImpl *seat_impl, GTask *task, GSourceFunc dispatch_func); diff --git a/src/backends/native/meta-seat-native.h b/src/backends/native/meta-seat-native.h index c9ddcf63ea0..750d3c14c13 100644 --- a/src/backends/native/meta-seat-native.h +++ b/src/backends/native/meta-seat-native.h @@ -34,6 +34,7 @@ #include "backends/native/meta-pointer-constraint-native.h" #include "backends/native/meta-xkb-utils.h" #include "clutter/clutter.h" +#include "core/util-private.h" typedef struct _MetaSeatNative MetaSeatNative; @@ -66,6 +67,7 @@ struct _MetaSeatNative }; #define META_TYPE_SEAT_NATIVE meta_seat_native_get_type () +META_EXPORT_TEST G_DECLARE_FINAL_TYPE (MetaSeatNative, meta_seat_native, META, SEAT_NATIVE, ClutterSeat) diff --git a/src/tests/meta-test-utils.c b/src/tests/meta-test-utils.c index 261d2c41911..ff47ce15790 100644 --- a/src/tests/meta-test-utils.c +++ b/src/tests/meta-test-utils.c @@ -27,6 +27,9 @@ #include "backends/meta-monitor-config-store.h" #include "backends/meta-virtual-monitor.h" +#include "backends/native/meta-backend-native.h" +#include "backends/native/meta-input-thread.h" +#include "backends/native/meta-seat-native.h" #include "core/display-private.h" #include "core/window-private.h" #include "meta-test/meta-context-test.h" @@ -675,3 +678,47 @@ meta_create_test_monitor (MetaContext *context, return virtual_monitor; } + +#ifdef HAVE_NATIVE_BACKEND +static gboolean +callback_idle (gpointer user_data) +{ + GMainLoop *loop = user_data; + + g_main_loop_quit (loop); + return G_SOURCE_REMOVE; +} + +static gboolean +queue_callback (GTask *task) +{ + g_idle_add (callback_idle, g_task_get_task_data (task)); + return G_SOURCE_REMOVE; +} +#endif + +void +meta_flush_input (MetaContext *context) +{ +#ifdef HAVE_NATIVE_BACKEND + MetaBackend *backend = meta_context_get_backend (context); + ClutterSeat *seat; + MetaSeatNative *seat_native; + g_autoptr (GTask) task = NULL; + g_autoptr (GMainLoop) loop = NULL; + + g_assert_true (META_IS_BACKEND_NATIVE (backend)); + + seat = meta_backend_get_default_seat (backend); + seat_native = META_SEAT_NATIVE (seat); + + task = g_task_new (backend, NULL, NULL, NULL); + loop = g_main_loop_new (NULL, FALSE); + g_task_set_task_data (task, loop, NULL); + + meta_seat_impl_run_input_task (seat_native->impl, task, + (GSourceFunc) queue_callback); + + g_main_loop_run (loop); +#endif +} diff --git a/src/tests/meta-test-utils.h b/src/tests/meta-test-utils.h index 7af0b67598d..7c26b0e6254 100644 --- a/src/tests/meta-test-utils.h +++ b/src/tests/meta-test-utils.h @@ -128,4 +128,7 @@ MetaVirtualMonitor * meta_create_test_monitor (MetaContext *context, int height, float refresh_rate); +META_EXPORT +void meta_flush_input (MetaContext *context); + #endif /* TEST_UTILS_H */ -- GitLab From fbf9a1f368d3d152f85c53305d56beb7643a657e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 1 Apr 2022 10:04:52 +0200 Subject: [PATCH 22/40] barrier/native: Minor coding style fix --- src/backends/native/meta-barrier-native.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index c03942bc95e..807bb7f775f 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -470,9 +470,9 @@ maybe_emit_barrier_event (gpointer key, gpointer value, gpointer user_data) /* Clamp (x, y) to the barrier and remove clamped direction from motion_dir. */ static void clamp_to_barrier (MetaBarrierImplNative *self, - MetaBarrierDirection *motion_dir, - float *x, - float *y) + MetaBarrierDirection *motion_dir, + float *x, + float *y) { MetaBarrier *barrier = self->barrier; MetaBorder *border = meta_barrier_get_border (barrier); -- GitLab From 89d2ec6ed194550b803b00ceb97c0abd2c8f4dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 1 Apr 2022 10:09:46 +0200 Subject: [PATCH 23/40] barrier: Allow releasing without any event in the native implementation This allows for a sticky barrier to hold the pointer until it is released, but the owner of the barrier doesn't need a barrier event to release it. It will be used to implement input capturing. --- src/backends/native/meta-barrier-native.c | 2 +- src/backends/x11/meta-barrier-x11.c | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 807bb7f775f..4475bfbc16b 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -643,7 +643,7 @@ meta_barrier_impl_native_release (MetaBarrierImpl *impl, MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); if (self->state == META_BARRIER_STATE_HELD && - event->event_id == self->trigger_serial) + (!event || event->event_id == self->trigger_serial)) { self->state = META_BARRIER_STATE_RELEASE; self->manager->stickied_barrier = NULL; diff --git a/src/backends/x11/meta-barrier-x11.c b/src/backends/x11/meta-barrier-x11.c index 45ea1421b62..ff693175fbe 100644 --- a/src/backends/x11/meta-barrier-x11.c +++ b/src/backends/x11/meta-barrier-x11.c @@ -75,6 +75,12 @@ meta_barrier_impl_x11_release (MetaBarrierImpl *impl, MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); + if (!event) + { + g_warning ("X11 barriers always need barrier events to release"); + return; + } + XIBarrierReleasePointer (xdisplay, META_VIRTUAL_CORE_POINTER_ID, self->xbarrier, event->event_id); -- GitLab From e63520ec4218aea1cf4b09e238a1415558fa514c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 1 Apr 2022 15:22:17 +0200 Subject: [PATCH 24/40] input-capture: Hook up barrier adding Adding a barrier and later enabling the input capture session will create MetaBarrier instances for each added input capture barrier. The barriers are created as "sticky" which means that when a pointer hits the barrier, it'll stick to the point of entry, until it's released. The input capture session is also turned into a state machine with explicit state, to more easily track things. --- src/backends/meta-input-capture-session.c | 449 ++++++++++++++++++++-- src/tests/input-capture-test-client.c | 193 ++++++++++ src/tests/input-capture-tests.c | 108 +++++- 3 files changed, 725 insertions(+), 25 deletions(-) diff --git a/src/backends/meta-input-capture-session.c b/src/backends/meta-input-capture-session.c index 079e073399c..ea1a14d5d13 100644 --- a/src/backends/meta-input-capture-session.c +++ b/src/backends/meta-input-capture-session.c @@ -29,6 +29,7 @@ #include "backends/meta-monitor-manager-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-remote-access-controller-private.h" +#include "meta/barrier.h" #include "meta/boxes.h" #include "meta/meta-backend.h" @@ -36,6 +37,8 @@ #define META_INPUT_CAPTURE_SESSION_DBUS_PATH "/org/gnome/Mutter/InputCapture/Session" +static GQuark quark_barrier_id; + enum { PROP_0, @@ -49,6 +52,25 @@ enum static GParamSpec *obj_props[N_PROPS]; +typedef enum _InputCaptureState +{ + INPUT_CAPTURE_STATE_INIT, + INPUT_CAPTURE_STATE_ENABLED, + INPUT_CAPTURE_STATE_ACTIVATED, + INPUT_CAPTURE_STATE_CLOSED, +} InputCaptureState; + +typedef struct _InputCaptureBarrier +{ + int x1; + int y1; + int x2; + int y2; + + unsigned int id; + MetaBarrier *barrier; +} InputCaptureBarrier; + struct _MetaInputCaptureSession { MetaDBusInputCaptureSessionSkeleton parent; @@ -61,9 +83,11 @@ struct _MetaInputCaptureSession char *session_id; char *object_path; - gboolean enabled; + InputCaptureState state; + GHashTable *barriers; - uint32_t serial; + uint32_t zones_serial; + uint32_t activated_serial; MetaInputCaptureSessionHandle *handle; }; @@ -113,31 +137,152 @@ init_remote_access_handle (MetaInputCaptureSession *session) remote_access_handle); } +static void +release_remote_access_handle (MetaInputCaptureSession *session) +{ + MetaRemoteAccessHandle *remote_access_handle = + META_REMOTE_ACCESS_HANDLE (session->handle); + + meta_remote_access_handle_notify_stopped (remote_access_handle); + g_clear_object (&session->handle); +} + +static void +on_barrier_hit (MetaBarrier *barrier, + const MetaBarrierEvent *event, + MetaInputCaptureSession *session) +{ + MetaDBusInputCaptureSession *skeleton = + META_DBUS_INPUT_CAPTURE_SESSION (session); + GVariant *cursor_position; + unsigned int barrier_id; + + switch (session->state) + { + case INPUT_CAPTURE_STATE_ACTIVATED: + return; + case INPUT_CAPTURE_STATE_ENABLED: + break; + case INPUT_CAPTURE_STATE_INIT: + case INPUT_CAPTURE_STATE_CLOSED: + g_warn_if_reached (); + return; + } + + session->state = INPUT_CAPTURE_STATE_ACTIVATED; + + barrier_id = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (barrier), + quark_barrier_id)); + cursor_position = g_variant_new ("(dd)", event->x, event->y); + + meta_dbus_input_capture_session_emit_activated (skeleton, + barrier_id, + ++session->activated_serial, + cursor_position); + + init_remote_access_handle (session); +} + +static void +clear_all_barriers (MetaInputCaptureSession *session) +{ + GHashTableIter iter; + InputCaptureBarrier *input_capture_barrier; + + g_hash_table_iter_init (&iter, session->barriers); + while (g_hash_table_iter_next (&iter, NULL, + (gpointer *) &input_capture_barrier)) + g_clear_pointer (&input_capture_barrier->barrier, meta_barrier_destroy); +} + +static void +release_all_barriers (MetaInputCaptureSession *session) +{ + GHashTableIter iter; + InputCaptureBarrier *input_capture_barrier; + + g_hash_table_iter_init (&iter, session->barriers); + while (g_hash_table_iter_next (&iter, NULL, + (gpointer *) &input_capture_barrier)) + { + MetaBarrier *barrier; + + barrier = input_capture_barrier->barrier; + if (!barrier) + continue; + + meta_barrier_release (barrier, NULL); + } +} + static gboolean meta_input_capture_session_enable (MetaInputCaptureSession *session, GError **error) { - g_assert (!session->enabled); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + GHashTableIter iter; + gpointer key, value; - session->enabled = TRUE; + g_warn_if_fail (session->state == INPUT_CAPTURE_STATE_INIT); - init_remote_access_handle (session); + g_hash_table_iter_init (&iter, session->barriers); + while (g_hash_table_iter_next (&iter, &key, &value)) + { + unsigned int barrier_id = GPOINTER_TO_UINT (key); + InputCaptureBarrier *input_capture_barrier = value; + g_autoptr (MetaBarrier) barrier = NULL; + + barrier = meta_barrier_new (backend, + input_capture_barrier->x1, + input_capture_barrier->y1, + input_capture_barrier->x2, + input_capture_barrier->y2, + 0&&(META_BARRIER_DIRECTION_POSITIVE_X | + META_BARRIER_DIRECTION_POSITIVE_Y | + META_BARRIER_DIRECTION_NEGATIVE_X | + META_BARRIER_DIRECTION_NEGATIVE_Y), + META_BARRIER_FLAG_STICKY, + error); + if (!barrier) + goto err; + + g_object_set_qdata (G_OBJECT (barrier), quark_barrier_id, + GUINT_TO_POINTER (barrier_id)); + g_signal_connect (barrier, "hit", G_CALLBACK (on_barrier_hit), session); + input_capture_barrier->barrier = barrier; + } + + session->state = INPUT_CAPTURE_STATE_ENABLED; return TRUE; + +err: + clear_all_barriers (session); + return FALSE; } static void meta_input_capture_session_disable (MetaInputCaptureSession *session) { - session->enabled = FALSE; - - if (session->handle) + switch (session->state) { - MetaRemoteAccessHandle *remote_access_handle = - META_REMOTE_ACCESS_HANDLE (session->handle); - - meta_remote_access_handle_notify_stopped (remote_access_handle); + case INPUT_CAPTURE_STATE_INIT: + return; + case INPUT_CAPTURE_STATE_ACTIVATED: + case INPUT_CAPTURE_STATE_ENABLED: + break; + case INPUT_CAPTURE_STATE_CLOSED: + g_warn_if_reached (); + return; } + + clear_all_barriers (session); + + session->state = INPUT_CAPTURE_STATE_INIT; + + if (session->handle) + release_remote_access_handle (session); } static void @@ -148,8 +293,8 @@ meta_input_capture_session_close (MetaDbusSession *dbus_session) MetaDBusInputCaptureSession *skeleton = META_DBUS_INPUT_CAPTURE_SESSION (session); - if (session->enabled) - meta_input_capture_session_disable (session); + meta_input_capture_session_disable (session); + session->state = INPUT_CAPTURE_STATE_CLOSED; meta_dbus_session_notify_closed (META_DBUS_SESSION (session)); meta_dbus_input_capture_session_emit_closed (skeleton); @@ -174,15 +319,231 @@ check_permission (MetaInputCaptureSession *session, g_dbus_method_invocation_get_sender (invocation)) == 0; } +typedef enum +{ + LINE_ADJECENCY_ERROR, + LINE_ADJECENCY_NONE, + LINE_ADJECENCY_OVERLAP, + LINE_ADJECENCY_CONTAINED, + LINE_ADJECENCY_PARTIAL, +} LineAdjecency; + +static LineAdjecency +get_barrier_adjecency (MetaRectangle *rect, + int x1, + int y1, + int x2, + int y2, + GError **error) +{ + int x_min, x_max; + int y_min, y_max; + + x_min = MIN (x1, x2); + x_max = MAX (x1, x2); + y_min = MIN (y1, y2); + y_max = MAX (y1, y2); + + if (x1 == x2) + { + int x = x1; + + if (x < rect->x || x > rect->x + rect->width) + return LINE_ADJECENCY_NONE; + + if (y_max < rect->y || + y_min > rect->y + rect->height) + return LINE_ADJECENCY_NONE; + + if (rect->x + rect->width == x || rect->x == x) + { + if (y_max > rect->y + rect->height || + y_min < rect->y) + return LINE_ADJECENCY_PARTIAL; + else + return LINE_ADJECENCY_CONTAINED; + } + else + { + return LINE_ADJECENCY_OVERLAP; + } + } + else + { + int y = y1; + + if (y < rect->y || y > rect->y + rect->height) + return LINE_ADJECENCY_NONE; + + if (x_max < rect->x || + x_min > rect->x + rect->width) + return LINE_ADJECENCY_NONE; + + if (rect->y + rect->height == y || rect->y == y) + { + if (x_max > rect->x + rect->width || + x_min < rect->x) + return LINE_ADJECENCY_PARTIAL; + else + return LINE_ADJECENCY_CONTAINED; + } + else + { + return LINE_ADJECENCY_OVERLAP; + } + } +} + +static gboolean +check_barrier (MetaInputCaptureSession *session, + int x1, + int y1, + int x2, + int y2, + GError **error) +{ + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + MetaMonitorManager *monitor_manager = + meta_backend_get_monitor_manager (backend); + gboolean has_adjecent_monitor = FALSE; + GList *logical_monitors; + GList *l; + + if (x1 != x2 && y1 != y2) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Barrier coordinates not axis aligned"); + return FALSE; + } + + if (x1 == x2 && y1 == y2) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Barrier cannot be a singularity"); + return FALSE; + } + + logical_monitors = + meta_monitor_manager_get_logical_monitors (monitor_manager); + for (l = logical_monitors; l; l = l->next) + { + MetaLogicalMonitor *logical_monitor = l->data; + MetaRectangle layout; + + layout = meta_logical_monitor_get_layout (logical_monitor); + switch (get_barrier_adjecency (&layout, x1, y1, x2, y2, error)) + { + case LINE_ADJECENCY_ERROR: + return FALSE; + case LINE_ADJECENCY_NONE: + break; + case LINE_ADJECENCY_CONTAINED: + if (has_adjecent_monitor) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Adjecent to multiple monitor edges"); + return FALSE; + } + has_adjecent_monitor = TRUE; + break; + case LINE_ADJECENCY_OVERLAP: + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Line overlaps with monitor region"); + return FALSE; + case LINE_ADJECENCY_PARTIAL: + g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, + "Line partially with monitor region"); + return FALSE; + } + } + + return has_adjecent_monitor; +} + +static unsigned int +find_available_barrier_id (MetaInputCaptureSession *session) +{ + unsigned int id; + + for (id = 1;; id++) + { + if (!g_hash_table_contains (session->barriers, GUINT_TO_POINTER (id))) + return id; + } +} + +static void +input_capture_barrier_free (gpointer user_data) +{ + InputCaptureBarrier *input_capture_barrier = user_data; + + g_clear_pointer (&input_capture_barrier->barrier, meta_barrier_destroy); + g_free (input_capture_barrier); +} + static gboolean handle_add_barrier (MetaDBusInputCaptureSession *object, GDBusMethodInvocation *invocation, unsigned int serial, GVariant *position) { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_FAILED, - "Not implemented"); + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + int x1, y1, x2, y2; + g_autoptr (GError) error = NULL; + InputCaptureBarrier *input_capture_barrier; + unsigned int barrier_id; + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + if (session->zones_serial != serial) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_BAD_ADDRESS, + "State out of date"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + if (session->state != INPUT_CAPTURE_STATE_INIT) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Session already enabled"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + g_variant_get (position, "(iiii)", &x1, &y1, &x2, &y2); + if (!check_barrier (session, x1, y1, x2, y2, &error)) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + error->message); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + barrier_id = find_available_barrier_id (session); + + input_capture_barrier = g_new0 (InputCaptureBarrier, 1); + *input_capture_barrier = (InputCaptureBarrier) { + .id = barrier_id, + .x1 = x1, + .y1 = y1, + .x2 = x2, + .y2 = y2, + }; + g_hash_table_insert (session->barriers, + GUINT_TO_POINTER (barrier_id), + input_capture_barrier); + + meta_dbus_input_capture_session_complete_add_barrier (object, invocation, + barrier_id); + return G_DBUS_METHOD_INVOCATION_HANDLED; } @@ -218,7 +579,7 @@ handle_get_zones (MetaDBusInputCaptureSession *object, zones_variant = g_variant_builder_end (&zones_builder); meta_dbus_input_capture_session_complete_get_zones (object, invocation, - session->serial, + session->zones_serial, zones_variant); return G_DBUS_METHOD_INVOCATION_HANDLED; } @@ -238,7 +599,7 @@ handle_enable (MetaDBusInputCaptureSession *skeleton, return G_DBUS_METHOD_INVOCATION_HANDLED; } - if (session->enabled) + if (session->state != INPUT_CAPTURE_STATE_INIT) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, @@ -275,7 +636,8 @@ handle_disable (MetaDBusInputCaptureSession *skeleton, return G_DBUS_METHOD_INVOCATION_HANDLED; } - if (!session->enabled) + if (session->state != INPUT_CAPTURE_STATE_ENABLED && + session->state != INPUT_CAPTURE_STATE_ACTIVATED) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, @@ -295,9 +657,40 @@ handle_release (MetaDBusInputCaptureSession *object, GDBusMethodInvocation *invocation, GVariant *position) { - g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, - G_DBUS_ERROR_ACCESS_DENIED, - "Not implemented"); + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + double x, y; + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + if (session->state != INPUT_CAPTURE_STATE_ACTIVATED) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Capture not active"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + release_all_barriers (session); + + session->state = INPUT_CAPTURE_STATE_ENABLED; + + g_variant_get (position, "(dd)", &x, &y); + clutter_seat_warp_pointer (seat, x, y); + + if (session->handle) + release_remote_access_handle (session); + + meta_dbus_input_capture_session_complete_release (object, invocation); + return G_DBUS_METHOD_INVOCATION_HANDLED; } @@ -329,7 +722,7 @@ on_monitors_changed (MetaMonitorManager *monitor_manager, MetaDBusInputCaptureSession *skeleton = META_DBUS_INPUT_CAPTURE_SESSION (session); - session->serial++; + session->zones_serial++; meta_input_capture_session_disable (session); meta_dbus_input_capture_session_emit_zones_changed (skeleton); } @@ -390,6 +783,8 @@ meta_input_capture_session_finalize (GObject *object) { MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + g_clear_pointer (&session->barriers, g_hash_table_unref); + g_clear_object (&session->handle); g_free (session->peer_name); g_free (session->session_id); @@ -483,6 +878,9 @@ meta_input_capture_session_class_init (MetaInputCaptureSessionClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); + + quark_barrier_id = + g_quark_from_static_string ("meta-input-capture-barrier-id-quark"); } static void @@ -493,6 +891,9 @@ meta_input_capture_session_init (MetaInputCaptureSession *session) session->object_path = g_strdup_printf (META_INPUT_CAPTURE_SESSION_DBUS_PATH "/u%u", ++global_session_number); + + session->barriers = g_hash_table_new_full (NULL, NULL, NULL, + input_capture_barrier_free); } char * diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c index c4ca7b8a0ed..9fe2816fc48 100644 --- a/src/tests/input-capture-test-client.c +++ b/src/tests/input-capture-test-client.c @@ -21,6 +21,7 @@ #include "config.h" #include +#include #include #include "meta-dbus-input-capture.h" @@ -55,6 +56,8 @@ typedef struct _InputCaptureSession unsigned int serial; } InputCaptureSession; +static GDataInputStream *stdin_reader; + static void ping_mutter (InputCaptureSession *session) { @@ -81,6 +84,54 @@ write_state (InputCaptureSession *session, fflush (stdout); } +typedef struct +{ + GMainLoop *loop; + const char *expected_state; +} WaitData; + +static void +on_line_read (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + WaitData *data = user_data; + g_autofree char *line = NULL; + g_autoptr (GError) error = NULL; + + line = + g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (source_object), + res, NULL, &error); + if (error) + g_error ("Failed to read line from test client: %s", error->message); + if (!line) + g_error ("Unexpected EOF"); + + g_assert_cmpstr (data->expected_state, ==, line); + + g_main_loop_quit (data->loop); +} + +static void +wait_for_state (InputCaptureSession *session, + const char *expected_state) +{ + WaitData data; + + data.loop = g_main_loop_new (NULL, FALSE); + data.expected_state = expected_state; + + g_data_input_stream_read_line_async (stdin_reader, + G_PRIORITY_DEFAULT, + NULL, + on_line_read, + &data); + + g_main_loop_run (data.loop); + g_main_loop_unref (data.loop); + ping_mutter (session); +} + static InputCapture * input_capture_new (void) { @@ -176,6 +227,31 @@ input_capture_session_get_zones (InputCaptureSession *session) return zones; } +static unsigned int +input_capture_session_add_barrier (InputCaptureSession *session, + int x1, + int y1, + int x2, + int y2) +{ + g_autoptr (GError) error = NULL; + unsigned int barrier_id; + + if (!meta_dbus_input_capture_session_call_add_barrier_sync ( + session->proxy, + session->serial, + g_variant_new ("(iiii)", x1, y1, x2, y2), + &barrier_id, + NULL, + &error)) + { + g_warning ("Failed to add barrier: %s", error->message); + return 0; + } + + return barrier_id; +} + static void input_capture_session_enable (InputCaptureSession *session) { @@ -196,6 +272,21 @@ input_capture_session_disable (InputCaptureSession *session) g_warning ("Failed to disable session: %s", error->message); } +static void +input_capture_session_release (InputCaptureSession *session, + double x, + double y) +{ + g_autoptr (GError) error = NULL; + GVariant *position; + + position = g_variant_new ("(dd)", x, y); + if (!meta_dbus_input_capture_session_call_release_sync (session->proxy, + position, + NULL, &error)) + g_warning ("Failed to release pointer: %s", error->message); +} + static void test_sanity (void) { @@ -298,6 +389,98 @@ test_zones (void) input_capture_session_close (session); } +typedef struct +{ + unsigned int activated_barrier_id; + double activated_x; + double activated_y; + unsigned int activated_serial; +} BarriersTestData; + +static void +on_activated (MetaDBusInputCaptureSession *proxy, + unsigned int barrier_id, + unsigned int serial, + GVariant *cursor_position, + BarriersTestData *data) +{ + g_assert_cmpuint (data->activated_barrier_id, ==, 0); + + data->activated_barrier_id = barrier_id; + data->activated_serial = serial; + g_variant_get (cursor_position, "(dd)", + &data->activated_x, &data->activated_y); +} + +static void +test_barriers (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + g_autolist (Zone) zones = NULL; + unsigned int barrier1, barrier2; + BarriersTestData data = {}; + unsigned int prev_activated_serial; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + zones = input_capture_session_get_zones (session); + + /* + * +-------------+--------------+ + * || | | + * ||<--B#1 | | + * || | B#2 | + * +-------------+ | | + * | V | + * +==============+ + */ + barrier1 = input_capture_session_add_barrier (session, 0, 0, 0, 600); + barrier2 = input_capture_session_add_barrier (session, 800, 768, 1824, 768); + + g_assert_cmpuint (barrier1, !=, 0); + g_assert_cmpuint (barrier2, !=, 0); + g_assert_cmpuint (barrier1, !=, barrier2); + + g_signal_connect (session->proxy, "activated", + G_CALLBACK (on_activated), &data); + + input_capture_session_enable (session); + + write_state (session, "1"); + + while (data.activated_barrier_id == 0) + g_main_context_iteration (NULL, TRUE); + + g_assert_cmpuint (data.activated_serial, !=, 0); + g_assert_cmpuint (data.activated_barrier_id, ==, barrier1); + g_assert_cmpfloat_with_epsilon (data.activated_x, 0.0, DBL_EPSILON); + g_assert_cmpfloat_with_epsilon (data.activated_y, 15.0, DBL_EPSILON); + + wait_for_state (session, "1"); + + input_capture_session_release (session, 200, 150); + + write_state (session, "2"); + + prev_activated_serial = data.activated_serial; + + data = (BarriersTestData) {}; + while (data.activated_barrier_id == 0) + g_main_context_iteration (NULL, TRUE); + g_assert_cmpuint (data.activated_serial, !=, 0); + g_assert_cmpuint (data.activated_serial, !=, prev_activated_serial); + g_assert_cmpuint (data.activated_barrier_id, ==, barrier2); + g_assert_cmpfloat_with_epsilon (data.activated_x, 1000.0, DBL_EPSILON); + g_assert_cmpfloat_with_epsilon (data.activated_y, 768.0, DBL_EPSILON); + + input_capture_session_release (session, 1200, 700); + write_state (session, "3"); + + input_capture_session_close (session); +} + static const struct { const char *name; @@ -305,6 +488,7 @@ static const struct } test_cases[] = { { "sanity", test_sanity, }, { "zones", test_zones, }, + { "barriers", test_barriers, }, }; static void @@ -332,7 +516,16 @@ main (int argc, { if (g_strcmp0 (test_cases[i].name, test_case) == 0) { + g_autoptr (GInputStream) stdin_stream = NULL; + + stdin_stream = g_unix_input_stream_new (fileno (stdin), FALSE); + stdin_reader = g_data_input_stream_new (stdin_stream); + test_cases[i].func (); + + g_clear_object (&stdin_reader); + g_clear_object (&stdin_stream); + return EXIT_SUCCESS; } } diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c index cb319aff033..9e1eceb7124 100644 --- a/src/tests/input-capture-tests.c +++ b/src/tests/input-capture-tests.c @@ -22,6 +22,7 @@ #include +#include "backends/meta-backend-private.h" #include "meta-test/meta-context-test.h" #include "tests/meta-test-utils.h" @@ -31,6 +32,7 @@ typedef struct _InputCaptureTestClient char *path; GMainLoop *main_loop; GDataInputStream *line_reader; + GDataOutputStream *line_writer; } InputCaptureTestClient; static MetaContext *test_context; @@ -45,13 +47,16 @@ input_capture_test_client_new (const char *test_case) InputCaptureTestClient *test_client; GInputStream *stdout_stream; GDataInputStream *line_reader; + GOutputStream *stdin_stream; + GDataOutputStream *line_writer; test_client_path = g_test_build_filename (G_TEST_BUILT, "src", "tests", "mutter-input-capture-test-client", NULL); - launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE); + launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE | + G_SUBPROCESS_FLAGS_STDIN_PIPE); subprocess = g_subprocess_launcher_spawn (launcher, &error, test_client_path, @@ -63,10 +68,14 @@ input_capture_test_client_new (const char *test_case) stdout_stream = g_subprocess_get_stdout_pipe (subprocess); line_reader = g_data_input_stream_new (stdout_stream); + stdin_stream = g_subprocess_get_stdin_pipe (subprocess); + line_writer = g_data_output_stream_new (stdin_stream); + test_client = g_new0 (InputCaptureTestClient, 1); test_client->subprocess = subprocess; test_client->main_loop = g_main_loop_new (NULL, FALSE); test_client->line_reader = line_reader; + test_client->line_writer = line_writer; return test_client; } @@ -118,6 +127,24 @@ input_capture_test_client_wait_for_state (InputCaptureTestClient *test_client, g_main_loop_unref (data.loop); } +static void +input_capture_test_client_write_state (InputCaptureTestClient *test_client, + const char *state) +{ + g_autoptr (GError) error = NULL; + g_autofree char *line = NULL; + + line = g_strdup_printf ("%s\n", state); + + if (!g_data_output_stream_put_string (test_client->line_writer, + line, NULL, &error)) + g_error ("Failed to write state: %s", error->message); + + if (!g_output_stream_flush (G_OUTPUT_STREAM (test_client->line_writer), + NULL, &error)) + g_error ("Failed to flush state: %s", error->message); +} + static void input_capture_test_client_finished (GObject *source_object, GAsyncResult *res, @@ -181,6 +208,83 @@ meta_test_input_capture_zones (void) input_capture_test_client_finish (test_client); } +static void +assert_pointer_position (ClutterSeat *seat, + double x, + double y) +{ + graphene_point_t pos; + + clutter_seat_query_state (seat, + clutter_seat_get_pointer (seat), + NULL, &pos, NULL); + + g_assert_cmpfloat_with_epsilon (pos.x, x, DBL_EPSILON); + g_assert_cmpfloat_with_epsilon (pos.y, y, DBL_EPSILON); +} + +static void +meta_test_input_capture_barriers (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (MetaVirtualMonitor) virtual_monitor1 = NULL; + g_autoptr (MetaVirtualMonitor) virtual_monitor2 = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + InputCaptureTestClient *test_client; + + virtual_monitor1 = meta_create_test_monitor (test_context, 800, 600, 20.0); + virtual_monitor2 = meta_create_test_monitor (test_context, 1024, 768, 20.0); + + virtual_pointer = clutter_seat_create_virtual_device (seat, + CLUTTER_POINTER_DEVICE); + clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + + test_client = input_capture_test_client_new ("barriers"); + input_capture_test_client_wait_for_state (test_client, "1"); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, 10.0); + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, 10.0); + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, 10.0); + + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + + assert_pointer_position (seat, 0.0, 15.0); + + input_capture_test_client_write_state (test_client, "1"); + input_capture_test_client_wait_for_state (test_client, "2"); + + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + + assert_pointer_position (seat, 200.0, 150.0); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + 800.0, 300.0); + meta_flush_input (test_context); + + assert_pointer_position (seat, 1000.0, 450.0); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + 0.0, 400.0); + + input_capture_test_client_wait_for_state (test_client, "3"); + assert_pointer_position (seat, 1200.0, 700.0); + + input_capture_test_client_finish (test_client); +} + static void init_tests (void) { @@ -188,6 +292,8 @@ init_tests (void) meta_test_input_capture_sanity); g_test_add_func ("/backends/native/input-capture/zones", meta_test_input_capture_zones); + g_test_add_func ("/backends/native/input-capture/barriers", + meta_test_input_capture_barriers); } int -- GitLab From 1992515d33cbf669882a9b8d70fc7860556695b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Fri, 1 Apr 2022 19:14:48 +0200 Subject: [PATCH 25/40] input-capture: Add D-Bus method to clear barriers This will be needed by the portal backend. --- .../org.gnome.Mutter.InputCapture.xml | 2 + src/backends/meta-input-capture-session.c | 22 ++++++++++ src/tests/input-capture-test-client.c | 43 +++++++++++++++++++ src/tests/input-capture-tests.c | 43 +++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml index 44edacd8554..7a8528ef795 100644 --- a/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml +++ b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml @@ -45,6 +45,8 @@ + + diff --git a/src/backends/meta-input-capture-session.c b/src/backends/meta-input-capture-session.c index ea1a14d5d13..457672b63ae 100644 --- a/src/backends/meta-input-capture-session.c +++ b/src/backends/meta-input-capture-session.c @@ -547,6 +547,27 @@ handle_add_barrier (MetaDBusInputCaptureSession *object, return G_DBUS_METHOD_INVOCATION_HANDLED; } +static gboolean +handle_clear_barriers (MetaDBusInputCaptureSession *object, + GDBusMethodInvocation *invocation) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + g_hash_table_remove_all (session->barriers); + + meta_dbus_input_capture_session_complete_clear_barriers (object, invocation); + + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + static gboolean handle_get_zones (MetaDBusInputCaptureSession *object, GDBusMethodInvocation *invocation) @@ -764,6 +785,7 @@ static void meta_input_capture_session_init_iface (MetaDBusInputCaptureSessionIface *iface) { iface->handle_add_barrier = handle_add_barrier; + iface->handle_clear_barriers = handle_clear_barriers; iface->handle_enable = handle_enable; iface->handle_disable = handle_disable; iface->handle_release = handle_release; diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c index 9fe2816fc48..b2d9884af4e 100644 --- a/src/tests/input-capture-test-client.c +++ b/src/tests/input-capture-test-client.c @@ -252,6 +252,16 @@ input_capture_session_add_barrier (InputCaptureSession *session, return barrier_id; } +static void +input_capture_session_clear_barriers (InputCaptureSession *session) +{ + g_autoptr (GError) error = NULL; + + if (!meta_dbus_input_capture_session_call_clear_barriers_sync ( + session->proxy, NULL, &error)) + g_warning ("Failed to clear barriers: %s", error->message); +} + static void input_capture_session_enable (InputCaptureSession *session) { @@ -481,6 +491,38 @@ test_barriers (void) input_capture_session_close (session); } +static void +test_clear_barriers (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + g_autolist (Zone) zones = NULL; + BarriersTestData data = {}; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + zones = input_capture_session_get_zones (session); + + input_capture_session_add_barrier (session, 0, 0, 0, 600); + + g_signal_connect (session->proxy, "activated", + G_CALLBACK (on_activated), &data); + + input_capture_session_enable (session); + + write_state (session, "1"); + + while (data.activated_barrier_id == 0) + g_main_context_iteration (NULL, TRUE); + + input_capture_session_clear_barriers (session); + write_state (session, "2"); + wait_for_state (session, "1"); + + input_capture_session_close (session); +} + static const struct { const char *name; @@ -489,6 +531,7 @@ static const struct { "sanity", test_sanity, }, { "zones", test_zones, }, { "barriers", test_barriers, }, + { "clear-barriers", test_clear_barriers, }, }; static void diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c index 9e1eceb7124..867949565cd 100644 --- a/src/tests/input-capture-tests.c +++ b/src/tests/input-capture-tests.c @@ -285,6 +285,47 @@ meta_test_input_capture_barriers (void) input_capture_test_client_finish (test_client); } +static void +meta_test_input_capture_clear_barriers (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (MetaVirtualMonitor) virtual_monitor1 = NULL; + g_autoptr (MetaVirtualMonitor) virtual_monitor2 = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + InputCaptureTestClient *test_client; + + virtual_monitor1 = meta_create_test_monitor (test_context, 800, 600, 20.0); + + virtual_pointer = clutter_seat_create_virtual_device (seat, + CLUTTER_POINTER_DEVICE); + clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + + test_client = input_capture_test_client_new ("clear-barriers"); + input_capture_test_client_wait_for_state (test_client, "1"); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, 0.0); + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + assert_pointer_position (seat, 0.0, 10.0); + + input_capture_test_client_wait_for_state (test_client, "2"); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + assert_pointer_position (seat, 10.0, 20.0); + + input_capture_test_client_write_state (test_client, "1"); + input_capture_test_client_finish (test_client); +} + static void init_tests (void) { @@ -294,6 +335,8 @@ init_tests (void) meta_test_input_capture_zones); g_test_add_func ("/backends/native/input-capture/barriers", meta_test_input_capture_barriers); + g_test_add_func ("/backends/native/input-capture/clear-barriers", + meta_test_input_capture_clear_barriers); } int -- GitLab From e33f813ffd39bae3b03292fa546776d2ec7759c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 7 Apr 2022 11:59:32 +0200 Subject: [PATCH 26/40] clutter/event: Add clutter_event_get_name() This is useful for debug logging. --- clutter/clutter/clutter-event-private.h | 3 ++ clutter/clutter/clutter-event.c | 64 +++++++++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/clutter/clutter/clutter-event-private.h b/clutter/clutter/clutter-event-private.h index 69fdf24ed67..dcee65a8eeb 100644 --- a/clutter/clutter/clutter-event-private.h +++ b/clutter/clutter/clutter-event-private.h @@ -38,6 +38,9 @@ CLUTTER_EXPORT void _clutter_event_push (const ClutterEvent *event, gboolean do_copy); +CLUTTER_EXPORT +const char * clutter_event_get_name (const ClutterEvent *event); + G_END_DECLS #endif /* __CLUTTER_EVENT_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-event.c b/clutter/clutter/clutter-event.c index 0433a1ec98b..beedbea1d07 100644 --- a/clutter/clutter/clutter-event.c +++ b/clutter/clutter/clutter-event.c @@ -2230,3 +2230,67 @@ clutter_event_get_relative_motion (const ClutterEvent *event, else return FALSE; } + +const char * +clutter_event_get_name (const ClutterEvent *event) +{ + switch (event->type) + { + case CLUTTER_KEY_PRESS: + return "key-press"; + case CLUTTER_KEY_RELEASE: + return "key-release"; + case CLUTTER_MOTION: + return "motion"; + case CLUTTER_ENTER: + return "enter"; + case CLUTTER_LEAVE: + return "leave"; + case CLUTTER_BUTTON_PRESS: + return "button-press"; + case CLUTTER_BUTTON_RELEASE: + return "button-release"; + case CLUTTER_SCROLL: + return "scroll"; + case CLUTTER_TOUCH_BEGIN: + return "touch-begin"; + case CLUTTER_TOUCH_UPDATE: + return "touch-update"; + case CLUTTER_TOUCH_END: + return "touch-end"; + case CLUTTER_TOUCH_CANCEL: + return "touch-cancel"; + case CLUTTER_TOUCHPAD_PINCH: + return "touchpad-pinch"; + case CLUTTER_TOUCHPAD_SWIPE: + return "touchpad-swipe"; + case CLUTTER_TOUCHPAD_HOLD: + return "touchpad-hold"; + case CLUTTER_PROXIMITY_IN: + return "proximity-in"; + case CLUTTER_PROXIMITY_OUT: + return "proximity-out"; + case CLUTTER_PAD_BUTTON_PRESS: + return "pad-button-press"; + case CLUTTER_PAD_BUTTON_RELEASE: + return "pad-button-release"; + case CLUTTER_PAD_STRIP: + return "pad-strip"; + case CLUTTER_PAD_RING: + return "pad-ring"; + case CLUTTER_DEVICE_ADDED: + return "device-added"; + case CLUTTER_DEVICE_REMOVED: + return "device-removed"; + case CLUTTER_IM_COMMIT: + return "im-commit"; + case CLUTTER_IM_DELETE: + return "im-delete"; + case CLUTTER_IM_PREEDIT: + return "im-preedit"; + case CLUTTER_NOTHING: + case CLUTTER_EVENT_LAST: + break; + } + g_assert_not_reached (); +} -- GitLab From 6b407b90dd1b46bf1a6e260f789aeaff4935087f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 7 Apr 2022 12:00:34 +0200 Subject: [PATCH 27/40] tests/clutter/grab: Use clutter_event_get_name() This changes the debug output slightly, but that's fine. --- src/tests/clutter/conform/grab.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tests/clutter/conform/grab.c b/src/tests/clutter/conform/grab.c index b5ee3f41f3e..4040773d13e 100644 --- a/src/tests/clutter/conform/grab.c +++ b/src/tests/clutter/conform/grab.c @@ -2,6 +2,7 @@ #include #include "tests/clutter-test-utils.h" +#include "clutter/clutter-event-private.h" typedef struct { @@ -57,7 +58,7 @@ event_cb (ClutterActor *actor, EventLog entry = { clutter_actor_get_name (actor), event->type }; g_debug ("Event '%s' on actor '%s'", - entry.type == CLUTTER_ENTER ? "ENTER" : "LEAVE", + clutter_event_get_name (event), entry.name); g_array_append_val (events, entry); } -- GitLab From 34b914a615db3ec423c7b370207e1eb4bf48e9a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 7 Apr 2022 12:10:13 +0200 Subject: [PATCH 28/40] clutter/stage: Add input-only grabs An input only grab is a ClutterGrab on the stage that doesn't have an explicit actor associated with it. This is useful for cases where event should be captured as if focus was stolen to some mysterious place that doesn't have anything in the scene graph that represents it. Internally, it's implemented using a 0x0 sized actor attached directly to the stage, and a clutter action that consumes the events. An input-only grab takes a handler, user data and a destroy function for the user data. These are handed to the ClutterAction, which handles the actual event handling. --- clutter/clutter/clutter-input-only-action.c | 97 +++++++++++++++ clutter/clutter/clutter-input-only-action.h | 38 ++++++ clutter/clutter/clutter-input-only-actor.c | 60 ++++++++++ clutter/clutter/clutter-input-only-actor.h | 36 ++++++ clutter/clutter/clutter-stage-private.h | 8 ++ clutter/clutter/clutter-stage.c | 86 ++++++++++--- clutter/clutter/meson.build | 4 + src/tests/clutter/conform/grab.c | 126 +++++++++++++++++++- 8 files changed, 432 insertions(+), 23 deletions(-) create mode 100644 clutter/clutter/clutter-input-only-action.c create mode 100644 clutter/clutter/clutter-input-only-action.h create mode 100644 clutter/clutter/clutter-input-only-actor.c create mode 100644 clutter/clutter/clutter-input-only-actor.h diff --git a/clutter/clutter/clutter-input-only-action.c b/clutter/clutter/clutter-input-only-action.c new file mode 100644 index 00000000000..98bd551e388 --- /dev/null +++ b/clutter/clutter/clutter-input-only-action.c @@ -0,0 +1,97 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "clutter-build-config.h" + +#include "clutter/clutter-input-only-action.h" + +#include "clutter/clutter-action-private.h" +#include "clutter/clutter.h" + +struct _ClutterInputOnlyAction +{ + ClutterAction parent; + + ClutterInputOnlyHandleEvent handle_event; + gpointer user_data; + GDestroyNotify user_data_destroy; +}; + +G_DEFINE_TYPE (ClutterInputOnlyAction, clutter_input_only_action, + CLUTTER_TYPE_ACTION) + +static void +clutter_input_only_action_dispose (GObject *object) +{ + ClutterInputOnlyAction *input_only_action = + CLUTTER_INPUT_ONLY_ACTION (object); + + if (input_only_action->user_data_destroy) + { + g_clear_pointer (&input_only_action->user_data, + input_only_action->user_data_destroy); + } + + G_OBJECT_CLASS (clutter_input_only_action_parent_class)->dispose (object); +} + +static gboolean +clutter_input_only_action_handle_event (ClutterAction *action, + const ClutterEvent *event) +{ + ClutterInputOnlyAction *input_only_action = + CLUTTER_INPUT_ONLY_ACTION (action); + + return input_only_action->handle_event (event, input_only_action->user_data); +} + +static void +clutter_input_only_action_class_init (ClutterInputOnlyActionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ClutterActionClass *action_class = CLUTTER_ACTION_CLASS (klass); + + object_class->finalize = clutter_input_only_action_dispose; + + action_class->handle_event = clutter_input_only_action_handle_event; +} + +static void +clutter_input_only_action_init (ClutterInputOnlyAction *input_only_action) +{ +} + +ClutterInputOnlyAction * +clutter_input_only_action_new (ClutterInputOnlyHandleEvent handle_event, + gpointer user_data, + GDestroyNotify user_data_destroy) +{ + ClutterInputOnlyAction *input_only_action; + + input_only_action = g_object_new (CLUTTER_TYPE_INPUT_ONLY_ACTION, NULL); + input_only_action->handle_event = handle_event; + input_only_action->user_data = user_data; + input_only_action->user_data_destroy = user_data_destroy; + clutter_action_set_phase (CLUTTER_ACTION (input_only_action), + CLUTTER_PHASE_CAPTURE); + + return input_only_action; +} diff --git a/clutter/clutter/clutter-input-only-action.h b/clutter/clutter/clutter-input-only-action.h new file mode 100644 index 00000000000..22cb2e205ce --- /dev/null +++ b/clutter/clutter/clutter-input-only-action.h @@ -0,0 +1,38 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef CLUTTER_INPUT_ONLY_ACTION_H +#define CLUTTER_INPUT_ONLY_ACTION_H + +#include "clutter/clutter.h" + +typedef gboolean (* ClutterInputOnlyHandleEvent) (const ClutterEvent *event, + gpointer user_data); + +#define CLUTTER_TYPE_INPUT_ONLY_ACTION (clutter_input_only_action_get_type ()) +G_DECLARE_FINAL_TYPE (ClutterInputOnlyAction, clutter_input_only_action, + CLUTTER, INPUT_ONLY_ACTION, ClutterAction) + +ClutterInputOnlyAction * clutter_input_only_action_new (ClutterInputOnlyHandleEvent handle_event, + gpointer user_data, + GDestroyNotify destroy); + +#endif /* CLUTTER_INPUT_ONLY_ACTION_H */ diff --git a/clutter/clutter/clutter-input-only-actor.c b/clutter/clutter/clutter-input-only-actor.c new file mode 100644 index 00000000000..bb1e709c0dc --- /dev/null +++ b/clutter/clutter/clutter-input-only-actor.c @@ -0,0 +1,60 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#include "clutter-build-config.h" + +#include "clutter-input-only-actor.h" + +#include "clutter-input-only-action.h" + +struct _ClutterInputOnlyActor +{ + ClutterActor parent; +}; + +G_DEFINE_TYPE (ClutterInputOnlyActor, clutter_input_only_actor, + CLUTTER_TYPE_ACTOR) + +static void +clutter_input_only_actor_class_init (ClutterInputOnlyActorClass *klass) +{ +} + +static void +clutter_input_only_actor_init (ClutterInputOnlyActor *input_only_actor) +{ +} + +ClutterInputOnlyActor * +clutter_input_only_actor_new (ClutterInputOnlyHandleEvent handle_event, + gpointer user_data, + GDestroyNotify user_data_destroy) +{ + ClutterInputOnlyAction *input_only_action; + + input_only_action = clutter_input_only_action_new (handle_event, + user_data, + user_data_destroy); + return g_object_new (CLUTTER_TYPE_INPUT_ONLY_ACTOR, + "reactive", TRUE, + "actions", input_only_action, + NULL); +} diff --git a/clutter/clutter/clutter-input-only-actor.h b/clutter/clutter/clutter-input-only-actor.h new file mode 100644 index 00000000000..6217915c08c --- /dev/null +++ b/clutter/clutter/clutter-input-only-actor.h @@ -0,0 +1,36 @@ +/* + * Clutter. + * + * An OpenGL based 'interactive canvas' library. + * + * Copyright (C) 2022 Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + */ + +#ifndef CLUTTER_INPUT_ONLY_ACTOR_H +#define CLUTTER_INPUT_ONLY_ACTOR_H + +#include "clutter/clutter.h" +#include "clutter/clutter-stage-private.h" + +#define CLUTTER_TYPE_INPUT_ONLY_ACTOR (clutter_input_only_actor_get_type ()) +G_DECLARE_FINAL_TYPE (ClutterInputOnlyActor, clutter_input_only_actor, + CLUTTER, INPUT_ONLY_ACTOR, ClutterActor) + +ClutterInputOnlyActor * clutter_input_only_actor_new (ClutterEventHandler event_handler, + gpointer user_data, + GDestroyNotify destroy); + +#endif /* CLUTTER_INPUT_ONLY_ACTOR_H */ diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index e9ba5707edc..759ef06034f 100644 --- a/clutter/clutter/clutter-stage-private.h +++ b/clutter/clutter/clutter-stage-private.h @@ -32,6 +32,8 @@ G_BEGIN_DECLS +typedef gboolean (* ClutterEventHandler) (const ClutterEvent *event, + gpointer user_data); typedef enum { CLUTTER_DEVICE_UPDATE_NONE = 0, @@ -157,6 +159,12 @@ void clutter_stage_unlink_grab (ClutterStage *self, void clutter_stage_invalidate_focus (ClutterStage *self, ClutterActor *actor); +CLUTTER_EXPORT +ClutterGrab * clutter_stage_grab_input_only (ClutterStage *self, + ClutterEventHandler handler, + gpointer user_data, + GDestroyNotify user_data_destroy); + G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 705bd92a8f0..905ff88df9b 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -55,6 +55,7 @@ #include "clutter-grab.h" #include "clutter-id-pool.h" #include "clutter-input-device-private.h" +#include "clutter-input-only-actor.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-mutter.h" @@ -139,7 +140,10 @@ struct _ClutterGrab { grefcount ref_count; ClutterStage *stage; + ClutterActor *actor; + gboolean owns_actor; + ClutterGrab *prev; ClutterGrab *next; }; @@ -3762,20 +3766,28 @@ clutter_grab_unref (ClutterGrab *grab) G_DEFINE_BOXED_TYPE (ClutterGrab, clutter_grab, clutter_grab_ref, clutter_grab_unref) -/** - * clutter_stage_grab: - * @stage: The #ClutterStage - * @actor: The actor grabbing input - * - * Grabs input onto a certain actor. Events will be propagated as - * usual inside its hierarchy. - * - * Returns: (transfer full): (nullable): an opaque #ClutterGrab handle, drop - * with clutter_grab_dismiss() - **/ -ClutterGrab * -clutter_stage_grab (ClutterStage *stage, - ClutterActor *actor) +static ClutterGrab * +clutter_grab_new (ClutterStage *stage, + ClutterActor *actor, + gboolean owns_actor) +{ + ClutterGrab *grab; + + grab = g_new0 (ClutterGrab, 1); + g_ref_count_init (&grab->ref_count); + grab->stage = stage; + + grab->actor = actor; + if (owns_actor) + grab->owns_actor = TRUE; + + return grab; +} + +static ClutterGrab * +clutter_stage_grab_full (ClutterStage *stage, + ClutterActor *actor, + gboolean owns_actor) { ClutterStagePrivate *priv; ClutterGrab *grab; @@ -3797,10 +3809,8 @@ clutter_stage_grab (ClutterStage *stage, clutter_seat_grab (seat, clutter_get_current_event_time ()); } - grab = g_new0 (ClutterGrab, 1); - g_ref_count_init (&grab->ref_count); - grab->stage = stage; - grab->actor = actor; + grab = clutter_grab_new (stage, actor, owns_actor); + grab->prev = NULL; grab->next = priv->topmost_grab; @@ -3814,6 +3824,43 @@ clutter_stage_grab (ClutterStage *stage, return grab; } +/** + * clutter_stage_grab: + * @stage: The #ClutterStage + * @actor: The actor grabbing input + * + * Grabs input onto a certain actor. Events will be propagated as + * usual inside its hierarchy. + * + * Returns: (transfer full): (nullable): an opaque #ClutterGrab handle, drop + * with clutter_grab_dismiss() + **/ +ClutterGrab * +clutter_stage_grab (ClutterStage *stage, + ClutterActor *actor) +{ + return clutter_stage_grab_full (stage, actor, FALSE); +} + +ClutterGrab * +clutter_stage_grab_input_only (ClutterStage *stage, + ClutterEventHandler handler, + gpointer user_data, + GDestroyNotify user_data_destroy) +{ + ClutterInputOnlyActor *input_only_actor; + ClutterActor *actor; + + input_only_actor = clutter_input_only_actor_new (handler, user_data, + user_data_destroy); + actor = CLUTTER_ACTOR (input_only_actor); + clutter_actor_set_name (actor, "input only grab actor"); + + clutter_actor_insert_child_at_index (CLUTTER_ACTOR (stage), actor, 0); + + return clutter_stage_grab_full (stage, actor, TRUE); +} + void clutter_stage_unlink_grab (ClutterStage *stage, ClutterGrab *grab) @@ -3857,6 +3904,9 @@ clutter_stage_unlink_grab (ClutterStage *stage, grab->next = NULL; grab->prev = NULL; + + if (grab->owns_actor) + g_clear_pointer (&grab->actor, clutter_actor_destroy); } /** diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build index 2b8764a0f17..f60e382e184 100644 --- a/clutter/clutter/meson.build +++ b/clutter/clutter/meson.build @@ -138,6 +138,8 @@ clutter_sources = [ 'clutter-input-focus.c', 'clutter-input-method.c', 'clutter-input-pointer-a11y.c', + 'clutter-input-only-action.c', + 'clutter-input-only-actor.c', 'clutter-virtual-input-device.c', 'clutter-interval.c', 'clutter-keyframe-transition.c', @@ -207,6 +209,8 @@ clutter_private_headers = [ 'clutter-input-focus-private.h', 'clutter-input-method-private.h', 'clutter-input-pointer-a11y-private.h', + 'clutter-input-only-action.h', + 'clutter-input-only-actor.h', 'clutter-keymap-private.h', 'clutter-offscreen-effect-private.h', 'clutter-paint-context-private.h', diff --git a/src/tests/clutter/conform/grab.c b/src/tests/clutter/conform/grab.c index 4040773d13e..1e28606a44c 100644 --- a/src/tests/clutter/conform/grab.c +++ b/src/tests/clutter/conform/grab.c @@ -3,6 +3,7 @@ #include "tests/clutter-test-utils.h" #include "clutter/clutter-event-private.h" +#include "clutter/clutter-stage-private.h" typedef struct { @@ -50,17 +51,30 @@ event_cb (ClutterActor *actor, gpointer user_data) { GArray *events = user_data; + EventLog entry; - if ((event->type == CLUTTER_ENTER || - event->type == CLUTTER_LEAVE) && - (event->any.flags & CLUTTER_EVENT_FLAG_GRAB_NOTIFY) != 0) + switch (event->type) { - EventLog entry = { clutter_actor_get_name (actor), event->type }; - + case CLUTTER_ENTER: + case CLUTTER_LEAVE: + if ((event->any.flags & CLUTTER_EVENT_FLAG_GRAB_NOTIFY) != 0) + { + entry = (EventLog) { clutter_actor_get_name (actor), event->type }; + + g_debug ("Event '%s' on actor '%s'", + clutter_event_get_name (event), + entry.name); + g_array_append_val (events, entry); + } + break; + + default: + entry = (EventLog) { clutter_actor_get_name (actor), event->type }; g_debug ("Event '%s' on actor '%s'", clutter_event_get_name (event), entry.name); g_array_append_val (events, entry); + break; } return CLUTTER_EVENT_PROPAGATE; @@ -541,6 +555,107 @@ grab_key_focus_outside_grab (void) test_data_shutdown (&data); } +static gboolean +handle_input_only_event (const ClutterEvent *event, + gpointer user_data) +{ + GArray *events = user_data; + EventLog entry = { "input-only grab", event->type }; + + g_debug ("Input only grab event '%s'", clutter_event_get_name (event)); + g_array_append_val (events, entry); + + return CLUTTER_EVENT_PROPAGATE; +} + +static gboolean +last_event_is (GArray *events, + ClutterEventType event_type) +{ + EventLog *entry; + + if (events->len == 0) + return FALSE; + + entry = &g_array_index (events, EventLog, events->len - 1); + return entry->type == event_type; +} + +static void +grab_input_only (void) +{ + TestData data; + ClutterGrab *grab; + EventLog grab1_log[] = { + { "b", CLUTTER_LEAVE }, + { "a", CLUTTER_LEAVE }, + { "stage", CLUTTER_LEAVE }, + { NULL, 0 }, + }; + EventLog grab2_log[] = { + { "input-only grab", CLUTTER_BUTTON_PRESS }, + { "input-only grab", CLUTTER_BUTTON_RELEASE }, + { NULL, 0 }, + }; + EventLog grab3_log[] = { + { "b", CLUTTER_ENTER }, + { "a", CLUTTER_ENTER }, + { "stage", CLUTTER_ENTER }, + { NULL, 0 }, + }; + EventLog grab4_log[] = { + { "b", CLUTTER_BUTTON_PRESS }, + { "a", CLUTTER_BUTTON_PRESS }, + { "stage", CLUTTER_BUTTON_PRESS }, + { "b", CLUTTER_BUTTON_RELEASE }, + { "a", CLUTTER_BUTTON_RELEASE }, + { "stage", CLUTTER_BUTTON_RELEASE }, + { NULL, 0 }, + }; + ClutterSeat *seat; + g_autoptr (ClutterVirtualInputDevice) pointer = NULL; + + seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); + pointer = clutter_seat_create_virtual_device (seat, CLUTTER_POINTER_DEVICE); + + test_data_init (&data); + + grab = clutter_stage_grab_input_only (CLUTTER_STAGE (data.stage), + handle_input_only_event, + data.events, NULL); + event_log_compare ((EventLog *) &grab1_log, data.events); + + clutter_virtual_input_device_notify_button (pointer, + 0, + CLUTTER_BUTTON_PRIMARY, + CLUTTER_BUTTON_STATE_PRESSED); + clutter_virtual_input_device_notify_button (pointer, + 0, + CLUTTER_BUTTON_PRIMARY, + CLUTTER_BUTTON_STATE_RELEASED); + + while (!last_event_is (data.events, CLUTTER_BUTTON_RELEASE)) + g_main_context_iteration (NULL, TRUE); + event_log_compare ((EventLog *) &grab2_log, data.events); + + clutter_grab_unref (grab); + event_log_compare ((EventLog *) &grab3_log, data.events); + + clutter_virtual_input_device_notify_button (pointer, + 0, + CLUTTER_BUTTON_SECONDARY, + CLUTTER_BUTTON_STATE_PRESSED); + clutter_virtual_input_device_notify_button (pointer, + 0, + CLUTTER_BUTTON_SECONDARY, + CLUTTER_BUTTON_STATE_RELEASED); + while (!last_event_is (data.events, CLUTTER_BUTTON_RELEASE)) + g_main_context_iteration (NULL, TRUE); + event_log_compare ((EventLog *) &grab4_log, data.events); + + test_data_shutdown (&data); +} + CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/grab/grab-under-pointer", grab_under_pointer) CLUTTER_TEST_UNIT ("/grab/grab-under-pointers-parent", grab_under_pointers_parent) @@ -552,4 +667,5 @@ CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/grab/grab-unordered-ungrab-2", grab_unordered_ungrab_2) CLUTTER_TEST_UNIT ("/grab/key-focus-in-grab", grab_key_focus_in_grab); CLUTTER_TEST_UNIT ("/grab/key-focus-outside-grab", grab_key_focus_outside_grab); + CLUTTER_TEST_UNIT ("/grab/input-only", grab_input_only); ) -- GitLab From 67749391f36215beab59195a2484e18e50ee2a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 13 Apr 2022 11:34:55 +0200 Subject: [PATCH 29/40] clutter/seat: Add seat name This is similar to the existing seat-id that is part of MetaSeatNative, but meant to be passed to created input capture seats. --- clutter/clutter/clutter-seat.c | 51 +++++++++++++++++++++++ clutter/clutter/clutter-seat.h | 3 ++ src/backends/native/meta-backend-native.c | 1 + 3 files changed, 55 insertions(+) diff --git a/clutter/clutter/clutter-seat.c b/clutter/clutter/clutter-seat.c index 12130429719..0779ac1f125 100644 --- a/clutter/clutter/clutter-seat.c +++ b/clutter/clutter/clutter-seat.c @@ -51,7 +51,10 @@ static guint signals[N_SIGNALS] = { 0 }; enum { PROP_0, + + PROP_NAME, PROP_TOUCH_MODE, + N_PROPS }; @@ -65,6 +68,8 @@ struct _ClutterSeatPrivate /* Pointer a11y */ ClutterPointerA11ySettings pointer_a11y_settings; + + char *name; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterSeat, clutter_seat, G_TYPE_OBJECT) @@ -75,8 +80,14 @@ clutter_seat_set_property (GObject *object, const GValue *value, GParamSpec *pspec) { + ClutterSeat *seat = CLUTTER_SEAT (object); + ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); + switch (prop_id) { + case PROP_NAME: + priv->name = g_value_dup_string (value); + break; case PROP_TOUCH_MODE: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -89,11 +100,17 @@ clutter_seat_get_property (GObject *object, GValue *value, GParamSpec *pspec) { + ClutterSeat *seat = CLUTTER_SEAT (object); + ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); + switch (prop_id) { case PROP_TOUCH_MODE: g_value_set_boolean (value, FALSE); break; + case PROP_NAME: + g_value_set_string (value, priv->name); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -109,6 +126,17 @@ clutter_seat_constructed (GObject *object) CLUTTER_SEAT (object)); } +static void +clutter_seat_finalize (GObject *object) +{ + ClutterSeat *seat = CLUTTER_SEAT (object); + ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); + + g_clear_pointer (&priv->name, g_free); + + G_OBJECT_CLASS (clutter_seat_parent_class)->finalize (object); +} + static void clutter_seat_class_init (ClutterSeatClass *klass) { @@ -117,6 +145,7 @@ clutter_seat_class_init (ClutterSeatClass *klass) object_class->set_property = clutter_seat_set_property; object_class->get_property = clutter_seat_get_property; object_class->constructed = clutter_seat_constructed; + object_class->finalize = clutter_seat_finalize; signals[DEVICE_ADDED] = g_signal_new (I_("device-added"), @@ -277,6 +306,20 @@ clutter_seat_class_init (ClutterSeatClass *klass) FALSE, CLUTTER_PARAM_READABLE); + /** + * ClutterSeat::name: + * + * The name of the seat. + **/ + props[PROP_NAME] = + g_param_spec_string ("name", + P_("Seat name"), + P_("Seat name"), + NULL, + (G_PARAM_STATIC_STRINGS | + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); + g_object_class_install_properties (object_class, N_PROPS, props); } @@ -729,3 +772,11 @@ clutter_seat_ungrab (ClutterSeat *seat, if (seat_class->ungrab) return seat_class->ungrab (seat, time); } + +const char * +clutter_seat_get_name (ClutterSeat *seat) +{ + ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); + + return priv->name; +} diff --git a/clutter/clutter/clutter-seat.h b/clutter/clutter/clutter-seat.h index 30d176acf15..cc47f6f508b 100644 --- a/clutter/clutter/clutter-seat.h +++ b/clutter/clutter/clutter-seat.h @@ -168,4 +168,7 @@ gboolean clutter_seat_query_state (ClutterSeat *seat, graphene_point_t *coords, ClutterModifierType *modifiers); +CLUTTER_EXPORT +const char * clutter_seat_get_name (ClutterSeat *seat); + #endif /* CLUTTER_SEAT_H */ diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index 3ff78f39f7c..acde26e8e65 100644 --- a/src/backends/native/meta-backend-native.c +++ b/src/backends/native/meta-backend-native.c @@ -149,6 +149,7 @@ meta_backend_native_create_default_seat (MetaBackend *backend, return CLUTTER_SEAT (g_object_new (META_TYPE_SEAT_NATIVE, "backend", backend, "seat-id", seat_id, + "name", seat_id, "flags", flags, NULL)); } -- GitLab From 33248d398d2cc5eec30679cdf17f4d6e6d745f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 13 Apr 2022 11:38:08 +0200 Subject: [PATCH 30/40] keybindings: Make event handlers take a const ClutterEvent This avoids discarding the const qualifier. --- src/core/display-private.h | 6 +- src/core/display.c | 8 +- src/core/keybindings.c | 440 ++++++++++++++++++------------------- src/meta/prefs.h | 10 +- 4 files changed, 232 insertions(+), 232 deletions(-) diff --git a/src/core/display-private.h b/src/core/display-private.h index e91d730352f..ad212208c46 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -351,9 +351,9 @@ void meta_display_queue_autoraise_callback (MetaDisplay *display, void meta_display_remove_autoraise_callback (MetaDisplay *display); void meta_display_overlay_key_activate (MetaDisplay *display); -void meta_display_accelerator_activate (MetaDisplay *display, - guint action, - ClutterKeyEvent *event); +void meta_display_accelerator_activate (MetaDisplay *display, + guint action, + const ClutterKeyEvent *event); gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display); void meta_display_sync_wayland_input_focus (MetaDisplay *display); diff --git a/src/core/display.c b/src/core/display.c index 7573956fa05..55293f0fbe6 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2827,13 +2827,13 @@ meta_display_overlay_key_activate (MetaDisplay *display) } void -meta_display_accelerator_activate (MetaDisplay *display, - guint action, - ClutterKeyEvent *event) +meta_display_accelerator_activate (MetaDisplay *display, + guint action, + const ClutterKeyEvent *event) { g_signal_emit (display, display_signals[ACCELERATOR_ACTIVATED], 0, action, - clutter_event_get_source_device ((ClutterEvent *) event), + clutter_event_get_source_device ((const ClutterEvent *) event), event->time); } diff --git a/src/core/keybindings.c b/src/core/keybindings.c index 975e68897a2..fd47808f9e4 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -1638,11 +1638,11 @@ meta_window_ungrab_keys (MetaWindow *window) } static void -handle_external_grab (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer user_data) +handle_external_grab (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaKeyBindingManager *keys = &display->key_binding_manager; guint action = get_keybinding_action (keys, &binding->resolved_combo); @@ -1928,11 +1928,11 @@ is_modifier (xkb_keysym_t keysym) } static void -invoke_handler (MetaDisplay *display, - MetaKeyHandler *handler, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding) +invoke_handler (MetaDisplay *display, + MetaKeyHandler *handler, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding) { if (handler->func) (* handler->func) (display, @@ -2878,11 +2878,11 @@ process_keyboard_resize_grab (MetaDisplay *display, } static void -handle_switch_to_last_workspace (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_switch_to_last_workspace (MetaDisplay *display, + MetaWindow *event_window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; gint target = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1; @@ -2891,11 +2891,11 @@ handle_switch_to_last_workspace (MetaDisplay *display, } static void -handle_switch_to_workspace (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_switch_to_workspace (MetaDisplay *display, + MetaWindow *event_window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { gint which = binding->handler->data; MetaWorkspaceManager *workspace_manager = display->workspace_manager; @@ -2927,11 +2927,11 @@ handle_switch_to_workspace (MetaDisplay *display, static void -handle_maximize_vertically (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_maximize_vertically (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_resize_func) { @@ -2943,11 +2943,11 @@ handle_maximize_vertically (MetaDisplay *display, } static void -handle_maximize_horizontally (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_maximize_horizontally (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_resize_func) { @@ -2959,11 +2959,11 @@ handle_maximize_horizontally (MetaDisplay *display, } static void -handle_always_on_top (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_always_on_top (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->wm_state_above == FALSE) meta_window_make_above (window); @@ -3033,91 +3033,91 @@ handle_move_to_corner_backend (MetaDisplay *display, } static void -handle_move_to_corner_nw (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_corner_nw (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_WEST); } static void -handle_move_to_corner_ne (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_corner_ne (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_EAST); } static void -handle_move_to_corner_sw (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_corner_sw (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_WEST); } static void -handle_move_to_corner_se (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_corner_se (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_EAST); } static void -handle_move_to_side_n (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_side_n (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH); } static void -handle_move_to_side_s (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_side_s (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH); } static void -handle_move_to_side_e (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_side_e (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_EAST); } static void -handle_move_to_side_w (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_side_w (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { handle_move_to_corner_backend (display, window, META_GRAVITY_WEST); } static void -handle_move_to_center (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_center (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaRectangle work_area; MetaRectangle frame_rect; @@ -3132,11 +3132,11 @@ handle_move_to_center (MetaDisplay *display, } static void -handle_show_desktop (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_show_desktop (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; @@ -3152,11 +3152,11 @@ handle_show_desktop (MetaDisplay *display, } static void -handle_activate_window_menu (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_activate_window_menu (MetaDisplay *display, + MetaWindow *event_window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (display->focus_window) { @@ -3177,11 +3177,11 @@ handle_activate_window_menu (MetaDisplay *display, } static void -do_choose_window (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gboolean backward) +do_choose_window (MetaDisplay *display, + MetaWindow *event_window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gboolean backward) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; MetaTabList type = binding->handler->data; @@ -3201,33 +3201,33 @@ do_choose_window (MetaDisplay *display, } static void -handle_switch (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_switch (MetaDisplay *display, + MetaWindow *event_window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { gboolean backwards = meta_key_binding_is_reversed (binding); do_choose_window (display, event_window, event, binding, backwards); } static void -handle_cycle (MetaDisplay *display, - MetaWindow *event_window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_cycle (MetaDisplay *display, + MetaWindow *event_window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { gboolean backwards = meta_key_binding_is_reversed (binding); do_choose_window (display, event_window, event, binding, backwards); } static void -handle_toggle_fullscreen (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_toggle_fullscreen (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->fullscreen) meta_window_unmake_fullscreen (window); @@ -3236,11 +3236,11 @@ handle_toggle_fullscreen (MetaDisplay *display, } static void -handle_toggle_above (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_toggle_above (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->wm_state_above) meta_window_unmake_above (window); @@ -3249,11 +3249,11 @@ handle_toggle_above (MetaDisplay *display, } static void -handle_toggle_tiled (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_toggle_tiled (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaTileMode mode = binding->handler->data; @@ -3277,11 +3277,11 @@ handle_toggle_tiled (MetaDisplay *display, } static void -handle_toggle_maximized (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_toggle_maximized (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (META_WINDOW_MAXIMIZED (window)) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); @@ -3290,33 +3290,33 @@ handle_toggle_maximized (MetaDisplay *display, } static void -handle_maximize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_maximize (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_maximize_func) meta_window_maximize (window, META_MAXIMIZE_BOTH); } static void -handle_unmaximize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_unmaximize (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->maximized_vertically || window->maximized_horizontally) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } static void -handle_toggle_shaded (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_toggle_shaded (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->shaded) meta_window_unshade (window, event->time); @@ -3325,33 +3325,33 @@ handle_toggle_shaded (MetaDisplay *display, } static void -handle_close (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_close (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_close_func) meta_window_delete (window, event->time); } static void -handle_minimize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_minimize (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_minimize_func) meta_window_minimize (window); } static void -handle_begin_move (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_begin_move (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_move_func) { @@ -3363,11 +3363,11 @@ handle_begin_move (MetaDisplay *display, } static void -handle_begin_resize (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_begin_resize (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->has_resize_func) { @@ -3379,11 +3379,11 @@ handle_begin_resize (MetaDisplay *display, } static void -handle_toggle_on_all_workspaces (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_toggle_on_all_workspaces (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { if (window->on_all_workspaces_requested) meta_window_unstick (window); @@ -3392,11 +3392,11 @@ handle_toggle_on_all_workspaces (MetaDisplay *display, } static void -handle_move_to_workspace_last (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_workspace_last (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; gint which; @@ -3412,11 +3412,11 @@ handle_move_to_workspace_last (MetaDisplay *display, static void -handle_move_to_workspace (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_workspace (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; gint which = binding->handler->data; @@ -3467,11 +3467,11 @@ handle_move_to_workspace (MetaDisplay *display, } static void -handle_move_to_monitor (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_move_to_monitor (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = @@ -3490,11 +3490,11 @@ handle_move_to_monitor (MetaDisplay *display, } static void -handle_raise_or_lower (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_raise_or_lower (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { /* Get window at pointer */ @@ -3536,42 +3536,42 @@ handle_raise_or_lower (MetaDisplay *display, } static void -handle_raise (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_raise (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { meta_window_raise (window); } static void -handle_lower (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_lower (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { meta_window_lower (window); } static void -handle_set_spew_mark (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_set_spew_mark (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { meta_verbose ("-- MARK MARK MARK MARK --"); } #ifdef HAVE_NATIVE_BACKEND static void -handle_switch_vt (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_switch_vt (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { gint vt = binding->handler->data; GError *error = NULL; @@ -3585,11 +3585,11 @@ handle_switch_vt (MetaDisplay *display, #endif /* HAVE_NATIVE_BACKEND */ static void -handle_switch_monitor (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_switch_monitor (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = @@ -3605,11 +3605,11 @@ handle_switch_monitor (MetaDisplay *display, } static void -handle_rotate_monitor (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_rotate_monitor (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = @@ -3619,11 +3619,11 @@ handle_rotate_monitor (MetaDisplay *display, } static void -handle_restore_shortcuts (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +handle_restore_shortcuts (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { ClutterInputDevice *source; diff --git a/src/meta/prefs.h b/src/meta/prefs.h index 227de68bf2e..46666571b45 100644 --- a/src/meta/prefs.h +++ b/src/meta/prefs.h @@ -456,11 +456,11 @@ typedef enum * @user_data: data passed to the function * */ -typedef void (* MetaKeyHandlerFunc) (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer user_data); +typedef void (* MetaKeyHandlerFunc) (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data); META_EXPORT GType meta_key_binding_get_type (void); -- GitLab From 21cb2d2ace1f3b3c544c617a50e2a31a58c2e618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 13 Apr 2022 11:39:56 +0200 Subject: [PATCH 31/40] backend: Add input capture getter --- src/backends/meta-backend-private.h | 2 ++ src/backends/meta-backend-types.h | 3 +++ src/backends/meta-backend.c | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h index 82343eaf072..aefeb79a930 100644 --- a/src/backends/meta-backend-private.h +++ b/src/backends/meta-backend-private.h @@ -149,6 +149,8 @@ MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend); MetaScreenCast * meta_backend_get_screen_cast (MetaBackend *backend); #endif +MetaInputCapture * meta_backend_get_input_capture (MetaBackend *backend); + gboolean meta_backend_grab_device (MetaBackend *backend, int device_id, uint32_t timestamp); diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h index 4640ff3614d..405d901f342 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -76,4 +76,7 @@ typedef struct _MetaDbusSessionWatcher MetaDbusSessionWatcher; typedef struct _MetaRemoteDesktop MetaRemoteDesktop; #endif +typedef struct _MetaInputCapture MetaInputCapture; +typedef struct _MetaInputCaptureSession MetaInputCaptureSession; + #endif /* META_BACKEND_TYPE_H */ diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 5c924dc539b..fad92240d22 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -1407,6 +1407,14 @@ meta_backend_get_screen_cast (MetaBackend *backend) } #endif /* HAVE_REMOTE_DESKTOP */ +MetaInputCapture * +meta_backend_get_input_capture (MetaBackend *backend) +{ + MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); + + return priv->input_capture; +} + /** * meta_backend_get_remote_access_controller: * @backend: A #MetaBackend -- GitLab From 47be8b3791adb6cf7353247051c0ffc27df8933a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 13 Apr 2022 11:40:53 +0200 Subject: [PATCH 32/40] seat/impl: Move out the GSource implementation to a helper object This will help adding similar sources that work practically the same. --- src/backends/meta-fd-source.c | 110 +++++++++++++++++++++ src/backends/meta-fd-source.h | 33 +++++++ src/backends/native/meta-seat-impl.c | 140 ++++++++------------------- src/backends/native/meta-seat-impl.h | 2 +- src/meson.build | 2 + 5 files changed, 186 insertions(+), 101 deletions(-) create mode 100644 src/backends/meta-fd-source.c create mode 100644 src/backends/meta-fd-source.h diff --git a/src/backends/meta-fd-source.c b/src/backends/meta-fd-source.c new file mode 100644 index 00000000000..2c0fb09b7e9 --- /dev/null +++ b/src/backends/meta-fd-source.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2010 Intel Corp. + * Copyright (C) 2014 Jonas Ã…dahl + * Copyright (C) 2016-2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include "config.h" + +#include "backends/meta-fd-source.h" + +typedef struct _MetaFdtSource +{ + GSource source; + + GSourceFunc prepare; + GSourceFunc dispatch; + gpointer user_data; + + GPollFD poll_fd; +} MetaFdSource; + +static gboolean +meta_fd_source_prepare (GSource *source, + int *timeout_ms) +{ + MetaFdSource *fd_source = (MetaFdSource *) source; + + *timeout_ms = -1; + + return fd_source->prepare (fd_source->user_data); +} + +static gboolean +meta_fd_source_check (GSource *source) +{ + MetaFdSource *fd_source = (MetaFdSource *) source; + + return !!(fd_source->poll_fd.revents & G_IO_IN); +} + +static gboolean +meta_fd_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + MetaFdSource *fd_source = (MetaFdSource *) source; + + return fd_source->dispatch (fd_source->user_data); +} + +static void +meta_fd_source_finalize (GSource *source) +{ + MetaFdSource *fd_source = (MetaFdSource *) source; + + close (fd_source->poll_fd.fd); +} + +static GSourceFuncs fd_source_funcs = { + .prepare = meta_fd_source_prepare, + .check = meta_fd_source_check, + .dispatch = meta_fd_source_dispatch, + .finalize = meta_fd_source_finalize, +}; + +GSource * +meta_create_fd_source (int fd, + const char *name, + GSourceFunc prepare, + GSourceFunc dispatch, + gpointer user_data, + GDestroyNotify notify) +{ + GSource *source; + MetaFdSource *fd_source; + + source = g_source_new (&fd_source_funcs, sizeof (MetaFdSource)); + g_source_set_name (source, name); + fd_source = (MetaFdSource *) source; + + fd_source->poll_fd.fd = fd; + fd_source->poll_fd.events = G_IO_IN; + + fd_source->prepare = prepare; + fd_source->dispatch = dispatch; + fd_source->user_data = user_data; + + g_source_set_callback (source, dispatch, user_data, notify); + g_source_set_priority (source, G_PRIORITY_DEFAULT); + g_source_add_poll (source, &fd_source->poll_fd); + g_source_set_can_recurse (source, TRUE); + + return source; +} diff --git a/src/backends/meta-fd-source.h b/src/backends/meta-fd-source.h new file mode 100644 index 00000000000..b005170c300 --- /dev/null +++ b/src/backends/meta-fd-source.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2016-2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_FD_SOURCE_H +#define META_FD_SOURCE_H + +#include + +GSource * meta_create_fd_source (int fd, + const char *name, + GSourceFunc prepare, + GSourceFunc dispatch, + gpointer user_data, + GDestroyNotify notify); + +#endif /* META_FD_SOURCE_H */ diff --git a/src/backends/native/meta-seat-impl.c b/src/backends/native/meta-seat-impl.c index 624b2fe6f10..e8a7c8e21c8 100644 --- a/src/backends/native/meta-seat-impl.c +++ b/src/backends/native/meta-seat-impl.c @@ -34,6 +34,7 @@ #include #include "backends/meta-cursor-tracker-private.h" +#include "backends/meta-fd-source.h" #include "backends/native/meta-backend-native-private.h" #include "backends/native/meta-barrier-native.h" #include "backends/native/meta-device-pool.h" @@ -949,39 +950,6 @@ meta_seat_impl_notify_touch_event_in_impl (MetaSeatImpl *seat_impl, queue_event (seat_impl, event); } -/* - * MetaEventSource for reading input devices - */ - -static gboolean -meta_event_prepare (GSource *g_source, - int *timeout_ms) -{ - MetaEventSource *source = (MetaEventSource *) g_source; - MetaSeatImpl *seat_impl = source->seat_impl; - - *timeout_ms = -1; - - switch (libinput_next_event_type (seat_impl->libinput)) - { - case LIBINPUT_EVENT_NONE: - return FALSE; - default: - return TRUE; - } -} - -static gboolean -meta_event_check (GSource *source) -{ - MetaEventSource *event_source = (MetaEventSource *) source; - gboolean retval; - - retval = !!(event_source->event_poll_fd.revents & G_IO_IN); - - return retval; -} - static void constrain_to_barriers (MetaSeatImpl *seat_impl, ClutterInputDevice *device, @@ -1479,68 +1447,6 @@ notify_pad_ring (ClutterInputDevice *input_device, queue_event (seat_impl, event); } -static gboolean -meta_event_dispatch (GSource *g_source, - GSourceFunc callback, - gpointer user_data) -{ - MetaEventSource *source = (MetaEventSource *) g_source; - MetaSeatImpl *seat_impl; - - seat_impl = source->seat_impl; - - dispatch_libinput (seat_impl); - - return TRUE; -} - -static GSourceFuncs event_funcs = { - meta_event_prepare, - meta_event_check, - meta_event_dispatch, - NULL -}; - -static MetaEventSource * -meta_event_source_new (MetaSeatImpl *seat_impl) -{ - GSource *source; - MetaEventSource *event_source; - int fd; - - source = g_source_new (&event_funcs, sizeof (MetaEventSource)); - g_source_set_name (source, "[mutter] Events"); - event_source = (MetaEventSource *) source; - - /* setup the source */ - event_source->seat_impl = seat_impl; - - fd = libinput_get_fd (seat_impl->libinput); - event_source->event_poll_fd.fd = fd; - event_source->event_poll_fd.events = G_IO_IN; - - /* and finally configure and attach the GSource */ - g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); - g_source_add_poll (source, &event_source->event_poll_fd); - g_source_set_can_recurse (source, TRUE); - g_source_attach (source, seat_impl->input_context); - - return event_source; -} - -static void -meta_event_source_free (MetaEventSource *source) -{ - GSource *g_source = (GSource *) source; - - /* ignore the return value of close, it's not like we can do something - * about it */ - close (source->event_poll_fd.fd); - - g_source_destroy (g_source); - g_source_unref (g_source); -} - static gboolean has_touchscreen (MetaSeatImpl *seat_impl) { @@ -2718,13 +2624,38 @@ meta_seat_impl_set_keyboard_numlock_in_impl (MetaSeatImpl *seat_impl, seat_impl->xkb); } +static gboolean +meta_libinput_source_prepare (gpointer user_data) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (user_data); + + switch (libinput_next_event_type (seat_impl->libinput)) + { + case LIBINPUT_EVENT_NONE: + return FALSE; + default: + return TRUE; + } +} + +static gboolean +meta_libinput_source_dispatch (gpointer user_data) +{ + MetaSeatImpl *seat_impl = META_SEAT_IMPL (user_data); + + dispatch_libinput (seat_impl); + + return G_SOURCE_CONTINUE; +} + static gboolean init_libinput (MetaSeatImpl *seat_impl, GError **error) { - MetaEventSource *source; struct udev *udev; struct libinput *libinput; + int fd; + GSource *source; udev = udev_new (); if (G_UNLIKELY (udev == NULL)) @@ -2754,8 +2685,17 @@ init_libinput (MetaSeatImpl *seat_impl, } seat_impl->libinput = libinput; - source = meta_event_source_new (seat_impl); - seat_impl->event_source = source; + + fd = libinput_get_fd (seat_impl->libinput); + source = meta_create_fd_source (fd, + "[mutter] libinput", + meta_libinput_source_prepare, + meta_libinput_source_dispatch, + seat_impl, + NULL); + seat_impl->libinput_source = source; + g_source_attach (source, seat_impl->input_context); + g_source_unref (source); return TRUE; } @@ -2940,7 +2880,7 @@ destroy_in_impl (GTask *task) g_clear_pointer (&seat_impl->libinput, libinput_unref); g_clear_pointer (&seat_impl->tools, g_hash_table_unref); g_clear_pointer (&seat_impl->touch_states, g_hash_table_destroy); - g_clear_pointer (&seat_impl->event_source, meta_event_source_free); + g_clear_pointer (&seat_impl->libinput_source, g_source_destroy); numlock_active = xkb_state_mod_name_is_active (seat_impl->xkb, XKB_MOD_NAME_NUM, @@ -2987,7 +2927,7 @@ meta_seat_impl_finalize (GObject *object) g_assert (!seat_impl->libinput); g_assert (!seat_impl->tools); - g_assert (!seat_impl->event_source); + g_assert (!seat_impl->libinput_source); g_free (seat_impl->seat_id); diff --git a/src/backends/native/meta-seat-impl.h b/src/backends/native/meta-seat-impl.h index 5ca31e10f5d..39345b22b94 100644 --- a/src/backends/native/meta-seat-impl.h +++ b/src/backends/native/meta-seat-impl.h @@ -68,7 +68,7 @@ struct _MetaSeatImpl MetaSeatNative *seat_native; char *seat_id; MetaSeatNativeFlag flags; - MetaEventSource *event_source; + GSource *libinput_source; struct libinput *libinput; GRWLock state_lock; diff --git a/src/meson.build b/src/meson.build index c6b30307c5d..38c5456049a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -199,6 +199,8 @@ mutter_sources = [ 'backends/meta-dbus-session-watcher.h', 'backends/meta-display-config-shared.h', 'backends/meta-dnd-private.h', + 'backends/meta-fd-source.c', + 'backends/meta-fd-source.h', 'backends/meta-gpu.c', 'backends/meta-gpu.h', 'backends/meta-idle-monitor.c', -- GitLab From c3c882fa5f060cea7a6f2084a5f892d135509522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Apr 2022 10:50:43 +0200 Subject: [PATCH 33/40] input-capture: Hook up capturing of events to active session This adds the actual input capturing rerouting that takes events and first hands them to the input capture session, would it be active. Events are right now not actually processed in any way, but will eventually be passed to a libei client using libeis. A key binding for allowing cancelling the capture session is added (defaults to Escape) to avoid getting stuck in case the client doesn't even terminate the session. The added test case makes sure that the pointer moves again after pressing the keybinding. --- data/org.gnome.mutter.gschema.xml.in | 5 ++ src/backends/meta-input-capture-private.h | 32 +++++++++ src/backends/meta-input-capture-session.c | 45 ++++++++++++- src/backends/meta-input-capture-session.h | 5 ++ src/backends/meta-input-capture.c | 72 +++++++++++++++++++- src/backends/meta-input-capture.h | 16 +++++ src/core/display-private.h | 5 ++ src/core/display.c | 70 ++++++++++++++++++++ src/core/events.c | 7 ++ src/core/keybindings-private.h | 4 ++ src/core/keybindings.c | 79 +++++++++++++++++++++- src/meta/prefs.h | 1 + src/tests/input-capture-test-client.c | 21 ++++++ src/tests/input-capture-tests.c | 80 +++++++++++++++++++++++ 14 files changed, 437 insertions(+), 5 deletions(-) create mode 100644 src/backends/meta-input-capture-private.h diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in index c014b749fcf..d59c45bf405 100644 --- a/data/org.gnome.mutter.gschema.xml.in +++ b/data/org.gnome.mutter.gschema.xml.in @@ -198,5 +198,10 @@ Rotates the built-in monitor configuration + + Escape']]]> + Cancel any active input capture session + + diff --git a/src/backends/meta-input-capture-private.h b/src/backends/meta-input-capture-private.h new file mode 100644 index 00000000000..93a9800b699 --- /dev/null +++ b/src/backends/meta-input-capture-private.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#ifndef META_INPUT_CAPTURE_PRIVATE_H +#define META_INPUT_CAPTURE_PRIVATE_H + +#include "backends/meta-input-capture.h" + +void meta_input_capture_activate (MetaInputCapture *input_capture, + MetaInputCaptureSession *session); + +void meta_input_capture_deactivate (MetaInputCapture *input_capture, + MetaInputCaptureSession *session); + +#endif /* META_INPUT_CAPTURE_PRIVATE_H */ diff --git a/src/backends/meta-input-capture-session.c b/src/backends/meta-input-capture-session.c index 457672b63ae..3ab4f32db68 100644 --- a/src/backends/meta-input-capture-session.c +++ b/src/backends/meta-input-capture-session.c @@ -26,6 +26,7 @@ #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-dbus-session-manager.h" +#include "backends/meta-input-capture-private.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-remote-access-controller-private.h" @@ -154,6 +155,8 @@ on_barrier_hit (MetaBarrier *barrier, { MetaDBusInputCaptureSession *skeleton = META_DBUS_INPUT_CAPTURE_SESSION (session); + MetaInputCapture *input_capture = + META_INPUT_CAPTURE (session->session_manager); GVariant *cursor_position; unsigned int barrier_id; @@ -175,6 +178,8 @@ on_barrier_hit (MetaBarrier *barrier, quark_barrier_id)); cursor_position = g_variant_new ("(dd)", event->x, event->y); + meta_input_capture_activate (input_capture, session); + meta_dbus_input_capture_session_emit_activated (skeleton, barrier_id, ++session->activated_serial, @@ -262,6 +267,17 @@ err: return FALSE; } +static void +meta_input_capture_session_deactivate (MetaInputCaptureSession *session) +{ + MetaInputCapture *input_capture = + META_INPUT_CAPTURE (session->session_manager); + + meta_input_capture_deactivate (input_capture, session); + + session->state = INPUT_CAPTURE_STATE_ENABLED; +} + static void meta_input_capture_session_disable (MetaInputCaptureSession *session) { @@ -270,6 +286,8 @@ meta_input_capture_session_disable (MetaInputCaptureSession *session) case INPUT_CAPTURE_STATE_INIT: return; case INPUT_CAPTURE_STATE_ACTIVATED: + meta_input_capture_session_deactivate (session); + G_GNUC_FALLTHROUGH; case INPUT_CAPTURE_STATE_ENABLED: break; case INPUT_CAPTURE_STATE_CLOSED: @@ -701,8 +719,7 @@ handle_release (MetaDBusInputCaptureSession *object, } release_all_barriers (session); - - session->state = INPUT_CAPTURE_STATE_ENABLED; + meta_input_capture_session_deactivate (session); g_variant_get (position, "(dd)", &x, &y); clutter_seat_warp_pointer (seat, x, y); @@ -924,6 +941,30 @@ meta_input_capture_session_get_object_path (MetaInputCaptureSession *session) return session->object_path; } +gboolean +meta_input_capture_session_process_event (MetaInputCaptureSession *session, + const ClutterEvent *event) +{ + switch (event->type) + { + case CLUTTER_MOTION: + case CLUTTER_BUTTON_PRESS: + case CLUTTER_BUTTON_RELEASE: + case CLUTTER_SCROLL: + case CLUTTER_KEY_PRESS: + case CLUTTER_KEY_RELEASE: + return TRUE; + default: + return FALSE; + } +} + +void +meta_input_capture_session_notify_cancelled (MetaInputCaptureSession *session) +{ + meta_input_capture_session_disable (session); +} + static MetaInputCaptureSessionHandle * meta_input_capture_session_handle_new (MetaInputCaptureSession *session) { diff --git a/src/backends/meta-input-capture-session.h b/src/backends/meta-input-capture-session.h index 1a52085b024..4e94737fee3 100644 --- a/src/backends/meta-input-capture-session.h +++ b/src/backends/meta-input-capture-session.h @@ -39,4 +39,9 @@ G_DECLARE_FINAL_TYPE (MetaInputCaptureSessionHandle, char * meta_input_capture_session_get_object_path (MetaInputCaptureSession *session); +gboolean meta_input_capture_session_process_event (MetaInputCaptureSession *session, + const ClutterEvent *event); + +void meta_input_capture_session_notify_cancelled (MetaInputCaptureSession *session); + #endif /* META_INPUT_CAPTURE_SESSION_H */ diff --git a/src/backends/meta-input-capture.c b/src/backends/meta-input-capture.c index 30c796b65cf..a030a669871 100644 --- a/src/backends/meta-input-capture.c +++ b/src/backends/meta-input-capture.c @@ -20,7 +20,7 @@ #include "config.h" -#include "backends/meta-input-capture.h" +#include "backends/meta-input-capture-private.h" #include "backends/meta-input-capture-session.h" #include "backends/meta-backend-private.h" @@ -31,6 +31,11 @@ #define META_INPUT_CAPTURE_DBUS_SERVICE "org.gnome.Mutter.InputCapture" #define META_INPUT_CAPTURE_DBUS_PATH "/org/gnome/Mutter/InputCapture" +enum +{ + CANCELLED, +}; + typedef enum _MetaInputCaptureCapabilities { META_INPUT_CAPTURE_CAPABILITY_NONE = 0, @@ -43,6 +48,14 @@ typedef enum _MetaInputCaptureCapabilities struct _MetaInputCapture { MetaDbusSessionManager parent; + + struct { + MetaInputCaptureEnable enable; + MetaInputCaptureDisable disable; + gpointer user_data; + } event_router; + + MetaInputCaptureSession *active_session; }; G_DEFINE_TYPE (MetaInputCapture, meta_input_capture, @@ -160,3 +173,60 @@ meta_input_capture_new (MetaBackend *backend) return input_capture; } + +void +meta_input_capture_set_event_router (MetaInputCapture *input_capture, + MetaInputCaptureEnable enable, + MetaInputCaptureDisable disable, + gpointer user_data) +{ + g_warn_if_fail (!input_capture->event_router.enable && + !input_capture->event_router.disable && + !input_capture->event_router.user_data); + + input_capture->event_router.enable = enable; + input_capture->event_router.disable = disable; + input_capture->event_router.user_data = user_data; +} + +void +meta_input_capture_activate (MetaInputCapture *input_capture, + MetaInputCaptureSession *session) +{ + g_return_if_fail (input_capture->event_router.enable); + + meta_topic (META_DEBUG_INPUT, "Activating input capturing"); + input_capture->active_session = session; + input_capture->event_router.enable (input_capture, + input_capture->event_router.user_data); +} + +void +meta_input_capture_deactivate (MetaInputCapture *input_capture, + MetaInputCaptureSession *session) +{ + g_return_if_fail (input_capture->event_router.disable); + + meta_topic (META_DEBUG_INPUT, "Deactivating input capturing"); + input_capture->event_router.disable (input_capture, + input_capture->event_router.user_data); + input_capture->active_session = NULL; +} + +void +meta_input_capture_notify_cancelled (MetaInputCapture *input_capture) +{ + g_return_if_fail (input_capture->active_session); + + meta_input_capture_session_notify_cancelled (input_capture->active_session); +} + +gboolean +meta_input_capture_process_event (MetaInputCapture *input_capture, + const ClutterEvent *event) +{ + g_return_val_if_fail (input_capture->active_session, FALSE); + + return meta_input_capture_session_process_event (input_capture->active_session, + event); +} diff --git a/src/backends/meta-input-capture.h b/src/backends/meta-input-capture.h index d651e77b346..97dde42d953 100644 --- a/src/backends/meta-input-capture.h +++ b/src/backends/meta-input-capture.h @@ -22,9 +22,15 @@ #define META_INPUT_CAPTURE_H #include "backends/meta-dbus-session-manager.h" +#include "clutter/clutter.h" #include "meta-dbus-input-capture.h" +typedef void (* MetaInputCaptureEnable) (MetaInputCapture *input_capture, + gpointer user_data); +typedef void (* MetaInputCaptureDisable) (MetaInputCapture *input_capture, + gpointer user_data); + #define META_TYPE_INPUT_CAPTURE (meta_input_capture_get_type ()) G_DECLARE_FINAL_TYPE (MetaInputCapture, meta_input_capture, META, INPUT_CAPTURE, @@ -32,4 +38,14 @@ G_DECLARE_FINAL_TYPE (MetaInputCapture, meta_input_capture, MetaInputCapture * meta_input_capture_new (MetaBackend *backend); +void meta_input_capture_set_event_router (MetaInputCapture *input_capture, + MetaInputCaptureEnable enable, + MetaInputCaptureDisable disable, + gpointer user_data); + +void meta_input_capture_notify_cancelled (MetaInputCapture *input_capture); + +gboolean meta_input_capture_process_event (MetaInputCapture *input_capture, + const ClutterEvent *event); + #endif /* META_INPUT_CAPTURE_H */ diff --git a/src/core/display-private.h b/src/core/display-private.h index ad212208c46..2d1ac849273 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -440,4 +440,9 @@ void meta_display_unqueue_window (MetaDisplay *display, MetaWindow *window, MetaQueueType queue_types); +gboolean meta_display_process_captured_input (MetaDisplay *display, + const ClutterEvent *event); + +void meta_display_cancel_input_capture (MetaDisplay *display); + #endif diff --git a/src/core/display.c b/src/core/display.c index 55293f0fbe6..32842263c10 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -43,6 +43,7 @@ #include "backends/meta-backend-private.h" #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-cursor-tracker-private.h" +#include "backends/meta-input-capture.h" #include "backends/meta-input-device-private.h" #include "backends/meta-input-mapper-private.h" #include "backends/meta-stage-private.h" @@ -127,6 +128,8 @@ typedef struct _MetaDisplayPrivate guint queue_later_ids[META_N_QUEUE_TYPES]; GList *queue_windows[META_N_QUEUE_TYPES]; + + gboolean enable_input_capture; } MetaDisplayPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaDisplay, meta_display, G_TYPE_OBJECT) @@ -688,6 +691,66 @@ on_monitor_privacy_screen_changed (MetaDisplay *display, : _("Privacy Screen Disabled")); } +gboolean +meta_display_process_captured_input (MetaDisplay *display, + const ClutterEvent *event) +{ + MetaDisplayPrivate *priv = meta_display_get_instance_private (display); + MetaContext *context = priv->context; + MetaBackend *backend = meta_context_get_backend (context); + MetaInputCapture *input_capture = meta_backend_get_input_capture (backend); + + if (!priv->enable_input_capture) + return FALSE; + + if (meta_display_process_keybinding_event (display, + "cancel-input-capture", + event)) + { + g_warn_if_fail (!priv->enable_input_capture); + + return TRUE; + } + + return meta_input_capture_process_event (input_capture, event); +} + +void +meta_display_cancel_input_capture (MetaDisplay *display) +{ + MetaDisplayPrivate *priv = meta_display_get_instance_private (display); + MetaContext *context = priv->context; + MetaBackend *backend = meta_context_get_backend (context); + MetaInputCapture *input_capture = meta_backend_get_input_capture (backend); + + meta_input_capture_notify_cancelled (input_capture); + g_assert (!priv->enable_input_capture); +} + +static void +enable_input_capture (MetaInputCapture *input_capture, + gpointer user_data) +{ + MetaDisplay *display = META_DISPLAY (user_data); + MetaDisplayPrivate *priv = meta_display_get_instance_private (display); + + g_return_if_fail (!priv->enable_input_capture); + + priv->enable_input_capture = TRUE; +} + +static void +disable_input_capture (MetaInputCapture *input_capture, + gpointer user_data) +{ + MetaDisplay *display = META_DISPLAY (user_data); + MetaDisplayPrivate *priv = meta_display_get_instance_private (display); + + g_return_if_fail (priv->enable_input_capture); + + priv->enable_input_capture = FALSE; +} + static gboolean meta_display_init_x11_display (MetaDisplay *display, GError **error) @@ -837,6 +900,7 @@ meta_display_new (MetaContext *context, Window old_active_xwindow = None; MetaMonitorManager *monitor_manager; MetaSettings *settings; + MetaInputCapture *input_capture; g_assert (the_display == NULL); display = the_display = g_object_new (META_TYPE_DISPLAY, NULL); @@ -902,6 +966,12 @@ meta_display_new (MetaContext *context, display->pad_action_mapper = meta_pad_action_mapper_new (monitor_manager); + input_capture = meta_backend_get_input_capture (backend); + meta_input_capture_set_event_router (input_capture, + enable_input_capture, + disable_input_capture, + display); + settings = meta_backend_get_settings (backend); g_signal_connect (settings, "ui-scaling-factor-changed", G_CALLBACK (on_ui_scaling_factor_changed), display); diff --git a/src/core/events.c b/src/core/events.c index d5a9a93da34..0bc4c27d617 100644 --- a/src/core/events.c +++ b/src/core/events.c @@ -260,6 +260,13 @@ meta_display_handle_event (MetaDisplay *display, } } + if (meta_display_process_captured_input (display, event)) + { + bypass_clutter = TRUE; + bypass_wayland = TRUE; + goto out; + } + device = clutter_event_get_device (event); clutter_input_pointer_a11y_update (device, event); diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h index 56792c200a9..13eb2431c49 100644 --- a/src/core/keybindings-private.h +++ b/src/core/keybindings-private.h @@ -157,4 +157,8 @@ gboolean meta_prefs_is_locate_pointer_enabled (void); void meta_x11_display_grab_keys (MetaX11Display *x11_display); void meta_x11_display_ungrab_keys (MetaX11Display *x11_display); +gboolean meta_display_process_keybinding_event (MetaDisplay *display, + const char *name, + const ClutterEvent *event); + #endif diff --git a/src/core/keybindings.c b/src/core/keybindings.c index fd47808f9e4..838d759e645 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -1974,8 +1974,13 @@ process_event (MetaDisplay *display, binding = get_keybinding (keys, &resolved_combo); - if (!binding || - (!window && binding->flags & META_KEY_BINDING_PER_WINDOW)) + if (!binding) + goto not_found; + + if (!window && binding->flags & META_KEY_BINDING_PER_WINDOW) + goto not_found; + + if (binding->flags & META_KEY_BINDING_CUSTOM_TRIGGER) goto not_found; if (binding->handler == NULL) @@ -3618,6 +3623,16 @@ handle_rotate_monitor (MetaDisplay *display, meta_monitor_manager_rotate_monitor (monitor_manager); } +static void +handle_cancel_input_capture (MetaDisplay *display, + MetaWindow *window, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) +{ + meta_display_cancel_input_capture (display); +} + static void handle_restore_shortcuts (MetaDisplay *display, MetaWindow *window, @@ -3945,6 +3960,14 @@ init_builtin_key_bindings (MetaDisplay *display) META_KEYBINDING_ACTION_ROTATE_MONITOR, handle_rotate_monitor, 0); + add_builtin_keybinding (display, + "cancel-input-capture", + mutter_keybindings, + META_KEY_BINDING_IGNORE_AUTOREPEAT | + META_KEY_BINDING_CUSTOM_TRIGGER, + META_KEYBINDING_ACTION_NONE, + handle_cancel_input_capture, 0); + #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) @@ -4515,3 +4538,55 @@ meta_display_init_keys (MetaDisplay *display) g_signal_connect_swapped (backend, "keymap-layout-group-changed", G_CALLBACK (reload_keybindings), display); } + +static gboolean +process_keybinding_key_event (MetaDisplay *display, + MetaKeyHandler *handler, + const ClutterKeyEvent *event) +{ + MetaKeyBindingManager *keys = &display->key_binding_manager; + xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; + MetaResolvedKeyCombo resolved_combo = { &keycode, 1 }; + MetaKeyBinding *binding; + + if (event->type == CLUTTER_KEY_RELEASE) + return FALSE; + + resolved_combo.mask = mask_from_event_params (keys, event->modifier_state); + + binding = get_keybinding (keys, &resolved_combo); + if (!binding) + return FALSE; + + if (handler != binding->handler) + return FALSE; + + g_return_val_if_fail (binding->flags & META_KEY_BINDING_CUSTOM_TRIGGER, + FALSE); + + invoke_handler (display, binding->handler, NULL, event, binding); + return TRUE; +} + +gboolean +meta_display_process_keybinding_event (MetaDisplay *display, + const char *name, + const ClutterEvent *event) +{ + MetaKeyHandler *handler; + + handler = g_hash_table_lookup (key_handlers, name); + if (!handler) + return FALSE; + + switch (event->type) + { + case CLUTTER_KEY_PRESS: + case CLUTTER_KEY_RELEASE: + return process_keybinding_key_event (display, handler, + (ClutterKeyEvent *) event); + + default: + return FALSE; + } +} diff --git a/src/meta/prefs.h b/src/meta/prefs.h index 46666571b45..3d3256856a2 100644 --- a/src/meta/prefs.h +++ b/src/meta/prefs.h @@ -445,6 +445,7 @@ typedef enum META_KEY_BINDING_NON_MASKABLE = 1 << 3, META_KEY_BINDING_IGNORE_AUTOREPEAT = 1 << 4, META_KEY_BINDING_NO_AUTO_GRAB = 1 << 5, + META_KEY_BINDING_CUSTOM_TRIGGER = 1 << 6, } MetaKeyBindingFlags; /** diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c index b2d9884af4e..ef098502655 100644 --- a/src/tests/input-capture-test-client.c +++ b/src/tests/input-capture-test-client.c @@ -523,6 +523,26 @@ test_clear_barriers (void) input_capture_session_close (session); } +static void +test_cancel_keybinding (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + g_autolist (Zone) zones = NULL; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + zones = input_capture_session_get_zones (session); + input_capture_session_add_barrier (session, 0, 0, 0, 600); + input_capture_session_enable (session); + + write_state (session, "1"); + wait_for_state (session, "1"); + + input_capture_session_close (session); +} + static const struct { const char *name; @@ -532,6 +552,7 @@ static const struct { "zones", test_zones, }, { "barriers", test_barriers, }, { "clear-barriers", test_clear_barriers, }, + { "cancel-keybinding", test_cancel_keybinding, }, }; static void diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c index 867949565cd..e27b4fef5f6 100644 --- a/src/tests/input-capture-tests.c +++ b/src/tests/input-capture-tests.c @@ -21,6 +21,7 @@ #include "config.h" #include +#include #include "backends/meta-backend-private.h" #include "meta-test/meta-context-test.h" @@ -326,6 +327,83 @@ meta_test_input_capture_clear_barriers (void) input_capture_test_client_finish (test_client); } +static void +meta_test_input_capture_cancel_keybinding (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (MetaVirtualMonitor) virtual_monitor = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + InputCaptureTestClient *test_client; + + virtual_monitor = meta_create_test_monitor (test_context, 800, 600, 20.0); + virtual_keyboard = clutter_seat_create_virtual_device (seat, + CLUTTER_KEYBOARD_DEVICE); + virtual_pointer = clutter_seat_create_virtual_device (seat, + CLUTTER_POINTER_DEVICE); + clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + + test_client = input_capture_test_client_new ("cancel-keybinding"); + input_capture_test_client_wait_for_state (test_client, "1"); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, 0.0); + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + assert_pointer_position (seat, 0.0, 10.0); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + assert_pointer_position (seat, 0.0, 10.0); + + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + KEY_LEFTMETA, + CLUTTER_KEY_STATE_PRESSED); + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + KEY_LEFTSHIFT, + CLUTTER_KEY_STATE_PRESSED); + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + KEY_ESC, + CLUTTER_KEY_STATE_PRESSED); + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + KEY_ESC, + CLUTTER_KEY_STATE_RELEASED); + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + KEY_LEFTSHIFT, + CLUTTER_KEY_STATE_RELEASED); + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + KEY_LEFTMETA, + CLUTTER_KEY_STATE_RELEASED); + + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + + meta_flush_input (test_context); + meta_wait_for_paint (test_context); + assert_pointer_position (seat, 10.0, 20.0); + + input_capture_test_client_write_state (test_client, "1"); + + input_capture_test_client_finish (test_client); +} + static void init_tests (void) { @@ -337,6 +415,8 @@ init_tests (void) meta_test_input_capture_barriers); g_test_add_func ("/backends/native/input-capture/clear-barriers", meta_test_input_capture_clear_barriers); + g_test_add_func ("/backends/native/input-capture/cancel-keybinding", + meta_test_input_capture_cancel_keybinding); } int -- GitLab From 9a24221bddbcf029071c84f5583486a878c24d4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Apr 2022 12:12:12 +0200 Subject: [PATCH 34/40] barrier/native: Fix coding style --- src/backends/native/meta-barrier-native.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/native/meta-barrier-native.c b/src/backends/native/meta-barrier-native.c index 4475bfbc16b..de1613a8163 100644 --- a/src/backends/native/meta-barrier-native.c +++ b/src/backends/native/meta-barrier-native.c @@ -638,7 +638,7 @@ meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) static void meta_barrier_impl_native_release (MetaBarrierImpl *impl, - MetaBarrierEvent *event) + MetaBarrierEvent *event) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); -- GitLab From d0bbe255f3d8a2778155ba4a10ea1f2faf037936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 27 Apr 2022 12:13:20 +0200 Subject: [PATCH 35/40] keybindings: Use proper enum type in MetaKeyHandler This simplifies inspecting runtime state using gdb. --- src/core/keybindings-private.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h index 13eb2431c49..b119c5559cc 100644 --- a/src/core/keybindings-private.h +++ b/src/core/keybindings-private.h @@ -39,7 +39,8 @@ struct _MetaKeyHandler char *name; MetaKeyHandlerFunc func; MetaKeyHandlerFunc default_func; - gint data, flags; + int data; + MetaKeyBindingFlags flags; gpointer user_data; GDestroyNotify user_data_free_func; }; -- GitLab From 5fa8e93e41a829f0bade05d576762168d05eda0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 15:53:16 +0200 Subject: [PATCH 36/40] clutter/event: Set the constrained relative motion too When a relative pointer motion gets constrained (e.g. a monitor edge or barrier), save the constrained relative motion delta too. This will later be used to send the remainding motion delta to input capture clients. --- clutter/clutter/clutter-event.h | 2 ++ src/backends/native/meta-seat-impl.c | 16 ++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/clutter/clutter/clutter-event.h b/clutter/clutter/clutter-event.h index 94595542cd1..943961b0bf5 100644 --- a/clutter/clutter/clutter-event.h +++ b/clutter/clutter/clutter-event.h @@ -305,6 +305,8 @@ struct _ClutterMotionEvent double dy; double dx_unaccel; double dy_unaccel; + double dx_constrained; + double dy_constrained; }; /** diff --git a/src/backends/native/meta-seat-impl.c b/src/backends/native/meta-seat-impl.c index e8a7c8e21c8..b391818cbdb 100644 --- a/src/backends/native/meta-seat-impl.c +++ b/src/backends/native/meta-seat-impl.c @@ -572,8 +572,9 @@ meta_seat_impl_notify_relative_motion_in_impl (MetaSeatImpl *seat_impl, float dx_unaccel, float dy_unaccel) { - float new_x, new_y; ClutterEvent *event; + float old_x, old_y; + double dx_constrained, dy_constrained; meta_seat_impl_filter_relative_motion (seat_impl, input_device, @@ -582,16 +583,23 @@ meta_seat_impl_notify_relative_motion_in_impl (MetaSeatImpl *seat_impl, &dx, &dy); - new_x = seat_impl->pointer_x + dx; - new_y = seat_impl->pointer_y + dy; + old_x = seat_impl->pointer_x; + old_y = seat_impl->pointer_y; event = new_absolute_motion_event (seat_impl, input_device, - time_us, new_x, new_y, NULL); + time_us, + old_x + dx, + old_y + dy, + NULL); + dx_constrained = event->motion.x - old_x; + dy_constrained = event->motion.y - old_y; event->motion.flags |= CLUTTER_EVENT_FLAG_RELATIVE_MOTION; event->motion.dx = dx; event->motion.dy = dy; event->motion.dx_unaccel = dx_unaccel; event->motion.dy_unaccel = dy_unaccel; + event->motion.dx_constrained = dx_constrained; + event->motion.dy_constrained = dy_constrained; queue_event (seat_impl, event); } -- GitLab From f477d13903e9d159539fbe3e027e5abf29e735f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 18:26:43 +0200 Subject: [PATCH 37/40] ci: Build libei from git --- .gitlab-ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a9b3441ea9..cf451e41811 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ variables: .mutter.fedora:35@common: variables: FDO_DISTRIBUTION_VERSION: 35 - BASE_TAG: '2022-03-05.0' + BASE_TAG: '2022-04-28.0' FDO_DISTRIBUTION_PACKAGES: asciidoc clang @@ -85,6 +85,10 @@ variables: https://gitlab.gnome.org/GNOME/libgweather.git \ main . 1f687f6375a3f3f006600119f7eee7df7348ade5 && + ./.gitlab-ci/install-meson-project.sh \ + https://gitlab.freedesktop.org/libinput/libei.git \ + master . && + rpm -e --nodeps gnome-bluetooth-libs-devel \ mutter mutter-devel \ gnome-shell && -- GitLab From 11d7f6fc2cce465095c82a3af19b86254f5f65e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 16:24:17 +0200 Subject: [PATCH 38/40] input-capture-session: Hook up everything to EIS This commit hooks up the input capture session to EIS, which means creating devices, sending events, etc. A test case is added that checks that events pass through correctly. --- .../org.gnome.Mutter.InputCapture.xml | 6 + meson.build | 2 + src/backends/meta-input-capture-session.c | 464 +++++++++++++++++- src/meson.build | 1 + src/tests/input-capture-test-client.c | 332 +++++++++++++ src/tests/input-capture-tests.c | 66 +++ src/tests/meson.build | 3 + 7 files changed, 865 insertions(+), 9 deletions(-) diff --git a/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml index 7a8528ef795..a3919c3e8c9 100644 --- a/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml +++ b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml @@ -50,6 +50,12 @@ + + + + + + diff --git a/meson.build b/meson.build index a10232bbb72..d6efc84da7c 100644 --- a/meson.build +++ b/meson.build @@ -142,6 +142,8 @@ ice_dep = dependency('ice') atk_dep = dependency('atk', version: atk_req) libcanberra_dep = dependency('libcanberra', version: libcanberra_req) dbus_dep = dependency('dbus-1') +libeis_dep = dependency('libeis') +libei_dep = dependency('libei') # For now always require X11 support have_x11 = true diff --git a/src/backends/meta-input-capture-session.c b/src/backends/meta-input-capture-session.c index 3ab4f32db68..c649c12fcff 100644 --- a/src/backends/meta-input-capture-session.c +++ b/src/backends/meta-input-capture-session.c @@ -22,14 +22,18 @@ #include "backends/meta-input-capture-session.h" +#include +#include #include #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-dbus-session-manager.h" +#include "backends/meta-fd-source.h" #include "backends/meta-input-capture-private.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-remote-access-controller-private.h" +#include "core/meta-anonymous-file.h" #include "meta/barrier.h" #include "meta/boxes.h" #include "meta/meta-backend.h" @@ -91,6 +95,15 @@ struct _MetaInputCaptureSession uint32_t activated_serial; MetaInputCaptureSessionHandle *handle; + + struct eis *eis; + struct eis_client *eis_client; + struct eis_seat *eis_seat; + struct eis_device *eis_pointer; + struct eis_device *eis_keyboard; + GSource *eis_source; + + MetaAnonymousFile *keymap_file; }; static void initable_init_iface (GInitableIface *iface); @@ -148,6 +161,219 @@ release_remote_access_handle (MetaInputCaptureSession *session) g_clear_object (&session->handle); } +static void +setup_client (MetaInputCaptureSession *session, + struct eis_client *eis_client) +{ + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + struct eis_seat *eis_seat; + + session->eis_client = eis_client_ref (eis_client); + + eis_client_connect (eis_client); + + eis_seat = eis_client_new_seat (eis_client, clutter_seat_get_name (seat)); + eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_POINTER); + eis_seat_configure_capability (eis_seat, EIS_DEVICE_CAP_KEYBOARD); + eis_seat_add (eis_seat); + + session->eis_seat = eis_seat; +} + +static void +ensure_eis_pointer (MetaInputCaptureSession *session) +{ + struct eis_device *eis_pointer; + + if (session->eis_pointer) + return; + + eis_pointer = eis_seat_new_device (session->eis_seat); + eis_device_configure_type (eis_pointer, EIS_DEVICE_TYPE_PHYSICAL); + eis_device_configure_name (eis_pointer, "captured relative pointer"); + eis_device_configure_capability (eis_pointer, EIS_DEVICE_CAP_POINTER); + + eis_device_add (eis_pointer); + eis_device_resume (eis_pointer); + eis_device_start_emulating (eis_pointer); + + session->eis_pointer = eis_pointer; +} + +static MetaAnonymousFile * +ensure_xkb_keymap_file (MetaInputCaptureSession *session, + GError **error) +{ + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + struct xkb_keymap *keymap; + g_autofree char *keymap_string = NULL; + size_t keymap_size; + + if (session->keymap_file) + return session->keymap_file; + + keymap = meta_backend_get_keymap (backend); + if (!keymap) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Backend has no keymap"); + return NULL; + } + + keymap_string = xkb_keymap_get_as_string (keymap, XKB_KEYMAP_FORMAT_TEXT_V1); + keymap_size = strlen (keymap_string) + 1; + + session->keymap_file = + meta_anonymous_file_new (keymap_size, (const uint8_t *) keymap_string); + + return session->keymap_file; +} + +static void +ensure_eis_keyboard (MetaInputCaptureSession *session) +{ + struct eis_device *eis_keyboard; + g_autoptr (GError) error = NULL; + struct eis_keymap *eis_keymap; + MetaAnonymousFile *keymap_file; + int keymap_fd; + size_t keymap_size; + + if (session->eis_keyboard) + return; + + keymap_file = ensure_xkb_keymap_file (session, &error); + if (!keymap_file) + { + g_warning ("Failed to create input capture keymap file: %s", + error->message); + return; + } + + eis_keyboard = eis_seat_new_device (session->eis_seat); + eis_device_configure_type (eis_keyboard, EIS_DEVICE_TYPE_PHYSICAL); + eis_device_configure_name (eis_keyboard, "captured keyboard"); + eis_device_configure_capability (eis_keyboard, EIS_DEVICE_CAP_KEYBOARD); + + keymap_fd = meta_anonymous_file_open_fd (keymap_file, + META_ANONYMOUS_FILE_MAPMODE_PRIVATE); + keymap_size = meta_anonymous_file_size (keymap_file); + eis_keymap = eis_device_new_keymap (eis_keyboard, + EIS_KEYMAP_TYPE_XKB, + keymap_fd, keymap_size); + eis_keymap_add (eis_keymap); + eis_keymap_unref (eis_keymap); + meta_anonymous_file_close_fd (keymap_fd); + + eis_device_add (eis_keyboard); + eis_device_resume (eis_keyboard); + eis_device_start_emulating (eis_keyboard); + + session->eis_keyboard = eis_keyboard; +} + +static void +clear_eis_pointer (MetaInputCaptureSession *session) +{ + if (!session->eis_pointer) + return; + + eis_device_remove (session->eis_pointer); + g_clear_pointer (&session->eis_pointer, eis_device_unref); +} + +static void +clear_eis_keyboard (MetaInputCaptureSession *session) +{ + if (!session->eis_keyboard) + return; + + eis_device_remove (session->eis_keyboard); + g_clear_pointer (&session->eis_keyboard, eis_device_unref); +} + +static void +on_keymap_changed (MetaBackend *backend, + gpointer user_data) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (user_data); + + g_clear_pointer (&session->keymap_file, meta_anonymous_file_free); + + if (session->eis_keyboard) + { + clear_eis_keyboard (session); + ensure_eis_keyboard (session); + } +} + +static void +process_eis_event (MetaInputCaptureSession *session, + struct eis_event *eis_event) +{ + struct eis_client *eis_client; + struct eis_device *eis_device; + + switch (eis_event_get_type (eis_event)) + { + case EIS_EVENT_CLIENT_CONNECT: + eis_client = eis_event_get_client (eis_event); + if (eis_client_is_sender (eis_client)) + { + g_warning ("Unexpected sender libei client '%s' connected to " + "input capture session", + eis_client_get_name (eis_client)); + eis_client_disconnect (eis_client); + return; + } + + if (session->eis_client) + { + g_warning ("Unexpected additional libei client '%s' connected to " + "input capture session", + eis_client_get_name (eis_client)); + eis_client_disconnect (eis_client); + return; + } + + setup_client (session, eis_client); + break; + + case EIS_EVENT_CLIENT_DISCONNECT: + g_clear_pointer (&session->eis_seat, eis_seat_unref); + g_clear_pointer (&session->eis_client, eis_client_unref); + break; + case EIS_EVENT_CLIENT_PROPERTY: + break; + case EIS_EVENT_SEAT_BIND: + if (eis_event_seat_has_capability (eis_event, EIS_DEVICE_CAP_POINTER)) + ensure_eis_pointer (session); + else if (session->eis_pointer) + clear_eis_pointer (session); + + if (eis_event_seat_has_capability (eis_event, EIS_DEVICE_CAP_KEYBOARD)) + ensure_eis_keyboard (session); + else if (session->eis_keyboard) + clear_eis_keyboard (session); + + break; + case EIS_EVENT_DEVICE_CLOSED: + eis_device = eis_event_get_device (eis_event); + + if (eis_device == session->eis_pointer) + clear_eis_pointer (session); + else if (eis_device == session->eis_keyboard) + clear_eis_keyboard (session); + + break; + default: + break; + } +} + static void on_barrier_hit (MetaBarrier *barrier, const MetaBarrierEvent *event, @@ -297,6 +523,10 @@ meta_input_capture_session_disable (MetaInputCaptureSession *session) clear_all_barriers (session); + g_clear_pointer (&session->eis_pointer, eis_device_unref); + g_clear_pointer (&session->eis_keyboard, eis_device_unref); + g_clear_pointer (&session->eis_seat, eis_seat_unref); + session->state = INPUT_CAPTURE_STATE_INIT; if (session->handle) @@ -732,6 +962,47 @@ handle_release (MetaDBusInputCaptureSession *object, return G_DBUS_METHOD_INVOCATION_HANDLED; } +static gboolean +handle_connect_to_eis (MetaDBusInputCaptureSession *object, + GDBusMethodInvocation *invocation, + GUnixFDList *fd_list_in) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + int fd; + g_autoptr (GUnixFDList) fd_list = NULL; + int fd_idx; + GVariant *fd_variant; + + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_ACCESS_DENIED, + "Permission denied"); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + fd = eis_backend_fd_add_client (session->eis); + if (fd < 0) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Failed to create socket: %s", + g_strerror (-fd)); + return G_DBUS_METHOD_INVOCATION_HANDLED; + } + + fd_list = g_unix_fd_list_new (); + fd_idx = g_unix_fd_list_append (fd_list, fd, NULL); + close (fd); + fd_variant = g_variant_new_handle (fd_idx); + + meta_dbus_input_capture_session_complete_connect_to_eis (object, + invocation, + fd_list, + fd_variant); + return G_DBUS_METHOD_INVOCATION_HANDLED; +} + static gboolean handle_close (MetaDBusInputCaptureSession *object, GDBusMethodInvocation *invocation) @@ -806,6 +1077,7 @@ meta_input_capture_session_init_iface (MetaDBusInputCaptureSessionIface *iface) iface->handle_enable = handle_enable; iface->handle_disable = handle_disable; iface->handle_release = handle_release; + iface->handle_connect_to_eis = handle_connect_to_eis; iface->handle_close = handle_close; iface->handle_get_zones = handle_get_zones; } @@ -828,10 +1100,113 @@ meta_input_capture_session_finalize (GObject *object) g_free (session->peer_name); g_free (session->session_id); g_free (session->object_path); + g_clear_pointer (&session->keymap_file, meta_anonymous_file_free); + g_clear_pointer (&session->eis_source, g_source_destroy); + g_clear_pointer (&session->eis, eis_unref); G_OBJECT_CLASS (meta_input_capture_session_parent_class)->finalize (object); } +static void +meta_eis_log_handler (struct eis *eis, + enum eis_log_priority priority, + const char *file, + int lineno, + const char *func, + const char *message, + bool is_continuation) +{ + int message_length; + + message_length = strlen (message); + if (message[message_length - 1] == '\n') + message_length -= 1; + + if (priority >= EIS_LOG_PRIORITY_ERROR) + g_critical ("EIS: %.*s", message_length, message); + else if (priority >= EIS_LOG_PRIORITY_WARNING) + g_warning ("EIS: %.*s", message_length, message); + else if (priority >= EIS_LOG_PRIORITY_INFO) + g_info ("EIS: %.*s", message_length, message); + else + meta_topic (META_DEBUG_INPUT, "EIS: %.*s", message_length, message); +} + +static gboolean +meta_eis_source_prepare (gpointer user_data) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (user_data); + struct eis_event *eis_event; + gboolean retval; + + eis_event = eis_peek_event (session->eis); + retval = !!eis_event; + eis_event_unref (eis_event); + + return retval; +} + +static gboolean +meta_eis_source_dispatch (gpointer user_data) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (user_data); + + eis_dispatch (session->eis); + + while (TRUE) + { + struct eis_event *eis_event; + + eis_event = eis_get_event (session->eis); + if (!eis_event) + break; + + process_eis_event (session, eis_event); + eis_event_unref (eis_event); + } + + return G_SOURCE_CONTINUE; +} + +static void +meta_input_capture_session_constructed (GObject *object) +{ + MetaInputCaptureSession *session = META_INPUT_CAPTURE_SESSION (object); + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + static unsigned int global_session_number = 0; + int fd; + GSource *source; + + session->object_path = + g_strdup_printf (META_INPUT_CAPTURE_SESSION_DBUS_PATH "/u%u", + ++global_session_number); + + session->barriers = g_hash_table_new_full (NULL, NULL, NULL, + input_capture_barrier_free); + + session->eis = eis_new (session); + eis_log_set_handler (session->eis, meta_eis_log_handler); + eis_log_set_priority (session->eis, EIS_LOG_PRIORITY_DEBUG); + eis_setup_backend_fd (session->eis); + + fd = eis_get_fd (session->eis); + source = meta_create_fd_source (fd, + "[mutter] eis", + meta_eis_source_prepare, + meta_eis_source_dispatch, + session, + NULL); + session->eis_source = source; + g_source_attach (source, NULL); + g_source_unref (source); + + g_signal_connect (backend, "keymap-changed", + G_CALLBACK (on_keymap_changed), session); + + G_OBJECT_CLASS (meta_input_capture_session_parent_class)->constructed (object); +} + static void meta_input_capture_session_set_property (GObject *object, guint prop_id, @@ -889,6 +1264,7 @@ meta_input_capture_session_class_init (MetaInputCaptureSessionClass *klass) GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_input_capture_session_finalize; + object_class->constructed = meta_input_capture_session_constructed; object_class->set_property = meta_input_capture_session_set_property; object_class->get_property = meta_input_capture_session_get_property; @@ -925,14 +1301,6 @@ meta_input_capture_session_class_init (MetaInputCaptureSessionClass *klass) static void meta_input_capture_session_init (MetaInputCaptureSession *session) { - static unsigned int global_session_number = 0; - - session->object_path = - g_strdup_printf (META_INPUT_CAPTURE_SESSION_DBUS_PATH "/u%u", - ++global_session_number); - - session->barriers = g_hash_table_new_full (NULL, NULL, NULL, - input_capture_barrier_free); } char * @@ -948,15 +1316,93 @@ meta_input_capture_session_process_event (MetaInputCaptureSession *session, switch (event->type) { case CLUTTER_MOTION: + if (!session->eis_pointer) + return TRUE; + + eis_device_pointer_motion (session->eis_pointer, + event->motion.dx - event->motion.dx_constrained, + event->motion.dy - event->motion.dy_constrained); + eis_device_frame (session->eis_pointer); + break; case CLUTTER_BUTTON_PRESS: + if (!session->eis_pointer) + return TRUE; + + eis_device_pointer_button (session->eis_pointer, + clutter_event_get_event_code (event), + true); + eis_device_frame (session->eis_pointer); + break; case CLUTTER_BUTTON_RELEASE: + eis_device_pointer_button (session->eis_pointer, + clutter_event_get_event_code (event), + false); + eis_device_frame (session->eis_pointer); + break; case CLUTTER_SCROLL: + { + const double factor = 10.0; + bool stop_x, stop_y; + double dx, dy; + + if (!session->eis_pointer) + return TRUE; + + if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL)) + stop_x = true; + if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL)) + stop_y = true; + + if (stop_x || stop_y) + eis_device_pointer_scroll_stop (session->eis_pointer, stop_x, stop_y); + + switch (clutter_event_get_scroll_direction (event)) + { + case CLUTTER_SCROLL_UP: + eis_device_pointer_scroll_discrete (session->eis_pointer, 0, -1); + break; + case CLUTTER_SCROLL_DOWN: + eis_device_pointer_scroll_discrete (session->eis_pointer, 0, 1); + break; + case CLUTTER_SCROLL_LEFT: + eis_device_pointer_scroll_discrete (session->eis_pointer, -1, 0); + break; + case CLUTTER_SCROLL_RIGHT: + eis_device_pointer_scroll_discrete (session->eis_pointer, 1, 0); + break; + case CLUTTER_SCROLL_SMOOTH: + clutter_event_get_scroll_delta (event, &dx, &dy); + eis_device_pointer_scroll (session->eis_pointer, + dx * factor, + dy * factor); + break; + } + eis_device_frame (session->eis_pointer); + break; + } case CLUTTER_KEY_PRESS: + if (!session->eis_keyboard) + return TRUE; + + eis_device_keyboard_key (session->eis_keyboard, + clutter_event_get_event_code (event), + true); + eis_device_frame (session->eis_keyboard); + break; case CLUTTER_KEY_RELEASE: - return TRUE; + if (!session->eis_keyboard) + return TRUE; + + eis_device_keyboard_key (session->eis_keyboard, + clutter_event_get_event_code (event), + false); + eis_device_frame (session->eis_keyboard); + break; default: return FALSE; } + + return TRUE; } void diff --git a/src/meson.build b/src/meson.build index 38c5456049a..1a00f79123b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -27,6 +27,7 @@ mutter_pkg_private_deps = [ json_glib_dep, libcanberra_dep, xkbcommon_dep, + libeis_dep, ] if have_gnome_desktop diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c index ef098502655..a7b5818b10f 100644 --- a/src/tests/input-capture-test-client.c +++ b/src/tests/input-capture-test-client.c @@ -22,8 +22,13 @@ #include #include +#include +#include +#include #include +#include "backends/meta-fd-source.h" + #include "meta-dbus-input-capture.h" typedef struct @@ -50,10 +55,37 @@ typedef struct _InputCapture MetaDBusInputCapture *proxy; } InputCapture; +typedef struct _Event +{ + enum ei_event_type type; + struct { + double dx; + double dy; + } motion; + struct { + uint32_t button; + gboolean is_press; + } button; + struct { + uint32_t key; + gboolean is_press; + } key; +} Event; + typedef struct _InputCaptureSession { MetaDBusInputCaptureSession *proxy; unsigned int serial; + + struct ei *ei; + GSource *ei_source; + + Event *expected_events; + int n_expeceted_events; + int next_event; + + gboolean has_pointer; + gboolean has_keyboard; } InputCaptureSession; static GDataInputStream *stdin_reader; @@ -185,6 +217,9 @@ input_capture_session_close (InputCaptureSession *session) { GError *error = NULL; + g_clear_pointer (&session->ei, ei_unref); + g_clear_pointer (&session->ei_source, g_source_destroy); + if (!meta_dbus_input_capture_session_call_close_sync (session->proxy, NULL, &error)) g_error ("Failed to close session: %s", error->message); @@ -193,6 +228,223 @@ input_capture_session_close (InputCaptureSession *session) g_free (session); } +static void +record_event (InputCaptureSession *session, + const Event *event) +{ + const Event *expected_event; + + g_debug ("Record event #%d, with type %d", + session->next_event + 1, event->type); + g_assert_nonnull (session->expected_events); + g_assert_cmpint (session->next_event, <, session->n_expeceted_events); + + expected_event = &session->expected_events[session->next_event++]; + + g_assert_cmpint (expected_event->type, ==, event->type); + + switch (event->type) + { + case EI_EVENT_POINTER_MOTION: + g_assert_cmpfloat_with_epsilon (event->motion.dx, + expected_event->motion.dx, + DBL_EPSILON); + g_assert_cmpfloat_with_epsilon (event->motion.dy, + expected_event->motion.dy, + DBL_EPSILON); + break; + case EI_EVENT_POINTER_BUTTON: + g_assert_cmpint (event->button.button, ==, expected_event->button.button); + break; + case EI_EVENT_KEYBOARD_KEY: + g_assert_cmpint (event->key.key, ==, expected_event->key.key); + break; + case EI_EVENT_FRAME: + break; + default: + break; + } +} + +static void +process_ei_event (InputCaptureSession *session, + struct ei_event *ei_event) +{ + g_debug ("Processing event %d", ei_event_get_type (ei_event)); + + switch (ei_event_get_type (ei_event)) + { + case EI_EVENT_SEAT_ADDED: + { + struct ei_seat *ei_seat = ei_event_get_seat (ei_event); + + g_assert_true (ei_seat_has_capability (ei_seat, EI_DEVICE_CAP_POINTER)); + g_assert_true (ei_seat_has_capability (ei_seat, EI_DEVICE_CAP_KEYBOARD)); + ei_seat_bind_capability (ei_seat, EI_DEVICE_CAP_POINTER); + ei_seat_bind_capability (ei_seat, EI_DEVICE_CAP_KEYBOARD); + break; + } + case EI_EVENT_DEVICE_ADDED: + { + struct ei_device *ei_device = ei_event_get_device (ei_event); + + if (ei_device_has_capability (ei_device, EI_DEVICE_CAP_POINTER)) + session->has_pointer = TRUE; + if (ei_device_has_capability (ei_device, EI_DEVICE_CAP_KEYBOARD)) + session->has_keyboard = TRUE; + break; + } + case EI_EVENT_DEVICE_REMOVED: + { + struct ei_device *ei_device = ei_event_get_device (ei_event); + + if (ei_device_has_capability (ei_device, EI_DEVICE_CAP_POINTER)) + session->has_pointer = FALSE; + if (ei_device_has_capability (ei_device, EI_DEVICE_CAP_KEYBOARD)) + session->has_keyboard = FALSE; + break; + } + case EI_EVENT_POINTER_MOTION: + record_event (session, + &(Event) { + .type = EI_EVENT_POINTER_MOTION, + .motion.dx = ei_event_pointer_get_dx (ei_event), + .motion.dy = ei_event_pointer_get_dy (ei_event), + }); + break; + case EI_EVENT_POINTER_BUTTON: + record_event (session, + &(Event) { + .type = EI_EVENT_POINTER_BUTTON, + .button.button = ei_event_pointer_get_button (ei_event), + }); + break; + case EI_EVENT_KEYBOARD_KEY: + record_event (session, + &(Event) { + .type = EI_EVENT_KEYBOARD_KEY, + .key.key = ei_event_keyboard_get_key (ei_event), + }); + break; + case EI_EVENT_FRAME: + record_event (session, &(Event) { .type = EI_EVENT_FRAME }); + break; + default: + break; + } +} + +static gboolean +ei_source_prepare (gpointer user_data) +{ + InputCaptureSession *session = user_data; + struct ei_event *ei_event; + gboolean retval; + + ei_event = ei_peek_event (session->ei); + retval = !!ei_event; + ei_event_unref (ei_event); + + return retval; +} + +static gboolean +ei_source_dispatch (gpointer user_data) +{ + InputCaptureSession *session = user_data; + + ei_dispatch (session->ei); + + while (TRUE) + { + struct ei_event *ei_event; + + ei_event = ei_get_event (session->ei); + if (!ei_event) + break; + + process_ei_event (session, ei_event); + ei_event_unref (ei_event); + } + + return G_SOURCE_CONTINUE; +} + +static void +set_expected_events (InputCaptureSession *session, + Event *expected_events, + int n_expeceted_events) +{ + session->expected_events = expected_events; + session->n_expeceted_events = n_expeceted_events; + session->next_event = 0; +} + +static void +log_handler (struct ei *ei, + enum ei_log_priority priority, + const char *file, + int lineno, + const char *func, + const char *message, + bool is_continuation) +{ + int message_length; + + message_length = strlen (message); + if (message[message_length - 1] == '\n') + message_length -= 1; + + if (priority >= EI_LOG_PRIORITY_ERROR) + g_critical ("libei: %.*s", message_length, message); + else if (priority >= EI_LOG_PRIORITY_WARNING) + g_warning ("libei: %.*s", message_length, message); + else if (priority >= EI_LOG_PRIORITY_INFO) + g_info ("libei: %.*s", message_length, message); + else + g_debug ("libei: %.*s", message_length, message); +} + +static void +input_capture_session_connect_to_eis (InputCaptureSession *session) +{ + g_autoptr (GVariant) fd_variant = NULL; + g_autoptr (GUnixFDList) fd_list = NULL; + GError *error = NULL; + int fd; + struct ei *ei; + int ret; + + if (!meta_dbus_input_capture_session_call_connect_to_eis_sync (session->proxy, + NULL, + &fd_variant, + &fd_list, + NULL, &error)) + g_error ("Failed to connect to EIS: %s", error->message); + + fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), &error); + if (fd == -1) + g_error ("Failed to get EIS file descriptor: %s", error->message); + + ei = ei_new_receiver (session); + ei_log_set_handler (ei, log_handler); + ei_log_set_priority (ei, EI_LOG_PRIORITY_DEBUG); + + ret = ei_setup_backend_fd (ei, fd); + if (ret < 0) + g_error ("Failed to setup libei backend: %s", g_strerror (errno)); + + session->ei = ei; + session->ei_source = meta_create_fd_source (ei_get_fd (ei), + "libei", + ei_source_prepare, + ei_source_dispatch, + session, + NULL); + g_source_attach (session->ei_source, NULL); + g_source_unref (session->ei_source); +} + static GList * input_capture_session_get_zones (InputCaptureSession *session) { @@ -543,6 +795,85 @@ test_cancel_keybinding (void) input_capture_session_close (session); } +static void +test_events (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + g_autolist (Zone) zones = NULL; + Event expected_events[] = { + /* Move the pointer with deltas (10, 15) and (2, -5), then click */ + { + .type = EI_EVENT_POINTER_MOTION, + .motion = { .dx = -10.0, .dy = -10.0 }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_POINTER_MOTION, + .motion = { .dx = 2.0, .dy = -5.0 }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_POINTER_BUTTON, + .button = { .button = BTN_LEFT, .is_press = TRUE }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_POINTER_BUTTON, + .button = { .button = BTN_LEFT, .is_press = FALSE }, + }, + { + .type = EI_EVENT_FRAME, + }, + + /* Press, then release, KEY_A */ + { + .type = EI_EVENT_KEYBOARD_KEY, + .key = { .key = KEY_A, .is_press = TRUE }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_KEYBOARD_KEY, + .key = { .key = KEY_A, .is_press = FALSE }, + }, + { + .type = EI_EVENT_FRAME, + }, + }; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + input_capture_session_connect_to_eis (session); + zones = input_capture_session_get_zones (session); + input_capture_session_add_barrier (session, 0, 0, 0, 600); + + input_capture_session_enable (session); + + while (!session->has_pointer || + !session->has_keyboard) + g_main_context_iteration (NULL, TRUE); + + write_state (session, "1"); + + set_expected_events (session, + expected_events, + G_N_ELEMENTS (expected_events)); + + while (session->next_event < session->n_expeceted_events) + g_main_context_iteration (NULL, TRUE); + + input_capture_session_close (session); +} + static const struct { const char *name; @@ -553,6 +884,7 @@ static const struct { "barriers", test_barriers, }, { "clear-barriers", test_clear_barriers, }, { "cancel-keybinding", test_cancel_keybinding, }, + { "events", test_events, }, }; static void diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c index e27b4fef5f6..b7bacb1337f 100644 --- a/src/tests/input-capture-tests.c +++ b/src/tests/input-capture-tests.c @@ -181,6 +181,34 @@ input_capture_test_client_finish (InputCaptureTestClient *test_client) g_free (test_client); } +static void +click_button (ClutterVirtualInputDevice *virtual_pointer, + uint32_t button) +{ + clutter_virtual_input_device_notify_button (virtual_pointer, + g_get_monotonic_time (), + button, + CLUTTER_BUTTON_STATE_PRESSED); + clutter_virtual_input_device_notify_button (virtual_pointer, + g_get_monotonic_time (), + button, + CLUTTER_BUTTON_STATE_RELEASED); +} + +static void +press_key (ClutterVirtualInputDevice *virtual_keyboard, + uint32_t key) +{ + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + key, + CLUTTER_KEY_STATE_PRESSED); + clutter_virtual_input_device_notify_key (virtual_keyboard, + g_get_monotonic_time (), + key, + CLUTTER_KEY_STATE_RELEASED); +} + static void meta_test_input_capture_sanity (void) { @@ -404,6 +432,42 @@ meta_test_input_capture_cancel_keybinding (void) input_capture_test_client_finish (test_client); } +static void +meta_test_input_capture_events (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (MetaVirtualMonitor) virtual_monitor1 = NULL; + g_autoptr (MetaVirtualMonitor) virtual_monitor2 = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL; + InputCaptureTestClient *test_client; + + virtual_monitor1 = meta_create_test_monitor (test_context, 800, 600, 20.0); + + virtual_pointer = clutter_seat_create_virtual_device (seat, + CLUTTER_POINTER_DEVICE); + clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + virtual_keyboard = clutter_seat_create_virtual_device (seat, + CLUTTER_KEYBOARD_DEVICE); + + test_client = input_capture_test_client_new ("events"); + input_capture_test_client_wait_for_state (test_client, "1"); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, -20.0); + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + 2.0, -5.0); + click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + press_key (virtual_keyboard, KEY_A); + + input_capture_test_client_finish (test_client); +} + static void init_tests (void) { @@ -417,6 +481,8 @@ init_tests (void) meta_test_input_capture_clear_barriers); g_test_add_func ("/backends/native/input-capture/cancel-keybinding", meta_test_input_capture_cancel_keybinding); + g_test_add_func ("/backends/native/input-capture/events", + meta_test_input_capture_events); } int diff --git a/src/tests/meson.build b/src/tests/meson.build index 1029371c4cb..3499392ffa9 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -226,6 +226,8 @@ if have_native_tests input_capture_client = executable('mutter-input-capture-test-client', sources: [ 'input-capture-test-client.c', + '../backends/meta-fd-source.c', + '../backends/meta-fd-source.h', dbus_input_capture_built_sources, ], include_directories: tests_includes, @@ -235,6 +237,7 @@ if have_native_tests ], dependencies: [ gio_unix_dep, + libei_dep, ], install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, -- GitLab From b7cb3e23cd719f810e17ff78660eac2209279767 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 17:46:34 +0200 Subject: [PATCH 39/40] tests/input-capture: Test that a11y isn't triggered when capturing Accessibility should be handled on the receiving end, if needed. Make sure this is the case by listening on some signals, verifying they are only triggered if we're not capturing input. --- src/tests/input-capture-test-client.c | 52 ++++++++++++++++++ src/tests/input-capture-tests.c | 77 +++++++++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c index a7b5818b10f..468bf8a8372 100644 --- a/src/tests/input-capture-test-client.c +++ b/src/tests/input-capture-test-client.c @@ -874,6 +874,57 @@ test_events (void) input_capture_session_close (session); } +static void +test_a11y (void) +{ + InputCapture *input_capture; + InputCaptureSession *session; + g_autolist (Zone) zones = NULL; + Event expected_events[] = { + { + .type = EI_EVENT_POINTER_MOTION, + .motion = { .dx = -10.0, .dy = 0.0 }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_POINTER_BUTTON, + .button = { .button = BTN_LEFT, .is_press = TRUE }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_POINTER_BUTTON, + .button = { .button = BTN_LEFT, .is_press = FALSE }, + }, + { + .type = EI_EVENT_FRAME, + }, + }; + + input_capture = input_capture_new (); + session = input_capture_create_session (input_capture); + + input_capture_session_connect_to_eis (session); + zones = input_capture_session_get_zones (session); + input_capture_session_add_barrier (session, 0, 0, 0, 600); + input_capture_session_enable (session); + + set_expected_events (session, + expected_events, + G_N_ELEMENTS (expected_events)); + write_state (session, "1"); + + while (session->next_event < session->n_expeceted_events) + g_main_context_iteration (NULL, TRUE); + + wait_for_state (session, "1"); + + input_capture_session_close (session); +} + static const struct { const char *name; @@ -885,6 +936,7 @@ static const struct { "clear-barriers", test_clear_barriers, }, { "cancel-keybinding", test_cancel_keybinding, }, { "events", test_events, }, + { "a11y", test_a11y, }, }; static void diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c index b7bacb1337f..baa3fc1996e 100644 --- a/src/tests/input-capture-tests.c +++ b/src/tests/input-capture-tests.c @@ -468,6 +468,79 @@ meta_test_input_capture_events (void) input_capture_test_client_finish (test_client); } +static void +on_a11y_timeout_started (ClutterSeat *seat, + ClutterInputDevice *device, + ClutterPointerA11yTimeoutType timeout_type, + unsigned int delay_ms, + int *a11y_started_counter) +{ + (*a11y_started_counter)++; +} + +static void +meta_test_input_capture_a11y (void) +{ + MetaBackend *backend = meta_context_get_backend (test_context); + ClutterSeat *seat = meta_backend_get_default_seat (backend); + g_autoptr (MetaVirtualMonitor) virtual_monitor = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + InputCaptureTestClient *test_client; + ClutterPointerA11yDwellClickType dwell_click_type; + int a11y_started_counter = 0; + g_autoptr (GSettings) a11y_mouse_settings = NULL; + + a11y_mouse_settings = g_settings_new ("org.gnome.desktop.a11y.mouse"); + + virtual_monitor = meta_create_test_monitor (test_context, 800, 600, 20.0); + virtual_pointer = clutter_seat_create_virtual_device (seat, + CLUTTER_POINTER_DEVICE); + + clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, + g_get_monotonic_time (), + 10.0, 10.0); + + g_settings_set_boolean (a11y_mouse_settings, "dwell-click-enabled", TRUE); + g_settings_set_boolean (a11y_mouse_settings, "secondary-click-enabled", TRUE); + + dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY; + clutter_seat_set_pointer_a11y_dwell_click_type (seat, dwell_click_type); + g_signal_connect (seat, "ptr-a11y-timeout-started", + G_CALLBACK (on_a11y_timeout_started), + &a11y_started_counter); + + click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + meta_flush_input (test_context); + g_assert_cmpint (a11y_started_counter, ==, 1); + + test_client = input_capture_test_client_new ("a11y"); + input_capture_test_client_wait_for_state (test_client, "1"); + + click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + meta_flush_input (test_context); + g_assert_cmpint (a11y_started_counter, ==, 2); + + clutter_virtual_input_device_notify_relative_motion (virtual_pointer, + g_get_monotonic_time (), + -20.0, 0.0); + + click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + meta_flush_input (test_context); + g_assert_cmpint (a11y_started_counter, ==, 2); + + input_capture_test_client_write_state (test_client, "1"); + input_capture_test_client_finish (test_client); + + click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + meta_flush_input (test_context); + g_assert_cmpint (a11y_started_counter, ==, 3); + + dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE; + clutter_seat_set_pointer_a11y_dwell_click_type (seat, dwell_click_type); + g_settings_set_boolean (a11y_mouse_settings, "dwell-click-enabled", FALSE); + g_settings_set_boolean (a11y_mouse_settings, "secondary-click-enabled", FALSE); +} + static void init_tests (void) { @@ -483,6 +556,8 @@ init_tests (void) meta_test_input_capture_cancel_keybinding); g_test_add_func ("/backends/native/input-capture/events", meta_test_input_capture_events); + g_test_add_func ("/backends/native/input-capture/a11y", + meta_test_input_capture_a11y); } int @@ -492,6 +567,8 @@ main (int argc, g_autoptr (MetaContext) context = NULL; g_autoptr (GError) error = NULL; + g_assert_cmpstr (getenv ("GSETTINGS_BACKEND"), ==, "memory"); + context = test_context = meta_create_test_context (META_CONTEXT_TEST_TYPE_HEADLESS, META_CONTEXT_TEST_FLAG_NO_X11); -- GitLab From 87bfb3d9e1375bf984a50af684a9909cb4e39482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Thu, 28 Apr 2022 18:01:35 +0200 Subject: [PATCH 40/40] tests/input-capture: Add test for ATK input capture Cally should not see the events when they are captured. --- src/tests/input-capture-test-client.c | 14 ++++++++++++++ src/tests/input-capture-tests.c | 28 +++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c index 468bf8a8372..a767bd2a5d3 100644 --- a/src/tests/input-capture-test-client.c +++ b/src/tests/input-capture-test-client.c @@ -902,6 +902,20 @@ test_a11y (void) { .type = EI_EVENT_FRAME, }, + { + .type = EI_EVENT_KEYBOARD_KEY, + .key = { .key = KEY_A, .is_press = TRUE }, + }, + { + .type = EI_EVENT_FRAME, + }, + { + .type = EI_EVENT_KEYBOARD_KEY, + .key = { .key = KEY_A, .is_press = FALSE }, + }, + { + .type = EI_EVENT_FRAME, + }, }; input_capture = input_capture_new (); diff --git a/src/tests/input-capture-tests.c b/src/tests/input-capture-tests.c index baa3fc1996e..8de51747c22 100644 --- a/src/tests/input-capture-tests.c +++ b/src/tests/input-capture-tests.c @@ -478,6 +478,17 @@ on_a11y_timeout_started (ClutterSeat *seat, (*a11y_started_counter)++; } +static gboolean +atk_key_listener (AtkKeyEventStruct *event, + gpointer user_data) +{ + int *a11y_key_counter = user_data; + + (*a11y_key_counter)++; + + return TRUE; +} + static void meta_test_input_capture_a11y (void) { @@ -485,16 +496,24 @@ meta_test_input_capture_a11y (void) ClutterSeat *seat = meta_backend_get_default_seat (backend); g_autoptr (MetaVirtualMonitor) virtual_monitor = NULL; g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL; + g_autoptr (ClutterVirtualInputDevice) virtual_keyboard = NULL; InputCaptureTestClient *test_client; ClutterPointerA11yDwellClickType dwell_click_type; int a11y_started_counter = 0; + int a11y_key_counter = 0; g_autoptr (GSettings) a11y_mouse_settings = NULL; + guint atk_key_listener_id; + + atk_key_listener_id = atk_add_key_event_listener (atk_key_listener, + &a11y_key_counter); a11y_mouse_settings = g_settings_new ("org.gnome.desktop.a11y.mouse"); virtual_monitor = meta_create_test_monitor (test_context, 800, 600, 20.0); virtual_pointer = clutter_seat_create_virtual_device (seat, CLUTTER_POINTER_DEVICE); + virtual_keyboard = clutter_seat_create_virtual_device (seat, + CLUTTER_KEYBOARD_DEVICE); clutter_virtual_input_device_notify_absolute_motion (virtual_pointer, g_get_monotonic_time (), @@ -510,35 +529,44 @@ meta_test_input_capture_a11y (void) &a11y_started_counter); click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + press_key (virtual_keyboard, KEY_A); meta_flush_input (test_context); g_assert_cmpint (a11y_started_counter, ==, 1); + g_assert_cmpint (a11y_key_counter, ==, 2); test_client = input_capture_test_client_new ("a11y"); input_capture_test_client_wait_for_state (test_client, "1"); click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + press_key (virtual_keyboard, KEY_A); meta_flush_input (test_context); g_assert_cmpint (a11y_started_counter, ==, 2); + g_assert_cmpint (a11y_key_counter, ==, 4); clutter_virtual_input_device_notify_relative_motion (virtual_pointer, g_get_monotonic_time (), -20.0, 0.0); click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + press_key (virtual_keyboard, KEY_A); meta_flush_input (test_context); g_assert_cmpint (a11y_started_counter, ==, 2); + g_assert_cmpint (a11y_key_counter, ==, 4); input_capture_test_client_write_state (test_client, "1"); input_capture_test_client_finish (test_client); click_button (virtual_pointer, CLUTTER_BUTTON_PRIMARY); + press_key (virtual_keyboard, KEY_A); meta_flush_input (test_context); g_assert_cmpint (a11y_started_counter, ==, 3); + g_assert_cmpint (a11y_key_counter, ==, 6); dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE; clutter_seat_set_pointer_a11y_dwell_click_type (seat, dwell_click_type); g_settings_set_boolean (a11y_mouse_settings, "dwell-click-enabled", FALSE); g_settings_set_boolean (a11y_mouse_settings, "secondary-click-enabled", FALSE); + atk_remove_key_event_listener (atk_key_listener_id); } static void -- GitLab