input-capture: Hook up barrier adding
Adding a barrier and later enabling the input capture session will create MetaBarrier instances for each added input capture barrier. The barriers are created as "sticky" which means that when a pointer hits the barrier, it'll stick to the point of entry, until it's released. The input capture session is also turned into a state machine with explicit state, to more easily track things. Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/2628>
This commit is contained in:

committed by
Carlos Garnacho

parent
a8f461a4eb
commit
1d1983edb5
@@ -21,6 +21,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <gio/gunixinputstream.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "meta-dbus-input-capture.h"
|
||||
@@ -54,6 +55,8 @@ typedef struct _InputCaptureSession
|
||||
unsigned int serial;
|
||||
} InputCaptureSession;
|
||||
|
||||
static GDataInputStream *stdin_reader;
|
||||
|
||||
static void
|
||||
ping_mutter (InputCaptureSession *session)
|
||||
{
|
||||
@@ -80,6 +83,54 @@ write_state (InputCaptureSession *session,
|
||||
fflush (stdout);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GMainLoop *loop;
|
||||
const char *expected_state;
|
||||
} WaitData;
|
||||
|
||||
static void
|
||||
on_line_read (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
gpointer user_data)
|
||||
{
|
||||
WaitData *data = user_data;
|
||||
g_autofree char *line = NULL;
|
||||
g_autoptr (GError) error = NULL;
|
||||
|
||||
line =
|
||||
g_data_input_stream_read_line_finish (G_DATA_INPUT_STREAM (source_object),
|
||||
res, NULL, &error);
|
||||
if (error)
|
||||
g_error ("Failed to read line from test client: %s", error->message);
|
||||
if (!line)
|
||||
g_error ("Unexpected EOF");
|
||||
|
||||
g_assert_cmpstr (data->expected_state, ==, line);
|
||||
|
||||
g_main_loop_quit (data->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
wait_for_state (InputCaptureSession *session,
|
||||
const char *expected_state)
|
||||
{
|
||||
WaitData data;
|
||||
|
||||
data.loop = g_main_loop_new (NULL, FALSE);
|
||||
data.expected_state = expected_state;
|
||||
|
||||
g_data_input_stream_read_line_async (stdin_reader,
|
||||
G_PRIORITY_DEFAULT,
|
||||
NULL,
|
||||
on_line_read,
|
||||
&data);
|
||||
|
||||
g_main_loop_run (data.loop);
|
||||
g_main_loop_unref (data.loop);
|
||||
ping_mutter (session);
|
||||
}
|
||||
|
||||
static InputCapture *
|
||||
input_capture_new (void)
|
||||
{
|
||||
@@ -174,6 +225,31 @@ input_capture_session_get_zones (InputCaptureSession *session)
|
||||
return zones;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
input_capture_session_add_barrier (InputCaptureSession *session,
|
||||
int x1,
|
||||
int y1,
|
||||
int x2,
|
||||
int y2)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
unsigned int barrier_id;
|
||||
|
||||
if (!meta_dbus_input_capture_session_call_add_barrier_sync (
|
||||
session->proxy,
|
||||
session->serial,
|
||||
g_variant_new ("(iiii)", x1, y1, x2, y2),
|
||||
&barrier_id,
|
||||
NULL,
|
||||
&error))
|
||||
{
|
||||
g_warning ("Failed to add barrier: %s", error->message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return barrier_id;
|
||||
}
|
||||
|
||||
static void
|
||||
input_capture_session_enable (InputCaptureSession *session)
|
||||
{
|
||||
@@ -194,6 +270,21 @@ input_capture_session_disable (InputCaptureSession *session)
|
||||
g_warning ("Failed to disable session: %s", error->message);
|
||||
}
|
||||
|
||||
static void
|
||||
input_capture_session_release (InputCaptureSession *session,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
GVariant *position;
|
||||
|
||||
position = g_variant_new ("(dd)", x, y);
|
||||
if (!meta_dbus_input_capture_session_call_release_sync (session->proxy,
|
||||
position,
|
||||
NULL, &error))
|
||||
g_warning ("Failed to release pointer: %s", error->message);
|
||||
}
|
||||
|
||||
static void
|
||||
test_sanity (void)
|
||||
{
|
||||
@@ -296,6 +387,98 @@ test_zones (void)
|
||||
input_capture_session_close (session);
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int activated_barrier_id;
|
||||
double activated_x;
|
||||
double activated_y;
|
||||
unsigned int activated_serial;
|
||||
} BarriersTestData;
|
||||
|
||||
static void
|
||||
on_activated (MetaDBusInputCaptureSession *proxy,
|
||||
unsigned int barrier_id,
|
||||
unsigned int serial,
|
||||
GVariant *cursor_position,
|
||||
BarriersTestData *data)
|
||||
{
|
||||
g_assert_cmpuint (data->activated_barrier_id, ==, 0);
|
||||
|
||||
data->activated_barrier_id = barrier_id;
|
||||
data->activated_serial = serial;
|
||||
g_variant_get (cursor_position, "(dd)",
|
||||
&data->activated_x, &data->activated_y);
|
||||
}
|
||||
|
||||
static void
|
||||
test_barriers (void)
|
||||
{
|
||||
InputCapture *input_capture;
|
||||
InputCaptureSession *session;
|
||||
g_autolist (Zone) zones = NULL;
|
||||
unsigned int barrier1, barrier2;
|
||||
BarriersTestData data = {};
|
||||
unsigned int prev_activated_serial;
|
||||
|
||||
input_capture = input_capture_new ();
|
||||
session = input_capture_create_session (input_capture);
|
||||
|
||||
zones = input_capture_session_get_zones (session);
|
||||
|
||||
/*
|
||||
* +-------------+--------------+
|
||||
* || | |
|
||||
* ||<--B#1 | |
|
||||
* || | B#2 |
|
||||
* +-------------+ | |
|
||||
* | V |
|
||||
* +==============+
|
||||
*/
|
||||
barrier1 = input_capture_session_add_barrier (session, 0, 0, 0, 600);
|
||||
barrier2 = input_capture_session_add_barrier (session, 800, 768, 1824, 768);
|
||||
|
||||
g_assert_cmpuint (barrier1, !=, 0);
|
||||
g_assert_cmpuint (barrier2, !=, 0);
|
||||
g_assert_cmpuint (barrier1, !=, barrier2);
|
||||
|
||||
g_signal_connect (session->proxy, "activated",
|
||||
G_CALLBACK (on_activated), &data);
|
||||
|
||||
input_capture_session_enable (session);
|
||||
|
||||
write_state (session, "1");
|
||||
|
||||
while (data.activated_barrier_id == 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
|
||||
g_assert_cmpuint (data.activated_serial, !=, 0);
|
||||
g_assert_cmpuint (data.activated_barrier_id, ==, barrier1);
|
||||
g_assert_cmpfloat_with_epsilon (data.activated_x, 0.0, DBL_EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (data.activated_y, 15.0, DBL_EPSILON);
|
||||
|
||||
wait_for_state (session, "1");
|
||||
|
||||
input_capture_session_release (session, 200, 150);
|
||||
|
||||
write_state (session, "2");
|
||||
|
||||
prev_activated_serial = data.activated_serial;
|
||||
|
||||
data = (BarriersTestData) {};
|
||||
while (data.activated_barrier_id == 0)
|
||||
g_main_context_iteration (NULL, TRUE);
|
||||
g_assert_cmpuint (data.activated_serial, !=, 0);
|
||||
g_assert_cmpuint (data.activated_serial, !=, prev_activated_serial);
|
||||
g_assert_cmpuint (data.activated_barrier_id, ==, barrier2);
|
||||
g_assert_cmpfloat_with_epsilon (data.activated_x, 1000.0, DBL_EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (data.activated_y, 768.0, DBL_EPSILON);
|
||||
|
||||
input_capture_session_release (session, 1200, 700);
|
||||
write_state (session, "3");
|
||||
|
||||
input_capture_session_close (session);
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
const char *name;
|
||||
@@ -303,6 +486,7 @@ static const struct
|
||||
} test_cases[] = {
|
||||
{ "sanity", test_sanity, },
|
||||
{ "zones", test_zones, },
|
||||
{ "barriers", test_barriers, },
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -330,7 +514,16 @@ main (int argc,
|
||||
{
|
||||
if (g_strcmp0 (test_cases[i].name, test_case) == 0)
|
||||
{
|
||||
g_autoptr (GInputStream) stdin_stream = NULL;
|
||||
|
||||
stdin_stream = g_unix_input_stream_new (fileno (stdin), FALSE);
|
||||
stdin_reader = g_data_input_stream_new (stdin_stream);
|
||||
|
||||
test_cases[i].func ();
|
||||
|
||||
g_clear_object (&stdin_reader);
|
||||
g_clear_object (&stdin_stream);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
#include "backends/meta-backend-private.h"
|
||||
#include "meta-test/meta-context-test.h"
|
||||
#include "tests/meta-test-utils.h"
|
||||
|
||||
@@ -31,6 +32,7 @@ typedef struct _InputCaptureTestClient
|
||||
char *path;
|
||||
GMainLoop *main_loop;
|
||||
GDataInputStream *line_reader;
|
||||
GDataOutputStream *line_writer;
|
||||
} InputCaptureTestClient;
|
||||
|
||||
static MetaContext *test_context;
|
||||
@@ -45,13 +47,16 @@ input_capture_test_client_new (const char *test_case)
|
||||
InputCaptureTestClient *test_client;
|
||||
GInputStream *stdout_stream;
|
||||
GDataInputStream *line_reader;
|
||||
GOutputStream *stdin_stream;
|
||||
GDataOutputStream *line_writer;
|
||||
|
||||
test_client_path = g_test_build_filename (G_TEST_BUILT,
|
||||
"src",
|
||||
"tests",
|
||||
"mutter-input-capture-test-client",
|
||||
NULL);
|
||||
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE);
|
||||
launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
|
||||
G_SUBPROCESS_FLAGS_STDIN_PIPE);
|
||||
subprocess = g_subprocess_launcher_spawn (launcher,
|
||||
&error,
|
||||
test_client_path,
|
||||
@@ -63,10 +68,14 @@ input_capture_test_client_new (const char *test_case)
|
||||
stdout_stream = g_subprocess_get_stdout_pipe (subprocess);
|
||||
line_reader = g_data_input_stream_new (stdout_stream);
|
||||
|
||||
stdin_stream = g_subprocess_get_stdin_pipe (subprocess);
|
||||
line_writer = g_data_output_stream_new (stdin_stream);
|
||||
|
||||
test_client = g_new0 (InputCaptureTestClient, 1);
|
||||
test_client->subprocess = subprocess;
|
||||
test_client->main_loop = g_main_loop_new (NULL, FALSE);
|
||||
test_client->line_reader = line_reader;
|
||||
test_client->line_writer = line_writer;
|
||||
|
||||
return test_client;
|
||||
}
|
||||
@@ -118,6 +127,24 @@ input_capture_test_client_wait_for_state (InputCaptureTestClient *test_client,
|
||||
g_main_loop_unref (data.loop);
|
||||
}
|
||||
|
||||
static void
|
||||
input_capture_test_client_write_state (InputCaptureTestClient *test_client,
|
||||
const char *state)
|
||||
{
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autofree char *line = NULL;
|
||||
|
||||
line = g_strdup_printf ("%s\n", state);
|
||||
|
||||
if (!g_data_output_stream_put_string (test_client->line_writer,
|
||||
line, NULL, &error))
|
||||
g_error ("Failed to write state: %s", error->message);
|
||||
|
||||
if (!g_output_stream_flush (G_OUTPUT_STREAM (test_client->line_writer),
|
||||
NULL, &error))
|
||||
g_error ("Failed to flush state: %s", error->message);
|
||||
}
|
||||
|
||||
static void
|
||||
input_capture_test_client_finished (GObject *source_object,
|
||||
GAsyncResult *res,
|
||||
@@ -181,6 +208,83 @@ meta_test_input_capture_zones (void)
|
||||
input_capture_test_client_finish (test_client);
|
||||
}
|
||||
|
||||
static void
|
||||
assert_pointer_position (ClutterSeat *seat,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
graphene_point_t pos;
|
||||
|
||||
clutter_seat_query_state (seat,
|
||||
clutter_seat_get_pointer (seat),
|
||||
NULL, &pos, NULL);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (pos.x, x, DBL_EPSILON);
|
||||
g_assert_cmpfloat_with_epsilon (pos.y, y, DBL_EPSILON);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_test_input_capture_barriers (void)
|
||||
{
|
||||
MetaBackend *backend = meta_context_get_backend (test_context);
|
||||
ClutterSeat *seat = meta_backend_get_default_seat (backend);
|
||||
g_autoptr (MetaVirtualMonitor) virtual_monitor1 = NULL;
|
||||
g_autoptr (MetaVirtualMonitor) virtual_monitor2 = NULL;
|
||||
g_autoptr (ClutterVirtualInputDevice) virtual_pointer = NULL;
|
||||
InputCaptureTestClient *test_client;
|
||||
|
||||
virtual_monitor1 = meta_create_test_monitor (test_context, 800, 600, 20.0);
|
||||
virtual_monitor2 = meta_create_test_monitor (test_context, 1024, 768, 20.0);
|
||||
|
||||
virtual_pointer = clutter_seat_create_virtual_device (seat,
|
||||
CLUTTER_POINTER_DEVICE);
|
||||
clutter_virtual_input_device_notify_absolute_motion (virtual_pointer,
|
||||
g_get_monotonic_time (),
|
||||
10.0, 10.0);
|
||||
|
||||
test_client = input_capture_test_client_new ("barriers");
|
||||
input_capture_test_client_wait_for_state (test_client, "1");
|
||||
|
||||
clutter_virtual_input_device_notify_relative_motion (virtual_pointer,
|
||||
g_get_monotonic_time (),
|
||||
-20.0, 10.0);
|
||||
clutter_virtual_input_device_notify_relative_motion (virtual_pointer,
|
||||
g_get_monotonic_time (),
|
||||
-20.0, 10.0);
|
||||
clutter_virtual_input_device_notify_relative_motion (virtual_pointer,
|
||||
g_get_monotonic_time (),
|
||||
-20.0, 10.0);
|
||||
|
||||
meta_flush_input (test_context);
|
||||
meta_wait_for_paint (test_context);
|
||||
|
||||
assert_pointer_position (seat, 0.0, 15.0);
|
||||
|
||||
input_capture_test_client_write_state (test_client, "1");
|
||||
input_capture_test_client_wait_for_state (test_client, "2");
|
||||
|
||||
meta_flush_input (test_context);
|
||||
meta_wait_for_paint (test_context);
|
||||
|
||||
assert_pointer_position (seat, 200.0, 150.0);
|
||||
|
||||
clutter_virtual_input_device_notify_relative_motion (virtual_pointer,
|
||||
g_get_monotonic_time (),
|
||||
800.0, 300.0);
|
||||
meta_flush_input (test_context);
|
||||
|
||||
assert_pointer_position (seat, 1000.0, 450.0);
|
||||
|
||||
clutter_virtual_input_device_notify_relative_motion (virtual_pointer,
|
||||
g_get_monotonic_time (),
|
||||
0.0, 400.0);
|
||||
|
||||
input_capture_test_client_wait_for_state (test_client, "3");
|
||||
assert_pointer_position (seat, 1200.0, 700.0);
|
||||
|
||||
input_capture_test_client_finish (test_client);
|
||||
}
|
||||
|
||||
static void
|
||||
init_tests (void)
|
||||
{
|
||||
@@ -188,6 +292,8 @@ init_tests (void)
|
||||
meta_test_input_capture_sanity);
|
||||
g_test_add_func ("/backends/native/input-capture/zones",
|
||||
meta_test_input_capture_zones);
|
||||
g_test_add_func ("/backends/native/input-capture/barriers",
|
||||
meta_test_input_capture_barriers);
|
||||
}
|
||||
|
||||
int
|
||||
|
Reference in New Issue
Block a user