Mir

Merge lp:~alan-griffiths/mir/client-initiates-user-move-and-resize into lp:mir

Proposed by Alan Griffiths
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 4129
Proposed branch: lp:~alan-griffiths/mir/client-initiates-user-move-and-resize
Merge into: lp:mir
Prerequisite: lp:~alan-griffiths/mir/request-with-authority
Diff against target: 385 lines (+293/-0)
9 files modified
include/client/mir_toolkit/mir_window.h (+9/-0)
src/client/mir_surface.cpp (+5/-0)
src/client/mir_surface.h (+1/-0)
src/client/mir_surface_api.cpp (+14/-0)
src/client/symbols.map (+2/-0)
src/protobuf/mir_protobuf.proto (+1/-0)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/client_mediated_user_gestures.cpp (+259/-0)
tests/acceptance-tests/drag_and_drop.cpp (+1/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/client-initiates-user-move-and-resize
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Gerry Boland (community) Abstain
Daniel van Vugt Abstain
Chris Halse Rogers Approve
Andreas Pokorny (community) Approve
Review via email: mp+320917@code.launchpad.net

Commit message

Client side and initial tests for a client initiating a user move gesture

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:4112
https://mir-jenkins.ubuntu.com/job/mir-ci/3238/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4358
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4445
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4435
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4435
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4435
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4390
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4390/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4390
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4390/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4390
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4390/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4390
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4390/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4390
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4390/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4390
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4390/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I've linked the 'move' bug 1420334.

But why write more code for client-initiated resizing? That already works :)

review: Needs Information
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

As an example of a self-resizing client, try 'tiled' which has a resize grip in the bottom right corner. It already works.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> I've linked the 'move' bug 1420334.
>
> But why write more code for client-initiated resizing? That already works :)

No it doesn't. ;)

I agree clients can resize, but they can't request the shell to initiate a resize gesture. There is no way to invoke the touch resize handles under U8, drag the left edge or top.

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

That's true, but the feature you're talking about in Unity7 also does not have resize handles (right click titlebar and then Resize, or just Alt+F8).

So either the client initiates the resize, and that works perfectly already; or
the server initiates resize, and that works perfectly already too.

Even if you did want it to pop up the resize handles, that's still a shell operation since the window menu itself is part of the shell. Not part of the client.

I think this branch should be just about movement.

Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

There is a difference between submitting a buffer with a different size, and resizing a window. So if we want to support CSD, this change is necessary.

review: Approve
Revision history for this message
Gerry Boland (gerboland) wrote :

How is this API to be used by the client? I.e. where is the client specifying the new size/position?

Is it that the client gets a mouseDown event with a cookie that it decides should cause a resize/move, so calls the mir_window_request_user_resize with that cookie. Then
(a) shell intercepts all other input events, instead sending resize events to the client? Or
(b) shell keep sending the mouseMove events to the client, and client resizes its window/buffer as it pleases (WM can restrict those choices though)?
On resize completion, mouseUp event finally sent to client?

If a user wants to resize the top-left corner of a window, is that a move & resize? Should that be an atomic operation instead?

The only reason I see that Mir's current window resize api is insufficient for client-size-initiated-resize is the cursor - we want the cursor to be the shell-themed resize image, and to remain that image even if the cursor moves beyond the window borders.

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> How is this API to be used by the client? I.e. where is the client specifying
> the new size/position?

The client isn't specifying the position or size. It is requesting the shell treats an input event as the beginning of a gesture.

> Is it that the client gets a mouseDown event with a cookie that it decides
> should cause a resize/move, so calls the mir_window_request_user_resize with
> that cookie. Then
> (a) shell intercepts all other input events, instead sending resize events to
> the client? Or
> (b) shell keep sending the mouseMove events to the client, and client resizes
> its window/buffer as it pleases (WM can restrict those choices though)?
> On resize completion, mouseUp event finally sent to client?

(a)

> If a user wants to resize the top-left corner of a window, is that a move &
> resize? Should that be an atomic operation instead?

That's an atomic operation in those shells that currently support it. Initiating the resize through a client API shouldn't change that.

