Merge lp:~alan-griffiths/miral/drag-and-drop into lp:miral

Proposed by Alan Griffiths
Status: Merged
Approved by: Brandon Schaefer
Approved revision: 558
Merged at revision: 549
Proposed branch: lp:~alan-griffiths/miral/drag-and-drop
Merge into: lp:miral
Prerequisite: lp:~alan-griffiths/miral/1.3
Diff against target: 1092 lines (+850/-11)
16 files modified
CMakeLists.txt (+2/-2)
debian/changelog (+13/-2)
debian/libmiral2.symbols (+4/-0)
include/miral/window_management_policy_addendum2.h (+61/-0)
include/miral/window_manager_tools.h (+12/-0)
miral/CMakeLists.txt (+1/-0)
miral/basic_window_manager.cpp (+42/-5)
miral/basic_window_manager.h (+4/-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 (+10/-1)
test/drag_and_drop.cpp (+639/-0)
To merge this branch: bzr merge lp:~alan-griffiths/miral/drag-and-drop
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Brandon Schaefer (community) Approve
Albert Astals Cid Pending
Review via email: mp+321593@code.launchpad.net

This proposal supersedes a proposal from 2017-03-21.

Commit message

[libmiral] Support for Drag and Drop messages

To post a comment you must log in.
Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

By just reading the documentation i would not know on what to pass as std::vector<uint8_t> const& handle in start_drag_and_drop, is it "whatever i want"?

I guess it is since it's a randomly generated uuid in the example.

But what is this parameter for? I mean as a miral user, you pass it in, but do you ever get it back? It seems not, so maybe miral can constrcut the uuid itself?

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote : Posted in a previous version of this proposal

> By just reading the documentation i would not know on what to pass as
> std::vector<uint8_t> const& handle in start_drag_and_drop, is it "whatever i
> want"?
>
> I guess it is since it's a randomly generated uuid in the example.
>
> But what is this parameter for? I mean as a miral user, you pass it in, but do
> you ever get it back? It seems not, so maybe miral can constrcut the uuid
> itself?

OK, so the documentation needs to state that "the handle will be passed to the client which can then use it to talk to the whatever service is being used to support drag and drop (e.g. on Ubuntu the content hub)".

Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

> > By just reading the documentation i would not know on what to pass as
> > std::vector<uint8_t> const& handle in start_drag_and_drop, is it "whatever i
> > want"?
> >
> > I guess it is since it's a randomly generated uuid in the example.
> >
> > But what is this parameter for? I mean as a miral user, you pass it in, but
> do
> > you ever get it back? It seems not, so maybe miral can constrcut the uuid
> > itself?
>
> OK, so the documentation needs to state that "the handle will be passed to the
> client which can then use it to talk to the whatever service is being used to
> support drag and drop (e.g. on Ubuntu the content hub)".

That looks good to me :)

Revision history for this message
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal

Looks good to me

review: Approve
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal

PASSED: Continuous integration, rev:556
https://mir-jenkins.ubuntu.com/job/miral-ci/3/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-miral/4
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4542
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4532
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4532
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4532
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/6
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/6/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/6
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/6/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/6
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/6/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/6
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/6/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/miral-ci/3/rebuild

