diff --git a/clutter/clutter/clutter-input-only-action.c b/clutter/clutter/clutter-input-only-action.c
new file mode 100644
index 000000000..98bd551e3
--- /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 000000000..22cb2e205
--- /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 000000000..bb1e709c0
--- /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 000000000..6217915c0
--- /dev/null
+++ b/clutter/clutter/clutter-input-only-actor.h
@@ -0,0 +1,36 @@
+/*
+ * Clutter.
+ *
+ * An OpenGL based 'interactive canvas' library.
+ *
+ * Copyright (C) 2022 Red Hat Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see .
+ */
+
+#ifndef CLUTTER_INPUT_ONLY_ACTOR_H
+#define CLUTTER_INPUT_ONLY_ACTOR_H
+
+#include "clutter/clutter.h"
+#include "clutter/clutter-stage-private.h"
+
+#define CLUTTER_TYPE_INPUT_ONLY_ACTOR (clutter_input_only_actor_get_type ())
+G_DECLARE_FINAL_TYPE (ClutterInputOnlyActor, clutter_input_only_actor,
+ CLUTTER, INPUT_ONLY_ACTOR, ClutterActor)
+
+ClutterInputOnlyActor * clutter_input_only_actor_new (ClutterEventHandler event_handler,
+ gpointer user_data,
+ GDestroyNotify destroy);
+
+#endif /* CLUTTER_INPUT_ONLY_ACTOR_H */
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index e88c17d37..fb973088c 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,
@@ -163,6 +165,12 @@ void clutter_stage_notify_action_implicit_grab (ClutterStage *self,
void clutter_stage_add_to_redraw_clip (ClutterStage *self,
ClutterPaintVolume *clip);
+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 048c46a58..b879db9c7 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -57,6 +57,7 @@
#include "clutter-frame.h"
#include "clutter-grab.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"
@@ -148,7 +149,10 @@ struct _ClutterGrab
{
grefcount ref_count;
ClutterStage *stage;
+
ClutterActor *actor;
+ gboolean owns_actor;
+
ClutterGrab *prev;
ClutterGrab *next;
};
@@ -3866,20 +3870,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): an opaque #ClutterGrab handle, drop
- * with [method@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;
@@ -3904,10 +3916,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;
@@ -3935,6 +3945,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): an opaque #ClutterGrab handle, drop
+ * with [method@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)
@@ -3991,6 +4038,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 8a96e3150..0a3270523 100644
--- a/clutter/clutter/meson.build
+++ b/clutter/clutter/meson.build
@@ -140,6 +140,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',
@@ -208,6 +210,8 @@ clutter_private_headers = [
'clutter-input-focus-private.h',
'clutter-input-method-private.h',
'clutter-input-pointer-a11y-private.h',
+ 'clutter-input-only-action.h',
+ 'clutter-input-only-actor.h',
'clutter-keymap-private.h',
'clutter-offscreen-effect-private.h',
'clutter-paint-context-private.h',
diff --git a/src/tests/clutter/conform/grab.c b/src/tests/clutter/conform/grab.c
index 4040773d1..1e28606a4 100644
--- a/src/tests/clutter/conform/grab.c
+++ b/src/tests/clutter/conform/grab.c
@@ -3,6 +3,7 @@
#include "tests/clutter-test-utils.h"
#include "clutter/clutter-event-private.h"
+#include "clutter/clutter-stage-private.h"
typedef struct
{
@@ -50,17 +51,30 @@ event_cb (ClutterActor *actor,
gpointer user_data)
{
GArray *events = user_data;
+ EventLog entry;
- if ((event->type == CLUTTER_ENTER ||
- event->type == CLUTTER_LEAVE) &&
- (event->any.flags & CLUTTER_EVENT_FLAG_GRAB_NOTIFY) != 0)
+ switch (event->type)
{
- EventLog entry = { clutter_actor_get_name (actor), event->type };
+ case CLUTTER_ENTER:
+ case CLUTTER_LEAVE:
+ if ((event->any.flags & CLUTTER_EVENT_FLAG_GRAB_NOTIFY) != 0)
+ {
+ entry = (EventLog) { clutter_actor_get_name (actor), event->type };
+ g_debug ("Event '%s' on actor '%s'",
+ clutter_event_get_name (event),
+ entry.name);
+ g_array_append_val (events, entry);
+ }
+ break;
+
+ default:
+ entry = (EventLog) { clutter_actor_get_name (actor), event->type };
g_debug ("Event '%s' on actor '%s'",
clutter_event_get_name (event),
entry.name);
g_array_append_val (events, entry);
+ break;
}
return CLUTTER_EVENT_PROPAGATE;
@@ -541,6 +555,107 @@ grab_key_focus_outside_grab (void)
test_data_shutdown (&data);
}
+static gboolean
+handle_input_only_event (const ClutterEvent *event,
+ gpointer user_data)
+{
+ GArray *events = user_data;
+ EventLog entry = { "input-only grab", event->type };
+
+ g_debug ("Input only grab event '%s'", clutter_event_get_name (event));
+ g_array_append_val (events, entry);
+
+ return CLUTTER_EVENT_PROPAGATE;
+}
+
+static gboolean
+last_event_is (GArray *events,
+ ClutterEventType event_type)
+{
+ EventLog *entry;
+
+ if (events->len == 0)
+ return FALSE;
+
+ entry = &g_array_index (events, EventLog, events->len - 1);
+ return entry->type == event_type;
+}
+
+static void
+grab_input_only (void)
+{
+ TestData data;
+ ClutterGrab *grab;
+ EventLog grab1_log[] = {
+ { "b", CLUTTER_LEAVE },
+ { "a", CLUTTER_LEAVE },
+ { "stage", CLUTTER_LEAVE },
+ { NULL, 0 },
+ };
+ EventLog grab2_log[] = {
+ { "input-only grab", CLUTTER_BUTTON_PRESS },
+ { "input-only grab", CLUTTER_BUTTON_RELEASE },
+ { NULL, 0 },
+ };
+ EventLog grab3_log[] = {
+ { "b", CLUTTER_ENTER },
+ { "a", CLUTTER_ENTER },
+ { "stage", CLUTTER_ENTER },
+ { NULL, 0 },
+ };
+ EventLog grab4_log[] = {
+ { "b", CLUTTER_BUTTON_PRESS },
+ { "a", CLUTTER_BUTTON_PRESS },
+ { "stage", CLUTTER_BUTTON_PRESS },
+ { "b", CLUTTER_BUTTON_RELEASE },
+ { "a", CLUTTER_BUTTON_RELEASE },
+ { "stage", CLUTTER_BUTTON_RELEASE },
+ { NULL, 0 },
+ };
+ ClutterSeat *seat;
+ g_autoptr (ClutterVirtualInputDevice) pointer = NULL;
+
+ seat = clutter_backend_get_default_seat (clutter_get_default_backend ());
+ pointer = clutter_seat_create_virtual_device (seat, CLUTTER_POINTER_DEVICE);
+
+ test_data_init (&data);
+
+ grab = clutter_stage_grab_input_only (CLUTTER_STAGE (data.stage),
+ handle_input_only_event,
+ data.events, NULL);
+ event_log_compare ((EventLog *) &grab1_log, data.events);
+
+ clutter_virtual_input_device_notify_button (pointer,
+ 0,
+ CLUTTER_BUTTON_PRIMARY,
+ CLUTTER_BUTTON_STATE_PRESSED);
+ clutter_virtual_input_device_notify_button (pointer,
+ 0,
+ CLUTTER_BUTTON_PRIMARY,
+ CLUTTER_BUTTON_STATE_RELEASED);
+
+ while (!last_event_is (data.events, CLUTTER_BUTTON_RELEASE))
+ g_main_context_iteration (NULL, TRUE);
+ event_log_compare ((EventLog *) &grab2_log, data.events);
+
+ clutter_grab_unref (grab);
+ event_log_compare ((EventLog *) &grab3_log, data.events);
+
+ clutter_virtual_input_device_notify_button (pointer,
+ 0,
+ CLUTTER_BUTTON_SECONDARY,
+ CLUTTER_BUTTON_STATE_PRESSED);
+ clutter_virtual_input_device_notify_button (pointer,
+ 0,
+ CLUTTER_BUTTON_SECONDARY,
+ CLUTTER_BUTTON_STATE_RELEASED);
+ while (!last_event_is (data.events, CLUTTER_BUTTON_RELEASE))
+ g_main_context_iteration (NULL, TRUE);
+ event_log_compare ((EventLog *) &grab4_log, data.events);
+
+ test_data_shutdown (&data);
+}
+
CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/grab/grab-under-pointer", grab_under_pointer)
CLUTTER_TEST_UNIT ("/grab/grab-under-pointers-parent", grab_under_pointers_parent)
@@ -552,4 +667,5 @@ CLUTTER_TEST_SUITE (
CLUTTER_TEST_UNIT ("/grab/grab-unordered-ungrab-2", grab_unordered_ungrab_2)
CLUTTER_TEST_UNIT ("/grab/key-focus-in-grab", grab_key_focus_in_grab);
CLUTTER_TEST_UNIT ("/grab/key-focus-outside-grab", grab_key_focus_outside_grab);
+ CLUTTER_TEST_UNIT ("/grab/input-only", grab_input_only);
)