> The only reason I see that Mir's current window resize api is insufficient for
> client-size-initiated-resize is the cursor - we want the cursor to be the
> shell-themed resize image, and to remain that image even if the cursor moves
> beyond the window borders.

Hmm, I can see it will be easier to reach consensus on the USER_MOVE codepath. Maybe I should split that out?

Revision history for this message
William Hua (attente) wrote :

Andreas is right, even if we can change the buffer size, this isn't the same as requesting a resize operation from an input event.

In gtk, we have this api we're expected to implement:

https://git.gnome.org/browse/gtk+/tree/gdk/gdkwindow.c#n8649

So for mir_window_request_user_resize(), I'd expect it to be able to at least accept a directional argument that allows us to do a fixed resize in a certain direction. I believe there should also be special semantics if the client doesn't specify any direction, where the server should decide that based on the initial mouse motion or initial arrow key press.

I wouldn't expect the client to get a mouse up event on completion of the resize, or any events other than resize ones during the client's resizing. So from Gerry's comment, (a) is the most sensible for us in gtk, especially from the api's point of view. If there's non-resizable windows, the server should already know that from the time the window was created, and just reject any resize operation in that case.

It also needs to be implemented so that the user can also resize using the keyboard arrow keys as well.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> Hmm, I can see it will be easier to reach consensus on the USER_MOVE codepath.
> Maybe I should split that out?

OK, I'm dropping the resize code from this MP as it clearly needs further consideration.

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

PASSED: Continuous integration, rev:4115
https://mir-jenkins.ubuntu.com/job/mir-ci/3253/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4381
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4470
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4460
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4460
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4460
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4413
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4413/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4413
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4413/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4413
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4413/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4413
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4413/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4413
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4413/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4413
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4413/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

This seems sensible to me.

review: Approve
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

*shrug*

My request has been satisfied but I would hazard a guess this functionality may take us a few attempts to cover all the major use cases.

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

I'm fairly sure we need zero more work done on resizing. All the resizing use cases are covered by existing APIs.

On the topic of client-initiated movement though, please start thinking about the Chrome/Chromium use case too: You drag a tab out of the window, that then creates a new window where the cursor remains on the same relative tab coordinate in the new window. If it's not baked in to the drag action then we'll need to be able to create that new top-level window at a precise coordinate relative to the old window (which may be the parent but can also be closed leaving just the child open).

Revision history for this message
Gerry Boland (gerboland) wrote :

No objections here

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

FAILED: Autolanding.
More details in the following jenkins job:
https://mir-jenkins.ubuntu.com/job/mir-autolanding/1243/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4408/console
    None: https://mir-jenkins.ubuntu.com/job/generic-land-mp/1312/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4500/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4490/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4490/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4490/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4440/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4440/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4440/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4440/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4440/console
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4440/console

review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

04:02:04 Text conflict in src/client/mir_surface.cpp
04:02:04 1 conflicts encountered.

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

