Merge lp:~mir-team/miral/1.4 into lp:miral/release

Proposed by Brandon Schaefer
Status: Rejected
Rejected by: Alan Griffiths
Proposed branch: lp:~mir-team/miral/1.4
Merge into: lp:miral/release
Diff against target: 1523 lines (+1194/-19)
18 files modified
CMakeLists.txt (+2/-2)
debian/changelog (+13/-2)
debian/libmiral2.symbols (+4/-0)
include/miral/window_management_policy_addendum2.h (+70/-0)
include/miral/window_manager_tools.h (+12/-0)
miral-shell/miral-screencast.sh (+1/-1)
miral/CMakeLists.txt (+1/-0)
miral/basic_window_manager.cpp (+76/-12)
miral/basic_window_manager.h (+14/-0)
miral/symbols.map (+21/-0)
miral/window_management_trace.cpp (+18/-0)
miral/window_management_trace.h (+2/-0)
miral/window_manager_tools.cpp (+6/-0)
miral/window_manager_tools_implementation.h (+3/-0)
scripts/process_doxygen_xml.py (+12/-1)
test/CMakeLists.txt (+13/-1)
test/client_mediated_gestures.cpp (+285/-0)
test/drag_and_drop.cpp (+641/-0)
To merge this branch: bzr merge lp:~mir-team/miral/1.4
Reviewer Review Type Date Requested Status
Alan Griffiths Disapprove
Review via email: mp+322533@code.launchpad.net

Commit message

1.4 Release

Description of the change

1.4 Release

To post a comment you must log in.
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

We're going to restart this with the Mir 0.27 release

review: Disapprove

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2017-03-24 17:44:48 +0000
+++ CMakeLists.txt 2017-04-13 15:18:28 +0000
@@ -41,8 +41,8 @@
41include_directories(include SYSTEM ${MIRCLIENT_INCLUDE_DIRS})41include_directories(include SYSTEM ${MIRCLIENT_INCLUDE_DIRS})
4242
43set(MIRAL_VERSION_MAJOR 1)43set(MIRAL_VERSION_MAJOR 1)
44set(MIRAL_VERSION_MINOR 3)44set(MIRAL_VERSION_MINOR 4)
45set(MIRAL_VERSION_PATCH 2)45set(MIRAL_VERSION_PATCH 0)
4646
47set(MIRAL_VERSION ${MIRAL_VERSION_MAJOR}.${MIRAL_VERSION_MINOR}.${MIRAL_VERSION_PATCH})47set(MIRAL_VERSION ${MIRAL_VERSION_MAJOR}.${MIRAL_VERSION_MINOR}.${MIRAL_VERSION_PATCH})
4848
4949
=== modified file 'debian/changelog'
--- debian/changelog 2017-03-30 14:18:38 +0000
+++ debian/changelog 2017-04-13 15:18:28 +0000
@@ -1,3 +1,14 @@
1miral (1.4.0) UNRELEASED; urgency=medium
2
3 * New upstream release 1.4.0 (https://launchpad.net/miral/+milestone/1.4.0)
4 - ABI summary:
5 . miral ABI unchanged at 2
6 - Enhancements:
7 . Support for passing messages to enable Drag & Drop
8 - Bugs fixed:
9
10 -- Alan Griffiths <alan@Octopull-X1c3> Tue, 21 Mar 2017 17:57:20 +0000
11
1miral (1.3.2+17.04.20170330.5-0ubuntu1) zesty; urgency=medium12miral (1.3.2+17.04.20170330.5-0ubuntu1) zesty; urgency=medium
213
3 * New upstream release 1.3.2 (https://launchpad.net/miral/+milestone/1.3.2)14 * New upstream release 1.3.2 (https://launchpad.net/miral/+milestone/1.3.2)
@@ -74,11 +85,11 @@
74 . Chrome-less shell hint does not work any more (LP: #1658117)85 . Chrome-less shell hint does not work any more (LP: #1658117)
75 . WindowSpec::set_state() wrapper for mir_window_spec_set_state()86 . WindowSpec::set_state() wrapper for mir_window_spec_set_state()
76 (LP: #1661256)87 (LP: #1661256)
77 . "$ miral-app -kiosk" fails with "Unknown command line options: 88 . "$ miral-app -kiosk" fails with "Unknown command line options:
78 --desktop_file_hint=miral-shell.desktop" (LP: #1660933)89 --desktop_file_hint=miral-shell.desktop" (LP: #1660933)
79 . libmiral] Fix focus and movement rules for Input Method and Satellite90 . libmiral] Fix focus and movement rules for Input Method and Satellite
80 windows. (LP: #1660691)91 windows. (LP: #1660691)
81 92
8293
83 -- Alan Griffiths <alan.griffiths@canonical.com> Wed, 15 Feb 2017 14:05:46 +000094 -- Alan Griffiths <alan.griffiths@canonical.com> Wed, 15 Feb 2017 14:05:46 +0000
8495
8596
=== modified file 'debian/libmiral2.symbols'
--- debian/libmiral2.symbols 2017-03-15 17:44:36 +0000
+++ debian/libmiral2.symbols 2017-04-13 15:18:28 +0000
@@ -389,3 +389,7 @@
389 (c++)"miral::SetWindowManagementPolicy::~SetWindowManagementPolicy()@MIRAL_1.3.1" 1.3.1389 (c++)"miral::SetWindowManagementPolicy::~SetWindowManagementPolicy()@MIRAL_1.3.1" 1.3.1
390 (c++)"miral::SetWindowManagementPolicy::~SetWindowManagementPolicy()@MIRAL_1.3.1" 1.3.1390 (c++)"miral::SetWindowManagementPolicy::~SetWindowManagementPolicy()@MIRAL_1.3.1" 1.3.1
391 (c++)"miral::SetWindowManagementPolicy::operator()(mir::Server&) const@MIRAL_1.3.1" 1.3.1391 (c++)"miral::SetWindowManagementPolicy::operator()(mir::Server&) const@MIRAL_1.3.1" 1.3.1
392 MIRAL_1.4.0@MIRAL_1.4.0 1.4.0
393 (c++)"miral::WindowManagerTools::end_drag_and_drop()@MIRAL_1.4.0" 1.4.0
394 (c++)"miral::WindowManagerTools::start_drag_and_drop(miral::WindowInfo&, std::vector<unsigned char, std::allocator<unsigned char> > const&)@MIRAL_1.4.0" 1.4.0
395 (c++)"typeinfo for miral::WindowManagementPolicyAddendum2@MIRAL_1.4.0" 1.4.0
392396
=== added file 'include/miral/window_management_policy_addendum2.h'
--- include/miral/window_management_policy_addendum2.h 1970-01-01 00:00:00 +0000
+++ include/miral/window_management_policy_addendum2.h 2017-04-13 15:18:28 +0000
@@ -0,0 +1,70 @@
1/*
2 * Copyright © 2017 Canonical Ltd.
3 *
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 3,
6 * as published by the Free Software Foundation.
7 *
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.
12 *
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/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#ifndef MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
20#define MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
21
22#include <mir_toolkit/client_types.h>
23#include <mir/version.h>
24
25namespace miral
26{
27struct WindowInfo;
28
29/**
30 * Handle additional client requests.
31 *
32 * \note This interface is intended to be implemented by a WindowManagementPolicy
33 * implementation, we can't add these functions directly to that interface without
34 * breaking ABI (the vtab could be incompatible).
35 * When initializing the window manager this interface will be detected by
36 * dynamic_cast and registered accordingly.
37 */
38class WindowManagementPolicyAddendum2
39{
40public:
41/** @name handle requests originating from the client
42 * The policy is expected to update the model as appropriate
43 * @{ */
44 /** request from client to initiate drag and drop
45 * \note the request has already been validated against the requesting event
46 *
47 * @param window_info the window
48 */
49 virtual void handle_request_drag_and_drop(WindowInfo& window_info) = 0;
50
51 /** request from client to initiate move
52 * \note the request has already been validated against the requesting event
53 *
54 * @param window_info the window
55 * @param input_event the requesting event
56 */
57 virtual void handle_request_move(WindowInfo& window_info, MirInputEvent const* input_event) = 0;
58/** @} */
59
60 virtual ~WindowManagementPolicyAddendum2() = default;
61 WindowManagementPolicyAddendum2() = default;
62 WindowManagementPolicyAddendum2(WindowManagementPolicyAddendum2 const&) = delete;
63 WindowManagementPolicyAddendum2& operator=(WindowManagementPolicyAddendum2 const&) = delete;
64};
65#if MIRAL_VERSION >= MIR_VERSION_NUMBER(2, 0, 0)
66#error "We've presumably broken ABI - please roll this interface into WindowManagementPolicy"
67#endif
68}
69
70#endif //MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
071
=== modified file 'include/miral/window_manager_tools.h'
--- include/miral/window_manager_tools.h 2017-03-02 14:29:04 +0000
+++ include/miral/window_manager_tools.h 2017-04-13 15:18:28 +0000
@@ -166,6 +166,18 @@
166 /// Raise window and all its children166 /// Raise window and all its children
167 void raise_tree(Window const& root);167 void raise_tree(Window const& root);
168168
169 /** Start drag and drop. The handle will be passed to the client which can
170 * then use it to talk to the whatever service is being used to support drag
171 * and drop (e.g. on Ubuntu the content hub).
172 *
173 * @param window_info source window
174 * @param handle drag handle
175 */
176 void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle);
177
178 /// End drag and drop
179 void end_drag_and_drop();
180
169 /// Apply modifications to a window181 /// Apply modifications to a window
170 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications);182 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications);
171183
172184
=== modified file 'miral-shell/miral-screencast.sh'
--- miral-shell/miral-screencast.sh 2016-12-09 11:08:07 +0000
+++ miral-shell/miral-screencast.sh 2017-04-13 15:18:28 +0000
@@ -2,7 +2,7 @@
2width=19202width=1920
3height=10803height=1080
4output=screencast.mp44output=screencast.mp4
5socket=${XDG_RUNTIME_DIR}/mir_socket5socket=${XDG_RUNTIME_DIR}/miral_socket
6if [ -v MIR_SERVER ]; then socket=${MIR_SERVER}; fi6if [ -v MIR_SERVER ]; then socket=${MIR_SERVER}; fi
77
8while [ $# -gt 0 ]8while [ $# -gt 0 ]
99
=== modified file 'miral/CMakeLists.txt'
--- miral/CMakeLists.txt 2017-03-20 12:28:06 +0000
+++ miral/CMakeLists.txt 2017-04-13 15:18:28 +0000
@@ -61,6 +61,7 @@
61 ${CMAKE_SOURCE_DIR}/include/mir/client/display_config.h61 ${CMAKE_SOURCE_DIR}/include/mir/client/display_config.h
62 ${CMAKE_SOURCE_DIR}/include/mir/client/window.h62 ${CMAKE_SOURCE_DIR}/include/mir/client/window.h
63 ${CMAKE_SOURCE_DIR}/include/mir/client/detail/mir_forward_compatibility.h63 ${CMAKE_SOURCE_DIR}/include/mir/client/detail/mir_forward_compatibility.h
64 ${CMAKE_SOURCE_DIR}/include/miral/window_management_policy_addendum2.h
64)65)
6566
66target_link_libraries(miral67target_link_libraries(miral
6768
=== modified file 'miral/basic_window_manager.cpp'
--- miral/basic_window_manager.cpp 2017-03-21 12:30:42 +0000
+++ miral/basic_window_manager.cpp 2017-04-13 15:18:28 +0000
@@ -19,6 +19,7 @@
19#include "basic_window_manager.h"19#include "basic_window_manager.h"
20#include "miral/window_manager_tools.h"20#include "miral/window_manager_tools.h"
21#include "miral/workspace_policy.h"21#include "miral/workspace_policy.h"
22#include "miral/window_management_policy_addendum2.h"
2223
23#include <mir/scene/session.h>24#include <mir/scene/session.h>
24#include <mir/scene/surface.h>25#include <mir/scene/surface.h>
@@ -70,7 +71,6 @@
7071
71namespace72namespace
72{73{
73
74auto find_workspace_policy(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WorkspacePolicy*74auto find_workspace_policy(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WorkspacePolicy*
75{75{
76 miral::WorkspacePolicy* result = dynamic_cast<miral::WorkspacePolicy*>(policy.get());76 miral::WorkspacePolicy* result = dynamic_cast<miral::WorkspacePolicy*>(policy.get());
@@ -82,6 +82,23 @@
8282
83 return &null_workspace_policy;83 return &null_workspace_policy;
84}84}
85
86auto find_policy_addendum2(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WindowManagementPolicyAddendum2*
87{
88 miral::WindowManagementPolicyAddendum2* result = dynamic_cast<miral::WindowManagementPolicyAddendum2*>(policy.get());
89
90 if (result)
91 return result;
92
93 struct NullWindowManagementPolicyAddendum2 : miral::WindowManagementPolicyAddendum2
94 {
95 void handle_request_drag_and_drop(miral::WindowInfo&) override {}
96 void handle_request_move(miral::WindowInfo&, MirInputEvent const*) override {}
97 };
98 static NullWindowManagementPolicyAddendum2 null_workspace_policy;
99
100 return &null_workspace_policy;
101}
85}102}
86103
87104
@@ -94,10 +111,18 @@
94 display_layout(display_layout),111 display_layout(display_layout),
95 persistent_surface_store{persistent_surface_store},112 persistent_surface_store{persistent_surface_store},
96 policy(build(WindowManagerTools{this})),113 policy(build(WindowManagerTools{this})),
97 workspace_policy{find_workspace_policy(policy)}114 workspace_policy{find_workspace_policy(policy)},
115 policy2{find_policy_addendum2(policy)}
98{116{
99}117}
100118
119miral::BasicWindowManager::~BasicWindowManager()
120{
121#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
122 if (last_input_event)
123 mir_event_unref(last_input_event);
124#endif
125}
101void miral::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session)126void miral::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session)
102{127{
103 Locker lock{this};128 Locker lock{this};
@@ -362,10 +387,24 @@
362#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)387#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
363void miral::BasicWindowManager::handle_request_drag_and_drop(388void miral::BasicWindowManager::handle_request_drag_and_drop(
364 std::shared_ptr<mir::scene::Session> const& /*session*/,389 std::shared_ptr<mir::scene::Session> const& /*session*/,
365 std::shared_ptr<mir::scene::Surface> const& /*surface*/,390 std::shared_ptr<mir::scene::Surface> const& surface,
366 uint64_t /*timestamp*/)391 uint64_t timestamp)
367{392{
368 // TODO393 Locker lock{this};
394 if (timestamp >= last_input_event_timestamp)
395 policy2->handle_request_drag_and_drop(info_for(surface));
396}
397
398void miral::BasicWindowManager::handle_request_move(
399 std::shared_ptr<mir::scene::Session> const& /*session*/,
400 std::shared_ptr<mir::scene::Surface> const& surface,
401 uint64_t timestamp)
402{
403 std::lock_guard<decltype(mutex)> lock(mutex);
404 if (timestamp >= last_input_event_timestamp && last_input_event)
405 {
406 policy2->handle_request_move(info_for(surface), mir_event_get_input_event(last_input_event));
407 }
369}408}
370#endif409#endif
371410
@@ -741,6 +780,24 @@
741 focus_controller->raise({begin(windows), end(windows)});780 focus_controller->raise({begin(windows), end(windows)});
742}781}
743782
783void miral::BasicWindowManager::start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle)
784{
785#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
786 std::shared_ptr<scene::Surface>(window_info.window())->start_drag_and_drop(handle);
787 focus_controller->set_drag_and_drop_handle(handle);
788#else
789 (void)window_info;
790 (void)handle;
791#endif
792}
793
794void miral::BasicWindowManager::end_drag_and_drop()
795{
796#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
797 focus_controller->clear_drag_and_drop_handle();
798#endif
799}
800
744void miral::BasicWindowManager::move_tree(miral::WindowInfo& root, mir::geometry::Displacement movement)801void miral::BasicWindowManager::move_tree(miral::WindowInfo& root, mir::geometry::Displacement movement)
745{802{
746 if (movement == mir::geometry::Displacement{})803 if (movement == mir::geometry::Displacement{})
@@ -1163,25 +1220,22 @@
11631220
1164void miral::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev)1221void miral::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev)
1165{1222{
1166 auto iev = mir_keyboard_event_input_event(kev);1223 update_event_timestamp(mir_keyboard_event_input_event(kev));
1167 last_input_event_timestamp = mir_input_event_get_event_time(iev);
1168}1224}
11691225
1170void miral::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev)1226void miral::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev)
1171{1227{
1172 auto iev = mir_pointer_event_input_event(pev);
1173 auto pointer_action = mir_pointer_event_action(pev);1228 auto pointer_action = mir_pointer_event_action(pev);
11741229
1175 if (pointer_action == mir_pointer_action_button_up ||1230 if (pointer_action == mir_pointer_action_button_up ||
1176 pointer_action == mir_pointer_action_button_down)1231 pointer_action == mir_pointer_action_button_down)
1177 {1232 {
1178 last_input_event_timestamp = mir_input_event_get_event_time(iev);1233 update_event_timestamp(mir_pointer_event_input_event(pev));
1179 }1234 }
1180}1235}
11811236
1182void miral::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev)1237void miral::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev)
1183{1238{
1184 auto iev = mir_touch_event_input_event(tev);
1185 auto touch_count = mir_touch_event_point_count(tev);1239 auto touch_count = mir_touch_event_point_count(tev);
1186 for (unsigned i = 0; i < touch_count; i++)1240 for (unsigned i = 0; i < touch_count; i++)
1187 {1241 {
@@ -1189,12 +1243,22 @@
1189 if (touch_action == mir_touch_action_up ||1243 if (touch_action == mir_touch_action_up ||
1190 touch_action == mir_touch_action_down)1244 touch_action == mir_touch_action_down)
1191 {1245 {
1192 last_input_event_timestamp = mir_input_event_get_event_time(iev);1246 update_event_timestamp(mir_touch_event_input_event(tev));
1193 break;1247 break;
1194 }1248 }
1195 }1249 }
1196}1250}
11971251
1252void miral::BasicWindowManager::update_event_timestamp(MirInputEvent const* iev)
1253{
1254 last_input_event_timestamp = mir_input_event_get_event_time(iev);
1255#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
1256 if (last_input_event)
1257 mir_event_unref(last_input_event);
1258 last_input_event = mir_event_ref(mir_input_event_get_event(iev));
1259#endif
1260}
1261
1198void miral::BasicWindowManager::invoke_under_lock(std::function<void()> const& callback)1262void miral::BasicWindowManager::invoke_under_lock(std::function<void()> const& callback)
1199{1263{
1200 Locker lock{this};1264 Locker lock{this};
12011265
=== modified file 'miral/basic_window_manager.h'
--- miral/basic_window_manager.h 2017-03-21 12:30:42 +0000
+++ miral/basic_window_manager.h 2017-04-13 15:18:28 +0000
@@ -46,6 +46,7 @@
46namespace miral46namespace miral
47{47{
48class WorkspacePolicy;48class WorkspacePolicy;
49class WindowManagementPolicyAddendum2;
49using mir::shell::SurfaceSet;50using mir::shell::SurfaceSet;
50using WindowManagementPolicyBuilder =51using WindowManagementPolicyBuilder =
51 std::function<std::unique_ptr<miral::WindowManagementPolicy>(miral::WindowManagerTools const& tools)>;52 std::function<std::unique_ptr<miral::WindowManagementPolicy>(miral::WindowManagerTools const& tools)>;
@@ -61,6 +62,7 @@
61 std::shared_ptr<mir::shell::DisplayLayout> const& display_layout,62 std::shared_ptr<mir::shell::DisplayLayout> const& display_layout,
62 std::shared_ptr<mir::shell::PersistentSurfaceStore> const& persistent_surface_store,63 std::shared_ptr<mir::shell::PersistentSurfaceStore> const& persistent_surface_store,
63 WindowManagementPolicyBuilder const& build);64 WindowManagementPolicyBuilder const& build);
65 ~BasicWindowManager();
6466
65 void add_session(std::shared_ptr<mir::scene::Session> const& session) override;67 void add_session(std::shared_ptr<mir::scene::Session> const& session) override;
6668
@@ -101,6 +103,11 @@
101 std::shared_ptr<mir::scene::Session> const& session,103 std::shared_ptr<mir::scene::Session> const& session,
102 std::shared_ptr<mir::scene::Surface> const& surface,104 std::shared_ptr<mir::scene::Surface> const& surface,
103 uint64_t timestamp) override;105 uint64_t timestamp) override;
106
107 void handle_request_move(
108 std::shared_ptr<mir::scene::Session> const& session,
109 std::shared_ptr<mir::scene::Surface> const& surface,
110 uint64_t timestamp) override;
104#endif111#endif
105112
106 int set_surface_attribute(113 int set_surface_attribute(
@@ -161,6 +168,8 @@
161 auto active_display() -> mir::geometry::Rectangle const override;168 auto active_display() -> mir::geometry::Rectangle const override;
162169
163 void raise_tree(Window const& root) override;170 void raise_tree(Window const& root) override;
171 void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) override;
172 void end_drag_and_drop() override;
164173
165 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;174 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;
166175
@@ -190,6 +199,7 @@
190199
191 std::unique_ptr<WindowManagementPolicy> const policy;200 std::unique_ptr<WindowManagementPolicy> const policy;
192 WorkspacePolicy* const workspace_policy;201 WorkspacePolicy* const workspace_policy;
202 WindowManagementPolicyAddendum2* const policy2;
193203
194 std::mutex mutex;204 std::mutex mutex;
195 SessionInfoMap app_info;205 SessionInfoMap app_info;
@@ -197,6 +207,9 @@
197 mir::geometry::Rectangles displays;207 mir::geometry::Rectangles displays;
198 mir::geometry::Point cursor;208 mir::geometry::Point cursor;
199 uint64_t last_input_event_timestamp{0};209 uint64_t last_input_event_timestamp{0};
210#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
211 MirEvent const* last_input_event{nullptr};
212#endif
200 miral::MRUWindowList mru_active_windows;213 miral::MRUWindowList mru_active_windows;
201 using FullscreenSurfaces = std::set<Window>;214 using FullscreenSurfaces = std::set<Window>;
202 FullscreenSurfaces fullscreen_surfaces;215 FullscreenSurfaces fullscreen_surfaces;
@@ -213,6 +226,7 @@
213 void update_event_timestamp(MirKeyboardEvent const* kev);226 void update_event_timestamp(MirKeyboardEvent const* kev);
214 void update_event_timestamp(MirPointerEvent const* pev);227 void update_event_timestamp(MirPointerEvent const* pev);
215 void update_event_timestamp(MirTouchEvent const* tev);228 void update_event_timestamp(MirTouchEvent const* tev);
229 void update_event_timestamp(MirInputEvent const* iev);
216230
217 auto can_activate_window_for_session(miral::Application const& session) -> bool;231 auto can_activate_window_for_session(miral::Application const& session) -> bool;
218 auto can_activate_window_for_session_in_workspace(232 auto can_activate_window_for_session_in_workspace(
219233
=== modified file 'miral/symbols.map'
--- miral/symbols.map 2017-03-15 17:44:36 +0000
+++ miral/symbols.map 2017-04-13 15:18:28 +0000
@@ -401,3 +401,24 @@
401 vtable?for?miral::SetWindowManagementPolicy;401 vtable?for?miral::SetWindowManagementPolicy;
402 };402 };
403} MIRAL_1.3;403} MIRAL_1.3;
404
405MIRAL_1.4.0 {
406global:
407 extern "C++" {
408 miral::WindowManagementPolicyAddendum2::?WindowManagementPolicyAddendum2*;
409 miral::WindowManagementPolicyAddendum2::WindowManagementPolicyAddendum2*;
410 miral::WindowManagementPolicyAddendum2::operator*;
411 miral::WindowManagerTools::end_drag_and_drop*;
412 miral::WindowManagerTools::start_drag_and_drop*;
413 miral::toolkit::Window::Window*;
414 non-virtual?thunk?to?miral::WindowManagementPolicyAddendum2::?WindowManagementPolicyAddendum2*;
415 typeinfo?for?miral::WindowManagementPolicyAddendum2;
416 typeinfo?for?miral::toolkit::Window;
417 typeinfo?for?miral::toolkit::WindowId;
418 typeinfo?for?miral::toolkit::WindowSpec;
419 vtable?for?miral::WindowManagementPolicyAddendum2;
420 vtable?for?miral::toolkit::Window;
421 vtable?for?miral::toolkit::WindowId;
422 vtable?for?miral::toolkit::WindowSpec;
423 };
424} MIRAL_1.3.1;
404425
=== modified file 'miral/window_management_trace.cpp'
--- miral/window_management_trace.cpp 2017-03-03 10:06:02 +0000
+++ miral/window_management_trace.cpp 2017-04-13 15:18:28 +0000
@@ -547,6 +547,24 @@
547}547}
548MIRAL_TRACE_EXCEPTION548MIRAL_TRACE_EXCEPTION
549549
550void miral::WindowManagementTrace::start_drag_and_drop(miral::WindowInfo& window_info, std::vector<uint8_t> const& handle)
551try {
552 log_input();
553 mir::log_info("%s window_info=%s", __func__, dump_of(window_info).c_str());
554 trace_count++;
555 wrapped.start_drag_and_drop(window_info, handle);
556}
557MIRAL_TRACE_EXCEPTION
558
559void miral::WindowManagementTrace::end_drag_and_drop()
560try {
561 log_input();
562 mir::log_info("%s window_info=%s", __func__);
563 trace_count++;
564 wrapped.end_drag_and_drop();
565}
566MIRAL_TRACE_EXCEPTION
567
550void miral::WindowManagementTrace::modify_window(568void miral::WindowManagementTrace::modify_window(
551 miral::WindowInfo& window_info, miral::WindowSpecification const& modifications)569 miral::WindowInfo& window_info, miral::WindowSpecification const& modifications)
552try {570try {
553571
=== modified file 'miral/window_management_trace.h'
--- miral/window_management_trace.h 2017-03-02 14:29:04 +0000
+++ miral/window_management_trace.h 2017-04-13 15:18:28 +0000
@@ -69,6 +69,8 @@
69 virtual void focus_prev_within_application() override;69 virtual void focus_prev_within_application() override;
7070
71 virtual void raise_tree(Window const& root) override;71 virtual void raise_tree(Window const& root) override;
72 virtual void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) override;
73 virtual void end_drag_and_drop() override;
7274
73 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;75 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;
7476
7577
=== modified file 'miral/window_manager_tools.cpp'
--- miral/window_manager_tools.cpp 2017-03-02 14:29:04 +0000
+++ miral/window_manager_tools.cpp 2017-04-13 15:18:28 +0000
@@ -83,6 +83,12 @@
83void miral::WindowManagerTools::raise_tree(Window const& root)83void miral::WindowManagerTools::raise_tree(Window const& root)
84{ tools->raise_tree(root); }84{ tools->raise_tree(root); }
8585
86void miral::WindowManagerTools::start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle)
87{ tools->start_drag_and_drop(window_info, handle); }
88
89void miral::WindowManagerTools::end_drag_and_drop()
90{ tools->end_drag_and_drop(); }
91
86void miral::WindowManagerTools::modify_window(WindowInfo& window_info, WindowSpecification const& modifications)92void miral::WindowManagerTools::modify_window(WindowInfo& window_info, WindowSpecification const& modifications)
87{ tools->modify_window(window_info,modifications); }93{ tools->modify_window(window_info,modifications); }
8894
8995
=== modified file 'miral/window_manager_tools_implementation.h'
--- miral/window_manager_tools_implementation.h 2017-03-02 14:29:04 +0000
+++ miral/window_manager_tools_implementation.h 2017-04-13 15:18:28 +0000
@@ -26,6 +26,7 @@
2626
27#include <functional>27#include <functional>
28#include <memory>28#include <memory>
29#include <vector>
2930
30namespace mir { namespace scene { class Surface; } }31namespace mir { namespace scene { class Surface; } }
3132
@@ -66,6 +67,8 @@
66 virtual auto window_at(mir::geometry::Point cursor) const -> Window = 0;67 virtual auto window_at(mir::geometry::Point cursor) const -> Window = 0;
67 virtual auto active_display() -> mir::geometry::Rectangle const = 0;68 virtual auto active_display() -> mir::geometry::Rectangle const = 0;
68 virtual void raise_tree(Window const& root) = 0;69 virtual void raise_tree(Window const& root) = 0;
70 virtual void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) = 0;
71 virtual void end_drag_and_drop() = 0;
69 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;72 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
70 virtual auto info_for_window_id(std::string const& id) const -> WindowInfo& = 0;73 virtual auto info_for_window_id(std::string const& id) const -> WindowInfo& = 0;
71 virtual auto id_for_window(Window const& window) const -> std::string = 0;74 virtual auto id_for_window(Window const& window) const -> std::string = 0;
7275
=== modified file 'scripts/process_doxygen_xml.py'
--- scripts/process_doxygen_xml.py 2017-03-15 17:44:36 +0000
+++ scripts/process_doxygen_xml.py 2017-04-13 15:18:28 +0000
@@ -476,10 +476,21 @@
476476
477MIRAL_1.3.1 {477MIRAL_1.3.1 {
478global:478global:
479 extern "C++" {
480 miral::SetWindowManagementPolicy::?SetWindowManagementPolicy*;
481 miral::SetWindowManagementPolicy::SetWindowManagementPolicy*;
482 miral::SetWindowManagementPolicy::operator*;
483 typeinfo?for?miral::SetWindowManagementPolicy;
484 vtable?for?miral::SetWindowManagementPolicy;
485 };
486} MIRAL_1.3;
487
488MIRAL_1.4.0 {
489global:
479 extern "C++" {'''490 extern "C++" {'''
480491
481END_NEW_STANZA = ''' };492END_NEW_STANZA = ''' };
482} MIRAL_1.3;'''493} MIRAL_1.3.1;'''
483494
484def _print_report():495def _print_report():
485 print OLD_STANZAS496 print OLD_STANZAS
486497
=== modified file 'test/CMakeLists.txt'
--- test/CMakeLists.txt 2017-02-13 16:23:29 +0000
+++ test/CMakeLists.txt 2017-04-13 15:18:28 +0000
@@ -39,6 +39,16 @@
39 ${GTEST_INCLUDE_DIR}39 ${GTEST_INCLUDE_DIR}
40)40)
4141
42# MIRAL_TEST_MODERN_FEATURES lists test sourcefiles that require a recent version of Mir
43if (MIRTEST_VERSION VERSION_LESS 0.27)
44 set(MIRAL_TEST_MODERN_FEATURES)
45else()
46 set(MIRAL_TEST_MODERN_FEATURES
47 drag_and_drop.cpp
48 client_mediated_gestures.cpp
49 )
50endif()
51
42add_executable(miral-test52add_executable(miral-test
43 mru_window_list.cpp53 mru_window_list.cpp
44 active_outputs.cpp54 active_outputs.cpp
@@ -56,7 +66,9 @@
56 display_reconfiguration.cpp66 display_reconfiguration.cpp
57 active_window.cpp67 active_window.cpp
58 raise_tree.cpp68 raise_tree.cpp
59 workspaces.cpp)69 workspaces.cpp
70 ${MIRAL_TEST_MODERN_FEATURES}
71)
6072
61target_link_libraries(miral-test73target_link_libraries(miral-test
62 ${MIRTEST_LDFLAGS}74 ${MIRTEST_LDFLAGS}
6375
=== added file 'test/client_mediated_gestures.cpp'
--- test/client_mediated_gestures.cpp 1970-01-01 00:00:00 +0000
+++ test/client_mediated_gestures.cpp 2017-04-13 15:18:28 +0000
@@ -0,0 +1,285 @@
1/*
2 * Copyright © 2017 Canonical Ltd.
3 *
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 3,
6 * as published by the Free Software Foundation.
7 *
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.
12 *
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/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include <mir_toolkit/mir_window.h>
20#include <mir_toolkit/mir_blob.h>
21
22#include <mir/geometry/displacement.h>
23#include <mir/input/input_device_info.h>
24#include <mir/input/device_capability.h>
25#include <mir/shell/canonical_window_manager.h>
26#include <mir/shell/shell.h>
27
28#include <mir_test_framework/connected_client_with_a_window.h>
29#include <mir_test_framework/fake_input_device.h>
30#include <mir_test_framework/stub_server_platform_factory.h>
31#include <mir/test/event_factory.h>
32#include <mir/test/fake_shared.h>
33#include <mir/test/signal.h>
34
35#include <gmock/gmock.h>
36#include <gtest/gtest.h>
37
38#include <linux/input.h>
39
40#include <atomic>
41
42using namespace std::chrono_literals;
43using namespace mir::geometry;
44using namespace testing;
45using mir::test::fake_shared;
46using mir::test::Signal;
47
48namespace
49{
50class Cookie
51{
52public:
53 Cookie() = default;
54
55 explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {}
56
57 operator MirCookie const*() const { return self.get(); }
58
59 auto get() const -> MirCookie const* { return self.get(); }
60
61 void reset() { self.reset(); }
62
63 void reset(MirCookie const* cookie) { self.reset(cookie, deleter); }
64
65private:
66 static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); }
67
68 std::shared_ptr<MirCookie const> self;
69};
70
71void mir_cookie_release(Cookie const&) = delete;
72
73#pragma GCC diagnostic push
74#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
75struct MockWindowManager : mir::shell::CanonicalWindowManager
76{
77 using mir::shell::CanonicalWindowManager::CanonicalWindowManager;
78
79 MOCK_METHOD3(handle_request_move,
80 void(std::shared_ptr<mir::scene::Session> const&, std::shared_ptr<mir::scene::Surface> const&, uint64_t));
81};
82#pragma GCC diagnostic pop
83
84struct MouseMoverAndFaker
85{
86 void start_dragging_mouse()
87 {
88 using namespace mir::input::synthesis;
89 fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
90 }
91
92 void move_mouse(Displacement const& displacement)
93 {
94 using mir::input::synthesis::a_pointer_event;
95 fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
96 }
97
98 void release_mouse()
99 {
100 using namespace mir::input::synthesis;
101 fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
102 }
103
104private:
105 std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
106 mir_test_framework::add_fake_input_device(
107 mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})
108 };
109};
110
111Rectangle const screen_geometry{{0, 0}, {800, 600}};
112auto const receive_event_timeout = 90s;
113
114struct ClientMediatedUserGestures : mir_test_framework::ConnectedClientWithAWindow,
115 MouseMoverAndFaker
116{
117 void SetUp() override
118 {
119 initial_display_layout({screen_geometry});
120 server.override_the_window_manager_builder([this](mir::shell::FocusController* focus_controller)
121 {
122 return window_manager =
123 std::make_shared<MockWindowManager>(focus_controller, server.the_shell_display_layout());
124 });
125
126 mir_test_framework::ConnectedClientWithAWindow::SetUp();
127 mir_window_set_event_handler(window, &window_event_handler, this);
128
129 paint_window();
130
131 center_mouse();
132 }
133
134 void TearDown() override
135 {
136 reset_window_event_handler();
137 window_manager.reset();
138 mir_test_framework::ConnectedClientWithAWindow::TearDown();
139 }
140
141 auto user_initiates_gesture() -> Cookie;
142
143 std::shared_ptr<MockWindowManager> window_manager;
144
145private:
146 void center_mouse();
147 void paint_window();
148 void set_window_event_handler(std::function<void(MirEvent const* event)> const& handler);
149 void reset_window_event_handler();
150 void invoke_window_event_handler(MirEvent const* event)
151 {
152 std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
153 window_event_handler_(event);
154 }
155
156 std::mutex window_event_handler_mutex;
157 std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
158
159 static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
160};
161
162void ClientMediatedUserGestures::set_window_event_handler(std::function<void(MirEvent const* event)> const& handler)
163{
164 std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
165 window_event_handler_ = handler;
166}
167
168void ClientMediatedUserGestures::reset_window_event_handler()
169{
170 std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
171 window_event_handler_ = [](MirEvent const*) {};
172}
173
174void ClientMediatedUserGestures::window_event_handler(MirWindow* /*window*/, MirEvent const* event, void* context)
175{
176 static_cast<ClientMediatedUserGestures*>(context)->invoke_window_event_handler(event);
177}
178
179void ClientMediatedUserGestures::paint_window()
180{
181 Signal have_focus;
182
183 set_window_event_handler([&](MirEvent const* event)
184 {
185 if (mir_event_get_type(event) != mir_event_type_window)
186 return;
187
188 auto const window_event = mir_event_get_window_event(event);
189 if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
190 return;
191
192 if (mir_window_event_get_attribute_value(window_event))
193 have_focus.raise();
194 });
195
196 mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(window));
197
198 EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
199
200 reset_window_event_handler();
201}
202
203void ClientMediatedUserGestures::center_mouse()
204{
205 Signal have_mouseover;
206
207 set_window_event_handler([&](MirEvent const* event)
208 {
209 if (mir_event_get_type(event) != mir_event_type_input)
210 return;
211
212 auto const input_event = mir_event_get_input_event(event);
213
214 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
215 return;
216
217 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
218
219 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
220 return;
221
222 have_mouseover.raise();
223 });
224
225 move_mouse(0.5 * as_displacement(screen_geometry.size));
226
227// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
228// But it isn't essential for the test and we've probably waited long enough
229// for the mouse-down needed by the test to reach the window.
230// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
231 have_mouseover.wait_for(receive_event_timeout);
232
233 reset_window_event_handler();
234}
235
236auto ClientMediatedUserGestures::user_initiates_gesture() -> Cookie
237{
238 Cookie cookie;
239 Signal have_cookie;
240
241 set_window_event_handler([&](MirEvent const* event)
242 {
243 if (mir_event_get_type(event) != mir_event_type_input)
244 return;
245
246 auto const input_event = mir_event_get_input_event(event);
247
248 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
249 return;
250
251 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
252
253 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
254 return;
255
256 cookie = Cookie{mir_input_event_get_cookie(input_event)};
257 have_cookie.raise();
258 });
259
260 start_dragging_mouse();
261
262 EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
263
264 reset_window_event_handler();
265 return cookie;
266}
267}
268
269TEST_F(ClientMediatedUserGestures, when_user_initiates_gesture_client_receives_cookie)
270{
271 auto const cookie = user_initiates_gesture();
272
273 EXPECT_THAT(cookie.get(), NotNull());
274}
275
276TEST_F(ClientMediatedUserGestures, when_client_initiates_move_window_manager_handles_request)
277{
278 auto const cookie = user_initiates_gesture();
279 Signal have_request;
280 EXPECT_CALL(*window_manager, handle_request_move(_, _, _)).WillOnce(InvokeWithoutArgs([&]{ have_request.raise(); }));
281
282 mir_window_request_user_move(window, cookie);
283
284 EXPECT_THAT(have_request.wait_for(receive_event_timeout), Eq(true));
285}
0286
=== added file 'test/drag_and_drop.cpp'
--- test/drag_and_drop.cpp 1970-01-01 00:00:00 +0000
+++ test/drag_and_drop.cpp 2017-04-13 15:18:28 +0000
@@ -0,0 +1,641 @@
1/*
2 * Copyright © 2017 Canonical Ltd.
3 *
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 3,
6 * as published by the Free Software Foundation.
7 *
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.
12 *
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/>.
15 *
16 * Authored by: Alan Griffiths <alan@octopull.co.uk>
17 */
18
19#include <miral/window_management_policy_addendum2.h>
20
21#include <mir/client/blob.h>
22#include <mir/client/cookie.h>
23#include <mir/client/window.h>
24#include <mir/client/window_spec.h>
25#include <mir_toolkit/mir_buffer_stream.h>
26#include <mir_toolkit/extensions/drag_and_drop.h>
27
28#include <mir/geometry/displacement.h>
29#include <mir/input/input_device_info.h>
30#include <mir/input/device_capability.h>
31#include <mir/shell/shell.h>
32
33#include "test_server.h"
34#include <mir_test_framework/fake_input_device.h>
35#include <mir_test_framework/stub_server_platform_factory.h>
36#include <mir/test/event_factory.h>
37#include <mir/test/signal.h>
38
39#include <gmock/gmock.h>
40#include <gtest/gtest.h>
41
42#include <linux/input.h>
43#include <uuid/uuid.h>
44
45#include <boost/throw_exception.hpp>
46#include <atomic>
47
48using namespace std::chrono_literals;
49using namespace mir::client;
50using namespace mir::geometry;
51using namespace testing;
52using mir::test::Signal;
53
54namespace
55{
56struct MouseMoverAndFaker
57{
58 void start_dragging_mouse()
59 {
60 using namespace mir::input::synthesis;
61 fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
62 }
63
64 void move_mouse(Displacement const& displacement)
65 {
66 using mir::input::synthesis::a_pointer_event;
67 fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
68 }
69
70 void release_mouse()
71 {
72 using namespace mir::input::synthesis;
73 fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
74 }
75
76private:
77 std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
78 mir_test_framework::add_fake_input_device(
79 mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})};
80};
81
82Rectangle const screen_geometry{{0,0}, {800,600}};
83auto const receive_event_timeout = 1s; //90s;
84
85struct ConnectedClientWithAWindow : miral::TestServer
86{
87 Connection connection;
88 Window window;
89
90 void SetUp() override
91 {
92 miral::TestServer::SetUp();
93 connection = connect_client(__func__);
94 window = WindowSpec::for_normal_window(connection, surface_size.width.as_int(), surface_size.height.as_int())
95 .set_pixel_format(mir_pixel_format_abgr_8888)
96 .set_name("ConnectedClientWithAWindow")
97 .set_buffer_usage(mir_buffer_usage_hardware)
98 .create_window();
99 }
100
101 void TearDown() override
102 {
103 window.reset();
104 connection.reset();
105 miral::TestServer::TearDown();
106 }
107
108 mir::geometry::Size const surface_size {640, 480};
109};
110
111struct DragAndDrop : ConnectedClientWithAWindow,
112 MouseMoverAndFaker
113{
114 MirDragAndDropV1 const* dnd = nullptr;
115
116 void SetUp() override
117 {
118 mir_test_framework::set_next_display_rects(std::unique_ptr<std::vector<Rectangle>>(new std::vector<Rectangle>({screen_geometry})));
119
120 ConnectedClientWithAWindow::SetUp();
121 dnd = mir_drag_and_drop_v1(connection);
122 mir_window_set_event_handler(window, &window_event_handler, this);
123 if (dnd) dnd->set_start_drag_and_drop_callback(window, &window_dnd_start_handler, this);
124
125 create_target_window();
126
127 paint_window(window);
128
129 center_mouse();
130 }
131
132 void TearDown() override
133 {
134 reset_window_event_handler(target_window);
135 reset_window_event_handler(window);
136 target_window.reset();
137 another_connection.reset();
138 ConnectedClientWithAWindow::TearDown();
139 }
140
141 auto user_initiates_drag() -> Cookie;
142 auto client_requests_drag(Cookie const& cookie) -> Blob;
143 auto handle_from_mouse_move() -> Blob;
144 auto handle_from_mouse_leave() -> Blob;
145 auto handle_from_mouse_enter() -> Blob;
146 auto handle_from_mouse_release() -> Blob;
147 auto count_of_handles_when_moving_mouse() -> int;
148
149private:
150 auto build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy> override;
151 void center_mouse();
152 void paint_window(MirWindow* w);
153 void set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler);
154 void set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler);
155 void reset_window_event_handler(MirWindow* window);
156
157 void create_target_window()
158 {
159 another_connection = connect_client("another_connection");
160 target_window = WindowSpec::
161 for_normal_window(connection, screen_geometry.size.width.as_int(), screen_geometry.size.height.as_int())
162 .set_pixel_format(mir_pixel_format_abgr_8888)
163 .set_name("target_window")
164 .set_buffer_usage(mir_buffer_usage_hardware)
165 .set_event_handler(&window_event_handler, this)
166 .create_window();
167
168 paint_window(target_window);
169 }
170
171 void invoke_window_event_handler(MirWindow* window, MirEvent const* event)
172 {
173 std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
174 if (window == this->window) window_event_handler_(event);
175 if (window == target_window) target_window_event_handler_(event);
176 }
177
178 void invoke_window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event)
179 {
180 std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
181 if (window == this->window) window_dnd_start_(event);
182 }
183
184 std::mutex window_event_handler_mutex;
185 std::function<void(MirDragAndDropEvent const* event)> window_dnd_start_ = [](MirDragAndDropEvent const*) {};
186 std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
187 std::function<void(MirEvent const* event)> target_window_event_handler_ = [](MirEvent const*) {};
188
189 static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
190 static void window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context);
191
192 Connection another_connection;
193 Window target_window;
194};
195
196void DragAndDrop::set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler)
197{
198 std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
199 if (window == this->window) window_event_handler_ = handler;
200 if (window == target_window) target_window_event_handler_ = handler;
201}
202
203void DragAndDrop::set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler)
204{
205std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
206if (window == this->window) window_dnd_start_ = handler;
207}
208
209
210void DragAndDrop::reset_window_event_handler(MirWindow* window)
211{
212 if (window == this->window) window_event_handler_ = [](MirEvent const*) {};
213 if (window == target_window) target_window_event_handler_ = [](MirEvent const*) {};
214}
215
216void DragAndDrop::paint_window(MirWindow* w)
217{
218 Signal have_focus;
219
220 set_window_event_handler(w, [&](MirEvent const* event)
221 {
222 if (mir_event_get_type(event) != mir_event_type_window)
223 return;
224
225 auto const window_event = mir_event_get_window_event(event);
226 if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
227 return;
228
229 if (mir_window_event_get_attribute_value(window_event))
230 have_focus.raise();
231 });
232
233 mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(w));
234
235 EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
236
237 reset_window_event_handler(w);
238}
239
240void DragAndDrop::center_mouse()
241{
242 Signal have_mouseover;
243
244 set_window_event_handler(window, [&](MirEvent const* event)
245 {
246 if (mir_event_get_type(event) != mir_event_type_input)
247 return;
248
249 auto const input_event = mir_event_get_input_event(event);
250
251 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
252 return;
253
254 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
255
256 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
257 return;
258
259 have_mouseover.raise();
260 });
261
262 move_mouse(0.5 * as_displacement(screen_geometry.size));
263
264// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
265// But it isn't essential for the test and we've probably waited long enough
266// for the mouse-down needed by the test to reach the window.
267// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
268 have_mouseover.wait_for(receive_event_timeout);
269
270 reset_window_event_handler(window);
271}
272
273void DragAndDrop::window_event_handler(MirWindow* window, MirEvent const* event, void* context)
274{
275 static_cast<DragAndDrop*>(context)->invoke_window_event_handler(window, event);
276}
277
278void DragAndDrop::window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context)
279{
280 static_cast<DragAndDrop*>(context)->invoke_window_dnd_start_handler(window, event);
281}
282
283
284auto DragAndDrop::user_initiates_drag() -> Cookie
285{
286 Cookie cookie;
287 Signal have_cookie;
288
289 set_window_event_handler(window, [&](MirEvent const* event)
290 {
291 if (mir_event_get_type(event) != mir_event_type_input)
292 return;
293
294 auto const input_event = mir_event_get_input_event(event);
295
296 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
297 return;
298
299 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
300
301 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
302 return;
303
304 cookie = Cookie{mir_input_event_get_cookie(input_event)};
305 have_cookie.raise();
306 });
307
308 start_dragging_mouse();
309
310 EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
311
312 reset_window_event_handler(window);
313 return cookie;
314}
315
316auto DragAndDrop::client_requests_drag(Cookie const& cookie) -> Blob
317{
318 Blob blob;
319 Signal initiated;
320
321 set_window_dnd_start_handler(window, [&](MirDragAndDropEvent const* event)
322 {
323 if (dnd)
324 blob.reset(dnd->start_drag_and_drop(event));
325
326 if (blob)
327 initiated.raise();
328 });
329
330 EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
331
332 if (dnd)
333 dnd->request_drag_and_drop(window, cookie);
334
335 EXPECT_TRUE(initiated.wait_for(receive_event_timeout));
336
337 reset_window_event_handler(window);
338 return blob;
339}
340
341auto DragAndDrop::handle_from_mouse_move() -> Blob
342{
343 Blob blob;
344 Signal have_blob;
345
346 set_window_event_handler(window, [&](MirEvent const* event)
347 {
348 if (mir_event_get_type(event) != mir_event_type_input)
349 return;
350
351 auto const input_event = mir_event_get_input_event(event);
352
353 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
354 return;
355
356 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
357
358 EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
359
360 if (dnd)
361 blob.reset(dnd->pointer_drag_and_drop(pointer_event));
362
363 if (blob)
364 have_blob.raise();
365 });
366
367 move_mouse({1,1});
368
369 EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
370
371 reset_window_event_handler(window);
372 return blob;
373}
374
375auto DragAndDrop::handle_from_mouse_leave() -> Blob
376{
377 Blob blob;
378 Signal have_blob;
379
380 set_window_event_handler(window, [&](MirEvent const* event)
381 {
382 if (mir_event_get_type(event) != mir_event_type_input)
383 return;
384
385 auto const input_event = mir_event_get_input_event(event);
386
387 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
388 return;
389
390 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
391
392 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_leave)
393 return;
394
395 EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
396
397 if (dnd)
398 blob.reset(dnd->pointer_drag_and_drop(pointer_event));
399
400 if (blob)
401 have_blob.raise();
402 });
403
404 move_mouse({1,1});
405 move_mouse(0.5 * as_displacement(surface_size));
406
407 EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
408
409 reset_window_event_handler(window);
410 return blob;
411}
412
413auto DragAndDrop::handle_from_mouse_enter() -> Blob
414{
415 Blob blob;
416 Signal have_blob;
417
418 set_window_event_handler(target_window, [&](MirEvent const* event)
419 {
420 if (mir_event_get_type(event) != mir_event_type_input)
421 return;
422
423 auto const input_event = mir_event_get_input_event(event);
424
425 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
426 return;
427
428 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
429
430 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
431 return;
432
433 EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
434
435 if (dnd)
436 blob.reset(dnd->pointer_drag_and_drop(pointer_event));
437
438 if (blob)
439 have_blob.raise();
440 });
441
442 move_mouse({1,1});
443 move_mouse(0.5 * as_displacement(surface_size));
444
445 EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
446
447 reset_window_event_handler(target_window);
448 return blob;
449}
450
451auto DragAndDrop::handle_from_mouse_release() -> Blob
452{
453 Blob blob;
454 Signal have_blob;
455
456 set_window_event_handler(target_window, [&](MirEvent const* event)
457 {
458 if (mir_event_get_type(event) != mir_event_type_input)
459 return;
460
461 auto const input_event = mir_event_get_input_event(event);
462
463 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
464 return;
465
466 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
467
468 if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_up)
469 return;
470
471 EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
472
473 if (dnd)
474 blob.reset(dnd->pointer_drag_and_drop(pointer_event));
475
476 if (blob)
477 have_blob.raise();
478 });
479
480 move_mouse({1,1});
481 move_mouse(0.5 * as_displacement(surface_size));
482 release_mouse();
483
484 EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
485
486 reset_window_event_handler(target_window);
487 return blob;
488}
489
490auto DragAndDrop::count_of_handles_when_moving_mouse() -> int
491{
492 Signal have_3_events;
493 std::atomic<int> events{0};
494 std::atomic<int> handles{0};
495
496 auto counter = [&](MirEvent const* event)
497 {
498 if (mir_event_get_type(event) != mir_event_type_input)
499 return;
500
501 auto const input_event = mir_event_get_input_event(event);
502
503 if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
504 return;
505
506 auto const pointer_event = mir_input_event_get_pointer_event(input_event);
507
508 EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
509
510 Blob blob;
511 if (dnd)
512 blob.reset(dnd->pointer_drag_and_drop(pointer_event));
513
514 if (blob)
515 handles.fetch_add(1);
516
517 if (events.fetch_add(1) == 2)
518 have_3_events.raise();
519 };
520
521 set_window_event_handler(window, counter);
522 set_window_event_handler(target_window, counter);
523
524 start_dragging_mouse();
525 move_mouse({1,1});
526 release_mouse();
527
528 EXPECT_TRUE(have_3_events.wait_for(receive_event_timeout));
529
530 reset_window_event_handler(window);
531 reset_window_event_handler(target_window);
532 return handles;
533}
534
535auto DragAndDrop::build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy>
536{
537 struct DnDWindowManagerPolicy : miral::TestServer::TestWindowManagerPolicy, miral::WindowManagementPolicyAddendum2
538 {
539 using miral::TestServer::TestWindowManagerPolicy::TestWindowManagerPolicy;
540
541 void handle_request_drag_and_drop(miral::WindowInfo& window_info) override
542 {
543 uuid_t uuid;
544 uuid_generate(uuid);
545 std::vector<uint8_t> const handle{std::begin(uuid), std::end(uuid)};
546
547 tools.start_drag_and_drop(window_info, handle);
548 }
549
550 void handle_request_move(miral::WindowInfo&, MirInputEvent const*) override {}
551 };
552
553 return std::make_unique<DnDWindowManagerPolicy>(tools, *this);
554}
555
556MATCHER_P(BlobContentEq, p, "")
557{
558 if (!arg || !p)
559 return false;
560 if (mir_blob_size(arg) != mir_blob_size(p))
561 return false;
562 return !memcmp(mir_blob_data(arg), mir_blob_data(p), mir_blob_size(p));
563}
564}
565
566TEST_F(DragAndDrop, when_user_initiates_drag_client_receives_cookie)
567{
568 auto const cookie = user_initiates_drag();
569
570 EXPECT_THAT(cookie, NotNull());
571}
572
573TEST_F(DragAndDrop, when_client_requests_drags_it_receives_handle)
574{
575 auto const cookie = user_initiates_drag();
576 ASSERT_THAT(cookie, NotNull());
577
578 auto const handle = client_requests_drag(cookie);
579
580 EXPECT_THAT(handle, NotNull());
581}
582
583TEST_F(DragAndDrop, during_drag_when_user_moves_mouse_client_receives_handle)
584{
585 auto const cookie = user_initiates_drag();
586 ASSERT_THAT(cookie, NotNull());
587 auto const handle_from_request = client_requests_drag(cookie);
588
589 auto const handle = handle_from_mouse_move();
590
591 EXPECT_THAT(handle, NotNull());
592 EXPECT_THAT(handle, BlobContentEq(handle_from_request));
593}
594
595TEST_F(DragAndDrop, when_drag_moves_from_window_leave_event_contains_handle)
596{
597 auto const cookie = user_initiates_drag();
598 ASSERT_THAT(cookie, NotNull());
599 auto const handle_from_request = client_requests_drag(cookie);
600
601 auto const handle = handle_from_mouse_leave();
602
603 EXPECT_THAT(handle, NotNull());
604 EXPECT_THAT(handle, BlobContentEq(handle_from_request));
605}
606
607TEST_F(DragAndDrop, when_drag_enters_target_window_enter_event_contains_handle)
608{
609 auto const cookie = user_initiates_drag();
610 ASSERT_THAT(cookie, NotNull());
611 auto const handle_from_request = client_requests_drag(cookie);
612
613 auto const handle = handle_from_mouse_enter();
614
615 EXPECT_THAT(handle, NotNull());
616 EXPECT_THAT(handle, BlobContentEq(handle_from_request));
617}
618
619TEST_F(DragAndDrop, when_drag_releases_target_window_release_event_contains_handle)
620{
621 auto const cookie = user_initiates_drag();
622 ASSERT_THAT(cookie, NotNull());
623 auto const handle_from_request = client_requests_drag(cookie);
624
625 auto const handle = handle_from_mouse_release();
626
627 EXPECT_THAT(handle, NotNull());
628 EXPECT_THAT(handle, BlobContentEq(handle_from_request));
629}
630
631TEST_F(DragAndDrop, after_drag_finishes_pointer_events_no_longer_contain_handle)
632{
633 auto const cookie = user_initiates_drag();
634 ASSERT_THAT(cookie, NotNull());
635 client_requests_drag(cookie);
636 handle_from_mouse_release();
637
638 invoke_tools([](miral::WindowManagerTools& tools) { tools.end_drag_and_drop(); });
639
640 EXPECT_THAT(count_of_handles_when_moving_mouse(), Eq(0));
641}

Subscribers

People subscribed via source and target branches

to all changes: