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

Proposed by Brandon Schaefer on 2017-04-13
Status: Rejected
Rejected by: Alan Griffiths on 2017-06-08
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 2017-04-13 Disapprove on 2017-06-08
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.
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
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2017-03-24 17:44:48 +0000
3+++ CMakeLists.txt 2017-04-13 15:18:28 +0000
4@@ -41,8 +41,8 @@
5 include_directories(include SYSTEM ${MIRCLIENT_INCLUDE_DIRS})
6
7 set(MIRAL_VERSION_MAJOR 1)
8-set(MIRAL_VERSION_MINOR 3)
9-set(MIRAL_VERSION_PATCH 2)
10+set(MIRAL_VERSION_MINOR 4)
11+set(MIRAL_VERSION_PATCH 0)
12
13 set(MIRAL_VERSION ${MIRAL_VERSION_MAJOR}.${MIRAL_VERSION_MINOR}.${MIRAL_VERSION_PATCH})
14
15
16=== modified file 'debian/changelog'
17--- debian/changelog 2017-03-30 14:18:38 +0000
18+++ debian/changelog 2017-04-13 15:18:28 +0000
19@@ -1,3 +1,14 @@
20+miral (1.4.0) UNRELEASED; urgency=medium
21+
22+ * New upstream release 1.4.0 (https://launchpad.net/miral/+milestone/1.4.0)
23+ - ABI summary:
24+ . miral ABI unchanged at 2
25+ - Enhancements:
26+ . Support for passing messages to enable Drag & Drop
27+ - Bugs fixed:
28+
29+ -- Alan Griffiths <alan@Octopull-X1c3> Tue, 21 Mar 2017 17:57:20 +0000
30+
31 miral (1.3.2+17.04.20170330.5-0ubuntu1) zesty; urgency=medium
32
33 * New upstream release 1.3.2 (https://launchpad.net/miral/+milestone/1.3.2)
34@@ -74,11 +85,11 @@
35 . Chrome-less shell hint does not work any more (LP: #1658117)
36 . WindowSpec::set_state() wrapper for mir_window_spec_set_state()
37 (LP: #1661256)
38- . "$ miral-app -kiosk" fails with "Unknown command line options:
39+ . "$ miral-app -kiosk" fails with "Unknown command line options:
40 --desktop_file_hint=miral-shell.desktop" (LP: #1660933)
41 . libmiral] Fix focus and movement rules for Input Method and Satellite
42 windows. (LP: #1660691)
43-
44+
45
46 -- Alan Griffiths <alan.griffiths@canonical.com> Wed, 15 Feb 2017 14:05:46 +0000
47
48
49=== modified file 'debian/libmiral2.symbols'
50--- debian/libmiral2.symbols 2017-03-15 17:44:36 +0000
51+++ debian/libmiral2.symbols 2017-04-13 15:18:28 +0000
52@@ -389,3 +389,7 @@
53 (c++)"miral::SetWindowManagementPolicy::~SetWindowManagementPolicy()@MIRAL_1.3.1" 1.3.1
54 (c++)"miral::SetWindowManagementPolicy::~SetWindowManagementPolicy()@MIRAL_1.3.1" 1.3.1
55 (c++)"miral::SetWindowManagementPolicy::operator()(mir::Server&) const@MIRAL_1.3.1" 1.3.1
56+ MIRAL_1.4.0@MIRAL_1.4.0 1.4.0
57+ (c++)"miral::WindowManagerTools::end_drag_and_drop()@MIRAL_1.4.0" 1.4.0
58+ (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
59+ (c++)"typeinfo for miral::WindowManagementPolicyAddendum2@MIRAL_1.4.0" 1.4.0
60
61=== added file 'include/miral/window_management_policy_addendum2.h'
62--- include/miral/window_management_policy_addendum2.h 1970-01-01 00:00:00 +0000
63+++ include/miral/window_management_policy_addendum2.h 2017-04-13 15:18:28 +0000
64@@ -0,0 +1,70 @@
65+/*
66+ * Copyright © 2017 Canonical Ltd.
67+ *
68+ * This program is free software: you can redistribute it and/or modify it
69+ * under the terms of the GNU General Public License version 3,
70+ * as published by the Free Software Foundation.
71+ *
72+ * This program is distributed in the hope that it will be useful,
73+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
74+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
75+ * GNU General Public License for more details.
76+ *
77+ * You should have received a copy of the GNU General Public License
78+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
79+ *
80+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
81+ */
82+
83+#ifndef MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
84+#define MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
85+
86+#include <mir_toolkit/client_types.h>
87+#include <mir/version.h>
88+
89+namespace miral
90+{
91+struct WindowInfo;
92+
93+/**
94+ * Handle additional client requests.
95+ *
96+ * \note This interface is intended to be implemented by a WindowManagementPolicy
97+ * implementation, we can't add these functions directly to that interface without
98+ * breaking ABI (the vtab could be incompatible).
99+ * When initializing the window manager this interface will be detected by
100+ * dynamic_cast and registered accordingly.
101+ */
102+class WindowManagementPolicyAddendum2
103+{
104+public:
105+/** @name handle requests originating from the client
106+ * The policy is expected to update the model as appropriate
107+ * @{ */
108+ /** request from client to initiate drag and drop
109+ * \note the request has already been validated against the requesting event
110+ *
111+ * @param window_info the window
112+ */
113+ virtual void handle_request_drag_and_drop(WindowInfo& window_info) = 0;
114+
115+ /** request from client to initiate move
116+ * \note the request has already been validated against the requesting event
117+ *
118+ * @param window_info the window
119+ * @param input_event the requesting event
120+ */
121+ virtual void handle_request_move(WindowInfo& window_info, MirInputEvent const* input_event) = 0;
122+/** @} */
123+
124+ virtual ~WindowManagementPolicyAddendum2() = default;
125+ WindowManagementPolicyAddendum2() = default;
126+ WindowManagementPolicyAddendum2(WindowManagementPolicyAddendum2 const&) = delete;
127+ WindowManagementPolicyAddendum2& operator=(WindowManagementPolicyAddendum2 const&) = delete;
128+};
129+#if MIRAL_VERSION >= MIR_VERSION_NUMBER(2, 0, 0)
130+#error "We've presumably broken ABI - please roll this interface into WindowManagementPolicy"
131+#endif
132+}
133+
134+#endif //MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
135
136=== modified file 'include/miral/window_manager_tools.h'
137--- include/miral/window_manager_tools.h 2017-03-02 14:29:04 +0000
138+++ include/miral/window_manager_tools.h 2017-04-13 15:18:28 +0000
139@@ -166,6 +166,18 @@
140 /// Raise window and all its children
141 void raise_tree(Window const& root);
142
143+ /** Start drag and drop. The handle will be passed to the client which can
144+ * then use it to talk to the whatever service is being used to support drag
145+ * and drop (e.g. on Ubuntu the content hub).
146+ *
147+ * @param window_info source window
148+ * @param handle drag handle
149+ */
150+ void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle);
151+
152+ /// End drag and drop
153+ void end_drag_and_drop();
154+
155 /// Apply modifications to a window
156 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications);
157
158
159=== modified file 'miral-shell/miral-screencast.sh'
160--- miral-shell/miral-screencast.sh 2016-12-09 11:08:07 +0000
161+++ miral-shell/miral-screencast.sh 2017-04-13 15:18:28 +0000
162@@ -2,7 +2,7 @@
163 width=1920
164 height=1080
165 output=screencast.mp4
166-socket=${XDG_RUNTIME_DIR}/mir_socket
167+socket=${XDG_RUNTIME_DIR}/miral_socket
168 if [ -v MIR_SERVER ]; then socket=${MIR_SERVER}; fi
169
170 while [ $# -gt 0 ]
171
172=== modified file 'miral/CMakeLists.txt'
173--- miral/CMakeLists.txt 2017-03-20 12:28:06 +0000
174+++ miral/CMakeLists.txt 2017-04-13 15:18:28 +0000
175@@ -61,6 +61,7 @@
176 ${CMAKE_SOURCE_DIR}/include/mir/client/display_config.h
177 ${CMAKE_SOURCE_DIR}/include/mir/client/window.h
178 ${CMAKE_SOURCE_DIR}/include/mir/client/detail/mir_forward_compatibility.h
179+ ${CMAKE_SOURCE_DIR}/include/miral/window_management_policy_addendum2.h
180 )
181
182 target_link_libraries(miral
183
184=== modified file 'miral/basic_window_manager.cpp'
185--- miral/basic_window_manager.cpp 2017-03-21 12:30:42 +0000
186+++ miral/basic_window_manager.cpp 2017-04-13 15:18:28 +0000
187@@ -19,6 +19,7 @@
188 #include "basic_window_manager.h"
189 #include "miral/window_manager_tools.h"
190 #include "miral/workspace_policy.h"
191+#include "miral/window_management_policy_addendum2.h"
192
193 #include <mir/scene/session.h>
194 #include <mir/scene/surface.h>
195@@ -70,7 +71,6 @@
196
197 namespace
198 {
199-
200 auto find_workspace_policy(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WorkspacePolicy*
201 {
202 miral::WorkspacePolicy* result = dynamic_cast<miral::WorkspacePolicy*>(policy.get());
203@@ -82,6 +82,23 @@
204
205 return &null_workspace_policy;
206 }
207+
208+auto find_policy_addendum2(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WindowManagementPolicyAddendum2*
209+{
210+ miral::WindowManagementPolicyAddendum2* result = dynamic_cast<miral::WindowManagementPolicyAddendum2*>(policy.get());
211+
212+ if (result)
213+ return result;
214+
215+ struct NullWindowManagementPolicyAddendum2 : miral::WindowManagementPolicyAddendum2
216+ {
217+ void handle_request_drag_and_drop(miral::WindowInfo&) override {}
218+ void handle_request_move(miral::WindowInfo&, MirInputEvent const*) override {}
219+ };
220+ static NullWindowManagementPolicyAddendum2 null_workspace_policy;
221+
222+ return &null_workspace_policy;
223+}
224 }
225
226
227@@ -94,10 +111,18 @@
228 display_layout(display_layout),
229 persistent_surface_store{persistent_surface_store},
230 policy(build(WindowManagerTools{this})),
231- workspace_policy{find_workspace_policy(policy)}
232+ workspace_policy{find_workspace_policy(policy)},
233+ policy2{find_policy_addendum2(policy)}
234 {
235 }
236
237+miral::BasicWindowManager::~BasicWindowManager()
238+{
239+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
240+ if (last_input_event)
241+ mir_event_unref(last_input_event);
242+#endif
243+}
244 void miral::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session)
245 {
246 Locker lock{this};
247@@ -362,10 +387,24 @@
248 #if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
249 void miral::BasicWindowManager::handle_request_drag_and_drop(
250 std::shared_ptr<mir::scene::Session> const& /*session*/,
251- std::shared_ptr<mir::scene::Surface> const& /*surface*/,
252- uint64_t /*timestamp*/)
253-{
254- // TODO
255+ std::shared_ptr<mir::scene::Surface> const& surface,
256+ uint64_t timestamp)
257+{
258+ Locker lock{this};
259+ if (timestamp >= last_input_event_timestamp)
260+ policy2->handle_request_drag_and_drop(info_for(surface));
261+}
262+
263+void miral::BasicWindowManager::handle_request_move(
264+ std::shared_ptr<mir::scene::Session> const& /*session*/,
265+ std::shared_ptr<mir::scene::Surface> const& surface,
266+ uint64_t timestamp)
267+{
268+ std::lock_guard<decltype(mutex)> lock(mutex);
269+ if (timestamp >= last_input_event_timestamp && last_input_event)
270+ {
271+ policy2->handle_request_move(info_for(surface), mir_event_get_input_event(last_input_event));
272+ }
273 }
274 #endif
275
276@@ -741,6 +780,24 @@
277 focus_controller->raise({begin(windows), end(windows)});
278 }
279
280+void miral::BasicWindowManager::start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle)
281+{
282+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
283+ std::shared_ptr<scene::Surface>(window_info.window())->start_drag_and_drop(handle);
284+ focus_controller->set_drag_and_drop_handle(handle);
285+#else
286+ (void)window_info;
287+ (void)handle;
288+#endif
289+}
290+
291+void miral::BasicWindowManager::end_drag_and_drop()
292+{
293+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
294+ focus_controller->clear_drag_and_drop_handle();
295+#endif
296+}
297+
298 void miral::BasicWindowManager::move_tree(miral::WindowInfo& root, mir::geometry::Displacement movement)
299 {
300 if (movement == mir::geometry::Displacement{})
301@@ -1163,25 +1220,22 @@
302
303 void miral::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev)
304 {
305- auto iev = mir_keyboard_event_input_event(kev);
306- last_input_event_timestamp = mir_input_event_get_event_time(iev);
307+ update_event_timestamp(mir_keyboard_event_input_event(kev));
308 }
309
310 void miral::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev)
311 {
312- auto iev = mir_pointer_event_input_event(pev);
313 auto pointer_action = mir_pointer_event_action(pev);
314
315 if (pointer_action == mir_pointer_action_button_up ||
316 pointer_action == mir_pointer_action_button_down)
317 {
318- last_input_event_timestamp = mir_input_event_get_event_time(iev);
319+ update_event_timestamp(mir_pointer_event_input_event(pev));
320 }
321 }
322
323 void miral::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev)
324 {
325- auto iev = mir_touch_event_input_event(tev);
326 auto touch_count = mir_touch_event_point_count(tev);
327 for (unsigned i = 0; i < touch_count; i++)
328 {
329@@ -1189,12 +1243,22 @@
330 if (touch_action == mir_touch_action_up ||
331 touch_action == mir_touch_action_down)
332 {
333- last_input_event_timestamp = mir_input_event_get_event_time(iev);
334+ update_event_timestamp(mir_touch_event_input_event(tev));
335 break;
336 }
337 }
338 }
339
340+void miral::BasicWindowManager::update_event_timestamp(MirInputEvent const* iev)
341+{
342+ last_input_event_timestamp = mir_input_event_get_event_time(iev);
343+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
344+ if (last_input_event)
345+ mir_event_unref(last_input_event);
346+ last_input_event = mir_event_ref(mir_input_event_get_event(iev));
347+#endif
348+}
349+
350 void miral::BasicWindowManager::invoke_under_lock(std::function<void()> const& callback)
351 {
352 Locker lock{this};
353
354=== modified file 'miral/basic_window_manager.h'
355--- miral/basic_window_manager.h 2017-03-21 12:30:42 +0000
356+++ miral/basic_window_manager.h 2017-04-13 15:18:28 +0000
357@@ -46,6 +46,7 @@
358 namespace miral
359 {
360 class WorkspacePolicy;
361+class WindowManagementPolicyAddendum2;
362 using mir::shell::SurfaceSet;
363 using WindowManagementPolicyBuilder =
364 std::function<std::unique_ptr<miral::WindowManagementPolicy>(miral::WindowManagerTools const& tools)>;
365@@ -61,6 +62,7 @@
366 std::shared_ptr<mir::shell::DisplayLayout> const& display_layout,
367 std::shared_ptr<mir::shell::PersistentSurfaceStore> const& persistent_surface_store,
368 WindowManagementPolicyBuilder const& build);
369+ ~BasicWindowManager();
370
371 void add_session(std::shared_ptr<mir::scene::Session> const& session) override;
372
373@@ -101,6 +103,11 @@
374 std::shared_ptr<mir::scene::Session> const& session,
375 std::shared_ptr<mir::scene::Surface> const& surface,
376 uint64_t timestamp) override;
377+
378+ void handle_request_move(
379+ std::shared_ptr<mir::scene::Session> const& session,
380+ std::shared_ptr<mir::scene::Surface> const& surface,
381+ uint64_t timestamp) override;
382 #endif
383
384 int set_surface_attribute(
385@@ -161,6 +168,8 @@
386 auto active_display() -> mir::geometry::Rectangle const override;
387
388 void raise_tree(Window const& root) override;
389+ void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) override;
390+ void end_drag_and_drop() override;
391
392 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;
393
394@@ -190,6 +199,7 @@
395
396 std::unique_ptr<WindowManagementPolicy> const policy;
397 WorkspacePolicy* const workspace_policy;
398+ WindowManagementPolicyAddendum2* const policy2;
399
400 std::mutex mutex;
401 SessionInfoMap app_info;
402@@ -197,6 +207,9 @@
403 mir::geometry::Rectangles displays;
404 mir::geometry::Point cursor;
405 uint64_t last_input_event_timestamp{0};
406+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
407+ MirEvent const* last_input_event{nullptr};
408+#endif
409 miral::MRUWindowList mru_active_windows;
410 using FullscreenSurfaces = std::set<Window>;
411 FullscreenSurfaces fullscreen_surfaces;
412@@ -213,6 +226,7 @@
413 void update_event_timestamp(MirKeyboardEvent const* kev);
414 void update_event_timestamp(MirPointerEvent const* pev);
415 void update_event_timestamp(MirTouchEvent const* tev);
416+ void update_event_timestamp(MirInputEvent const* iev);
417
418 auto can_activate_window_for_session(miral::Application const& session) -> bool;
419 auto can_activate_window_for_session_in_workspace(
420
421=== modified file 'miral/symbols.map'
422--- miral/symbols.map 2017-03-15 17:44:36 +0000
423+++ miral/symbols.map 2017-04-13 15:18:28 +0000
424@@ -401,3 +401,24 @@
425 vtable?for?miral::SetWindowManagementPolicy;
426 };
427 } MIRAL_1.3;
428+
429+MIRAL_1.4.0 {
430+global:
431+ extern "C++" {
432+ miral::WindowManagementPolicyAddendum2::?WindowManagementPolicyAddendum2*;
433+ miral::WindowManagementPolicyAddendum2::WindowManagementPolicyAddendum2*;
434+ miral::WindowManagementPolicyAddendum2::operator*;
435+ miral::WindowManagerTools::end_drag_and_drop*;
436+ miral::WindowManagerTools::start_drag_and_drop*;
437+ miral::toolkit::Window::Window*;
438+ non-virtual?thunk?to?miral::WindowManagementPolicyAddendum2::?WindowManagementPolicyAddendum2*;
439+ typeinfo?for?miral::WindowManagementPolicyAddendum2;
440+ typeinfo?for?miral::toolkit::Window;
441+ typeinfo?for?miral::toolkit::WindowId;
442+ typeinfo?for?miral::toolkit::WindowSpec;
443+ vtable?for?miral::WindowManagementPolicyAddendum2;
444+ vtable?for?miral::toolkit::Window;
445+ vtable?for?miral::toolkit::WindowId;
446+ vtable?for?miral::toolkit::WindowSpec;
447+ };
448+} MIRAL_1.3.1;
449
450=== modified file 'miral/window_management_trace.cpp'
451--- miral/window_management_trace.cpp 2017-03-03 10:06:02 +0000
452+++ miral/window_management_trace.cpp 2017-04-13 15:18:28 +0000
453@@ -547,6 +547,24 @@
454 }
455 MIRAL_TRACE_EXCEPTION
456
457+void miral::WindowManagementTrace::start_drag_and_drop(miral::WindowInfo& window_info, std::vector<uint8_t> const& handle)
458+try {
459+ log_input();
460+ mir::log_info("%s window_info=%s", __func__, dump_of(window_info).c_str());
461+ trace_count++;
462+ wrapped.start_drag_and_drop(window_info, handle);
463+}
464+MIRAL_TRACE_EXCEPTION
465+
466+void miral::WindowManagementTrace::end_drag_and_drop()
467+try {
468+ log_input();
469+ mir::log_info("%s window_info=%s", __func__);
470+ trace_count++;
471+ wrapped.end_drag_and_drop();
472+}
473+MIRAL_TRACE_EXCEPTION
474+
475 void miral::WindowManagementTrace::modify_window(
476 miral::WindowInfo& window_info, miral::WindowSpecification const& modifications)
477 try {
478
479=== modified file 'miral/window_management_trace.h'
480--- miral/window_management_trace.h 2017-03-02 14:29:04 +0000
481+++ miral/window_management_trace.h 2017-04-13 15:18:28 +0000
482@@ -69,6 +69,8 @@
483 virtual void focus_prev_within_application() override;
484
485 virtual void raise_tree(Window const& root) override;
486+ virtual void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) override;
487+ virtual void end_drag_and_drop() override;
488
489 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;
490
491
492=== modified file 'miral/window_manager_tools.cpp'
493--- miral/window_manager_tools.cpp 2017-03-02 14:29:04 +0000
494+++ miral/window_manager_tools.cpp 2017-04-13 15:18:28 +0000
495@@ -83,6 +83,12 @@
496 void miral::WindowManagerTools::raise_tree(Window const& root)
497 { tools->raise_tree(root); }
498
499+void miral::WindowManagerTools::start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle)
500+{ tools->start_drag_and_drop(window_info, handle); }
501+
502+void miral::WindowManagerTools::end_drag_and_drop()
503+{ tools->end_drag_and_drop(); }
504+
505 void miral::WindowManagerTools::modify_window(WindowInfo& window_info, WindowSpecification const& modifications)
506 { tools->modify_window(window_info,modifications); }
507
508
509=== modified file 'miral/window_manager_tools_implementation.h'
510--- miral/window_manager_tools_implementation.h 2017-03-02 14:29:04 +0000
511+++ miral/window_manager_tools_implementation.h 2017-04-13 15:18:28 +0000
512@@ -26,6 +26,7 @@
513
514 #include <functional>
515 #include <memory>
516+#include <vector>
517
518 namespace mir { namespace scene { class Surface; } }
519
520@@ -66,6 +67,8 @@
521 virtual auto window_at(mir::geometry::Point cursor) const -> Window = 0;
522 virtual auto active_display() -> mir::geometry::Rectangle const = 0;
523 virtual void raise_tree(Window const& root) = 0;
524+ virtual void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) = 0;
525+ virtual void end_drag_and_drop() = 0;
526 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
527 virtual auto info_for_window_id(std::string const& id) const -> WindowInfo& = 0;
528 virtual auto id_for_window(Window const& window) const -> std::string = 0;
529
530=== modified file 'scripts/process_doxygen_xml.py'
531--- scripts/process_doxygen_xml.py 2017-03-15 17:44:36 +0000
532+++ scripts/process_doxygen_xml.py 2017-04-13 15:18:28 +0000
533@@ -476,10 +476,21 @@
534
535 MIRAL_1.3.1 {
536 global:
537+ extern "C++" {
538+ miral::SetWindowManagementPolicy::?SetWindowManagementPolicy*;
539+ miral::SetWindowManagementPolicy::SetWindowManagementPolicy*;
540+ miral::SetWindowManagementPolicy::operator*;
541+ typeinfo?for?miral::SetWindowManagementPolicy;
542+ vtable?for?miral::SetWindowManagementPolicy;
543+ };
544+} MIRAL_1.3;
545+
546+MIRAL_1.4.0 {
547+global:
548 extern "C++" {'''
549
550 END_NEW_STANZA = ''' };
551-} MIRAL_1.3;'''
552+} MIRAL_1.3.1;'''
553
554 def _print_report():
555 print OLD_STANZAS
556
557=== modified file 'test/CMakeLists.txt'
558--- test/CMakeLists.txt 2017-02-13 16:23:29 +0000
559+++ test/CMakeLists.txt 2017-04-13 15:18:28 +0000
560@@ -39,6 +39,16 @@
561 ${GTEST_INCLUDE_DIR}
562 )
563
564+# MIRAL_TEST_MODERN_FEATURES lists test sourcefiles that require a recent version of Mir
565+if (MIRTEST_VERSION VERSION_LESS 0.27)
566+ set(MIRAL_TEST_MODERN_FEATURES)
567+else()
568+ set(MIRAL_TEST_MODERN_FEATURES
569+ drag_and_drop.cpp
570+ client_mediated_gestures.cpp
571+ )
572+endif()
573+
574 add_executable(miral-test
575 mru_window_list.cpp
576 active_outputs.cpp
577@@ -56,7 +66,9 @@
578 display_reconfiguration.cpp
579 active_window.cpp
580 raise_tree.cpp
581- workspaces.cpp)
582+ workspaces.cpp
583+ ${MIRAL_TEST_MODERN_FEATURES}
584+)
585
586 target_link_libraries(miral-test
587 ${MIRTEST_LDFLAGS}
588
589=== added file 'test/client_mediated_gestures.cpp'
590--- test/client_mediated_gestures.cpp 1970-01-01 00:00:00 +0000
591+++ test/client_mediated_gestures.cpp 2017-04-13 15:18:28 +0000
592@@ -0,0 +1,285 @@
593+/*
594+ * Copyright © 2017 Canonical Ltd.
595+ *
596+ * This program is free software: you can redistribute it and/or modify it
597+ * under the terms of the GNU General Public License version 3,
598+ * as published by the Free Software Foundation.
599+ *
600+ * This program is distributed in the hope that it will be useful,
601+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
602+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
603+ * GNU General Public License for more details.
604+ *
605+ * You should have received a copy of the GNU General Public License
606+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
607+ *
608+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
609+ */
610+
611+#include <mir_toolkit/mir_window.h>
612+#include <mir_toolkit/mir_blob.h>
613+
614+#include <mir/geometry/displacement.h>
615+#include <mir/input/input_device_info.h>
616+#include <mir/input/device_capability.h>
617+#include <mir/shell/canonical_window_manager.h>
618+#include <mir/shell/shell.h>
619+
620+#include <mir_test_framework/connected_client_with_a_window.h>
621+#include <mir_test_framework/fake_input_device.h>
622+#include <mir_test_framework/stub_server_platform_factory.h>
623+#include <mir/test/event_factory.h>
624+#include <mir/test/fake_shared.h>
625+#include <mir/test/signal.h>
626+
627+#include <gmock/gmock.h>
628+#include <gtest/gtest.h>
629+
630+#include <linux/input.h>
631+
632+#include <atomic>
633+
634+using namespace std::chrono_literals;
635+using namespace mir::geometry;
636+using namespace testing;
637+using mir::test::fake_shared;
638+using mir::test::Signal;
639+
640+namespace
641+{
642+class Cookie
643+{
644+public:
645+ Cookie() = default;
646+
647+ explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {}
648+
649+ operator MirCookie const*() const { return self.get(); }
650+
651+ auto get() const -> MirCookie const* { return self.get(); }
652+
653+ void reset() { self.reset(); }
654+
655+ void reset(MirCookie const* cookie) { self.reset(cookie, deleter); }
656+
657+private:
658+ static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); }
659+
660+ std::shared_ptr<MirCookie const> self;
661+};
662+
663+void mir_cookie_release(Cookie const&) = delete;
664+
665+#pragma GCC diagnostic push
666+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
667+struct MockWindowManager : mir::shell::CanonicalWindowManager
668+{
669+ using mir::shell::CanonicalWindowManager::CanonicalWindowManager;
670+
671+ MOCK_METHOD3(handle_request_move,
672+ void(std::shared_ptr<mir::scene::Session> const&, std::shared_ptr<mir::scene::Surface> const&, uint64_t));
673+};
674+#pragma GCC diagnostic pop
675+
676+struct MouseMoverAndFaker
677+{
678+ void start_dragging_mouse()
679+ {
680+ using namespace mir::input::synthesis;
681+ fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
682+ }
683+
684+ void move_mouse(Displacement const& displacement)
685+ {
686+ using mir::input::synthesis::a_pointer_event;
687+ fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
688+ }
689+
690+ void release_mouse()
691+ {
692+ using namespace mir::input::synthesis;
693+ fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
694+ }
695+
696+private:
697+ std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
698+ mir_test_framework::add_fake_input_device(
699+ mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})
700+ };
701+};
702+
703+Rectangle const screen_geometry{{0, 0}, {800, 600}};
704+auto const receive_event_timeout = 90s;
705+
706+struct ClientMediatedUserGestures : mir_test_framework::ConnectedClientWithAWindow,
707+ MouseMoverAndFaker
708+{
709+ void SetUp() override
710+ {
711+ initial_display_layout({screen_geometry});
712+ server.override_the_window_manager_builder([this](mir::shell::FocusController* focus_controller)
713+ {
714+ return window_manager =
715+ std::make_shared<MockWindowManager>(focus_controller, server.the_shell_display_layout());
716+ });
717+
718+ mir_test_framework::ConnectedClientWithAWindow::SetUp();
719+ mir_window_set_event_handler(window, &window_event_handler, this);
720+
721+ paint_window();
722+
723+ center_mouse();
724+ }
725+
726+ void TearDown() override
727+ {
728+ reset_window_event_handler();
729+ window_manager.reset();
730+ mir_test_framework::ConnectedClientWithAWindow::TearDown();
731+ }
732+
733+ auto user_initiates_gesture() -> Cookie;
734+
735+ std::shared_ptr<MockWindowManager> window_manager;
736+
737+private:
738+ void center_mouse();
739+ void paint_window();
740+ void set_window_event_handler(std::function<void(MirEvent const* event)> const& handler);
741+ void reset_window_event_handler();
742+ void invoke_window_event_handler(MirEvent const* event)
743+ {
744+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
745+ window_event_handler_(event);
746+ }
747+
748+ std::mutex window_event_handler_mutex;
749+ std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
750+
751+ static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
752+};
753+
754+void ClientMediatedUserGestures::set_window_event_handler(std::function<void(MirEvent const* event)> const& handler)
755+{
756+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
757+ window_event_handler_ = handler;
758+}
759+
760+void ClientMediatedUserGestures::reset_window_event_handler()
761+{
762+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
763+ window_event_handler_ = [](MirEvent const*) {};
764+}
765+
766+void ClientMediatedUserGestures::window_event_handler(MirWindow* /*window*/, MirEvent const* event, void* context)
767+{
768+ static_cast<ClientMediatedUserGestures*>(context)->invoke_window_event_handler(event);
769+}
770+
771+void ClientMediatedUserGestures::paint_window()
772+{
773+ Signal have_focus;
774+
775+ set_window_event_handler([&](MirEvent const* event)
776+ {
777+ if (mir_event_get_type(event) != mir_event_type_window)
778+ return;
779+
780+ auto const window_event = mir_event_get_window_event(event);
781+ if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
782+ return;
783+
784+ if (mir_window_event_get_attribute_value(window_event))
785+ have_focus.raise();
786+ });
787+
788+ mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(window));
789+
790+ EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
791+
792+ reset_window_event_handler();
793+}
794+
795+void ClientMediatedUserGestures::center_mouse()
796+{
797+ Signal have_mouseover;
798+
799+ set_window_event_handler([&](MirEvent const* event)
800+ {
801+ if (mir_event_get_type(event) != mir_event_type_input)
802+ return;
803+
804+ auto const input_event = mir_event_get_input_event(event);
805+
806+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
807+ return;
808+
809+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
810+
811+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
812+ return;
813+
814+ have_mouseover.raise();
815+ });
816+
817+ move_mouse(0.5 * as_displacement(screen_geometry.size));
818+
819+// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
820+// But it isn't essential for the test and we've probably waited long enough
821+// for the mouse-down needed by the test to reach the window.
822+// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
823+ have_mouseover.wait_for(receive_event_timeout);
824+
825+ reset_window_event_handler();
826+}
827+
828+auto ClientMediatedUserGestures::user_initiates_gesture() -> Cookie
829+{
830+ Cookie cookie;
831+ Signal have_cookie;
832+
833+ set_window_event_handler([&](MirEvent const* event)
834+ {
835+ if (mir_event_get_type(event) != mir_event_type_input)
836+ return;
837+
838+ auto const input_event = mir_event_get_input_event(event);
839+
840+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
841+ return;
842+
843+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
844+
845+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
846+ return;
847+
848+ cookie = Cookie{mir_input_event_get_cookie(input_event)};
849+ have_cookie.raise();
850+ });
851+
852+ start_dragging_mouse();
853+
854+ EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
855+
856+ reset_window_event_handler();
857+ return cookie;
858+}
859+}
860+
861+TEST_F(ClientMediatedUserGestures, when_user_initiates_gesture_client_receives_cookie)
862+{
863+ auto const cookie = user_initiates_gesture();
864+
865+ EXPECT_THAT(cookie.get(), NotNull());
866+}
867+
868+TEST_F(ClientMediatedUserGestures, when_client_initiates_move_window_manager_handles_request)
869+{
870+ auto const cookie = user_initiates_gesture();
871+ Signal have_request;
872+ EXPECT_CALL(*window_manager, handle_request_move(_, _, _)).WillOnce(InvokeWithoutArgs([&]{ have_request.raise(); }));
873+
874+ mir_window_request_user_move(window, cookie);
875+
876+ EXPECT_THAT(have_request.wait_for(receive_event_timeout), Eq(true));
877+}
878
879=== added file 'test/drag_and_drop.cpp'
880--- test/drag_and_drop.cpp 1970-01-01 00:00:00 +0000
881+++ test/drag_and_drop.cpp 2017-04-13 15:18:28 +0000
882@@ -0,0 +1,641 @@
883+/*
884+ * Copyright © 2017 Canonical Ltd.
885+ *
886+ * This program is free software: you can redistribute it and/or modify it
887+ * under the terms of the GNU General Public License version 3,
888+ * as published by the Free Software Foundation.
889+ *
890+ * This program is distributed in the hope that it will be useful,
891+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
892+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
893+ * GNU General Public License for more details.
894+ *
895+ * You should have received a copy of the GNU General Public License
896+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
897+ *
898+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
899+ */
900+
901+#include <miral/window_management_policy_addendum2.h>
902+
903+#include <mir/client/blob.h>
904+#include <mir/client/cookie.h>
905+#include <mir/client/window.h>
906+#include <mir/client/window_spec.h>
907+#include <mir_toolkit/mir_buffer_stream.h>
908+#include <mir_toolkit/extensions/drag_and_drop.h>
909+
910+#include <mir/geometry/displacement.h>
911+#include <mir/input/input_device_info.h>
912+#include <mir/input/device_capability.h>
913+#include <mir/shell/shell.h>
914+
915+#include "test_server.h"
916+#include <mir_test_framework/fake_input_device.h>
917+#include <mir_test_framework/stub_server_platform_factory.h>
918+#include <mir/test/event_factory.h>
919+#include <mir/test/signal.h>
920+
921+#include <gmock/gmock.h>
922+#include <gtest/gtest.h>
923+
924+#include <linux/input.h>
925+#include <uuid/uuid.h>
926+
927+#include <boost/throw_exception.hpp>
928+#include <atomic>
929+
930+using namespace std::chrono_literals;
931+using namespace mir::client;
932+using namespace mir::geometry;
933+using namespace testing;
934+using mir::test::Signal;
935+
936+namespace
937+{
938+struct MouseMoverAndFaker
939+{
940+ void start_dragging_mouse()
941+ {
942+ using namespace mir::input::synthesis;
943+ fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
944+ }
945+
946+ void move_mouse(Displacement const& displacement)
947+ {
948+ using mir::input::synthesis::a_pointer_event;
949+ fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
950+ }
951+
952+ void release_mouse()
953+ {
954+ using namespace mir::input::synthesis;
955+ fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
956+ }
957+
958+private:
959+ std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
960+ mir_test_framework::add_fake_input_device(
961+ mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})};
962+};
963+
964+Rectangle const screen_geometry{{0,0}, {800,600}};
965+auto const receive_event_timeout = 1s; //90s;
966+
967+struct ConnectedClientWithAWindow : miral::TestServer
968+{
969+ Connection connection;
970+ Window window;
971+
972+ void SetUp() override
973+ {
974+ miral::TestServer::SetUp();
975+ connection = connect_client(__func__);
976+ window = WindowSpec::for_normal_window(connection, surface_size.width.as_int(), surface_size.height.as_int())
977+ .set_pixel_format(mir_pixel_format_abgr_8888)
978+ .set_name("ConnectedClientWithAWindow")
979+ .set_buffer_usage(mir_buffer_usage_hardware)
980+ .create_window();
981+ }
982+
983+ void TearDown() override
984+ {
985+ window.reset();
986+ connection.reset();
987+ miral::TestServer::TearDown();
988+ }
989+
990+ mir::geometry::Size const surface_size {640, 480};
991+};
992+
993+struct DragAndDrop : ConnectedClientWithAWindow,
994+ MouseMoverAndFaker
995+{
996+ MirDragAndDropV1 const* dnd = nullptr;
997+
998+ void SetUp() override
999+ {
1000+ mir_test_framework::set_next_display_rects(std::unique_ptr<std::vector<Rectangle>>(new std::vector<Rectangle>({screen_geometry})));
1001+
1002+ ConnectedClientWithAWindow::SetUp();
1003+ dnd = mir_drag_and_drop_v1(connection);
1004+ mir_window_set_event_handler(window, &window_event_handler, this);
1005+ if (dnd) dnd->set_start_drag_and_drop_callback(window, &window_dnd_start_handler, this);
1006+
1007+ create_target_window();
1008+
1009+ paint_window(window);
1010+
1011+ center_mouse();
1012+ }
1013+
1014+ void TearDown() override
1015+ {
1016+ reset_window_event_handler(target_window);
1017+ reset_window_event_handler(window);
1018+ target_window.reset();
1019+ another_connection.reset();
1020+ ConnectedClientWithAWindow::TearDown();
1021+ }
1022+
1023+ auto user_initiates_drag() -> Cookie;
1024+ auto client_requests_drag(Cookie const& cookie) -> Blob;
1025+ auto handle_from_mouse_move() -> Blob;
1026+ auto handle_from_mouse_leave() -> Blob;
1027+ auto handle_from_mouse_enter() -> Blob;
1028+ auto handle_from_mouse_release() -> Blob;
1029+ auto count_of_handles_when_moving_mouse() -> int;
1030+
1031+private:
1032+ auto build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy> override;
1033+ void center_mouse();
1034+ void paint_window(MirWindow* w);
1035+ void set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler);
1036+ void set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler);
1037+ void reset_window_event_handler(MirWindow* window);
1038+
1039+ void create_target_window()
1040+ {
1041+ another_connection = connect_client("another_connection");
1042+ target_window = WindowSpec::
1043+ for_normal_window(connection, screen_geometry.size.width.as_int(), screen_geometry.size.height.as_int())
1044+ .set_pixel_format(mir_pixel_format_abgr_8888)
1045+ .set_name("target_window")
1046+ .set_buffer_usage(mir_buffer_usage_hardware)
1047+ .set_event_handler(&window_event_handler, this)
1048+ .create_window();
1049+
1050+ paint_window(target_window);
1051+ }
1052+
1053+ void invoke_window_event_handler(MirWindow* window, MirEvent const* event)
1054+ {
1055+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1056+ if (window == this->window) window_event_handler_(event);
1057+ if (window == target_window) target_window_event_handler_(event);
1058+ }
1059+
1060+ void invoke_window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event)
1061+ {
1062+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1063+ if (window == this->window) window_dnd_start_(event);
1064+ }
1065+
1066+ std::mutex window_event_handler_mutex;
1067+ std::function<void(MirDragAndDropEvent const* event)> window_dnd_start_ = [](MirDragAndDropEvent const*) {};
1068+ std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
1069+ std::function<void(MirEvent const* event)> target_window_event_handler_ = [](MirEvent const*) {};
1070+
1071+ static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
1072+ static void window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context);
1073+
1074+ Connection another_connection;
1075+ Window target_window;
1076+};
1077+
1078+void DragAndDrop::set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler)
1079+{
1080+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1081+ if (window == this->window) window_event_handler_ = handler;
1082+ if (window == target_window) target_window_event_handler_ = handler;
1083+}
1084+
1085+void DragAndDrop::set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler)
1086+{
1087+std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1088+if (window == this->window) window_dnd_start_ = handler;
1089+}
1090+
1091+
1092+void DragAndDrop::reset_window_event_handler(MirWindow* window)
1093+{
1094+ if (window == this->window) window_event_handler_ = [](MirEvent const*) {};
1095+ if (window == target_window) target_window_event_handler_ = [](MirEvent const*) {};
1096+}
1097+
1098+void DragAndDrop::paint_window(MirWindow* w)
1099+{
1100+ Signal have_focus;
1101+
1102+ set_window_event_handler(w, [&](MirEvent const* event)
1103+ {
1104+ if (mir_event_get_type(event) != mir_event_type_window)
1105+ return;
1106+
1107+ auto const window_event = mir_event_get_window_event(event);
1108+ if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
1109+ return;
1110+
1111+ if (mir_window_event_get_attribute_value(window_event))
1112+ have_focus.raise();
1113+ });
1114+
1115+ mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(w));
1116+
1117+ EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
1118+
1119+ reset_window_event_handler(w);
1120+}
1121+
1122+void DragAndDrop::center_mouse()
1123+{
1124+ Signal have_mouseover;
1125+
1126+ set_window_event_handler(window, [&](MirEvent const* event)
1127+ {
1128+ if (mir_event_get_type(event) != mir_event_type_input)
1129+ return;
1130+
1131+ auto const input_event = mir_event_get_input_event(event);
1132+
1133+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1134+ return;
1135+
1136+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1137+
1138+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
1139+ return;
1140+
1141+ have_mouseover.raise();
1142+ });
1143+
1144+ move_mouse(0.5 * as_displacement(screen_geometry.size));
1145+
1146+// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
1147+// But it isn't essential for the test and we've probably waited long enough
1148+// for the mouse-down needed by the test to reach the window.
1149+// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
1150+ have_mouseover.wait_for(receive_event_timeout);
1151+
1152+ reset_window_event_handler(window);
1153+}
1154+
1155+void DragAndDrop::window_event_handler(MirWindow* window, MirEvent const* event, void* context)
1156+{
1157+ static_cast<DragAndDrop*>(context)->invoke_window_event_handler(window, event);
1158+}
1159+
1160+void DragAndDrop::window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context)
1161+{
1162+ static_cast<DragAndDrop*>(context)->invoke_window_dnd_start_handler(window, event);
1163+}
1164+
1165+
1166+auto DragAndDrop::user_initiates_drag() -> Cookie
1167+{
1168+ Cookie cookie;
1169+ Signal have_cookie;
1170+
1171+ set_window_event_handler(window, [&](MirEvent const* event)
1172+ {
1173+ if (mir_event_get_type(event) != mir_event_type_input)
1174+ return;
1175+
1176+ auto const input_event = mir_event_get_input_event(event);
1177+
1178+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1179+ return;
1180+
1181+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1182+
1183+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
1184+ return;
1185+
1186+ cookie = Cookie{mir_input_event_get_cookie(input_event)};
1187+ have_cookie.raise();
1188+ });
1189+
1190+ start_dragging_mouse();
1191+
1192+ EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
1193+
1194+ reset_window_event_handler(window);
1195+ return cookie;
1196+}
1197+
1198+auto DragAndDrop::client_requests_drag(Cookie const& cookie) -> Blob
1199+{
1200+ Blob blob;
1201+ Signal initiated;
1202+
1203+ set_window_dnd_start_handler(window, [&](MirDragAndDropEvent const* event)
1204+ {
1205+ if (dnd)
1206+ blob.reset(dnd->start_drag_and_drop(event));
1207+
1208+ if (blob)
1209+ initiated.raise();
1210+ });
1211+
1212+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
1213+
1214+ if (dnd)
1215+ dnd->request_drag_and_drop(window, cookie);
1216+
1217+ EXPECT_TRUE(initiated.wait_for(receive_event_timeout));
1218+
1219+ reset_window_event_handler(window);
1220+ return blob;
1221+}
1222+
1223+auto DragAndDrop::handle_from_mouse_move() -> Blob
1224+{
1225+ Blob blob;
1226+ Signal have_blob;
1227+
1228+ set_window_event_handler(window, [&](MirEvent const* event)
1229+ {
1230+ if (mir_event_get_type(event) != mir_event_type_input)
1231+ return;
1232+
1233+ auto const input_event = mir_event_get_input_event(event);
1234+
1235+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1236+ return;
1237+
1238+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1239+
1240+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
1241+
1242+ if (dnd)
1243+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
1244+
1245+ if (blob)
1246+ have_blob.raise();
1247+ });
1248+
1249+ move_mouse({1,1});
1250+
1251+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
1252+
1253+ reset_window_event_handler(window);
1254+ return blob;
1255+}
1256+
1257+auto DragAndDrop::handle_from_mouse_leave() -> Blob
1258+{
1259+ Blob blob;
1260+ Signal have_blob;
1261+
1262+ set_window_event_handler(window, [&](MirEvent const* event)
1263+ {
1264+ if (mir_event_get_type(event) != mir_event_type_input)
1265+ return;
1266+
1267+ auto const input_event = mir_event_get_input_event(event);
1268+
1269+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1270+ return;
1271+
1272+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1273+
1274+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_leave)
1275+ return;
1276+
1277+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
1278+
1279+ if (dnd)
1280+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
1281+
1282+ if (blob)
1283+ have_blob.raise();
1284+ });
1285+
1286+ move_mouse({1,1});
1287+ move_mouse(0.5 * as_displacement(surface_size));
1288+
1289+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
1290+
1291+ reset_window_event_handler(window);
1292+ return blob;
1293+}
1294+
1295+auto DragAndDrop::handle_from_mouse_enter() -> Blob
1296+{
1297+ Blob blob;
1298+ Signal have_blob;
1299+
1300+ set_window_event_handler(target_window, [&](MirEvent const* event)
1301+ {
1302+ if (mir_event_get_type(event) != mir_event_type_input)
1303+ return;
1304+
1305+ auto const input_event = mir_event_get_input_event(event);
1306+
1307+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1308+ return;
1309+
1310+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1311+
1312+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
1313+ return;
1314+
1315+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
1316+
1317+ if (dnd)
1318+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
1319+
1320+ if (blob)
1321+ have_blob.raise();
1322+ });
1323+
1324+ move_mouse({1,1});
1325+ move_mouse(0.5 * as_displacement(surface_size));
1326+
1327+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
1328+
1329+ reset_window_event_handler(target_window);
1330+ return blob;
1331+}
1332+
1333+auto DragAndDrop::handle_from_mouse_release() -> Blob
1334+{
1335+ Blob blob;
1336+ Signal have_blob;
1337+
1338+ set_window_event_handler(target_window, [&](MirEvent const* event)
1339+ {
1340+ if (mir_event_get_type(event) != mir_event_type_input)
1341+ return;
1342+
1343+ auto const input_event = mir_event_get_input_event(event);
1344+
1345+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1346+ return;
1347+
1348+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1349+
1350+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_up)
1351+ return;
1352+
1353+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
1354+
1355+ if (dnd)
1356+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
1357+
1358+ if (blob)
1359+ have_blob.raise();
1360+ });
1361+
1362+ move_mouse({1,1});
1363+ move_mouse(0.5 * as_displacement(surface_size));
1364+ release_mouse();
1365+
1366+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
1367+
1368+ reset_window_event_handler(target_window);
1369+ return blob;
1370+}
1371+
1372+auto DragAndDrop::count_of_handles_when_moving_mouse() -> int
1373+{
1374+ Signal have_3_events;
1375+ std::atomic<int> events{0};
1376+ std::atomic<int> handles{0};
1377+
1378+ auto counter = [&](MirEvent const* event)
1379+ {
1380+ if (mir_event_get_type(event) != mir_event_type_input)
1381+ return;
1382+
1383+ auto const input_event = mir_event_get_input_event(event);
1384+
1385+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
1386+ return;
1387+
1388+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
1389+
1390+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
1391+
1392+ Blob blob;
1393+ if (dnd)
1394+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
1395+
1396+ if (blob)
1397+ handles.fetch_add(1);
1398+
1399+ if (events.fetch_add(1) == 2)
1400+ have_3_events.raise();
1401+ };
1402+
1403+ set_window_event_handler(window, counter);
1404+ set_window_event_handler(target_window, counter);
1405+
1406+ start_dragging_mouse();
1407+ move_mouse({1,1});
1408+ release_mouse();
1409+
1410+ EXPECT_TRUE(have_3_events.wait_for(receive_event_timeout));
1411+
1412+ reset_window_event_handler(window);
1413+ reset_window_event_handler(target_window);
1414+ return handles;
1415+}
1416+
1417+auto DragAndDrop::build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy>
1418+{
1419+ struct DnDWindowManagerPolicy : miral::TestServer::TestWindowManagerPolicy, miral::WindowManagementPolicyAddendum2
1420+ {
1421+ using miral::TestServer::TestWindowManagerPolicy::TestWindowManagerPolicy;
1422+
1423+ void handle_request_drag_and_drop(miral::WindowInfo& window_info) override
1424+ {
1425+ uuid_t uuid;
1426+ uuid_generate(uuid);
1427+ std::vector<uint8_t> const handle{std::begin(uuid), std::end(uuid)};
1428+
1429+ tools.start_drag_and_drop(window_info, handle);
1430+ }
1431+
1432+ void handle_request_move(miral::WindowInfo&, MirInputEvent const*) override {}
1433+ };
1434+
1435+ return std::make_unique<DnDWindowManagerPolicy>(tools, *this);
1436+}
1437+
1438+MATCHER_P(BlobContentEq, p, "")
1439+{
1440+ if (!arg || !p)
1441+ return false;
1442+ if (mir_blob_size(arg) != mir_blob_size(p))
1443+ return false;
1444+ return !memcmp(mir_blob_data(arg), mir_blob_data(p), mir_blob_size(p));
1445+}
1446+}
1447+
1448+TEST_F(DragAndDrop, when_user_initiates_drag_client_receives_cookie)
1449+{
1450+ auto const cookie = user_initiates_drag();
1451+
1452+ EXPECT_THAT(cookie, NotNull());
1453+}
1454+
1455+TEST_F(DragAndDrop, when_client_requests_drags_it_receives_handle)
1456+{
1457+ auto const cookie = user_initiates_drag();
1458+ ASSERT_THAT(cookie, NotNull());
1459+
1460+ auto const handle = client_requests_drag(cookie);
1461+
1462+ EXPECT_THAT(handle, NotNull());
1463+}
1464+
1465+TEST_F(DragAndDrop, during_drag_when_user_moves_mouse_client_receives_handle)
1466+{
1467+ auto const cookie = user_initiates_drag();
1468+ ASSERT_THAT(cookie, NotNull());
1469+ auto const handle_from_request = client_requests_drag(cookie);
1470+
1471+ auto const handle = handle_from_mouse_move();
1472+
1473+ EXPECT_THAT(handle, NotNull());
1474+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1475+}
1476+
1477+TEST_F(DragAndDrop, when_drag_moves_from_window_leave_event_contains_handle)
1478+{
1479+ auto const cookie = user_initiates_drag();
1480+ ASSERT_THAT(cookie, NotNull());
1481+ auto const handle_from_request = client_requests_drag(cookie);
1482+
1483+ auto const handle = handle_from_mouse_leave();
1484+
1485+ EXPECT_THAT(handle, NotNull());
1486+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1487+}
1488+
1489+TEST_F(DragAndDrop, when_drag_enters_target_window_enter_event_contains_handle)
1490+{
1491+ auto const cookie = user_initiates_drag();
1492+ ASSERT_THAT(cookie, NotNull());
1493+ auto const handle_from_request = client_requests_drag(cookie);
1494+
1495+ auto const handle = handle_from_mouse_enter();
1496+
1497+ EXPECT_THAT(handle, NotNull());
1498+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1499+}
1500+
1501+TEST_F(DragAndDrop, when_drag_releases_target_window_release_event_contains_handle)
1502+{
1503+ auto const cookie = user_initiates_drag();
1504+ ASSERT_THAT(cookie, NotNull());
1505+ auto const handle_from_request = client_requests_drag(cookie);
1506+
1507+ auto const handle = handle_from_mouse_release();
1508+
1509+ EXPECT_THAT(handle, NotNull());
1510+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1511+}
1512+
1513+TEST_F(DragAndDrop, after_drag_finishes_pointer_events_no_longer_contain_handle)
1514+{
1515+ auto const cookie = user_initiates_drag();
1516+ ASSERT_THAT(cookie, NotNull());
1517+ client_requests_drag(cookie);
1518+ handle_from_mouse_release();
1519+
1520+ invoke_tools([](miral::WindowManagerTools& tools) { tools.end_drag_and_drop(); });
1521+
1522+ EXPECT_THAT(count_of_handles_when_moving_mouse(), Eq(0));
1523+}

Subscribers

People subscribed via source and target branches

to all changes: