19
19
#include "mir/graphics/display.h"
20
#include "mir/graphics/viewable_area.h"
21
20
#include "mir/shell/surface_creation_parameters.h"
22
21
#include "mir/shell/placement_strategy.h"
23
22
#include "mir/shell/surface_factory.h"
34
33
#include "mir_test/fake_event_hub.h"
35
34
#include "mir_test/event_factory.h"
36
35
#include "mir_test/wait_condition.h"
36
#include "mir_test_framework/cross_process_sync.h"
37
37
#include "mir_test_framework/display_server_test_fixture.h"
38
38
#include "mir_test_framework/input_testing_server_configuration.h"
121
123
struct InputClient : ClientConfig
123
InputClient(std::string const& surface_name)
124
: surface_name(surface_name)
125
InputClient(const mtf::CrossProcessSync& input_cb_setup_fence, std::string const& surface_name)
126
: input_cb_setup_fence(input_cb_setup_fence),
127
surface_name(surface_name)
138
141
// Set this in the callback, not main thread to avoid missing test events
139
142
mir_surface_set_event_handler(surface, &event_delegate);
146
input_cb_setup_fence.try_signal_ready_for(std::chrono::milliseconds(1000));
147
} catch (const std::runtime_error& e)
149
std::cout << e.what() << std::endl;
142
154
static void handle_input(MirSurface* /* surface */, MirEvent const* ev, void* context)
176
188
auto request_params = parameters();
177
189
mir_wait_for(mir_connection_create_surface(connection, &request_params, create_surface_callback, this));
179
events_received.wait_for_at_most_seconds(60);
191
events_received.wait_for_at_most_seconds(2);
181
193
mir_surface_release_sync(surface);
190
202
std::shared_ptr<MockInputHandler> handler;
191
203
mt::WaitCondition events_received;
205
mtf::CrossProcessSync input_cb_setup_fence;
193
206
std::string const surface_name;
195
208
static int const surface_width = 100;
295
308
int const num_events_produced = 3;
296
309
static std::string const test_client_name = "1";
311
mtf::CrossProcessSync fence;
298
313
struct ServerConfiguration : mtf::InputTestingServerConfiguration
315
mtf::CrossProcessSync input_cb_setup_fence;
317
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
318
: input_cb_setup_fence(input_cb_setup_fence)
300
322
void inject_input()
302
324
wait_until_client_appears(test_client_name);
325
input_cb_setup_fence.wait_for_signal_ready_for();
303
327
for (int i = 0; i < num_events_produced; i++)
304
328
fake_event_hub->synthesize_event(mis::a_key_down_event()
305
329
.of_scancode(KEY_ENTER));
331
} server_config(fence);
308
332
launch_server_process(server_config);
310
334
struct KeyReceivingClient : InputClient
312
KeyReceivingClient() : InputClient(test_client_name) {}
336
KeyReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
313
337
void expect_input(mt::WaitCondition& events_received) override
315
339
using namespace ::testing;
328
352
using namespace ::testing;
329
353
static std::string const test_client_name = "1";
354
mtf::CrossProcessSync fence;
331
356
struct ServerConfiguration : mtf::InputTestingServerConfiguration
358
mtf::CrossProcessSync input_cb_setup_fence;
360
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
361
: input_cb_setup_fence(input_cb_setup_fence)
333
365
void inject_input()
335
367
wait_until_client_appears(test_client_name);
368
input_cb_setup_fence.wait_for_signal_ready_for();
337
370
fake_event_hub->synthesize_event(mis::a_key_down_event()
338
371
.of_scancode(KEY_LEFTSHIFT));
340
373
.of_scancode(KEY_4));
376
} server_config{fence};
344
377
launch_server_process(server_config);
346
379
struct KeyReceivingClient : InputClient
348
KeyReceivingClient() : InputClient(test_client_name) {}
381
KeyReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
350
383
void expect_input(mt::WaitCondition& events_received) override
365
398
using namespace ::testing;
366
399
static std::string const test_client_name = "1";
400
mtf::CrossProcessSync fence;
368
402
struct ServerConfiguration : public mtf::InputTestingServerConfiguration
404
mtf::CrossProcessSync input_cb_setup_fence;
406
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
407
: input_cb_setup_fence(input_cb_setup_fence)
370
411
void inject_input()
372
413
wait_until_client_appears(test_client_name);
374
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width,
375
InputClient::surface_height));
414
input_cb_setup_fence.wait_for_signal_ready_for();
416
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(InputClient::surface_width - 1,
417
InputClient::surface_height - 1));
376
418
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,2));
420
} server_config{fence};
379
421
launch_server_process(server_config);
381
423
struct MotionReceivingClient : InputClient
383
MotionReceivingClient() : InputClient(test_client_name) {}
425
MotionReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
385
427
void expect_input(mt::WaitCondition& events_received) override
391
433
// We should see the cursor enter
392
434
EXPECT_CALL(*handler, handle_input(HoverEnterEvent())).Times(1);
393
435
EXPECT_CALL(*handler, handle_input(
394
MotionEventWithPosition(InputClient::surface_width,
395
InputClient::surface_height))).Times(1)
436
MotionEventWithPosition(InputClient::surface_width - 1,
437
InputClient::surface_height - 1))).Times(1)
396
438
.WillOnce(mt::WakeUp(&events_received));
397
439
// But we should not receive an event for the second movement outside of our surface!
441
} client_config{fence};
400
442
launch_client_process(client_config);
405
447
using namespace ::testing;
407
449
static std::string const test_client_name = "1";
450
mtf::CrossProcessSync fence;
409
452
struct ServerConfiguration : public mtf::InputTestingServerConfiguration
454
mtf::CrossProcessSync input_cb_setup_fence;
456
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
457
: input_cb_setup_fence(input_cb_setup_fence)
411
461
void inject_input()
413
463
wait_until_client_appears(test_client_name);
464
input_cb_setup_fence.wait_for_signal_ready_for();
414
466
fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
468
} server_config{fence};
417
469
launch_server_process(server_config);
419
471
struct ButtonReceivingClient : InputClient
421
ButtonReceivingClient() : InputClient(test_client_name) {}
473
ButtonReceivingClient(const mtf::CrossProcessSync& fence) : InputClient(fence, test_client_name) {}
423
475
void expect_input(mt::WaitCondition& events_received) override
430
482
EXPECT_CALL(*handler, handle_input(ButtonDownEvent(0, 0))).Times(1)
431
483
.WillOnce(mt::WakeUp(&events_received));
485
} client_config{fence};
434
486
launch_client_process(client_config);
439
typedef std::map<std::string, geom::Rectangle> GeometryList;
491
typedef std::map<std::string, geom::Rectangle> GeometryMap;
492
typedef std::map<std::string, ms::DepthId> DepthMap;
441
494
struct StaticPlacementStrategy : public msh::PlacementStrategy
443
StaticPlacementStrategy(GeometryList const& positions)
444
: surface_geometry_by_name(positions)
496
StaticPlacementStrategy(GeometryMap const& positions,
497
DepthMap const& depths)
498
: surface_geometry_by_name(positions),
499
surface_depths_by_name(depths)
503
StaticPlacementStrategy(GeometryMap const& positions)
504
: StaticPlacementStrategy(positions, DepthMap())
448
508
msh::SurfaceCreationParameters place(msh::SurfaceCreationParameters const& request_parameters)
450
510
auto placed = request_parameters;
451
auto geometry = surface_geometry_by_name[request_parameters.name];
511
auto const& name = request_parameters.name;
512
auto geometry = surface_geometry_by_name[name];
453
514
placed.top_left = geometry.top_left;
454
515
placed.size = geometry.size;
516
placed.depth = surface_depths_by_name[name];
458
GeometryList surface_geometry_by_name;
520
GeometryMap surface_geometry_by_name;
521
DepthMap surface_depths_by_name;
470
533
static int const client_width = screen_width/2;
471
534
static std::string const test_client_1 = "1";
472
535
static std::string const test_client_2 = "2";
536
mtf::CrossProcessSync fence;
474
538
struct ServerConfiguration : mtf::InputTestingServerConfiguration
540
mtf::CrossProcessSync input_cb_setup_fence;
542
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
543
: input_cb_setup_fence(input_cb_setup_fence)
476
547
std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override
478
static GeometryList positions;
549
static GeometryMap positions;
479
550
positions[test_client_1] = geom::Rectangle{geom::Point{0, 0},
480
551
geom::Size{client_width, client_height}};
481
552
positions[test_client_2] = geom::Rectangle{geom::Point{screen_width/2, screen_height/2},
484
555
return std::make_shared<StaticPlacementStrategy>(positions);
487
geom::Rectangle the_screen_geometry() override
489
return geom::Rectangle{geom::Point{0, 0}, geom::Size{screen_width, screen_height}};
492
558
void inject_input() override
494
560
wait_until_client_appears(test_client_1);
561
EXPECT_EQ(1, input_cb_setup_fence.wait_for_signal_ready_for());
495
562
wait_until_client_appears(test_client_2);
563
EXPECT_EQ(2, input_cb_setup_fence.wait_for_signal_ready_for());
496
565
// In the bounds of the first surface
497
566
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2-1, screen_height/2-1));
498
567
// In the bounds of the second surface
499
568
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(screen_width/2, screen_height/2));
570
} server_config{fence};
503
572
launch_server_process(server_config);
505
574
struct InputClientOne : InputClient
508
: InputClient(test_client_1)
576
InputClientOne(const mtf::CrossProcessSync& fence)
577
: InputClient(fence, test_client_1)
517
586
EXPECT_CALL(*handler, handle_input(HoverExitEvent())).Times(1)
518
587
.WillOnce(mt::WakeUp(&events_received));
522
591
struct InputClientTwo : InputClient
525
: InputClient(test_client_2)
593
InputClientTwo(const mtf::CrossProcessSync& fence)
594
: InputClient(fence, test_client_2)
533
602
EXPECT_CALL(*handler, handle_input(MotionEventWithPosition(client_width - 1, client_height - 1))).Times(1)
534
603
.WillOnce(mt::WakeUp(&events_received));
538
607
launch_client_process(client_1);
539
608
launch_client_process(client_2);
571
640
using namespace ::testing;
572
641
static std::string const test_client_name = "1";
642
mtf::CrossProcessSync fence;
574
644
static int const screen_width = 100;
575
645
static int const screen_height = 100;
585
655
struct ServerConfiguration : mtf::InputTestingServerConfiguration
657
mtf::CrossProcessSync input_cb_setup_fence;
659
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
660
: input_cb_setup_fence(input_cb_setup_fence)
587
664
std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override
589
static GeometryList positions;
666
static GeometryMap positions;
590
667
positions[test_client_name] = screen_geometry;
592
669
return std::make_shared<StaticPlacementStrategy>(positions);
596
673
return std::make_shared<RegionApplyingSurfaceFactory>(InputTestingServerConfiguration::the_shell_surface_factory(),
597
674
client_input_regions);
599
geom::Rectangle the_screen_geometry() override
601
return screen_geometry;
604
677
void inject_input() override
606
679
wait_until_client_appears(test_client_name);
680
input_cb_setup_fence.wait_for_signal_ready_for();
608
682
// First we will move the cursor in to the input region on the left side of the window. We should see a click here
609
683
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 1));
618
692
fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
619
693
fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
695
} server_config{fence};
623
697
launch_server_process(server_config);
625
699
struct ClientConfig : InputClient
628
: InputClient(test_client_name)
701
ClientConfig(const mtf::CrossProcessSync& fence)
702
: InputClient(fence, test_client_name)
646
720
.WillOnce(mt::WakeUp(&events_received));
723
} client_config{fence};
650
724
launch_client_process(client_config);
655
typedef std::map<std::string, ms::DepthId> DepthList;
657
struct StackingSurfaceController : public ms::SurfaceController
659
StackingSurfaceController(std::shared_ptr<ms::SurfaceStackModel> const& surface_stack_model, DepthList const& depths)
660
: SurfaceController(surface_stack_model),
661
surface_depths_by_name(depths)
665
std::weak_ptr<ms::Surface> create_surface(msh::Session*, msh::SurfaceCreationParameters const& params) override
667
return surface_stack->create_surface(params, surface_depths_by_name[params.name]);
670
DepthList surface_depths_by_name;
674
727
TEST_F(TestClientInput, surfaces_obscure_motion_events_by_stacking)
676
729
using namespace ::testing;
678
731
static std::string const test_client_name_1 = "1";
679
732
static std::string const test_client_name_2 = "2";
733
mtf::CrossProcessSync fence;
681
735
static int const screen_width = 100;
682
736
static int const screen_height = 100;
684
738
static geom::Rectangle const screen_geometry{geom::Point{0, 0},
685
739
geom::Size{screen_width, screen_height}};
687
struct ServerConiguration : mtf::InputTestingServerConfiguration
741
struct ServerConfiguration : mtf::InputTestingServerConfiguration
743
mtf::CrossProcessSync input_cb_setup_fence;
745
ServerConfiguration(const mtf::CrossProcessSync& input_cb_setup_fence)
746
: input_cb_setup_fence(input_cb_setup_fence)
689
750
std::shared_ptr<msh::PlacementStrategy> the_shell_placement_strategy() override
691
static GeometryList positions;
752
static GeometryMap positions;
692
753
positions[test_client_name_1] = screen_geometry;
694
755
auto smaller_geometry = screen_geometry;
695
756
smaller_geometry.size.width = geom::Width{screen_width/2};
696
757
positions[test_client_name_2] = smaller_geometry;
698
return std::make_shared<StaticPlacementStrategy>(positions);
701
std::shared_ptr<msh::SurfaceBuilder> the_surface_builder() override
703
static DepthList depths;
759
static DepthMap depths;
704
760
depths[test_client_name_1] = ms::DepthId{0};
705
761
depths[test_client_name_2] = ms::DepthId{1};
707
return std::make_shared<StackingSurfaceController>(the_surface_stack_model(), depths);
763
return std::make_shared<StaticPlacementStrategy>(positions, depths);
710
766
void inject_input() override
712
768
wait_until_client_appears(test_client_name_1);
769
input_cb_setup_fence.wait_for_signal_ready_for();
713
770
wait_until_client_appears(test_client_name_2);
771
input_cb_setup_fence.wait_for_signal_ready_for();
715
773
// First we will move the cursor in to the region where client 2 obscures client 1
716
774
fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 1));
717
775
fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
721
779
fake_event_hub->synthesize_event(mis::a_button_down_event().of_button(BTN_LEFT).with_action(mis::EventAction::Down));
722
780
fake_event_hub->synthesize_event(mis::a_button_up_event().of_button(BTN_LEFT));
782
} server_config{fence};
726
784
launch_server_process(server_config);
728
786
struct ClientConfigOne : InputClient
731
: InputClient(test_client_name_1)
788
ClientConfigOne(const mtf::CrossProcessSync& fence)
789
: InputClient(fence, test_client_name_1)
746
804
.WillOnce(mt::WakeUp(&events_received));
807
} client_config_1{fence};
750
808
launch_client_process(client_config_1);
752
810
struct ClientConfigTwo : InputClient
755
: InputClient(test_client_name_2)
812
ClientConfigTwo(const mtf::CrossProcessSync& fence)
813
: InputClient(fence, test_client_name_2)