diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3a9b3441ea9b2158635c562a45d12c009bc74445..cf451e4181162b304e8d58a6202d65c08739173f 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 && diff --git a/clutter/clutter/clutter-event-private.h b/clutter/clutter/clutter-event-private.h index 69fdf24ed670ea50a788fbef9419818a0c62f12d..dcee65a8eeb5104dcc2cb889cebe0764afb235dc 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 0433a1ec98be6953a78c1e91890ce8bb2075f544..beedbea1d073f0cf45f89fb1f10a1b58fccaeb05 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 (); +} diff --git a/clutter/clutter/clutter-event.h b/clutter/clutter/clutter-event.h index 94595542cd109967f840c91697f035b3189f9645..943961b0bf5a82c50dca6af5a86a0545d10900ba 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/clutter/clutter/clutter-input-only-action.c b/clutter/clutter/clutter-input-only-action.c new file mode 100644 index 0000000000000000000000000000000000000000..98bd551e388f02a2af71f02627ee053c014da18d --- /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 0000000000000000000000000000000000000000..22cb2e205ce18a453a4f41b7db658b3f8fc3552c --- /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 0000000000000000000000000000000000000000..bb1e709c0dc93cd9419a2d8840fc15f6556557ff --- /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 0000000000000000000000000000000000000000..6217915c08cd9121e518fdc02dfd069362416632 --- /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-input-pointer-a11y-private.h b/clutter/clutter/clutter-input-pointer-a11y-private.h index a66ddeac9d7f247e1951960bd79c3890855ee792..d7f4655012c4d17d8245466d90c8c442075c188b 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 cfee58e55f50ccedadd232f720c7cd72b02bde0b..a74aa4cc223ebc3b4a5fcb742d2c2b10eac5d5b7 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 a2ee338b66a5af9f8b1a984710e84b13e7403a7e..5b01783e5e98d84723682b16134186544d8e0d69 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/clutter/clutter/clutter-seat.c b/clutter/clutter/clutter-seat.c index 12130429719e4635174243ea7d225001775089e3..0779ac1f1255696367ba0376da436daaa0ec53ed 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 30d176acf1564082d51e1a3f21456a0294872d82..cc47f6f508b096faf9ee69e43a5499a78a1c5719 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/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h index e9ba5707edc23a369cc6dfa8caea1ebdc6fe5445..759ef06034f6b13679920059342a265be26d9cda 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 705bd92a8f0363a608e88966b379c4ee2fbf4049..905ff88df9b9634fc69202dd1e6c948e2c0d88ab 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 2b8764a0f170559c2d8cdf0f8abd5926ffe86e2e..f60e382e1845975141ea7bfad0186613d214a9a2 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/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml new file mode 100644 index 0000000000000000000000000000000000000000..a3919c3e8c9adc418191e6090e3a678b2364646b --- /dev/null +++ b/data/dbus-interfaces/org.gnome.Mutter.InputCapture.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/org.gnome.mutter.gschema.xml.in b/data/org.gnome.mutter.gschema.xml.in index c014b749fcfacacfcecb49e04bf2cb7a9de0b069..d59c45bf405d8d5e8b4b0eaff0ba49944e8cd75f 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/meson.build b/meson.build index a10232bbb72667f1c98dacafe3abe98a8c6051e8..d6efc84da7c4c07400e14d167e61585de971163c 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-backend-private.h b/src/backends/meta-backend-private.h index e9e94e044df4553ec7741af2e398dd0d4077ca34..aefeb79a9300b6aa9d61718528d5ae4f80daca4c 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); @@ -136,12 +141,16 @@ 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); 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); @@ -162,6 +171,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 6febe4dfa37c236fcdccc99e7f9095648ace278f..405d901f34247a6321ae3b4c91ab7772a6e729a6 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -63,10 +63,20 @@ 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; +typedef struct _MetaDbusSessionManager MetaDbusSessionManager; +typedef struct _MetaDbusSessionWatcher MetaDbusSessionWatcher; + #ifdef HAVE_REMOTE_DESKTOP 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 cf6a04943e8b78afbb112925a105af34b869c8c5..fad92240d2284364f25d949354cf91dd09ce74d9 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -53,10 +53,12 @@ #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" #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" @@ -96,6 +98,7 @@ enum PROP_0, PROP_CONTEXT, + PROP_CAPABILITIES, N_PROPS }; @@ -149,12 +152,13 @@ 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 + MetaInputCapture *input_capture; #ifdef HAVE_PROFILER MetaProfiler *profiler; @@ -224,9 +228,10 @@ 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->input_capture); 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,16 +571,27 @@ 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 */ + 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); @@ -840,6 +856,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; @@ -873,6 +892,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] = @@ -1348,6 +1375,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) @@ -1372,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 @@ -1543,6 +1586,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) { @@ -1562,6 +1613,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 d0483e43c6a8a6ec3f9e90dfa0a1a92739722f67..38b1bfa0724abe4754a2cea65cc10b22e2507a62 100644 --- a/src/backends/meta-barrier-private.h +++ b/src/backends/meta-barrier-private.h @@ -47,20 +47,19 @@ 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); -G_END_DECLS +MetaBackend * meta_barrier_get_backend (MetaBarrier *barrier); -struct _MetaBarrierPrivate -{ - MetaDisplay *display; - MetaBorder border; - MetaBarrierImpl *impl; -}; +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 94a4b7964e8ec6e423d420bf55f49881de8e60e2..103c39e1c56e3025b28678782d59d8256bcb8463 100644 --- a/src/backends/meta-barrier.c +++ b/src/backends/meta-barrier.c @@ -23,6 +23,20 @@ #include "backends/native/meta-barrier-native.h" #endif +struct _MetaBarrier +{ + GObject parent; +}; + +typedef struct _MetaBarrierPrivate +{ + MetaBackend *backend; + MetaBorder border; + MetaBarrierImpl *impl; + + MetaBarrierFlags flags; +} MetaBarrierPrivate; + G_DEFINE_TYPE_WITH_PRIVATE (MetaBarrier, meta_barrier, G_TYPE_OBJECT) G_DEFINE_TYPE (MetaBarrierImpl, meta_barrier_impl, G_TYPE_OBJECT) @@ -39,6 +53,7 @@ enum { PROP_0, + PROP_BACKEND, PROP_DISPLAY, PROP_X1, @@ -46,6 +61,7 @@ enum PROP_X2, PROP_Y2, PROP_DIRECTIONS, + PROP_FLAGS, PROP_LAST, }; @@ -62,6 +78,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, @@ -70,11 +101,15 @@ 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) { + 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); @@ -92,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; @@ -105,12 +143,22 @@ 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_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; @@ -127,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; @@ -137,7 +188,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)) { @@ -153,7 +204,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); @@ -175,18 +227,19 @@ 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); } static void -meta_barrier_constructed (GObject *object) +init_barrier_impl (MetaBarrier *barrier) { - MetaBarrier *barrier = META_BARRIER (object); - 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 || priv->border.line.a.y == priv->border.line.b.y); g_return_if_fail (priv->border.line.a.x >= 0); @@ -195,15 +248,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,40 +282,59 @@ 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_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + G_PARAM_DEPRECATED | + 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 +342,18 @@ 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); + 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); @@ -307,7 +397,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); @@ -318,29 +409,83 @@ meta_barrier_destroy (MetaBarrier *barrier) static void meta_barrier_init (MetaBarrier *barrier) { - barrier->priv = meta_barrier_get_instance_private (barrier); +} + +MetaBarrier * +meta_barrier_new (MetaBackend *backend, + int x1, + int y1, + int x2, + int y2, + MetaBarrierDirection directions, + MetaBarrierFlags flags, + 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, + "flags", flags, + 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) +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); } +MetaBackend * +meta_barrier_get_backend (MetaBarrier *barrier) +{ + 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; +} + +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) { - klass->is_active = NULL; - klass->release = NULL; - klass->destroy = NULL; } static void diff --git a/src/backends/meta-dbus-session-manager.c b/src/backends/meta-dbus-session-manager.c new file mode 100644 index 0000000000000000000000000000000000000000..72e4e26dc8e76c2afa875488c869316e33a8083b --- /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 0000000000000000000000000000000000000000..da2f027d20aca8e4ab44fad1ecd97461bf092e37 --- /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/backends/meta-dbus-session-watcher.c b/src/backends/meta-dbus-session-watcher.c index a885b423be580081054c66e430886dd82627ceae..79f07560cc2a253b01d66d8a635be56097a40178 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,15 @@ 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); +} + +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 06d3e1ff9a3a448fcba2c8e3fa5b393affa76309..98aee63e4ead8a5681d45a23f0b131c08d51e291 100644 --- a/src/backends/meta-dbus-session-watcher.h +++ b/src/backends/meta-dbus-session-watcher.h @@ -34,7 +34,8 @@ struct _MetaDbusSessionInterface { GTypeInterface parent_iface; - void (* client_vanished) (MetaDbusSession *session); + void (* close) (MetaDbusSession *session); + const char * (* get_id) (MetaDbusSession *session); }; #define META_TYPE_DBUS_SESSION_WATCHER (meta_dbus_session_watcher_get_type ()) @@ -49,4 +50,8 @@ 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); + +const char * meta_dbus_session_get_id (MetaDbusSession *session); + #endif /* META_DBUS_SESSION_WATCHER_H */ diff --git a/src/backends/meta-fd-source.c b/src/backends/meta-fd-source.c new file mode 100644 index 0000000000000000000000000000000000000000..2c0fb09b7e9ecaab3b8251d10b08ae087d474b2d --- /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 0000000000000000000000000000000000000000..b005170c30044d49c72d4cf4ac7540e2e016a321 --- /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/meta-input-capture-private.h b/src/backends/meta-input-capture-private.h new file mode 100644 index 0000000000000000000000000000000000000000..93a9800b6993f3adb786c49d22ebb22e7129a7de --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..c649c12fcff68800d423bc63cd197990eb4d9897 --- /dev/null +++ b/src/backends/meta-input-capture-session.c @@ -0,0 +1,1449 @@ +/* + * 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 +#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" + +#include "meta-dbus-input-capture.h" + +#define META_INPUT_CAPTURE_SESSION_DBUS_PATH "/org/gnome/Mutter/InputCapture/Session" + +static GQuark quark_barrier_id; + +enum +{ + PROP_0, + + PROP_SESSION_MANAGER, + PROP_PEER_NAME, + PROP_ID, + + N_PROPS +}; + +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; + + MetaDbusSessionManager *session_manager; + + GDBusConnection *connection; + char *peer_name; + + char *session_id; + char *object_path; + + InputCaptureState state; + GHashTable *barriers; + + uint32_t zones_serial; + 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); + +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 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 +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, + MetaInputCaptureSession *session) +{ + MetaDBusInputCaptureSession *skeleton = + META_DBUS_INPUT_CAPTURE_SESSION (session); + MetaInputCapture *input_capture = + META_INPUT_CAPTURE (session->session_manager); + 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_input_capture_activate (input_capture, session); + + 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) +{ + MetaBackend *backend = + meta_dbus_session_manager_get_backend (session->session_manager); + GHashTableIter iter; + gpointer key, value; + + g_warn_if_fail (session->state == INPUT_CAPTURE_STATE_INIT); + + 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_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) +{ + switch (session->state) + { + 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: + g_warn_if_reached (); + return; + } + + 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) + release_remote_access_handle (session); +} + +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); + + 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); + 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; +} + +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) +{ + 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; +} + +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) +{ + 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->zones_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->state != INPUT_CAPTURE_STATE_INIT) + { + 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->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, + "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) +{ + 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); + meta_input_capture_session_deactivate (session); + + 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; +} + +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) +{ + 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->zones_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_clear_barriers = handle_clear_barriers; + 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; +} + +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_pointer (&session->barriers, g_hash_table_unref); + + g_clear_object (&session->handle); + 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, + 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->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; + + 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); + + quark_barrier_id = + g_quark_from_static_string ("meta-input-capture-barrier-id-quark"); +} + +static void +meta_input_capture_session_init (MetaInputCaptureSession *session) +{ +} + +char * +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: + 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: + 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 +meta_input_capture_session_notify_cancelled (MetaInputCaptureSession *session) +{ + meta_input_capture_session_disable (session); +} + +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 0000000000000000000000000000000000000000..4e94737fee3e18689d400accf20cf4ea44b1981e --- /dev/null +++ b/src/backends/meta-input-capture-session.h @@ -0,0 +1,47 @@ +/* + * 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); + +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 new file mode 100644 index 0000000000000000000000000000000000000000..a030a6698714fad658ea84dd47cbc72f8ceb79cf --- /dev/null +++ b/src/backends/meta-input-capture.c @@ -0,0 +1,232 @@ +/* + * 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-private.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" + +enum +{ + CANCELLED, +}; + +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; + + struct { + MetaInputCaptureEnable enable; + MetaInputCaptureDisable disable; + gpointer user_data; + } event_router; + + MetaInputCaptureSession *active_session; +}; + +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; +} + +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 new file mode 100644 index 0000000000000000000000000000000000000000..97dde42d95346cff42380248180bb8c6c8910b70 --- /dev/null +++ b/src/backends/meta-input-capture.h @@ -0,0 +1,51 @@ +/* + * 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 "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, + MetaDbusSessionManager) + +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/backends/meta-remote-access-controller-private.h b/src/backends/meta-remote-access-controller-private.h index 8c8a319c405d2dc68d4b8b08877e0c7305785097..be301a5e9979a3e6787b61651b4f1032a8d14d13 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 b2ac93d888cb742416828c32e1a95001f4fbdfb0..db0d1bd5d30cc7db0c0bf0e1365406f7cb59659f 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 caee7aa6a7a1ccf600943eb5ef6b83006023d5b8..d80493e5e91cf3a72c74902b55b37d06d0bb0ee2 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; @@ -196,9 +214,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 +226,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; } @@ -231,16 +254,18 @@ meta_remote_desktop_session_close (MetaRemoteDesktopSession *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 @@ -248,7 +273,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 @@ -273,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) @@ -373,7 +360,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 +392,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); @@ -1641,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) { @@ -1664,16 +1691,11 @@ 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; + iface->get_id = meta_remote_desktop_session_get_id; } static void @@ -1699,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", @@ -1726,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 * @@ -1748,7 +1839,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 7af9c48977aca681f37baeee2f0bc9ae7cec9756..f3aa2a428d104438383fcb6f007d8bb49f720620 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,10 +49,4 @@ 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); - #endif /* META_REMOTE_DESKTOP_SESSION_H */ diff --git a/src/backends/meta-remote-desktop.c b/src/backends/meta-remote-desktop.c index f0a499818423ab9401d60555484485787b5f88df..3e03dc712f960573ac35e5ff6c4aecb6ed88f45c 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_remote_desktop_session_close (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_remote_desktop_session_close (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 3a7f38563249af87eb405fb36f8bbce135a0f5d0..98bbb57c400096d37c83a336b9b527c1b5caa3b7 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 aa15481010f8c93714b361b1cbcb45657d358a27..ac6e891050163ee0b3e5c2271f185b59f859910d 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 d96fb6709f4f1f471ab7bdd3ade2d873d81a074e..f5d93ff58a337bfc2a6d3326de353bbf2e6144c0 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; @@ -129,9 +152,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; @@ -162,6 +186,14 @@ meta_screen_cast_session_close (MetaScreenCastSession *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) @@ -183,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 @@ -219,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) @@ -286,7 +350,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 +361,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 @@ -575,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; @@ -707,57 +771,79 @@ meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface) } static void -meta_screen_cast_session_client_vanished (MetaDbusSession *dbus_session) +meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { - meta_screen_cast_session_close (META_SCREEN_CAST_SESSION (dbus_session)); + iface->close = meta_screen_cast_session_close; + iface->get_id = meta_screen_cast_session_get_id; } static void -meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) +meta_screen_cast_session_finalize (GObject *object) { - iface->client_vanished = meta_screen_cast_session_client_vanished; -} + MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (object); -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; + g_clear_object (&session->handle); + g_free (session->peer_name); + g_free (session->object_path); + g_free (session->session_id); - 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); + G_OBJECT_CLASS (meta_screen_cast_session_parent_class)->finalize (object); +} - 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; +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); - return session; + 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_finalize (GObject *object) +meta_screen_cast_session_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (object); - g_clear_object (&session->handle); - g_free (session->peer_name); - g_free (session->object_path); - - G_OBJECT_CLASS (meta_screen_cast_session_parent_class)->finalize (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 @@ -771,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 @@ -818,7 +941,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 d9c3d0eccace248255977ca536945de753d0658f..599f6d25b93569f65941e9ba8452affe09a62d96 100644 --- a/src/backends/meta-screen-cast-session.h +++ b/src/backends/meta-screen-cast-session.h @@ -51,18 +51,11 @@ 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); 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 6162b8d276929ba94ef016719119fb6cf3a38cb0..76e8b35ca123b630a756f22792752d1cde7c837e 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_screen_cast_session_close (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,126 +189,50 @@ 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_screen_cast_session_close (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; } - static void meta_screen_cast_init (MetaScreenCast *screen_cast) { @@ -370,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 @@ -381,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 be297e28da5304eace472a079f8fe06446bdb7ac..a68c9d175f3766c6c106cdd00735c0329c73b208 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/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c index 2245387876630d9c45e3506035675c57d8ddc61a..acde26e8e652b5e04cc94ed75597c7c96645b42b 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)); } @@ -251,6 +252,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 +682,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 2e17c49c73ce424eabda57a43e40c09a9f244228..de1613a81632ac2bff08395f6d75d9cd416f95ab 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 @@ -102,18 +103,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 +134,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 +174,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 +184,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 +193,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 +255,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 +276,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; @@ -356,9 +358,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); @@ -468,18 +470,19 @@ 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); 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 +492,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); @@ -502,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, @@ -522,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; @@ -546,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; } @@ -573,7 +629,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,22 +637,27 @@ _meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) } static void -_meta_barrier_impl_native_release (MetaBarrierImpl *impl, - MetaBarrierEvent *event) +meta_barrier_impl_native_release (MetaBarrierImpl *impl, + MetaBarrierEvent *event) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); if (self->state == META_BARRIER_STATE_HELD && - event->event_id == self->trigger_serial) - self->state = META_BARRIER_STATE_RELEASE; + (!event || event->event_id == self->trigger_serial)) + { + self->state = META_BARRIER_STATE_RELEASE; + self->manager->stickied_barrier = NULL; + } } static void -_meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) +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); @@ -606,10 +667,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); @@ -631,9 +692,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/native/meta-seat-impl.c b/src/backends/native/meta-seat-impl.c index 624b2fe6f104634ac1df27487d62bac7c601748f..b391818cbdb92cd5b9ccc02482a87904e26948f2 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" @@ -571,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, @@ -581,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); } @@ -949,39 +958,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 +1455,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 +2632,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 +2693,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 +2888,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 +2935,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 d3e2ab86079d946721b5e671b9583afff1bd9f90..39345b22b94453722b0897153bcd15e662d315a7 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; @@ -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 c9ddcf63ea0c800dbce2ba3d899ebe7ad7eb7eeb..750d3c14c13e9c93d4230cc360753301e5b85eec 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/backends/x11/cm/meta-backend-x11-cm.c b/src/backends/x11/cm/meta-backend-x11-cm.c index ed2b7bb8bba119c82e05a6731a507a3b3d96f104..1d0d2b0d3216d3ad8dc1138855105c3a3fcba227 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) @@ -364,21 +379,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 +401,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 +426,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 @@ -523,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 056a16ad71c5011274c8107ef643497ea7c10f04..b9640ece6ab4c1c18b228a81466790a898cf8125 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 515cde91b495e544dda5b3c22e4a963d1e2cd56f..748ab3c119a33142e7bf7373c9adfa3da5959688 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 998aefb38f05a4e99869db7236e3d27ede50fb11..ff693175fbe4915ff31f60afdfa12d89ceffdaca 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; @@ -53,7 +59,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,38 +67,39 @@ _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; - Display *dpy = meta_x11_display_get_xdisplay (display->x11_display); + MetaBackend *backend = meta_barrier_get_backend (self->barrier); + 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)) + if (!event) { - XIBarrierReleasePointer (dpy, - META_VIRTUAL_CORE_POINTER_ID, - self->xbarrier, event->event_id); + g_warning ("X11 barriers always need barrier events to release"); + return; } + + XIBarrierReleasePointer (xdisplay, + META_VIRTUAL_CORE_POINTER_ID, + self->xbarrier, event->event_id); } 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; - Display *dpy; - - if (display == NULL) - return; - - dpy = meta_x11_display_get_xdisplay (display->x11_display); + 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); 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,34 +107,34 @@ 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; + MetaBorder *border; 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); - - allowed_motion_dirs = - meta_border_get_allows_directions (&barrier->priv->border); - self->xbarrier = XFixesCreatePointerBarrier (dpy, 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, + backend = meta_barrier_get_backend (self->barrier); + backend_x11 = META_BACKEND_X11 (backend); + xdisplay = meta_backend_x11_get_xdisplay (backend_x11); + root = DefaultRootWindow (xdisplay); + + border = meta_barrier_get_border (barrier); + allowed_motion_dirs = meta_border_get_allows_directions (border); + self->xbarrier = XFixesCreatePointerBarrier (xdisplay, root, + border->line.a.x, + border->line.a.y, + border->line.b.x, + border->line.b.y, 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); } @@ -154,10 +161,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 (); @@ -167,15 +174,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 +190,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; @@ -201,12 +205,40 @@ 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 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 3562d106f54837a4839af976dc22e374881eb6c1..ae50fdccab27334ae7f3aa9ebd74732ae7c8203c 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 041b42860b355ac343e26c90fcf7e2c9c4e7f0f9..0f38f4ad210ae483d15ca27294fa6e82d9283a12 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/compositor/meta-dnd.c b/src/compositor/meta-dnd.c index 0edc797e70edce43e1bde6cd7806f63243c056ee..4053ccaeaf9a06ddd8104b1a26e956cf3cabf877 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 diff --git a/src/core/display-private.h b/src/core/display-private.h index e91d730352f1f85eff78a3233e42e347885b5417..2d1ac849273ed462d3d22eb360b5fe1425ce0e09 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); @@ -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 a1c5c2d9121aa80960c7a89fe6b4204efe9fef5c..32842263c10fde196c4ef411d59eabf7467dd860 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) @@ -829,14 +892,15 @@ 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; + 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); @@ -2827,13 +2897,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); } @@ -2863,20 +2933,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 + MetaContext *context = meta_display_get_context (display); + MetaBackend *backend = meta_context_get_backend (context); - 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); - } - - return FALSE; + return !!(meta_backend_get_capabilities (backend) & + META_BACKEND_CAPABILITY_BARRIERS); } /** diff --git a/src/core/events.c b/src/core/events.c index 0dc3a73222c3a3311038b64b379dbc51128ca1ae..0bc4c27d61700596ed88cd587b16f049d36a543a 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,16 @@ 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); + sequence = clutter_event_get_event_sequence (event); /* Set the pointer emulating sequence on touch begin, if eligible */ @@ -312,7 +331,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 +417,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; diff --git a/src/core/keybindings-private.h b/src/core/keybindings-private.h index 56792c200a955f219e010803ba5470faa7bd195c..b119c5559cc59a1466c9d8449a9ccb907d72f4e8 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; }; @@ -157,4 +158,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 975e68897a25062e2d5dc9f8940366230814f14c..838d759e6458633b12ef8425ff042fe5c4a45045 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, @@ -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) @@ -2878,11 +2883,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 +2896,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 +2932,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 +2948,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 +2964,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 +3038,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 +3137,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 +3157,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 +3182,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 +3206,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 +3241,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 +3254,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 +3282,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 +3295,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 +3330,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 +3368,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 +3384,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 +3397,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 +3417,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 +3472,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 +3495,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 +3541,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 +3590,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 +3610,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 +3624,21 @@ handle_rotate_monitor (MetaDisplay *display, } static void -handle_restore_shortcuts (MetaDisplay *display, - MetaWindow *window, - ClutterKeyEvent *event, - MetaKeyBinding *binding, - gpointer dummy) +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, + const ClutterKeyEvent *event, + MetaKeyBinding *binding, + gpointer user_data) { ClutterInputDevice *source; @@ -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/meson.build b/src/meson.build index 7b456f524fb5e16904b6f2c27b7e88a8ec7b0506..1a00f79123b07c4b45cb1422b22abdd52da5d569 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 @@ -163,7 +164,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()), ] @@ -194,14 +194,24 @@ 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-fd-source.c', + 'backends/meta-fd-source.h', 'backends/meta-gpu.c', 'backends/meta-gpu.h', 'backends/meta-idle-monitor.c', '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', @@ -490,8 +500,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', @@ -801,6 +809,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 @@ -901,6 +910,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 = [] @@ -1023,7 +1039,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 +1068,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 +1084,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/meta/barrier.h b/src/meta/barrier.h index 72ed33cfc7ab6d62ef5dbec4d2f496b650a9db09..412b25d5242f415b6b8c70643c503400761b3404 100644 --- a/src/meta/barrier.h +++ b/src/meta/barrier.h @@ -9,47 +9,45 @@ 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; +/** + * MetaBarrierDirection: + * @META_BARRIER_DIRECTION_POSITIVE_X: Positive direction in the X axis + * @META_BARRIER_DIRECTION_POSITIVE_Y: Positive direction in the Y axis + * @META_BARRIER_DIRECTION_NEGATIVE_X: Negative direction in the X axis + * @META_BARRIER_DIRECTION_NEGATIVE_Y: Negative direction in the Y axis + */ -typedef struct _MetaBarrierEvent MetaBarrierEvent; +/* Keep in sync with XFixes */ +typedef enum +{ + META_BARRIER_DIRECTION_POSITIVE_X = 1 << 0, + META_BARRIER_DIRECTION_POSITIVE_Y = 1 << 1, + META_BARRIER_DIRECTION_NEGATIVE_X = 1 << 2, + META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, +} MetaBarrierDirection; -/** - * MetaBarrier: - * - * The MetaBarrier structure contains - * only private data and should be accessed using the provided API - * - **/ -struct _MetaBarrier +typedef enum { - GObject parent; + META_BARRIER_FLAG_NONE = 1 << 0, + META_BARRIER_FLAG_STICKY = 1 << 1, +} MetaBarrierFlags; - MetaBarrierPrivate *priv; -}; +#define META_TYPE_BARRIER (meta_barrier_get_type ()) +META_EXPORT +G_DECLARE_FINAL_TYPE (MetaBarrier, meta_barrier, + META, BARRIER, GObject) -/** - * MetaBarrierClass: - * - * The MetaBarrierClass structure contains only - * private data. - */ -struct _MetaBarrierClass -{ - /*< private >*/ - GObjectClass parent_class; -}; +typedef struct _MetaBarrierEvent MetaBarrierEvent; META_EXPORT -GType meta_barrier_get_type (void) G_GNUC_CONST; +MetaBarrier * meta_barrier_new (MetaBackend *backend, + int x1, + int y1, + int x2, + int y2, + MetaBarrierDirection directions, + MetaBarrierFlags flags, + GError **error); META_EXPORT gboolean meta_barrier_is_active (MetaBarrier *barrier); @@ -61,23 +59,6 @@ META_EXPORT void meta_barrier_release (MetaBarrier *barrier, MetaBarrierEvent *event); -/** - * MetaBarrierDirection: - * @META_BARRIER_DIRECTION_POSITIVE_X: Positive direction in the X axis - * @META_BARRIER_DIRECTION_POSITIVE_Y: Positive direction in the Y axis - * @META_BARRIER_DIRECTION_NEGATIVE_X: Negative direction in the X axis - * @META_BARRIER_DIRECTION_NEGATIVE_Y: Negative direction in the Y axis - */ - -/* Keep in sync with XFixes */ -typedef enum -{ - META_BARRIER_DIRECTION_POSITIVE_X = 1 << 0, - META_BARRIER_DIRECTION_POSITIVE_Y = 1 << 1, - META_BARRIER_DIRECTION_NEGATIVE_X = 1 << 2, - META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, -} MetaBarrierDirection; - /** * MetaBarrierEvent: * @event_id: A unique integer ID identifying a diff --git a/src/meta/display.h b/src/meta/display.h index e59bd039392d152c971e09b23b2b607a40c64e49..206e08c0c0d2999d9f966225e3bd36abeef0ef81 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 cfb042a726d6d5865ad061788fdd53f0770c430f..05fb47234f68f98e4b147e2156a47464b2f031ac 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/meta/prefs.h b/src/meta/prefs.h index 227de68bf2e17afe4ab6702d2f53bae041a7e6d6..3d3256856a26dceab8e84a3a423d400911fbe870 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; /** @@ -456,11 +457,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); diff --git a/src/meta/util.h b/src/meta/util.h index c44a63fc8735b3b85610a6bb3a116932122b82ba..c8628bcf9ce825d240246cff7ea410005d382fa5 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 diff --git a/src/tests/clutter/conform/grab.c b/src/tests/clutter/conform/grab.c index b5ee3f41f3e4f6066df336908baf4ba0adeabee3..1e28606a44c4b1ada0da47bf8d267e886b439141 100644 --- a/src/tests/clutter/conform/grab.c +++ b/src/tests/clutter/conform/grab.c @@ -2,6 +2,8 @@ #include #include "tests/clutter-test-utils.h" +#include "clutter/clutter-event-private.h" +#include "clutter/clutter-stage-private.h" typedef struct { @@ -49,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'", - entry.type == CLUTTER_ENTER ? "ENTER" : "LEAVE", + clutter_event_get_name (event), entry.name); g_array_append_val (events, entry); + break; } return CLUTTER_EVENT_PROPAGATE; @@ -540,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) @@ -551,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); ) diff --git a/src/tests/input-capture-test-client.c b/src/tests/input-capture-test-client.c new file mode 100644 index 0000000000000000000000000000000000000000..a767bd2a5d381be743355bb538cb43d808cf48a5 --- /dev/null +++ b/src/tests/input-capture-test-client.c @@ -0,0 +1,997 @@ +/* + * 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 +#include +#include +#include + +#include "backends/meta-fd-source.h" + +#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 _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; + +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); +} + +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) +{ + 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; + + 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); + + g_object_unref (session->proxy); + 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) +{ + 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 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_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) +{ + 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 +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) +{ + 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); +} + +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 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 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 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 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, + }, + { + .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); + + 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; + void (* func) (void); +} test_cases[] = { + { "sanity", test_sanity, }, + { "zones", test_zones, }, + { "barriers", test_barriers, }, + { "clear-barriers", test_clear_barriers, }, + { "cancel-keybinding", test_cancel_keybinding, }, + { "events", test_events, }, + { "a11y", test_a11y, }, +}; + +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) + { + 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; + } + } + + 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 0000000000000000000000000000000000000000..8de51747c224c9c7f05225a8b1bf2a64f5025371 --- /dev/null +++ b/src/tests/input-capture-tests.c @@ -0,0 +1,609 @@ +/* + * 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 "backends/meta-backend-private.h" +#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; + GDataOutputStream *line_writer; +} 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; + 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 | + G_SUBPROCESS_FLAGS_STDIN_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); + + 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; +} + +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_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, + 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 +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) +{ + 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 +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 +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 +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 +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 +on_a11y_timeout_started (ClutterSeat *seat, + ClutterInputDevice *device, + ClutterPointerA11yTimeoutType timeout_type, + unsigned int delay_ms, + int *a11y_started_counter) +{ + (*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) +{ + 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; + 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 (), + 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); + 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 +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); + 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); + 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); + g_test_add_func ("/backends/native/input-capture/a11y", + meta_test_input_capture_a11y); +} + +int +main (int argc, + char **argv) +{ + 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); + 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 2cb05d9668eb0773a919914059ff7d4234341bd7..3499392ffa93fd4dd0c039fe6a5e3173fbd9c8f0 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, @@ -211,6 +223,26 @@ 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', + '../backends/meta-fd-source.c', + '../backends/meta-fd-source.h', + 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, + libei_dep, + ], + install: have_installed_tests, + install_dir: mutter_installed_tests_libexecdir, + ) + # Native backend tests test_cases += [ { @@ -244,6 +276,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 @@ -289,7 +326,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 +392,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, diff --git a/src/tests/meta-test-utils.c b/src/tests/meta-test-utils.c index 261d2c4191184892571a360efd9aa6bb0c08bd8d..ff47ce15790070573db800c957045b068c00ea2b 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 fe0bde766fa50cb24095f5eefca75e342b213882..7c26b0e6254267c13c2b79e5d060b7997cee0b65 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 () @@ -127,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 */ diff --git a/src/x11/events.c b/src/x11/events.c index 8882baac4ec2a22c2403679ba3d0663884e10f15..677d99c2bf905fb0dac86515a415ad5a1a81098b 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 d53073e11106fb65f6e945722d0be8d588440613..9158387d6952bb0c857ad6413b75d52e6622f8af 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 5343633ce4c3deb10975e733da0fbf0edccb70c5..55b79c1621a4710fb685d6eb04b53b9b84f137eb 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); } @@ -390,8 +383,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,22 +399,30 @@ 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; + int major = 2, minor = 2; gboolean has_xi = FALSE; if (XQueryExtension (x11_display->xdisplay, @@ -429,19 +431,22 @@ 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) has_xi = TRUE; - - if (version >= 23) - x11_display->have_xinput_23 = TRUE; } } 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; } /* @@ -792,11 +797,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 | @@ -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; @@ -1247,8 +1248,12 @@ meta_x11_display_new (MetaDisplay *display, GError **error) 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", @@ -1413,7 +1418,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 +1426,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