PASSED: Continuous integration, rev:4116
https://mir-jenkins.ubuntu.com/job/mir-ci/3277/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4418
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4510
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4500
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4500
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4500
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4450
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4450/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4450
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4450/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4450
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4450/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4450
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4450/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4450
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4450/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4450
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4450/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'include/client/mir_toolkit/mir_window.h'
2--- include/client/mir_toolkit/mir_window.h 2017-03-14 04:41:33 +0000
3+++ include/client/mir_toolkit/mir_window.h 2017-03-29 08:37:58 +0000
4@@ -738,6 +738,15 @@
5 void mir_window_raise(MirWindow* window, MirCookie const* cookie);
6
7 /**
8+ * Informs the window manager that the user is moving the window.
9+ *
10+ * \param [in] window The window to move
11+ * \param [in] cookie A cookie instance obtained from an input event.
12+ * An invalid cookie will terminate the client connection.
13+ */
14+void mir_window_request_user_move(MirWindow* window, MirCookie const* cookie);
15+
16+/**
17 * Get the type (purpose) of a window.
18 * \param [in] window The window to query
19 * \return The type of the window
20
21=== modified file 'src/client/mir_surface.cpp'
22--- src/client/mir_surface.cpp 2017-03-28 14:28:26 +0000
23+++ src/client/mir_surface.cpp 2017-03-29 08:37:58 +0000
24@@ -588,6 +588,11 @@
25 request_operation(cookie, mp::RequestOperation::MAKE_ACTIVE);
26 }
27
28+void MirSurface::request_user_move(MirCookie const* cookie)
29+{
30+ request_operation(cookie, mp::RequestOperation::USER_MOVE);
31+}
32+
33 void MirSurface::request_drag_and_drop(MirCookie const* cookie)
34 {
35 request_operation(cookie, mp::RequestOperation::START_DRAG_AND_DROP);
36
37=== modified file 'src/client/mir_surface.h'
38--- src/client/mir_surface.h 2017-03-28 14:28:26 +0000
39+++ src/client/mir_surface.h 2017-03-29 08:37:58 +0000
40@@ -200,6 +200,7 @@
41 MirWaitHandle* set_preferred_orientation(MirOrientationMode mode);
42
43 void raise_surface(MirCookie const* cookie);
44+ void request_user_move(MirCookie const* cookie);
45 void request_drag_and_drop(MirCookie const* cookie);
46 void set_drag_and_drop_start_handler(std::function<void(MirWindowEvent const*)> const& callback);
47
48
49=== modified file 'src/client/mir_surface_api.cpp'
50--- src/client/mir_surface_api.cpp 2017-03-14 04:41:33 +0000
51+++ src/client/mir_surface_api.cpp 2017-03-29 08:37:58 +0000
52@@ -695,6 +695,20 @@
53 }
54 }
55
56+void mir_window_request_user_move(MirWindow* window, MirCookie const* cookie)
57+{
58+ mir::require(mir_window_is_valid(window));
59+
60+ try
61+ {
62+ window->request_user_move(cookie);
63+ }
64+ catch (std::exception const& ex)
65+ {
66+ MIR_LOG_UNCAUGHT_EXCEPTION(ex);
67+ }
68+}
69+
70 MirWindowType mir_window_get_type(MirWindow* window)
71 {
72 MirWindowType type = mir_window_type_normal;
73
74=== modified file 'src/client/symbols.map'
75--- src/client/symbols.map 2017-03-28 11:32:58 +0000
76+++ src/client/symbols.map 2017-03-29 08:37:58 +0000
77@@ -604,6 +604,8 @@
78 mir_keyboard_event_key_text;
79 mir_output_get_logical_height;
80 mir_output_get_logical_width;
81+ mir_window_request_user_move;
82+ mir_window_request_user_resize;
83 mir_touchscreen_config_get_mapping_mode;
84 mir_touchscreen_config_get_output_id;
85 mir_touchscreen_config_set_mapping_mode;
86
87=== modified file 'src/protobuf/mir_protobuf.proto'
88--- src/protobuf/mir_protobuf.proto 2017-03-23 15:00:37 +0000
89+++ src/protobuf/mir_protobuf.proto 2017-03-29 08:37:58 +0000
90@@ -443,6 +443,7 @@
91 UNKNOWN = 0;
92 START_DRAG_AND_DROP = 1;
93 MAKE_ACTIVE = 2;
94+ USER_MOVE = 3;
95 }
96
97 message RequestWithAuthority {
98
99=== modified file 'tests/acceptance-tests/CMakeLists.txt'
100--- tests/acceptance-tests/CMakeLists.txt 2017-03-15 09:30:18 +0000
101+++ tests/acceptance-tests/CMakeLists.txt 2017-03-29 08:37:58 +0000
102@@ -3,6 +3,7 @@
103 set(
104 SOURCES
105
106+ client_mediated_user_gestures.cpp
107 drag_and_drop.cpp
108
109 # Catch double-free bugs by wrapping close() and abort()ing on EBADF
110
111=== added file 'tests/acceptance-tests/client_mediated_user_gestures.cpp'
112--- tests/acceptance-tests/client_mediated_user_gestures.cpp 1970-01-01 00:00:00 +0000
113+++ tests/acceptance-tests/client_mediated_user_gestures.cpp 2017-03-29 08:37:58 +0000
114@@ -0,0 +1,259 @@
115+/*
116+ * Copyright © 2017 Canonical Ltd.
117+ *
118+ * This program is free software: you can redistribute it and/or modify it
119+ * under the terms of the GNU General Public License version 3,
120+ * as published by the Free Software Foundation.
121+ *
122+ * This program is distributed in the hope that it will be useful,
123+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
124+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
125+ * GNU General Public License for more details.
126+ *
127+ * You should have received a copy of the GNU General Public License
128+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
129+ *
130+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
131+ */
132+
133+#include <mir_toolkit/mir_window.h>
134+#include <mir_toolkit/mir_blob.h>
135+
136+#include <mir/geometry/displacement.h>
137+#include <mir/input/input_device_info.h>
138+#include <mir/input/device_capability.h>
139+#include <mir/shell/shell.h>
140+
141+#include <mir_test_framework/connected_client_with_a_window.h>
142+#include <mir_test_framework/fake_input_device.h>
143+#include <mir_test_framework/stub_server_platform_factory.h>
144+#include <mir/test/event_factory.h>
145+#include <mir/test/signal.h>
146+
147+#include <gmock/gmock.h>
148+#include <gtest/gtest.h>
149+
150+#include <linux/input.h>
151+
152+#include <atomic>
153+
154+using namespace std::chrono_literals;
155+using namespace mir::geometry;
156+using namespace testing;
157+using mir::test::Signal;
158+
159+namespace
160+{
161+class Cookie
162+{
163+public:
164+ Cookie() = default;
165+
166+ explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {}
167+
168+ operator MirCookie const*() const { return self.get(); }
169+
170+ auto get() const -> MirCookie const* { return self.get(); }
171+
172+ void reset() { self.reset(); }
173+
174+ void reset(MirCookie const* cookie) { self.reset(cookie, deleter); }
175+
176+private:
177+ static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); }
178+
179+ std::shared_ptr<MirCookie const> self;
180+};
181+
182+void mir_cookie_release(Cookie const&) = delete;
183+
184+struct MouseMoverAndFaker
185+{
186+ void start_dragging_mouse()
187+ {
188+ using namespace mir::input::synthesis;
189+ fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
190+ }
191+
192+ void move_mouse(Displacement const& displacement)
193+ {
194+ using mir::input::synthesis::a_pointer_event;
195+ fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
196+ }
197+
198+ void release_mouse()
199+ {
200+ using namespace mir::input::synthesis;
201+ fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
202+ }
203+
204+private:
205+ std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
206+ mir_test_framework::add_fake_input_device(
207+ mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})
208+ };
209+};
210+
211+Rectangle const screen_geometry{{0, 0}, {800, 600}};
212+auto const receive_event_timeout = 90s;
213+
214+struct ClientMediatedUserGestures : mir_test_framework::ConnectedClientWithAWindow,
215+ MouseMoverAndFaker
216+{
217+ void SetUp() override
218+ {
219+ initial_display_layout({screen_geometry});
220+ mir_test_framework::ConnectedClientWithAWindow::SetUp();
221+ mir_window_set_event_handler(window, &window_event_handler, this);
222+
223+ paint_window();
224+
225+ center_mouse();
226+ }
227+
228+ void TearDown() override
229+ {
230+ reset_window_event_handler();
231+ mir_test_framework::ConnectedClientWithAWindow::TearDown();
232+ }
233+
234+ auto user_initiates_gesture() -> Cookie;
235+
236+private:
237+ void center_mouse();
238+ void paint_window();
239+ void set_window_event_handler(std::function<void(MirEvent const* event)> const& handler);
240+ void reset_window_event_handler();
241+ void invoke_window_event_handler(MirEvent const* event)
242+ {
243+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
244+ window_event_handler_(event);
245+ }
246+
247+ std::mutex window_event_handler_mutex;
248+ std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
249+
250+ static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
251+};
252+
253+void ClientMediatedUserGestures::set_window_event_handler(std::function<void(MirEvent const* event)> const& handler)
254+{
255+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
256+ window_event_handler_ = handler;
257+}
258+
259+void ClientMediatedUserGestures::reset_window_event_handler()
260+{
261+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
262+ window_event_handler_ = [](MirEvent const*) {};
263+}
264+
265+void ClientMediatedUserGestures::window_event_handler(MirWindow* /*window*/, MirEvent const* event, void* context)
266+{
267+ static_cast<ClientMediatedUserGestures*>(context)->invoke_window_event_handler(event);
268+}
269+
270+void ClientMediatedUserGestures::paint_window()
271+{
272+ Signal have_focus;
273+
274+ set_window_event_handler([&](MirEvent const* event)
275+ {
276+ if (mir_event_get_type(event) != mir_event_type_window)
277+ return;
278+
279+ auto const window_event = mir_event_get_window_event(event);
280+ if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
281+ return;
282+
283+ if (mir_window_event_get_attribute_value(window_event))
284+ have_focus.raise();
285+ });
286+
287+ mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(window));
288+
289+ EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
290+
291+ reset_window_event_handler();
292+}
293+
294+void ClientMediatedUserGestures::center_mouse()
295+{
296+ Signal have_mouseover;
297+
298+ set_window_event_handler([&](MirEvent const* event)
299+ {
300+ if (mir_event_get_type(event) != mir_event_type_input)
301+ return;
302+
303+ auto const input_event = mir_event_get_input_event(event);
304+
305+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
306+ return;
307+
308+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
309+
310+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
311+ return;
312+
313+ have_mouseover.raise();
314+ });
315+
316+ move_mouse(0.5 * as_displacement(screen_geometry.size));
317+
318+// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
319+// But it isn't essential for the test and we've probably waited long enough
320+// for the mouse-down needed by the test to reach the window.
321+// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
322+ have_mouseover.wait_for(receive_event_timeout);
323+
324+ reset_window_event_handler();
325+}
326+
327+auto ClientMediatedUserGestures::user_initiates_gesture() -> Cookie
328+{
329+ Cookie cookie;
330+ Signal have_cookie;
331+
332+ set_window_event_handler([&](MirEvent const* event)
333+ {
334+ if (mir_event_get_type(event) != mir_event_type_input)
335+ return;
336+
337+ auto const input_event = mir_event_get_input_event(event);
338+
339+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
340+ return;
341+
342+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
343+
344+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
345+ return;
346+
347+ cookie = Cookie{mir_input_event_get_cookie(input_event)};
348+ have_cookie.raise();
349+ });
350+
351+ start_dragging_mouse();
352+
353+ EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
354+
355+ reset_window_event_handler();
356+ return cookie;
357+}
358+}
359+
360+TEST_F(ClientMediatedUserGestures, when_user_initiates_gesture_client_receives_cookie)
361+{
362+ auto const cookie = user_initiates_gesture();
363+
364+ EXPECT_THAT(cookie.get(), NotNull());
365+}
366+
367+// TODO extend this test when server side implemented
368+TEST_F(ClientMediatedUserGestures, when_client_initiates_move_nothing_bad_happens)
369+{
370+ auto const cookie = user_initiates_gesture();
371+
372+ mir_window_request_user_move(window, cookie);
373+}
374
375=== modified file 'tests/acceptance-tests/drag_and_drop.cpp'
376--- tests/acceptance-tests/drag_and_drop.cpp 2017-03-17 15:32:15 +0000
377+++ tests/acceptance-tests/drag_and_drop.cpp 2017-03-29 08:37:58 +0000
378@@ -213,6 +213,7 @@
379
380 void DragAndDrop::reset_window_event_handler(MirWindow* window)
381 {
382+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
383 if (window == this->window) window_event_handler_ = [](MirEvent const*) {};
384 if (window == target_window) target_window_event_handler_ = [](MirEvent const*) {};
385 }

Subscribers

People subscribed via source and target branches