clutter/stage: Add input-only grabs

An input only grab is a ClutterGrab on the stage that doesn't have an
explicit actor associated with it. This is useful for cases where event
should be captured as if focus was stolen to some mysterious place that
doesn't have anything in the scene graph that represents it.

Internally, it's implemented using a 0x0 sized actor attached directly
to the stage, and a clutter action that consumes the events. An
input-only grab takes a handler, user data and a destroy function for
the user data. These are handed to the ClutterAction, which handles the
actual event handling.

Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2628>
This commit is contained in:
Jonas Ådahl
2022-04-07 12:10:13 +02:00
committed by Carlos Garnacho
parent a79f35612b
commit e917b7de43
8 changed files with 431 additions and 22 deletions

View File

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