2
* Copyright © 2017 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU General Public License version 2 or 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Alan Griffiths <alan@octopull.co.uk>
19
#include <mir/client/surface.h>
20
#include <mir/client/window_spec.h>
22
#include <mir_toolkit/mir_window.h>
23
#include <mir_toolkit/mir_blob.h>
25
#include <mir/geometry/displacement.h>
26
#include <mir/input/input_device_info.h>
27
#include <mir/input/device_capability.h>
28
#include <mir/shell/canonical_window_manager.h>
29
#include <mir/shell/shell.h>
31
#include <mir_test_framework/connected_client_with_a_window.h>
32
#include <mir_test_framework/fake_input_device.h>
33
#include <mir_test_framework/stub_server_platform_factory.h>
34
#include <mir/test/event_factory.h>
35
#include <mir/test/fake_shared.h>
36
#include <mir/test/signal.h>
38
#include <gmock/gmock.h>
39
#include <gtest/gtest.h>
41
#include <linux/input.h>
45
using namespace std::chrono_literals;
46
using namespace mir::geometry;
47
using namespace testing;
48
using mir::test::fake_shared;
49
using mir::test::Signal;
58
explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {}
60
operator MirCookie const*() const { return self.get(); }
62
auto get() const -> MirCookie const* { return self.get(); }
64
void reset() { self.reset(); }
66
void reset(MirCookie const* cookie) { self.reset(cookie, deleter); }
69
static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); }
71
std::shared_ptr<MirCookie const> self;
74
void mir_cookie_release(Cookie const&) = delete;
76
struct MockWindowManager : mir::shell::CanonicalWindowManager
78
#if defined(__clang__)
79
#pragma GCC diagnostic push
80
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
82
using mir::shell::CanonicalWindowManager::CanonicalWindowManager;
83
#if defined(__clang__)
84
#pragma GCC diagnostic pop
87
MOCK_METHOD3(handle_request_move,
88
void(std::shared_ptr<mir::scene::Session> const&, std::shared_ptr<mir::scene::Surface> const&, uint64_t));
91
struct MouseMoverAndFaker
93
void start_dragging_mouse()
95
using namespace mir::input::synthesis;
96
fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
99
void move_mouse(Displacement const& displacement)
101
using mir::input::synthesis::a_pointer_event;
102
fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
107
using namespace mir::input::synthesis;
108
fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
112
std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
113
mir_test_framework::add_fake_input_device(
114
mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})
118
Rectangle const screen_geometry{{0, 0}, {800, 600}};
119
auto const receive_event_timeout = 90s;
121
struct ClientMediatedUserGestures : mir_test_framework::ConnectedClientWithAWindow,
124
void SetUp() override
126
initial_display_layout({screen_geometry});
127
server.override_the_window_manager_builder([this](mir::shell::FocusController* focus_controller)
129
return window_manager =
130
std::make_shared<MockWindowManager>(focus_controller, server.the_shell_display_layout());
133
mir_test_framework::ConnectedClientWithAWindow::SetUp();
134
mir_window_set_event_handler(window, &window_event_handler, this);
141
void TearDown() override
143
reset_window_event_handler();
144
window_manager.reset();
146
mir_test_framework::ConnectedClientWithAWindow::TearDown();
149
auto user_initiates_gesture() -> Cookie;
151
std::shared_ptr<MockWindowManager> window_manager;
156
void set_window_event_handler(std::function<void(MirEvent const* event)> const& handler);
157
void reset_window_event_handler();
158
void invoke_window_event_handler(MirEvent const* event)
160
std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
161
window_event_handler_(event);
164
mir::client::Surface surface;
166
std::mutex window_event_handler_mutex;
167
std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
169
static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
172
void ClientMediatedUserGestures::set_window_event_handler(std::function<void(MirEvent const* event)> const& handler)
174
std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
175
window_event_handler_ = handler;
178
void ClientMediatedUserGestures::reset_window_event_handler()
180
std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
181
window_event_handler_ = [](MirEvent const*) {};
184
void ClientMediatedUserGestures::window_event_handler(MirWindow* /*window*/, MirEvent const* event, void* context)
186
static_cast<ClientMediatedUserGestures*>(context)->invoke_window_event_handler(event);
189
void ClientMediatedUserGestures::paint_window()
192
surface = mir::client::Surface{mir_connection_create_render_surface_sync(connection, 42, 42)};
193
auto const spec = mir::client::WindowSpec::for_changes(connection);
194
mir_window_spec_add_render_surface(spec, surface, 42, 42, 0, 0);
195
mir_window_apply_spec(window, spec);
200
set_window_event_handler([&](MirEvent const* event)
202
if (mir_event_get_type(event) != mir_event_type_window)
205
auto const window_event = mir_event_get_window_event(event);
206
if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
209
if (mir_window_event_get_attribute_value(window_event))
213
mir_buffer_stream_swap_buffers_sync(mir_render_surface_get_buffer_stream(surface, 42, 42, mir_pixel_format_argb_8888));
215
EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
217
reset_window_event_handler();
220
void ClientMediatedUserGestures::center_mouse()
222
Signal have_mouseover;
224
set_window_event_handler([&](MirEvent const* event)
226
if (mir_event_get_type(event) != mir_event_type_input)
229
auto const input_event = mir_event_get_input_event(event);
231
if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
234
auto const pointer_event = mir_input_event_get_pointer_event(input_event);
236
if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
239
have_mouseover.raise();
242
move_mouse(0.5 * as_displacement(screen_geometry.size));
244
// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
245
// But it isn't essential for the test and we've probably waited long enough
246
// for the mouse-down needed by the test to reach the window.
247
// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
248
have_mouseover.wait_for(receive_event_timeout);
250
reset_window_event_handler();
253
auto ClientMediatedUserGestures::user_initiates_gesture() -> Cookie
258
set_window_event_handler([&](MirEvent const* event)
260
if (mir_event_get_type(event) != mir_event_type_input)
263
auto const input_event = mir_event_get_input_event(event);
265
if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
268
auto const pointer_event = mir_input_event_get_pointer_event(input_event);
270
if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
273
cookie = Cookie{mir_input_event_get_cookie(input_event)};
277
start_dragging_mouse();
279
EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
281
reset_window_event_handler();
286
TEST_F(ClientMediatedUserGestures, when_user_initiates_gesture_client_receives_cookie)
288
auto const cookie = user_initiates_gesture();
290
EXPECT_THAT(cookie.get(), NotNull());
293
TEST_F(ClientMediatedUserGestures, when_client_initiates_move_window_manager_handles_request)
295
auto const cookie = user_initiates_gesture();
297
EXPECT_CALL(*window_manager, handle_request_move(_, _, _)).WillOnce(InvokeWithoutArgs([&]{ have_request.raise(); }));
299
mir_window_request_user_move(window, cookie);
301
EXPECT_THAT(have_request.wait_for(receive_event_timeout), Eq(true));