16
16
* Authored by: Robert Carr <robert.carr@canonical.com>
17
17
* Andreas Pokorny <andreas.pokorny@canonical.com>
18
* Alexandros Frantzis <alexandros.frantzis@canonical.com>
21
#include "mir/shell/surface_coordinator_wrapper.h"
20
22
#include "mir/scene/surface_creation_parameters.h"
21
#include "mir/scene/placement_strategy.h"
22
23
#include "mir/scene/surface.h"
24
#include "mir/scene/session.h"
23
25
#include "src/server/scene/session_container.h"
24
#include "mir/scene/session.h"
25
#include "mir/shell/surface_coordinator_wrapper.h"
27
#include "mir_test_framework/in_process_server.h"
28
#include "mir_test_framework/using_stub_client_platform.h"
29
#include "mir_test_framework/fake_event_hub_server_configuration.h"
30
#include "mir_test_framework/declarative_placement_strategy.h"
31
#include "mir_test/wait_condition.h"
27
32
#include "mir_test/fake_event_hub.h"
28
#include "mir_test/wait_condition.h"
29
33
#include "mir_test/client_event_matchers.h"
30
#include "mir_test/barrier.h"
31
#include "mir_test_framework/deferred_in_process_server.h"
32
#include "mir_test_framework/input_testing_server_configuration.h"
33
#include "mir_test_framework/input_testing_client_configuration.h"
34
#include "mir_test_framework/declarative_placement_strategy.h"
35
#include "mir_test_framework/using_stub_client_platform.h"
34
#include "mir_test/spin_wait.h"
36
#include "mir_toolkit/mir_client_library.h"
38
#include <linux/input.h>
37
40
#include <gtest/gtest.h>
38
41
#include <gmock/gmock.h>
41
namespace mi = mir::input;
42
namespace mis = mi::synthesis;
44
namespace mt = mir::test;
45
namespace mtf = mir_test_framework;
46
namespace mis = mir::input::synthesis;
47
namespace mia = mir::input::android;
43
48
namespace msh = mir::shell;
44
49
namespace ms = mir::scene;
45
50
namespace geom = mir::geometry;
46
namespace mt = mir::test;
47
namespace mtd = mt::doubles;
48
namespace mtf = mir_test_framework;
52
struct ServerConfiguration : mtf::InputTestingServerConfiguration
54
mt::Barrier& input_cb_setup_fence;
56
static geom::Rectangle const display_bounds;
58
std::function<void(mtf::InputTestingServerConfiguration& server)> produce_events;
59
mtf::SurfaceGeometries client_geometries;
60
mtf::SurfaceDepths client_depths;
62
ServerConfiguration(mt::Barrier& input_cb_setup_fence) :
63
InputTestingServerConfiguration({display_bounds}),
64
input_cb_setup_fence(input_cb_setup_fence)
68
std::shared_ptr<ms::PlacementStrategy> the_placement_strategy() override
55
struct MockInputHandler
57
MOCK_METHOD1(handle_input, void(MirEvent const*));
62
InputClient(std::string const& connect_string, std::string const& client_name)
63
: connect_string{connect_string}, client_name{client_name},
64
client_thread{[this] { run(); }}
66
ready_to_accept_events.wait_for_at_most_seconds(5);
71
if (client_thread.joinable())
77
auto const connection =
78
mir_connect_sync(connect_string.c_str(), client_name.c_str());
80
MirSurfaceParameters const request_params =
83
surface_width, surface_height,
84
mir_pixel_format_abgr_8888,
85
mir_buffer_usage_hardware,
86
mir_display_output_id_invalid
89
mir_connection_create_surface_sync(connection, &request_params);
91
MirEventDelegate const event_delegate { handle_input, this };
92
mir_surface_set_event_handler(surface, &event_delegate);
93
mir_surface_swap_buffers_sync(surface);
95
wait_for_surface_to_become_focused_and_exposed(surface);
97
ready_to_accept_events.wake_up_everyone();
98
all_events_received.wait_for_at_most_seconds(10);
100
mir_surface_release_sync(surface);
101
mir_connection_release(connection);
104
static void handle_input(MirSurface*, MirEvent const* ev, void* context)
106
auto const client = static_cast<InputClient*>(context);
108
if (ev->type == mir_event_type_surface)
111
client->handler.handle_input(ev);
114
void wait_for_surface_to_become_focused_and_exposed(MirSurface* surface)
116
bool success = mt::spin_wait_for_condition_or_timeout(
119
return mir_surface_get_visibility(surface) == mir_surface_visibility_exposed &&
120
mir_surface_get_focus(surface) == mir_surface_focused;
122
std::chrono::seconds{5});
125
throw std::runtime_error("Timeout waiting for surface to become focused and exposed");
128
static int const surface_width = 100;
129
static int const surface_height = 100;
131
std::string const connect_string;
132
std::string const client_name;
134
std::thread client_thread;
135
MockInputHandler handler;
136
mir::test::WaitCondition all_events_received;
137
mir::test::WaitCondition ready_to_accept_events;
140
using ClientInputRegions = std::map<std::string, std::vector<geom::Rectangle>>;
142
struct RegionApplyingSurfaceCoordinator : msh::SurfaceCoordinatorWrapper
144
RegionApplyingSurfaceCoordinator(
145
std::shared_ptr<ms::SurfaceCoordinator> wrapped_coordinator,
146
ClientInputRegions const& client_input_regions)
147
: msh::SurfaceCoordinatorWrapper(wrapped_coordinator),
148
client_input_regions(client_input_regions)
152
std::shared_ptr<ms::Surface> add_surface(
153
ms::SurfaceCreationParameters const& params,
154
ms::Session* session) override
156
auto const surface = wrapped->add_surface(params, session);
158
if (client_input_regions.find(params.name) != client_input_regions.end())
159
surface->set_input_region(client_input_regions.at(params.name));
164
ClientInputRegions const& client_input_regions;
167
struct TestServerConfiguration : mtf::FakeEventHubServerConfiguration
169
TestServerConfiguration(geom::Rectangle const& screen_geometry)
170
: mtf::FakeEventHubServerConfiguration(
171
std::vector<geom::Rectangle>{screen_geometry})
175
std::shared_ptr<mir::scene::PlacementStrategy> the_placement_strategy() override
70
177
return std::make_shared<mtf::DeclarativePlacementStrategy>(
71
InputTestingServerConfiguration::the_placement_strategy(),
178
FakeEventHubServerConfiguration::the_placement_strategy(),
72
179
client_geometries, client_depths);
77
input_cb_setup_fence.ready();
78
produce_events(*this);
81
std::function<std::shared_ptr<ms::SurfaceCoordinator>(std::shared_ptr<ms::SurfaceCoordinator> const& wrapped)> scwrapper
82
= [](std::shared_ptr<ms::SurfaceCoordinator> const& wrapped) { return wrapped; };
84
182
std::shared_ptr<ms::SurfaceCoordinator>
85
183
wrap_surface_coordinator(std::shared_ptr<ms::SurfaceCoordinator> const& wrapped) override
87
return scwrapper(wrapped);
91
geom::Rectangle const ServerConfiguration::display_bounds = {{0, 0}, {1600, 1600}};
93
struct ClientConfig : mtf::InputTestingClientConfiguration
95
std::function<void(MockInputHandler&, mt::WaitCondition&)> expect_cb;
97
ClientConfig(std::string const& client_name, mt::Barrier& client_ready_fence)
98
: InputTestingClientConfiguration(client_name, client_ready_fence)
102
void tear_down() { if (thread.joinable()) thread.join(); }
104
void expect_input(MockInputHandler &handler, mt::WaitCondition& events_received) override
106
expect_cb(handler, events_received);
109
void exec() { thread = std::thread([this]{ mtf::InputTestingClientConfiguration::exec(); }); }
115
struct TestClientInput : mtf::DeferredInProcessServer
117
std::string const arbitrary_client_name{"input-test-client"};
118
std::string const test_client_name_1 = "1";
119
std::string const test_client_name_2 = "2";
121
mt::Barrier fence{2};
122
mt::WaitCondition second_client_done;
123
ServerConfiguration server_configuration{fence};
185
return std::make_shared<RegionApplyingSurfaceCoordinator>(
187
client_input_regions);
190
mtf::SurfaceGeometries client_geometries;
191
mtf::SurfaceDepths client_depths;
192
ClientInputRegions client_input_regions;
195
struct TestClientInput : mtf::InProcessServer
197
mir::DefaultServerConfiguration& server_config() override
199
return server_configuration_;
202
TestServerConfiguration& test_server_config()
204
return server_configuration_;
207
mir::input::android::FakeEventHub* fake_event_hub()
209
return server_configuration_.fake_event_hub;
212
std::string const test_client_name_1{"client1"};
213
std::string const test_client_name_2{"client2"};
214
geom::Rectangle const screen_geometry{{0, 0}, {1000, 800}};
215
TestServerConfiguration server_configuration_{screen_geometry};
124
216
mtf::UsingStubClientPlatform using_stub_client_platform;
126
mir::DefaultServerConfiguration& server_config() override { return server_configuration; }
128
ClientConfig client_config{arbitrary_client_name, fence};
129
ClientConfig client_config_1{test_client_name_1, fence};
130
ClientConfig client_config_2{test_client_name_2, fence};
134
DeferredInProcessServer::start_server();
135
server_configuration.exec();
138
void start_client(mtf::InputTestingClientConfiguration& config)
140
config.connect_string = new_connection();
146
client_config.tear_down();
147
client_config_1.tear_down();
148
client_config_2.tear_down();
149
server_configuration.on_exit();
150
DeferredInProcessServer::TearDown();
155
using namespace ::testing;
156
using MockHandler = mtf::InputTestingClientConfiguration::MockInputHandler;
159
221
TEST_F(TestClientInput, clients_receive_key_input)
161
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
163
int const num_events_produced = 3;
165
for (int i = 0; i < num_events_produced; i++)
166
server.fake_event_hub->synthesize_event(mis::a_key_down_event()
167
.of_scancode(KEY_ENTER));
172
client_config.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
176
EXPECT_CALL(handler, handle_input(mt::KeyDownEvent())).Times(2);
177
EXPECT_CALL(handler, handle_input(mt::KeyDownEvent())).Times(1)
178
.WillOnce(mt::WakeUp(&events_received));
181
start_client(client_config);
223
using namespace testing;
225
InputClient client{new_connection(), test_client_name_1};
227
EXPECT_CALL(client.handler, handle_input(mt::KeyDownEvent()))
230
.WillOnce(mt::WakeUp(&client.all_events_received));
232
int const num_events_produced = 3;
234
for (int i = 0; i < num_events_produced; i++)
235
fake_event_hub()->synthesize_event(
236
mis::a_key_down_event().of_scancode(KEY_ENTER));
184
239
TEST_F(TestClientInput, clients_receive_us_english_mapped_keys)
186
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
188
server.fake_event_hub->synthesize_event(mis::a_key_down_event()
189
.of_scancode(KEY_LEFTSHIFT));
190
server.fake_event_hub->synthesize_event(mis::a_key_down_event()
191
.of_scancode(KEY_4));
195
client_config.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
199
EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_L)))).Times(1);
200
EXPECT_CALL(handler, handle_input(AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_dollar)))).Times(1)
201
.WillOnce(mt::WakeUp(&events_received));
203
start_client(client_config);
241
using namespace testing;
243
InputClient client{new_connection(), test_client_name_1};
247
EXPECT_CALL(client.handler,
249
AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_Shift_L))));
250
EXPECT_CALL(client.handler,
252
AllOf(mt::KeyDownEvent(), mt::KeyOfSymbol(XKB_KEY_dollar))))
253
.WillOnce(mt::WakeUp(&client.all_events_received));
255
fake_event_hub()->synthesize_event(
256
mis::a_key_down_event().of_scancode(KEY_LEFTSHIFT));
257
fake_event_hub()->synthesize_event(
258
mis::a_key_down_event().of_scancode(KEY_4));
206
261
TEST_F(TestClientInput, clients_receive_motion_inside_window)
208
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
210
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(mtf::InputTestingClientConfiguration::surface_width - 1,
211
mtf::InputTestingClientConfiguration::surface_height - 1));
212
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,2));
216
client_config.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
220
// We should see the cursor enter
221
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1);
222
EXPECT_CALL(handler, handle_input(
223
mt::MotionEventWithPosition(mtf::InputTestingClientConfiguration::surface_width - 1,
224
mtf::InputTestingClientConfiguration::surface_height - 1))).Times(1)
225
.WillOnce(mt::WakeUp(&events_received));
226
// But we should not receive an event for the second movement outside of our surface!
228
start_client(client_config);
263
using namespace testing;
265
InputClient client{new_connection(), test_client_name_1};
269
// We should see the cursor enter
270
EXPECT_CALL(client.handler, handle_input(mt::HoverEnterEvent()));
271
EXPECT_CALL(client.handler,
273
mt::MotionEventWithPosition(
274
InputClient::surface_width - 1,
275
InputClient::surface_height - 1)))
276
.WillOnce(mt::WakeUp(&client.all_events_received));
277
// But we should not receive an event for the second movement outside of our surface!
279
fake_event_hub()->synthesize_event(
280
mis::a_motion_event().with_movement(
281
InputClient::surface_width - 1,
282
InputClient::surface_height - 1));
283
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(2,2));
231
286
TEST_F(TestClientInput, clients_receive_button_events_inside_window)
233
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
235
server.fake_event_hub->synthesize_event(mis::a_button_down_event()
236
.of_button(BTN_LEFT).with_action(mis::EventAction::Down));
240
client_config.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
244
// The cursor starts at (0, 0).
245
EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(0, 0))).Times(1)
246
.WillOnce(mt::WakeUp(&events_received));
248
start_client(client_config);
288
using namespace testing;
290
InputClient client{new_connection(), test_client_name_1};
292
// The cursor starts at (0, 0).
293
EXPECT_CALL(client.handler, handle_input(mt::ButtonDownEvent(0, 0)))
294
.WillOnce(mt::WakeUp(&client.all_events_received));
296
fake_event_hub()->synthesize_event(
297
mis::a_button_down_event()
299
.with_action(mis::EventAction::Down));
251
302
TEST_F(TestClientInput, multiple_clients_receive_motion_inside_windows)
253
static int const screen_width = 1000;
254
static int const screen_height = 800;
255
static int const client_height = screen_height/2;
256
static int const client_width = screen_width/2;
259
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
261
// In the bounds of the first surface
262
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2-1, screen_height/2-1));
263
// In the bounds of the second surface
264
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2, screen_height/2));
266
server_configuration.client_geometries[test_client_name_1] = {{0, 0}, {client_width, client_height}};
267
server_configuration.client_geometries[test_client_name_2] = {{screen_width/2, screen_height/2}, {client_width, client_height}};
270
client_config_1.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
273
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1);
274
EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1);
275
EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(1)
276
.WillOnce(mt::WakeUp(&events_received));
278
client_config_2.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
281
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1);
282
EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1)
283
.WillOnce(mt::WakeUp(&events_received));
286
start_client(client_config_1);
287
start_client(client_config_2);
292
struct RegionApplyingSurfaceCoordinator : msh::SurfaceCoordinatorWrapper
294
RegionApplyingSurfaceCoordinator(std::shared_ptr<ms::SurfaceCoordinator> wrapped_coordinator,
295
std::initializer_list<geom::Rectangle> const& input_rectangles)
296
: msh::SurfaceCoordinatorWrapper(wrapped_coordinator),
297
input_rectangles(input_rectangles)
301
std::shared_ptr<ms::Surface> add_surface(
302
ms::SurfaceCreationParameters const& params,
303
ms::Session* session) override
305
auto surface = wrapped->add_surface(params, session);
307
surface->set_input_region(input_rectangles);
312
std::vector<geom::Rectangle> const input_rectangles;
304
using namespace testing;
306
int const screen_width = screen_geometry.size.width.as_int();
307
int const screen_height = screen_geometry.size.height.as_int();
308
int const client_height = screen_height / 2;
309
int const client_width = screen_width / 2;
311
test_server_config().client_geometries[test_client_name_1] =
312
{{0, 0}, {client_width, client_height}};
313
test_server_config().client_geometries[test_client_name_2] =
314
{{screen_width / 2, screen_height / 2}, {client_width, client_height}};
316
InputClient client1{new_connection(), test_client_name_1};
317
InputClient client2{new_connection(), test_client_name_2};
321
EXPECT_CALL(client1.handler, handle_input(mt::HoverEnterEvent()));
322
EXPECT_CALL(client1.handler,
324
mt::MotionEventWithPosition(client_width - 1, client_height - 1)));
325
EXPECT_CALL(client1.handler, handle_input(mt::HoverExitEvent()))
326
.WillOnce(mt::WakeUp(&client1.all_events_received));
331
EXPECT_CALL(client2.handler, handle_input(mt::HoverEnterEvent()));
332
EXPECT_CALL(client2.handler,
334
mt::MotionEventWithPosition(client_width - 1, client_height - 1)))
335
.WillOnce(mt::WakeUp(&client2.all_events_received));
338
// In the bounds of the first surface
339
fake_event_hub()->synthesize_event(
340
mis::a_motion_event().with_movement(screen_width / 2 - 1, screen_height / 2 - 1));
341
// In the bounds of the second surface
342
fake_event_hub()->synthesize_event(
343
mis::a_motion_event().with_movement(screen_width / 2, screen_height / 2));
316
346
TEST_F(TestClientInput, clients_do_not_receive_motion_outside_input_region)
318
static int const screen_width = 100;
319
static int const screen_height = 100;
321
static std::initializer_list<geom::Rectangle> client_input_regions{
322
{geom::Point{0, 0}, {screen_width-80, screen_height}},
323
{geom::Point{screen_width-20, 0}, {screen_width-80, screen_height}}
326
server_configuration.scwrapper = [&](std::shared_ptr<ms::SurfaceCoordinator> const& wrapped)
327
-> std::shared_ptr<ms::SurfaceCoordinator>
329
return std::make_shared<RegionApplyingSurfaceCoordinator>(wrapped, client_input_regions);
332
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
334
// First we will move the cursor in to the input region on the left side of the window. We should see a click here
335
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 1));
336
server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
337
server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
338
// Now in to the dead zone in the center of the window. We should not see a click here.
339
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(49, 49));
340
server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
341
server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
342
// Now in to the right edge of the window, in the right input region. Again we should see a click
343
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(49, 49));
344
server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
345
server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
349
client_config.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
351
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
352
EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
353
EXPECT_CALL(handler, handle_input(mt::MovementEvent())).Times(AnyNumber());
356
// We should see two of the three button pairs.
358
EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(1, 1))).Times(1);
359
EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(1, 1))).Times(1);
360
EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(99, 99))).Times(1);
361
EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(99, 99))).Times(1)
362
.WillOnce(mt::WakeUp(&events_received));
365
start_client(client_config);
348
using namespace testing;
350
int const client_height = InputClient::surface_height;
351
int const client_width = InputClient::surface_width;
353
test_server_config().client_input_regions[test_client_name_1] = {
354
{{0, 0}, {client_width - 80, client_height}},
355
{{client_width - 20, 0}, {client_width - 80, client_height}}};
357
InputClient client{new_connection(), test_client_name_1};
359
EXPECT_CALL(client.handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
360
EXPECT_CALL(client.handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
361
EXPECT_CALL(client.handler, handle_input(mt::MovementEvent())).Times(AnyNumber());
364
// We should see two of the three button pairs.
366
EXPECT_CALL(client.handler, handle_input(mt::ButtonDownEvent(1, 1)));
367
EXPECT_CALL(client.handler, handle_input(mt::ButtonUpEvent(1, 1)));
368
EXPECT_CALL(client.handler, handle_input(mt::ButtonDownEvent(99, 99)));
369
EXPECT_CALL(client.handler, handle_input(mt::ButtonUpEvent(99, 99)))
370
.WillOnce(mt::WakeUp(&client.all_events_received));
373
// First we will move the cursor in to the input region on the left side of
374
// the window. We should see a click here.
375
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 1));
376
fake_event_hub()->synthesize_event(
377
mis::a_button_down_event()
379
.with_action(mis::EventAction::Down));
380
fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
381
// Now in to the dead zone in the center of the window. We should not see
383
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(49, 49));
384
fake_event_hub()->synthesize_event(
385
mis::a_button_down_event()
387
.with_action(mis::EventAction::Down));
388
fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
389
// Now in to the right edge of the window, in the right input region.
390
// Again we should see a click.
391
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(49, 49));
392
fake_event_hub()->synthesize_event(
393
mis::a_button_down_event()
395
.with_action(mis::EventAction::Down));
396
fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
368
399
TEST_F(TestClientInput, scene_obscure_motion_events_by_stacking)
370
static int const screen_width = 100;
371
static int const screen_height = 100;
373
static geom::Rectangle const screen_geometry{geom::Point{0, 0},
374
geom::Size{screen_width, screen_height}};
401
using namespace testing;
377
403
auto smaller_geometry = screen_geometry;
378
smaller_geometry.size.width = geom::Width{screen_width/2};
381
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
383
// First we will move the cursor in to the region where client 2 obscures client 1
384
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 1));
385
server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
386
server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
387
// Now we move to the unobscured region of client 1
388
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(50, 0));
389
server.fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
390
server.fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
392
server_configuration.client_geometries[test_client_name_1] = screen_geometry;
393
server_configuration.client_geometries[test_client_name_2] = smaller_geometry;
394
server_configuration.client_depths[test_client_name_1] = ms::DepthId{0};
395
server_configuration.client_depths[test_client_name_2] = ms::DepthId{1};
399
client_config_1.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
401
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
402
EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
403
EXPECT_CALL(handler, handle_input(mt::MovementEvent())).Times(AnyNumber());
405
// We should only see one button event sequence.
407
EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(51, 1))).Times(1);
408
EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(51, 1))).Times(1)
409
.WillOnce(mt::WakeUp(&events_received));
413
client_config_2.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
415
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
416
EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
417
EXPECT_CALL(handler, handle_input(mt::MovementEvent())).Times(AnyNumber());
419
// Likewise we should only see one button sequence.
421
EXPECT_CALL(handler, handle_input(mt::ButtonDownEvent(1, 1))).Times(1);
422
EXPECT_CALL(handler, handle_input(mt::ButtonUpEvent(1, 1))).Times(1)
423
.WillOnce(mt::WakeUp(&events_received));
427
start_client(client_config_1);
428
start_client(client_config_2);
434
ACTION_P(SignalFence, fence)
436
fence->wake_up_everyone();
404
smaller_geometry.size.width =
405
geom::Width{screen_geometry.size.width.as_uint32_t() / 2};
407
test_server_config().client_geometries[test_client_name_1] = screen_geometry;
408
test_server_config().client_geometries[test_client_name_2] = smaller_geometry;
409
test_server_config().client_depths[test_client_name_1] = ms::DepthId{0};
410
test_server_config().client_depths[test_client_name_2] = ms::DepthId{1};
412
InputClient client1{new_connection(), test_client_name_1};
413
InputClient client2{new_connection(), test_client_name_2};
415
EXPECT_CALL(client1.handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
416
EXPECT_CALL(client1.handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
417
EXPECT_CALL(client1.handler, handle_input(mt::MovementEvent())).Times(AnyNumber());
419
// We should only see one button event sequence.
421
EXPECT_CALL(client1.handler, handle_input(mt::ButtonDownEvent(501, 1)));
422
EXPECT_CALL(client1.handler, handle_input(mt::ButtonUpEvent(501, 1)))
423
.WillOnce(mt::WakeUp(&client1.all_events_received));
426
EXPECT_CALL(client2.handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
427
EXPECT_CALL(client2.handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
428
EXPECT_CALL(client2.handler, handle_input(mt::MovementEvent())).Times(AnyNumber());
430
// Likewise we should only see one button sequence.
432
EXPECT_CALL(client2.handler, handle_input(mt::ButtonDownEvent(1, 1)));
433
EXPECT_CALL(client2.handler, handle_input(mt::ButtonUpEvent(1, 1)))
434
.WillOnce(mt::WakeUp(&client2.all_events_received));
437
// First we will move the cursor in to the region where client 2 obscures client 1
438
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1, 1));
439
fake_event_hub()->synthesize_event(
440
mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
441
fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
442
// Now we move to the unobscured region of client 1
443
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(500, 0));
444
fake_event_hub()->synthesize_event(
445
mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
446
fake_event_hub()->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
441
449
TEST_F(TestClientInput, hidden_clients_do_not_receive_pointer_events)
444
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
446
// We send one event and then hide the surface on top before sending the next.
447
// So we expect each of the two surfaces to receive one even
448
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1,1));
449
// We use a fence to ensure we do not hide the client
450
// before event dispatch occurs
451
second_client_done.wait_for_at_most_seconds(60);
453
server.the_session_container()->for_each([&](std::shared_ptr<ms::Session> const& session) -> void
455
if (session->name() == test_client_name_2)
459
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1,1));
461
server_configuration.client_depths[test_client_name_1] = ms::DepthId{0};
462
server_configuration.client_depths[test_client_name_2] = ms::DepthId{1};
465
client_config_1.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
467
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
468
EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
469
EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(2, 2))).Times(1)
470
.WillOnce(mt::WakeUp(&events_received));
473
client_config_2.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
475
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
476
EXPECT_CALL(handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
477
EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(1, 1))).Times(1)
478
.WillOnce(DoAll(SignalFence(&second_client_done), mt::WakeUp(&events_received)));
481
start_client(client_config_1);
482
start_client(client_config_2);
451
using namespace testing;
453
mt::WaitCondition second_client_done;
455
test_server_config().client_depths[test_client_name_1] = ms::DepthId{0};
456
test_server_config().client_depths[test_client_name_2] = ms::DepthId{1};
458
InputClient client1{new_connection(), test_client_name_1};
459
InputClient client2{new_connection(), test_client_name_2};
461
EXPECT_CALL(client1.handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
462
EXPECT_CALL(client1.handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
463
EXPECT_CALL(client1.handler, handle_input(mt::MotionEventWithPosition(2, 2)))
464
.WillOnce(mt::WakeUp(&client1.all_events_received));
466
EXPECT_CALL(client2.handler, handle_input(mt::HoverEnterEvent())).Times(AnyNumber());
467
EXPECT_CALL(client2.handler, handle_input(mt::HoverExitEvent())).Times(AnyNumber());
468
EXPECT_CALL(client2.handler, handle_input(mt::MotionEventWithPosition(1, 1)))
469
.WillOnce(DoAll(mt::WakeUp(&second_client_done),
470
mt::WakeUp(&client2.all_events_received)));
472
// We send one event and then hide the surface on top before sending the next.
473
// So we expect each of the two surfaces to receive one even
474
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1,1));
475
// We use a fence to ensure we do not hide the client
476
// before event dispatch occurs
477
second_client_done.wait_for_at_most_seconds(60);
479
server_config().the_session_container()->for_each(
480
[&](std::shared_ptr<ms::Session> const& session) -> void
482
if (session->name() == test_client_name_2)
486
fake_event_hub()->synthesize_event(mis::a_motion_event().with_movement(1,1));
485
TEST_F(TestClientInput, clients_receive_motion_within_co_ordinate_system_of_window)
489
TEST_F(TestClientInput, clients_receive_motion_within_coordinate_system_of_window)
487
static int const screen_width = 1000;
488
static int const screen_height = 800;
489
static int const client_height = screen_height/2;
490
static int const client_width = screen_width/2;
492
server_configuration.produce_events = [&](mtf::InputTestingServerConfiguration& server)
494
server.the_session_container()->for_each([&](std::shared_ptr<ms::Session> const& session) -> void
496
session->default_surface()->move_to(geom::Point{screen_width/2-40, screen_height/2-80});
498
server.fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2+40, screen_height/2+90));
500
server_configuration.client_geometries[arbitrary_client_name] ={{screen_width/2, screen_height/2}, {client_width, client_height}};
503
client_config.expect_cb = [&](MockHandler& handler, mt::WaitCondition& events_received)
506
EXPECT_CALL(handler, handle_input(mt::HoverEnterEvent())).Times(1);
507
EXPECT_CALL(handler, handle_input(mt::MotionEventWithPosition(80, 170))).Times(AnyNumber())
508
.WillOnce(mt::WakeUp(&events_received));
511
start_client(client_config);
491
using namespace testing;
493
int const screen_width = screen_geometry.size.width.as_int();
494
int const screen_height = screen_geometry.size.height.as_int();
495
int const client_height = screen_height / 2;
496
int const client_width = screen_width / 2;
498
test_server_config().client_geometries[test_client_name_1] =
499
{{screen_width / 2, screen_height / 2}, {client_width, client_height}};
501
InputClient client1{new_connection(), test_client_name_1};
504
EXPECT_CALL(client1.handler, handle_input(mt::HoverEnterEvent()));
505
EXPECT_CALL(client1.handler, handle_input(mt::MotionEventWithPosition(80, 170)))
507
.WillOnce(mt::WakeUp(&client1.all_events_received));
509
server_config().the_session_container()->for_each(
510
[&](std::shared_ptr<ms::Session> const& session) -> void
512
session->default_surface()->move_to(
513
geom::Point{screen_width / 2 - 40, screen_height / 2 - 80});
516
fake_event_hub()->synthesize_event(
517
mis::a_motion_event().with_movement(screen_width / 2 + 40, screen_height / 2 + 90));
514
520
// TODO: Consider tests for more input devices with custom mapping (i.e. joysticks...)