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