review: Approve (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote : Posted in a previous version of this proposal

PASSED: Continuous integration, rev:556
https://mir-jenkins.ubuntu.com/job/miral-ci/6/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-miral/8
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4546
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4536
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/4536
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4536
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4536
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial/13
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial/13/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/13
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/13/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/13
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/13/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial/13
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial/13/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/13
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/13/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/13
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/13/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/miral-ci/6/rebuild

review: Approve (continuous-integration)
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:557
https://mir-jenkins.ubuntu.com/job/miral-ci/7/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-miral/9
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4547
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4537
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/4537
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4537
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4537
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial/14
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial/14/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/14
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/14/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/14
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/14/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial/14
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial/14/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/14
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/14/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/14
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/14/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/miral-ci/7/rebuild

review: Approve (continuous-integration)
558. By Alan Griffiths

1.3.2 release changelog

Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:558
https://mir-jenkins.ubuntu.com/job/miral-ci/9/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-miral/13
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4593
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4582
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/4582
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4582
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4582
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial/18
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial/18/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/18
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=xenial+overlay/18/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/18
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=amd64,release=zesty/18/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial/18
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial/18/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/18
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=xenial+overlay/18/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/18
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-miral/arch=i386,release=zesty/18/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/miral-ci/9/rebuild

review: Approve (continuous-integration)
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

lgtm

review: Approve
Revision history for this message
Mir CI Bot (mir-ci-bot) :
review: Approve (continuous-integration)

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-04-05 08:25:21 +0000
3+++ CMakeLists.txt 2017-04-05 08:25:21 +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-04-05 08:25:21 +0000
18+++ debian/changelog 2017-04-05 08:25:21 +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-05 08:25:21 +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-05 08:25:21 +0000
64@@ -0,0 +1,61 @@
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/version.h>
87+
88+namespace miral
89+{
90+struct WindowInfo;
91+
92+/**
93+ * Handle additional client requests.
94+ *
95+ * \note This interface is intended to be implemented by a WindowManagementPolicy
96+ * implementation, we can't add these functions directly to that interface without
97+ * breaking ABI (the vtab could be incompatible).
98+ * When initializing the window manager this interface will be detected by
99+ * dynamic_cast and registered accordingly.
100+ */
101+class WindowManagementPolicyAddendum2
102+{
103+public:
104+/** @name handle requests originating from the client
105+ * The policy is expected to update the model as appropriate
106+ * @{ */
107+ /** request from client to initiate drag and drop
108+ * \note the request has already been validated against the requesting event
109+ *
110+ * @param window_info the window
111+ */
112+ virtual void handle_request_drag_and_drop(WindowInfo& window_info) = 0;
113+/** @} */
114+
115+ virtual ~WindowManagementPolicyAddendum2() = default;
116+ WindowManagementPolicyAddendum2() = default;
117+ WindowManagementPolicyAddendum2(WindowManagementPolicyAddendum2 const&) = delete;
118+ WindowManagementPolicyAddendum2& operator=(WindowManagementPolicyAddendum2 const&) = delete;
119+};
120+#if MIRAL_VERSION >= MIR_VERSION_NUMBER(2, 0, 0)
121+#error "We've presumably broken ABI - please roll this interface into WindowManagementPolicy"
122+#endif
123+}
124+
125+#endif //MIRAL_WINDOW_MANAGEMENT_ADDENDUM2_H
126
127=== modified file 'include/miral/window_manager_tools.h'
128--- include/miral/window_manager_tools.h 2017-03-02 14:29:04 +0000
129+++ include/miral/window_manager_tools.h 2017-04-05 08:25:21 +0000
130@@ -166,6 +166,18 @@
131 /// Raise window and all its children
132 void raise_tree(Window const& root);
133
134+ /** Start drag and drop. The handle will be passed to the client which can
135+ * then use it to talk to the whatever service is being used to support drag
136+ * and drop (e.g. on Ubuntu the content hub).
137+ *
138+ * @param window_info source window
139+ * @param handle drag handle
140+ */
141+ void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle);
142+
143+ /// End drag and drop
144+ void end_drag_and_drop();
145+
146 /// Apply modifications to a window
147 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications);
148
149
150=== modified file 'miral/CMakeLists.txt'
151--- miral/CMakeLists.txt 2017-03-20 12:28:06 +0000
152+++ miral/CMakeLists.txt 2017-04-05 08:25:21 +0000
153@@ -61,6 +61,7 @@
154 ${CMAKE_SOURCE_DIR}/include/mir/client/display_config.h
155 ${CMAKE_SOURCE_DIR}/include/mir/client/window.h
156 ${CMAKE_SOURCE_DIR}/include/mir/client/detail/mir_forward_compatibility.h
157+ ${CMAKE_SOURCE_DIR}/include/miral/window_management_policy_addendum2.h
158 )
159
160 target_link_libraries(miral
161
162=== modified file 'miral/basic_window_manager.cpp'
163--- miral/basic_window_manager.cpp 2017-03-21 12:30:42 +0000
164+++ miral/basic_window_manager.cpp 2017-04-05 08:25:21 +0000
165@@ -19,6 +19,7 @@
166 #include "basic_window_manager.h"
167 #include "miral/window_manager_tools.h"
168 #include "miral/workspace_policy.h"
169+#include "miral/window_management_policy_addendum2.h"
170
171 #include <mir/scene/session.h>
172 #include <mir/scene/surface.h>
173@@ -70,7 +71,6 @@
174
175 namespace
176 {
177-
178 auto find_workspace_policy(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WorkspacePolicy*
179 {
180 miral::WorkspacePolicy* result = dynamic_cast<miral::WorkspacePolicy*>(policy.get());
181@@ -82,6 +82,22 @@
182
183 return &null_workspace_policy;
184 }
185+
186+auto find_policy_addendum2(std::unique_ptr<miral::WindowManagementPolicy> const& policy) -> miral::WindowManagementPolicyAddendum2*
187+{
188+ miral::WindowManagementPolicyAddendum2* result = dynamic_cast<miral::WindowManagementPolicyAddendum2*>(policy.get());
189+
190+ if (result)
191+ return result;
192+
193+ struct NullWindowManagementPolicyAddendum2 : miral::WindowManagementPolicyAddendum2
194+ {
195+ void handle_request_drag_and_drop(miral::WindowInfo&) override {}
196+ };
197+ static NullWindowManagementPolicyAddendum2 null_workspace_policy;
198+
199+ return &null_workspace_policy;
200+}
201 }
202
203
204@@ -94,7 +110,8 @@
205 display_layout(display_layout),
206 persistent_surface_store{persistent_surface_store},
207 policy(build(WindowManagerTools{this})),
208- workspace_policy{find_workspace_policy(policy)}
209+ workspace_policy{find_workspace_policy(policy)},
210+ policy2{find_policy_addendum2(policy)}
211 {
212 }
213
214@@ -362,10 +379,12 @@
215 #if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
216 void miral::BasicWindowManager::handle_request_drag_and_drop(
217 std::shared_ptr<mir::scene::Session> const& /*session*/,
218- std::shared_ptr<mir::scene::Surface> const& /*surface*/,
219- uint64_t /*timestamp*/)
220+ std::shared_ptr<mir::scene::Surface> const& surface,
221+ uint64_t timestamp)
222 {
223- // TODO
224+ Locker lock{this};
225+ if (timestamp >= last_input_event_timestamp)
226+ policy2->handle_request_drag_and_drop(info_for(surface));
227 }
228 #endif
229
230@@ -741,6 +760,24 @@
231 focus_controller->raise({begin(windows), end(windows)});
232 }
233
234+void miral::BasicWindowManager::start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle)
235+{
236+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
237+ std::shared_ptr<scene::Surface>(window_info.window())->start_drag_and_drop(handle);
238+ focus_controller->set_drag_and_drop_handle(handle);
239+#else
240+ (void)window_info;
241+ (void)handle;
242+#endif
243+}
244+
245+void miral::BasicWindowManager::end_drag_and_drop()
246+{
247+#if MIR_SERVER_VERSION >= MIR_VERSION_NUMBER(0, 27, 0)
248+ focus_controller->clear_drag_and_drop_handle();
249+#endif
250+}
251+
252 void miral::BasicWindowManager::move_tree(miral::WindowInfo& root, mir::geometry::Displacement movement)
253 {
254 if (movement == mir::geometry::Displacement{})
255
256=== modified file 'miral/basic_window_manager.h'
257--- miral/basic_window_manager.h 2017-03-21 12:30:42 +0000
258+++ miral/basic_window_manager.h 2017-04-05 08:25:21 +0000
259@@ -46,6 +46,7 @@
260 namespace miral
261 {
262 class WorkspacePolicy;
263+class WindowManagementPolicyAddendum2;
264 using mir::shell::SurfaceSet;
265 using WindowManagementPolicyBuilder =
266 std::function<std::unique_ptr<miral::WindowManagementPolicy>(miral::WindowManagerTools const& tools)>;
267@@ -161,6 +162,8 @@
268 auto active_display() -> mir::geometry::Rectangle const override;
269
270 void raise_tree(Window const& root) override;
271+ void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) override;
272+ void end_drag_and_drop() override;
273
274 void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;
275
276@@ -190,6 +193,7 @@
277
278 std::unique_ptr<WindowManagementPolicy> const policy;
279 WorkspacePolicy* const workspace_policy;
280+ WindowManagementPolicyAddendum2* const policy2;
281
282 std::mutex mutex;
283 SessionInfoMap app_info;
284
285=== modified file 'miral/symbols.map'
286--- miral/symbols.map 2017-03-15 17:44:36 +0000
287+++ miral/symbols.map 2017-04-05 08:25:21 +0000
288@@ -401,3 +401,24 @@
289 vtable?for?miral::SetWindowManagementPolicy;
290 };
291 } MIRAL_1.3;
292+
293+MIRAL_1.4.0 {
294+global:
295+ extern "C++" {
296+ miral::WindowManagementPolicyAddendum2::?WindowManagementPolicyAddendum2*;
297+ miral::WindowManagementPolicyAddendum2::WindowManagementPolicyAddendum2*;
298+ miral::WindowManagementPolicyAddendum2::operator*;
299+ miral::WindowManagerTools::end_drag_and_drop*;
300+ miral::WindowManagerTools::start_drag_and_drop*;
301+ miral::toolkit::Window::Window*;
302+ non-virtual?thunk?to?miral::WindowManagementPolicyAddendum2::?WindowManagementPolicyAddendum2*;
303+ typeinfo?for?miral::WindowManagementPolicyAddendum2;
304+ typeinfo?for?miral::toolkit::Window;
305+ typeinfo?for?miral::toolkit::WindowId;
306+ typeinfo?for?miral::toolkit::WindowSpec;
307+ vtable?for?miral::WindowManagementPolicyAddendum2;
308+ vtable?for?miral::toolkit::Window;
309+ vtable?for?miral::toolkit::WindowId;
310+ vtable?for?miral::toolkit::WindowSpec;
311+ };
312+} MIRAL_1.3.1;
313
314=== modified file 'miral/window_management_trace.cpp'
315--- miral/window_management_trace.cpp 2017-03-03 10:06:02 +0000
316+++ miral/window_management_trace.cpp 2017-04-05 08:25:21 +0000
317@@ -547,6 +547,24 @@
318 }
319 MIRAL_TRACE_EXCEPTION
320
321+void miral::WindowManagementTrace::start_drag_and_drop(miral::WindowInfo& window_info, std::vector<uint8_t> const& handle)
322+try {
323+ log_input();
324+ mir::log_info("%s window_info=%s", __func__, dump_of(window_info).c_str());
325+ trace_count++;
326+ wrapped.start_drag_and_drop(window_info, handle);
327+}
328+MIRAL_TRACE_EXCEPTION
329+
330+void miral::WindowManagementTrace::end_drag_and_drop()
331+try {
332+ log_input();
333+ mir::log_info("%s window_info=%s", __func__);
334+ trace_count++;
335+ wrapped.end_drag_and_drop();
336+}
337+MIRAL_TRACE_EXCEPTION
338+
339 void miral::WindowManagementTrace::modify_window(
340 miral::WindowInfo& window_info, miral::WindowSpecification const& modifications)
341 try {
342
343=== modified file 'miral/window_management_trace.h'
344--- miral/window_management_trace.h 2017-03-02 14:29:04 +0000
345+++ miral/window_management_trace.h 2017-04-05 08:25:21 +0000
346@@ -69,6 +69,8 @@
347 virtual void focus_prev_within_application() override;
348
349 virtual void raise_tree(Window const& root) override;
350+ virtual void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) override;
351+ virtual void end_drag_and_drop() override;
352
353 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) override;
354
355
356=== modified file 'miral/window_manager_tools.cpp'
357--- miral/window_manager_tools.cpp 2017-03-02 14:29:04 +0000
358+++ miral/window_manager_tools.cpp 2017-04-05 08:25:21 +0000
359@@ -83,6 +83,12 @@
360 void miral::WindowManagerTools::raise_tree(Window const& root)
361 { tools->raise_tree(root); }
362
363+void miral::WindowManagerTools::start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle)
364+{ tools->start_drag_and_drop(window_info, handle); }
365+
366+void miral::WindowManagerTools::end_drag_and_drop()
367+{ tools->end_drag_and_drop(); }
368+
369 void miral::WindowManagerTools::modify_window(WindowInfo& window_info, WindowSpecification const& modifications)
370 { tools->modify_window(window_info,modifications); }
371
372
373=== modified file 'miral/window_manager_tools_implementation.h'
374--- miral/window_manager_tools_implementation.h 2017-03-02 14:29:04 +0000
375+++ miral/window_manager_tools_implementation.h 2017-04-05 08:25:21 +0000
376@@ -26,6 +26,7 @@
377
378 #include <functional>
379 #include <memory>
380+#include <vector>
381
382 namespace mir { namespace scene { class Surface; } }
383
384@@ -66,6 +67,8 @@
385 virtual auto window_at(mir::geometry::Point cursor) const -> Window = 0;
386 virtual auto active_display() -> mir::geometry::Rectangle const = 0;
387 virtual void raise_tree(Window const& root) = 0;
388+ virtual void start_drag_and_drop(WindowInfo& window_info, std::vector<uint8_t> const& handle) = 0;
389+ virtual void end_drag_and_drop() = 0;
390 virtual void modify_window(WindowInfo& window_info, WindowSpecification const& modifications) = 0;
391 virtual auto info_for_window_id(std::string const& id) const -> WindowInfo& = 0;
392 virtual auto id_for_window(Window const& window) const -> std::string = 0;
393
394=== modified file 'scripts/process_doxygen_xml.py'
395--- scripts/process_doxygen_xml.py 2017-03-15 17:44:36 +0000
396+++ scripts/process_doxygen_xml.py 2017-04-05 08:25:21 +0000
397@@ -476,10 +476,21 @@
398
399 MIRAL_1.3.1 {
400 global:
401+ extern "C++" {
402+ miral::SetWindowManagementPolicy::?SetWindowManagementPolicy*;
403+ miral::SetWindowManagementPolicy::SetWindowManagementPolicy*;
404+ miral::SetWindowManagementPolicy::operator*;
405+ typeinfo?for?miral::SetWindowManagementPolicy;
406+ vtable?for?miral::SetWindowManagementPolicy;
407+ };
408+} MIRAL_1.3;
409+
410+MIRAL_1.4.0 {
411+global:
412 extern "C++" {'''
413
414 END_NEW_STANZA = ''' };
415-} MIRAL_1.3;'''
416+} MIRAL_1.3.1;'''
417
418 def _print_report():
419 print OLD_STANZAS
420
421=== modified file 'test/CMakeLists.txt'
422--- test/CMakeLists.txt 2017-02-13 16:23:29 +0000
423+++ test/CMakeLists.txt 2017-04-05 08:25:21 +0000
424@@ -39,6 +39,13 @@
425 ${GTEST_INCLUDE_DIR}
426 )
427
428+# MIRAL_TEST_MODERN_FEATURES lists test sourcefiles that require a recent version of Mir
429+if (MIRTEST_VERSION VERSION_LESS 0.27)
430+ set(MIRAL_TEST_MODERN_FEATURES)
431+else()
432+ set(MIRAL_TEST_MODERN_FEATURES drag_and_drop.cpp)
433+endif()
434+
435 add_executable(miral-test
436 mru_window_list.cpp
437 active_outputs.cpp
438@@ -56,7 +63,9 @@
439 display_reconfiguration.cpp
440 active_window.cpp
441 raise_tree.cpp
442- workspaces.cpp)
443+ workspaces.cpp
444+ ${MIRAL_TEST_MODERN_FEATURES}
445+)
446
447 target_link_libraries(miral-test
448 ${MIRTEST_LDFLAGS}
449
450=== added file 'test/drag_and_drop.cpp'
451--- test/drag_and_drop.cpp 1970-01-01 00:00:00 +0000
452+++ test/drag_and_drop.cpp 2017-04-05 08:25:21 +0000
453@@ -0,0 +1,639 @@
454+/*
455+ * Copyright © 2017 Canonical Ltd.
456+ *
457+ * This program is free software: you can redistribute it and/or modify it
458+ * under the terms of the GNU General Public License version 3,
459+ * as published by the Free Software Foundation.
460+ *
461+ * This program is distributed in the hope that it will be useful,
462+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
463+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
464+ * GNU General Public License for more details.
465+ *
466+ * You should have received a copy of the GNU General Public License
467+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
468+ *
469+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
470+ */
471+
472+#include <miral/window_management_policy_addendum2.h>
473+
474+#include <mir/client/blob.h>
475+#include <mir/client/cookie.h>
476+#include <mir/client/window.h>
477+#include <mir/client/window_spec.h>
478+#include <mir_toolkit/mir_buffer_stream.h>
479+#include <mir_toolkit/extensions/drag_and_drop.h>
480+
481+#include <mir/geometry/displacement.h>
482+#include <mir/input/input_device_info.h>
483+#include <mir/input/device_capability.h>
484+#include <mir/shell/shell.h>
485+
486+#include "test_server.h"
487+#include <mir_test_framework/fake_input_device.h>
488+#include <mir_test_framework/stub_server_platform_factory.h>
489+#include <mir/test/event_factory.h>
490+#include <mir/test/signal.h>
491+
492+#include <gmock/gmock.h>
493+#include <gtest/gtest.h>
494+
495+#include <linux/input.h>
496+#include <uuid/uuid.h>
497+
498+#include <boost/throw_exception.hpp>
499+#include <atomic>
500+
501+using namespace std::chrono_literals;
502+using namespace mir::client;
503+using namespace mir::geometry;
504+using namespace testing;
505+using mir::test::Signal;
506+
507+namespace
508+{
509+struct MouseMoverAndFaker
510+{
511+ void start_dragging_mouse()
512+ {
513+ using namespace mir::input::synthesis;
514+ fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
515+ }
516+
517+ void move_mouse(Displacement const& displacement)
518+ {
519+ using mir::input::synthesis::a_pointer_event;
520+ fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
521+ }
522+
523+ void release_mouse()
524+ {
525+ using namespace mir::input::synthesis;
526+ fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
527+ }
528+
529+private:
530+ std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
531+ mir_test_framework::add_fake_input_device(
532+ mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})};
533+};
534+
535+Rectangle const screen_geometry{{0,0}, {800,600}};
536+auto const receive_event_timeout = 1s; //90s;
537+
538+struct ConnectedClientWithAWindow : miral::TestServer
539+{
540+ Connection connection;
541+ Window window;
542+
543+ void SetUp() override
544+ {
545+ miral::TestServer::SetUp();
546+ connection = connect_client(__func__);
547+ window = WindowSpec::for_normal_window(connection, surface_size.width.as_int(), surface_size.height.as_int())
548+ .set_pixel_format(mir_pixel_format_abgr_8888)
549+ .set_name("ConnectedClientWithAWindow")
550+ .set_buffer_usage(mir_buffer_usage_hardware)
551+ .create_window();
552+ }
553+
554+ void TearDown() override
555+ {
556+ window.reset();
557+ connection.reset();
558+ miral::TestServer::TearDown();
559+ }
560+
561+ mir::geometry::Size const surface_size {640, 480};
562+};
563+
564+struct DragAndDrop : ConnectedClientWithAWindow,
565+ MouseMoverAndFaker
566+{
567+ MirDragAndDropV1 const* dnd = nullptr;
568+
569+ void SetUp() override
570+ {
571+ mir_test_framework::set_next_display_rects(std::unique_ptr<std::vector<Rectangle>>(new std::vector<Rectangle>({screen_geometry})));
572+
573+ ConnectedClientWithAWindow::SetUp();
574+ dnd = mir_drag_and_drop_v1(connection);
575+ mir_window_set_event_handler(window, &window_event_handler, this);
576+ if (dnd) dnd->set_start_drag_and_drop_callback(window, &window_dnd_start_handler, this);
577+
578+ create_target_window();
579+
580+ paint_window(window);
581+
582+ center_mouse();
583+ }
584+
585+ void TearDown() override
586+ {
587+ reset_window_event_handler(target_window);
588+ reset_window_event_handler(window);
589+ target_window.reset();
590+ another_connection.reset();
591+ ConnectedClientWithAWindow::TearDown();
592+ }
593+
594+ auto user_initiates_drag() -> Cookie;
595+ auto client_requests_drag(Cookie const& cookie) -> Blob;
596+ auto handle_from_mouse_move() -> Blob;
597+ auto handle_from_mouse_leave() -> Blob;
598+ auto handle_from_mouse_enter() -> Blob;
599+ auto handle_from_mouse_release() -> Blob;
600+ auto count_of_handles_when_moving_mouse() -> int;
601+
602+private:
603+ auto build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy> override;
604+ void center_mouse();
605+ void paint_window(MirWindow* w);
606+ void set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler);
607+ void set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler);
608+ void reset_window_event_handler(MirWindow* window);
609+
610+ void create_target_window()
611+ {
612+ another_connection = connect_client("another_connection");
613+ target_window = WindowSpec::
614+ for_normal_window(connection, screen_geometry.size.width.as_int(), screen_geometry.size.height.as_int())
615+ .set_pixel_format(mir_pixel_format_abgr_8888)
616+ .set_name("target_window")
617+ .set_buffer_usage(mir_buffer_usage_hardware)
618+ .set_event_handler(&window_event_handler, this)
619+ .create_window();
620+
621+ paint_window(target_window);
622+ }
623+
624+ void invoke_window_event_handler(MirWindow* window, MirEvent const* event)
625+ {
626+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
627+ if (window == this->window) window_event_handler_(event);
628+ if (window == target_window) target_window_event_handler_(event);
629+ }
630+
631+ void invoke_window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event)
632+ {
633+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
634+ if (window == this->window) window_dnd_start_(event);
635+ }
636+
637+ std::mutex window_event_handler_mutex;
638+ std::function<void(MirDragAndDropEvent const* event)> window_dnd_start_ = [](MirDragAndDropEvent const*) {};
639+ std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
640+ std::function<void(MirEvent const* event)> target_window_event_handler_ = [](MirEvent const*) {};
641+
642+ static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
643+ static void window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context);
644+
645+ Connection another_connection;
646+ Window target_window;
647+};
648+
649+void DragAndDrop::set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler)
650+{
651+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
652+ if (window == this->window) window_event_handler_ = handler;
653+ if (window == target_window) target_window_event_handler_ = handler;
654+}
655+
656+void DragAndDrop::set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler)
657+{
658+std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
659+if (window == this->window) window_dnd_start_ = handler;
660+}
661+
662+
663+void DragAndDrop::reset_window_event_handler(MirWindow* window)
664+{
665+ if (window == this->window) window_event_handler_ = [](MirEvent const*) {};
666+ if (window == target_window) target_window_event_handler_ = [](MirEvent const*) {};
667+}
668+
669+void DragAndDrop::paint_window(MirWindow* w)
670+{
671+ Signal have_focus;
672+
673+ set_window_event_handler(w, [&](MirEvent const* event)
674+ {
675+ if (mir_event_get_type(event) != mir_event_type_window)
676+ return;
677+
678+ auto const window_event = mir_event_get_window_event(event);
679+ if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
680+ return;
681+
682+ if (mir_window_event_get_attribute_value(window_event))
683+ have_focus.raise();
684+ });
685+
686+ mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(w));
687+
688+ EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
689+
690+ reset_window_event_handler(w);
691+}
692+
693+void DragAndDrop::center_mouse()
694+{
695+ Signal have_mouseover;
696+
697+ set_window_event_handler(window, [&](MirEvent const* event)
698+ {
699+ if (mir_event_get_type(event) != mir_event_type_input)
700+ return;
701+
702+ auto const input_event = mir_event_get_input_event(event);
703+
704+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
705+ return;
706+
707+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
708+
709+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
710+ return;
711+
712+ have_mouseover.raise();
713+ });
714+
715+ move_mouse(0.5 * as_displacement(screen_geometry.size));
716+
717+// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
718+// But it isn't essential for the test and we've probably waited long enough
719+// for the mouse-down needed by the test to reach the window.
720+// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
721+ have_mouseover.wait_for(receive_event_timeout);
722+
723+ reset_window_event_handler(window);
724+}
725+
726+void DragAndDrop::window_event_handler(MirWindow* window, MirEvent const* event, void* context)
727+{
728+ static_cast<DragAndDrop*>(context)->invoke_window_event_handler(window, event);
729+}
730+
731+void DragAndDrop::window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context)
732+{
733+ static_cast<DragAndDrop*>(context)->invoke_window_dnd_start_handler(window, event);
734+}
735+
736+
737+auto DragAndDrop::user_initiates_drag() -> Cookie
738+{
739+ Cookie cookie;
740+ Signal have_cookie;
741+
742+ set_window_event_handler(window, [&](MirEvent const* event)
743+ {
744+ if (mir_event_get_type(event) != mir_event_type_input)
745+ return;
746+
747+ auto const input_event = mir_event_get_input_event(event);
748+
749+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
750+ return;
751+
752+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
753+
754+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
755+ return;
756+
757+ cookie = Cookie{mir_input_event_get_cookie(input_event)};
758+ have_cookie.raise();
759+ });
760+
761+ start_dragging_mouse();
762+
763+ EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
764+
765+ reset_window_event_handler(window);
766+ return cookie;
767+}
768+
769+auto DragAndDrop::client_requests_drag(Cookie const& cookie) -> Blob
770+{
771+ Blob blob;
772+ Signal initiated;
773+
774+ set_window_dnd_start_handler(window, [&](MirDragAndDropEvent const* event)
775+ {
776+ if (dnd)
777+ blob.reset(dnd->start_drag_and_drop(event));
778+
779+ if (blob)
780+ initiated.raise();
781+ });
782+
783+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
784+
785+ if (dnd)
786+ dnd->request_drag_and_drop(window, cookie);
787+
788+ EXPECT_TRUE(initiated.wait_for(receive_event_timeout));
789+
790+ reset_window_event_handler(window);
791+ return blob;
792+}
793+
794+auto DragAndDrop::handle_from_mouse_move() -> Blob
795+{
796+ Blob blob;
797+ Signal have_blob;
798+
799+ set_window_event_handler(window, [&](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+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
812+
813+ if (dnd)
814+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
815+
816+ if (blob)
817+ have_blob.raise();
818+ });
819+
820+ move_mouse({1,1});
821+
822+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
823+
824+ reset_window_event_handler(window);
825+ return blob;
826+}
827+
828+auto DragAndDrop::handle_from_mouse_leave() -> Blob
829+{
830+ Blob blob;
831+ Signal have_blob;
832+
833+ set_window_event_handler(window, [&](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_leave)
846+ return;
847+
848+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
849+
850+ if (dnd)
851+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
852+
853+ if (blob)
854+ have_blob.raise();
855+ });
856+
857+ move_mouse({1,1});
858+ move_mouse(0.5 * as_displacement(surface_size));
859+
860+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
861+
862+ reset_window_event_handler(window);
863+ return blob;
864+}
865+
866+auto DragAndDrop::handle_from_mouse_enter() -> Blob
867+{
868+ Blob blob;
869+ Signal have_blob;
870+
871+ set_window_event_handler(target_window, [&](MirEvent const* event)
872+ {
873+ if (mir_event_get_type(event) != mir_event_type_input)
874+ return;
875+
876+ auto const input_event = mir_event_get_input_event(event);
877+
878+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
879+ return;
880+
881+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
882+
883+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
884+ return;
885+
886+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
887+
888+ if (dnd)
889+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
890+
891+ if (blob)
892+ have_blob.raise();
893+ });
894+
895+ move_mouse({1,1});
896+ move_mouse(0.5 * as_displacement(surface_size));
897+
898+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
899+
900+ reset_window_event_handler(target_window);
901+ return blob;
902+}
903+
904+auto DragAndDrop::handle_from_mouse_release() -> Blob
905+{
906+ Blob blob;
907+ Signal have_blob;
908+
909+ set_window_event_handler(target_window, [&](MirEvent const* event)
910+ {
911+ if (mir_event_get_type(event) != mir_event_type_input)
912+ return;
913+
914+ auto const input_event = mir_event_get_input_event(event);
915+
916+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
917+ return;
918+
919+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
920+
921+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_up)
922+ return;
923+
924+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
925+
926+ if (dnd)
927+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
928+
929+ if (blob)
930+ have_blob.raise();
931+ });
932+
933+ move_mouse({1,1});
934+ move_mouse(0.5 * as_displacement(surface_size));
935+ release_mouse();
936+
937+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
938+
939+ reset_window_event_handler(target_window);
940+ return blob;
941+}
942+
943+auto DragAndDrop::count_of_handles_when_moving_mouse() -> int
944+{
945+ Signal have_3_events;
946+ std::atomic<int> events{0};
947+ std::atomic<int> handles{0};
948+
949+ auto counter = [&](MirEvent const* event)
950+ {
951+ if (mir_event_get_type(event) != mir_event_type_input)
952+ return;
953+
954+ auto const input_event = mir_event_get_input_event(event);
955+
956+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
957+ return;
958+
959+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
960+
961+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
962+
963+ Blob blob;
964+ if (dnd)
965+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
966+
967+ if (blob)
968+ handles.fetch_add(1);
969+
970+ if (events.fetch_add(1) == 2)
971+ have_3_events.raise();
972+ };
973+
974+ set_window_event_handler(window, counter);
975+ set_window_event_handler(target_window, counter);
976+
977+ start_dragging_mouse();
978+ move_mouse({1,1});
979+ release_mouse();
980+
981+ EXPECT_TRUE(have_3_events.wait_for(receive_event_timeout));
982+
983+ reset_window_event_handler(window);
984+ reset_window_event_handler(target_window);
985+ return handles;
986+}
987+
988+auto DragAndDrop::build_window_manager_policy(miral::WindowManagerTools const& tools) -> std::unique_ptr<TestWindowManagerPolicy>
989+{
990+ struct DnDWindowManagerPolicy : miral::TestServer::TestWindowManagerPolicy, miral::WindowManagementPolicyAddendum2
991+ {
992+ using miral::TestServer::TestWindowManagerPolicy::TestWindowManagerPolicy;
993+
994+ void handle_request_drag_and_drop(miral::WindowInfo& window_info) override
995+ {
996+ uuid_t uuid;
997+ uuid_generate(uuid);
998+ std::vector<uint8_t> const handle{std::begin(uuid), std::end(uuid)};
999+
1000+ tools.start_drag_and_drop(window_info, handle);
1001+ }
1002+ };
1003+
1004+ return std::make_unique<DnDWindowManagerPolicy>(tools, *this);
1005+}
1006+
1007+MATCHER_P(BlobContentEq, p, "")
1008+{
1009+ if (!arg || !p)
1010+ return false;
1011+ if (mir_blob_size(arg) != mir_blob_size(p))
1012+ return false;
1013+ return !memcmp(mir_blob_data(arg), mir_blob_data(p), mir_blob_size(p));
1014+}
1015+}
1016+
1017+TEST_F(DragAndDrop, when_user_initiates_drag_client_receives_cookie)
1018+{
1019+ auto const cookie = user_initiates_drag();
1020+
1021+ EXPECT_THAT(cookie, NotNull());
1022+}
1023+
1024+TEST_F(DragAndDrop, when_client_requests_drags_it_receives_handle)
1025+{
1026+ auto const cookie = user_initiates_drag();
1027+ ASSERT_THAT(cookie, NotNull());
1028+
1029+ auto const handle = client_requests_drag(cookie);
1030+
1031+ EXPECT_THAT(handle, NotNull());
1032+}
1033+
1034+TEST_F(DragAndDrop, during_drag_when_user_moves_mouse_client_receives_handle)
1035+{
1036+ auto const cookie = user_initiates_drag();
1037+ ASSERT_THAT(cookie, NotNull());
1038+ auto const handle_from_request = client_requests_drag(cookie);
1039+
1040+ auto const handle = handle_from_mouse_move();
1041+
1042+ EXPECT_THAT(handle, NotNull());
1043+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1044+}
1045+
1046+TEST_F(DragAndDrop, when_drag_moves_from_window_leave_event_contains_handle)
1047+{
1048+ auto const cookie = user_initiates_drag();
1049+ ASSERT_THAT(cookie, NotNull());
1050+ auto const handle_from_request = client_requests_drag(cookie);
1051+
1052+ auto const handle = handle_from_mouse_leave();
1053+
1054+ EXPECT_THAT(handle, NotNull());
1055+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1056+}
1057+
1058+TEST_F(DragAndDrop, when_drag_enters_target_window_enter_event_contains_handle)
1059+{
1060+ auto const cookie = user_initiates_drag();
1061+ ASSERT_THAT(cookie, NotNull());
1062+ auto const handle_from_request = client_requests_drag(cookie);
1063+
1064+ auto const handle = handle_from_mouse_enter();
1065+
1066+ EXPECT_THAT(handle, NotNull());
1067+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1068+}
1069+
1070+TEST_F(DragAndDrop, when_drag_releases_target_window_release_event_contains_handle)
1071+{
1072+ auto const cookie = user_initiates_drag();
1073+ ASSERT_THAT(cookie, NotNull());
1074+ auto const handle_from_request = client_requests_drag(cookie);
1075+
1076+ auto const handle = handle_from_mouse_release();
1077+
1078+ EXPECT_THAT(handle, NotNull());
1079+ EXPECT_THAT(handle, BlobContentEq(handle_from_request));
1080+}
1081+
1082+TEST_F(DragAndDrop, after_drag_finishes_pointer_events_no_longer_contain_handle)
1083+{
1084+ auto const cookie = user_initiates_drag();
1085+ ASSERT_THAT(cookie, NotNull());
1086+ client_requests_drag(cookie);
1087+ handle_from_mouse_release();
1088+
1089+ invoke_tools([](miral::WindowManagerTools& tools) { tools.end_drag_and_drop(); });
1090+
1091+ EXPECT_THAT(count_of_handles_when_moving_mouse(), Eq(0));
1092+}

Subscribers

People subscribed via source and target branches