Mir

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

Proposed by Alan Griffiths
Status: Superseded
Proposed branch: lp:~alan-griffiths/mir/drag-and-drop-II
Merge into: lp:mir
Diff against target: 2548 lines (+1425/-27)
75 files modified
examples/server_example_basic_window_manager.cpp (+8/-0)
examples/server_example_basic_window_manager.h (+5/-0)
include/client/mir/events/event_builders.h (+2/-0)
include/client/mir_toolkit/extensions/drag_and_drop.h (+81/-0)
include/server/mir/scene/null_surface_observer.h (+1/-0)
include/server/mir/scene/surface.h (+1/-0)
include/server/mir/scene/surface_observer.h (+2/-0)
include/server/mir/shell/abstract_shell.h (+8/-0)
include/server/mir/shell/focus_controller.h (+5/-0)
include/server/mir/shell/input_targeter.h (+5/-0)
include/server/mir/shell/shell.h (+4/-0)
include/server/mir/shell/shell_wrapper.h (+8/-0)
include/server/mir/shell/system_compositor_window_manager.h (+5/-0)
include/server/mir/shell/window_manager.h (+5/-0)
include/test/mir/test/doubles/mock_window_manager.h (+1/-0)
include/test/mir/test/doubles/stub_surface.h (+1/-0)
include/test/mir_test_framework/observant_shell.h (+8/-0)
src/capnproto/mir_event.capnp (+5/-1)
src/client/CMakeLists.txt (+2/-0)
src/client/drag_and_drop.cpp (+85/-0)
src/client/drag_and_drop.h (+32/-0)
src/client/events/event_builders.cpp (+25/-0)
src/client/mir_blob.cpp (+1/-8)
src/client/mir_connection.cpp (+4/-0)
src/client/mir_surface.cpp (+32/-0)
src/client/mir_surface.h (+4/-0)
src/client/rpc/mir_display_server.cpp (+9/-0)
src/client/rpc/mir_display_server.h (+4/-0)
src/client/symbols.map (+2/-0)
src/common/events/pointer_event.cpp (+41/-3)
src/common/events/surface_event.cpp (+39/-1)
src/common/symbols.map (+4/-0)
src/include/common/mir/events/pointer_event.h (+6/-1)
src/include/common/mir/events/surface_event.h (+6/-1)
src/include/common/mir/protobuf/display_server.h (+4/-0)
src/include/common/mir_blob.h (+32/-0)
src/include/server/mir/frontend/shell.h (+5/-0)
src/include/server/mir/scene/surface_event_source.h (+1/-0)
src/include/server/mir/scene/surface_observers.h (+1/-0)
src/include/server/mir/shell/basic_window_manager.h (+15/-0)
src/include/server/mir/shell/canonical_window_manager.h (+4/-0)
src/protobuf/mir_protobuf.proto (+5/-0)
src/protobuf/symbols.map (+26/-0)
src/server/frontend/protobuf_message_processor.cpp (+4/-0)
src/server/frontend/session_mediator.cpp (+20/-1)
src/server/frontend/session_mediator.h (+4/-0)
src/server/frontend/shell_wrapper.cpp (+8/-0)
src/server/frontend/shell_wrapper.h (+5/-0)
src/server/input/null_input_targeter.h (+3/-0)
src/server/input/surface_input_dispatcher.cpp (+42/-8)
src/server/input/surface_input_dispatcher.h (+5/-1)
src/server/scene/basic_surface.cpp (+11/-0)
src/server/scene/basic_surface.h (+1/-0)
src/server/scene/legacy_surface_change_notification.cpp (+4/-0)
src/server/scene/legacy_surface_change_notification.h (+1/-0)
src/server/scene/null_surface_observer.cpp (+1/-0)
src/server/scene/surface_event_source.cpp (+5/-0)
src/server/shell/abstract_shell.cpp (+17/-0)
src/server/shell/basic_window_manager.cpp (+21/-0)
src/server/shell/canonical_window_manager.cpp (+13/-0)
src/server/shell/frontend_shell.cpp (+10/-0)
src/server/shell/frontend_shell.h (+5/-0)
src/server/shell/shell_wrapper.cpp (+18/-0)
src/server/shell/system_compositor_window_manager.cpp (+7/-0)
src/server/symbols.map (+10/-0)
tests/acceptance-tests/CMakeLists.txt (+2/-0)
tests/acceptance-tests/drag_and_drop.cpp (+624/-0)
tests/acceptance-tests/test_client_cursor_api.cpp (+1/-0)
tests/include/mir/test/doubles/mock_input_targeter.h (+3/-0)
tests/include/mir/test/doubles/mock_shell.h (+3/-0)
tests/include/mir/test/doubles/stub_display_server.h (+4/-0)
tests/include/mir/test/doubles/stub_input_targeter.h (+3/-0)
tests/include/mir/test/doubles/stub_scene_surface.h (+1/-0)
tests/mir_test_framework/observant_shell.cpp (+21/-2)
tests/mir_test_framework/stub_surface.cpp (+4/-0)
To merge this branch: bzr merge lp:~alan-griffiths/mir/drag-and-drop-II
Reviewer Review Type Date Requested Status
Alan Griffiths Needs Information
Andreas Pokorny (community) Needs Information
Chris Halse Rogers Needs Information
Brandon Schaefer (community) Approve
Cemil Azizoglu (community) Needs Information
Mir CI Bot continuous-integration Approve
Kevin DuBois (community) Approve
Review via email: mp+319820@code.launchpad.net

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

This proposal has been superseded by a proposal from 2017-03-17.

Commit message

Initial support for Drag and Drop: tests (and implementation for) starting drag and releasing on target surface

Description of the change

Initial support for Drag and Drop: tests (and implementation for) starting drag and releasing on target surface

One more episode to follow for handling the cancellation from content hub.

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

FAILED: Continuous integration, rev:4086
https://mir-jenkins.ubuntu.com/job/mir-ci/3142/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4216/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4303
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4293
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4293
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4293
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4243
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4243/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/4243
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4243/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4243/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4243
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4243/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/4243
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4243/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/4243
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4243/artifact/output/*zip*/output.zip

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

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

18:39:25 [ RUN ] DragAndDrop.when_drag_moves_from_window_leave_event_contains_cookie
...
18:39:25 /<<BUILDDIR>>/mir-0.26.0+zesty4293bzr4086/tests/acceptance-tests/drag_and_drop.cpp:256: Failure
18:39:25 Value of: have_cookie.wait_for(receive_event_timeout)
18:39:25 Expected: is equal to true
18:39:25 Actual: false (of type bool)
18:39:25 ==11713== Invalid read of size 8

The memory errors and FD leaks that follow result from following the resulting nullptr.

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

> 18:39:25 [ RUN ]
> DragAndDrop.when_drag_moves_from_window_leave_event_contains_cookie
> ...
> 18:39:25 /<<BUILDDIR>>/mir-0.26.0+zesty4293bzr4086/tests/acceptance-
> tests/drag_and_drop.cpp:256: Failure
> 18:39:25 Value of: have_cookie.wait_for(receive_event_timeout)
> 18:39:25 Expected: is equal to true
> 18:39:25 Actual: false (of type bool)
> 18:39:25 ==11713== Invalid read of size 8
>
> The memory errors and FD leaks that follow result from following the resulting
> nullptr.

Hmm wrong paste buffer, but still I don't see how this can happen:

18:35:34 11: [ RUN ] DragAndDrop.when_drag_moves_from_window_leave_event_contains_cookie
...
18:37:06 11: /<<BUILDDIR>>/mir-0.26.0+zesty4293bzr4086/tests/acceptance-tests/drag_and_drop.cpp:256: Failure
18:37:06 11: Value of: have_cookie.wait_for(receive_event_timeout)
18:37:06 11: Expected: is equal to true
18:37:06 11: Actual: false (of type bool)
18:37:06 11: ==11713== Invalid read of size 8

Basically, the click isn't seen by the window. And on this branch the window has definitely received {mir_window_attrib_focus, true}

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

FAILED: Continuous integration, rev:4088
https://mir-jenkins.ubuntu.com/job/mir-ci/3151/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4230/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4317
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4307
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4307
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4307
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4257/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/4257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4257/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4257/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4257/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/4257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4257/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/4257
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4257/artifact/output/*zip*/output.zip

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

review: Needs Fixing (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:4089
https://mir-jenkins.ubuntu.com/job/mir-ci/3153/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4232
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4319
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4309
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4309
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4309
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4259
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4259/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/4259
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4259/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4259
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4259/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/4259
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4259/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/4259
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4259/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/4259
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4259/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/3153/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:4090
https://mir-jenkins.ubuntu.com/job/mir-ci/3154/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4233
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4320
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4310
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4310
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4310
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4260
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4260/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/4260
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4260/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4260
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4260/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/4260
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4260/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/4260
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4260/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/4260
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4260/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:4091
https://mir-jenkins.ubuntu.com/job/mir-ci/3155/
Executed test runs:
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-mir/4234/console
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4321
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4311
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4311
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4311
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4261
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4261/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/4261
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4261/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4261
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4261/artifact/output/*zip*/output.zip
    FAILURE: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4261/console
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4261/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/4261
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4261/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/4261
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4261/artifact/output/*zip*/output.zip

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

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

Failure is unrelated (lp:1660889)

So that's 3 CI runs with the new tests passing. (And soak testing them locally.)

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

PASSED: Continuous integration, rev:4092
https://mir-jenkins.ubuntu.com/job/mir-ci/3158/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4237
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4324
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4314
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4314
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4314
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4264
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4264/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/4264
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4264/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4264
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4264/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/4264
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4264/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/4264
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4264/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/4264
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4264/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

l773 could use comment here as well (like the other comment noting how CapPnP has problems)
+ //std::copy(dnd_handle.begin(), dnd_handle.end(), back_inserter(blob->data_));

+// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
1995 +// But it isn't essential for the test and we've probably waited long enough
1996 +// for the mouse-down needed by the test to reach the window.
1997 +// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
does that need a bug?

both those minor, lgtm overall

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

> l773 could use comment here as well (like the other comment noting how CapPnP
> has problems)
> + //std::copy(dnd_handle.begin(), dnd_handle.end(),
> back_inserter(blob->data_));

Fixed.

> +// We miss the "mouseover" occasionally (with valgrind and heavy stress about
> 1/20).
> 1995 +// But it isn't essential for the test and we've probably waited long
> enough
> 1996 +// for the mouse-down needed by the test to reach the window.
> 1997 +// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout),
> Eq(true));
> does that need a bug?

Possibly. It would appear there is a lack of consistency between the window management "model" that says the surface is ready and has focus (hence the mir_window_attrib_focus notification in paint_window) and the input "model" that does not (yet) route input to the surface. That's what you get by updating observers asynchronously - which, given the code we landed to support it, appears to have been a deliberate decision.

I doubt we'll see an issue in "real life".

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

PASSED: Continuous integration, rev:4094
https://mir-jenkins.ubuntu.com/job/mir-ci/3168/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/4251
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/4338
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/4328
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial+overlay/4328
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=zesty/4328
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4278
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=zesty/4278/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/4278
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial+overlay/4278/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4278
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=zesty/4278/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/4278
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/4278/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/4278
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/4278/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/4278
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial+overlay/4278/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

Why is this an extension? Because it requires an outside entity like a content hub?
---------------------------------

Is this needed?

130 + mir_surface_attrib_drag_and_drop_handle,
---------------------------------

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

> Why is this an extension? Because it requires an outside entity like a content
> hub?

So that we can evolve the API without breaking ABI

> Is this needed?
>
> 130 + mir_surface_attrib_drag_and_drop_handle,
> ---------------------------------

It doesn't have to be done this way, but the mir_surface_attribs and mir_window_attribs enum members have to be kept in step somehow. This seemed the clearest approach.

Revision history for this message
Kevin DuBois (kdub) wrote :

> > Why is this an extension? Because it requires an outside entity like a
> content
> > hub?
>
> So that we can evolve the API without breaking ABI
+1 to evolving client api this way, keeps 1.0 stable, and can be incorporated to main api once the work is done and there are no more warts.

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

lgtm

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

Needs info:
I'm not sure that overriding the events is appropriate - as I understand it, this will result in Mir sending pointer_enter/pointer_leave/pointer_motion events in cases where we currently don't?

It looks like this will cause clients to behave incorrectly unless their event handler checks each pointer event for a drag handle, and treats pointer events with drag handles as not pointer events?

For example: given a client B that doesn't check pointer events for drag handles, if you initiate a drag and then move the pointer over B's window, this will act like you're just moving the pointer normally over B's window, and B will highlight buttons, perform on-hover actions, and so on? And then if you release the drag over a button in B's window, B will respond as if you mouse-button-released on that button, possibly activating it?

Nit: The locking in
+void MirSurface::request_drag_and_drop(MirCookie const* cookie)
appears unnecessary? All of the things it accesses are stack variables (cookie) or written-once-at-initialisation (surface_id).

Is the locking for the appeasement of ThreadSanitizer?

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

> Needs info:
> I'm not sure that overriding the events is appropriate - as I understand it,
> this will result in Mir sending pointer_enter/pointer_leave/pointer_motion
> events in cases where we currently don't?

Yes leaving the boundaries of a window even when buttons are pressed will cause the source window to receive a pointer leave and potentially crossed windows to receive pointer_enter and motion events even though pointer buttons are still pressed.

>
> It looks like this will cause clients to behave incorrectly unless their event
> handler checks each pointer event for a drag handle, and treats pointer events
> with drag handles as not pointer events?

I doubt ui toolkits unaware of the drag handle will treat it as a click sequence, so there is wrong behavior but not really severe.

We cannot safely expand enums with extensions - so we either expand the MirPointerAction enum in an unsafe and problematic way (i.e. we have values like mir_pointer_actions) or we do not treat this as an extension or we accept the possible damage we do to current clients.

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

Now looking again - why do we need a surface attribute for drag and drop?

Are downstreams using values like: mir_window_attribs?

I think we should remove those.

Besides the enum changes and window attribute - lgtm

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

> > Needs info:
> > I'm not sure that overriding the events is appropriate - as I understand it,
> > this will result in Mir sending pointer_enter/pointer_leave/pointer_motion
> > events in cases where we currently don't?
>
> Yes leaving the boundaries of a window even when buttons are pressed will
> cause the source window to receive a pointer leave and potentially crossed
> windows to receive pointer_enter and motion events even though pointer buttons
> are still pressed.
>
> >
> > It looks like this will cause clients to behave incorrectly unless their
> event
> > handler checks each pointer event for a drag handle, and treats pointer
> events
> > with drag handles as not pointer events?
>
> I doubt ui toolkits unaware of the drag handle will treat it as a click
> sequence, so there is wrong behavior but not really severe.

The Nuremberg defence: https://goo.gl/AfG5v2 - shows the D&D handle carried by the mouse movement events. I don't know the domain well enough to be certain if the behaviour of drag-and-drop oblivious clients could be significantly affected, but it seems unlikely.

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

> Now looking again - why do we need a surface attribute for drag and drop?
>
> Are downstreams using values like: mir_window_attribs?
>
> I think we should remove those.
>
> Besides the enum changes and window attribute - lgtm

We needed some event to carry the drag handle. Extending the window event seemed the path of least resistance. But we've hard-coded the existence of an attribute value pair into the API.

I can think of two alternatives:

/1/ Use window events as the transport, but filter out those with a D&D handle (and pass the handle to an entirely separate callback registered through the extension API).

/2/ Introduce a new drag-and-drop event to the transport.

If we were to introduce a new drag-and-drop event to carry the initial handle then we could also use it for leave/enter/move/release - which would address some of the concerns about using pointer made above.

OTOH /1/ seems simpler.

*Seeking guidance* on /1/ or /2/

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

Which route will allow us to easily expand drag and drop to touch guestures?

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

> Which route will allow us to easily expand drag and drop to touch guestures?

I imagine the client-side processing needs to be different (i.e. different visual effects) so it might be easier to "piggy back" on the existing input events. But without a design for D&D touch I can't be sure.

/me wonders if Unity7 has prior art.

Revision history for this message
Chris Halse Rogers (raof) wrote :

I'd be reasonably happy with (1).

If people don't like throwing in an ad-hoc callback for each extension, an alternative would be to assign event types *in* the extension, like so:

struct DnDv1
{
    int (*dnd_event_type)();
    MirDndEvent const* (*dnd_event_from_event)(MirEvent const*);
    MirBlob* (*dnd_event_get_handle)(MirDndEvent const*);
    MirDndAction (*dnd_event_get_action)(MirDndEvent const*);
}

…
DnDv1* dnd = mir_drag_and_drop_v1(connection);
…
<in event handler>
if (mir_event_get_type(event) == dnd->dnd_event_type())
{
    MirDndEvent const* dnd_event = dnd->dnd_event_from_event(event);
    if (dnd->dnd_event_get_action(dnd_event) == mir_dnd_event_enter)
    {
        handle_drop_enter_with_handle(dnd->dnd_event_get_handle(dnd_event));
        â€¦
    }
}

I prefer this to one-callback-per-extension, but not strongly enough to block. I don't *think* it'd be significantly more work to implement than (1), though.

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

> I'd be reasonably happy with (1).
>
> If people don't like throwing in an ad-hoc callback for each extension, an
> alternative would be to assign event types *in* the extension, like so:
...
> if (mir_event_get_type(event) == dnd->dnd_event_type())

Perhaps:

- if (mir_event_get_type(event) == dnd->dnd_event_type())
+ if (dnd && dnd->is_dnd_event(event))

Revision history for this message
Chris Halse Rogers (raof) wrote :

Also happy with that :)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/server_example_basic_window_manager.cpp'
2--- examples/server_example_basic_window_manager.cpp 2017-01-18 02:29:37 +0000
3+++ examples/server_example_basic_window_manager.cpp 2017-03-17 15:49:43 +0000
4@@ -136,6 +136,14 @@
5 policy->handle_raise_surface(session, surface);
6 }
7
8+void me::BasicWindowManager::handle_request_drag_and_drop(
9+ std::shared_ptr<scene::Session> const& /*session*/,
10+ std::shared_ptr<scene::Surface> const& /*surface*/,
11+ uint64_t /*timestamp*/)
12+{
13+ // Not supported in example servers
14+}
15+
16 int me::BasicWindowManager::set_surface_attribute(
17 std::shared_ptr<scene::Session> const& /*session*/,
18 std::shared_ptr<scene::Surface> const& surface,
19
20=== modified file 'examples/server_example_basic_window_manager.h'
21--- examples/server_example_basic_window_manager.h 2017-01-18 02:29:37 +0000
22+++ examples/server_example_basic_window_manager.h 2017-03-17 15:49:43 +0000
23@@ -174,6 +174,11 @@
24 std::shared_ptr<scene::Surface> const& surface,
25 uint64_t timestamp) override;
26
27+ void handle_request_drag_and_drop(
28+ std::shared_ptr<scene::Session> const& session,
29+ std::shared_ptr<scene::Surface> const& surface,
30+ uint64_t timestamp) override;
31+
32 int set_surface_attribute(
33 std::shared_ptr<scene::Session> const& /*session*/,
34 std::shared_ptr<scene::Surface> const& surface,
35
36=== modified file 'include/client/mir/events/event_builders.h'
37--- include/client/mir/events/event_builders.h 2017-02-15 13:36:35 +0000
38+++ include/client/mir/events/event_builders.h 2017-03-17 15:49:43 +0000
39@@ -161,6 +161,8 @@
40 void transform_positions(MirEvent& event, mir::geometry::Displacement const& movement);
41 void set_window_id(MirEvent& event, int window_id);
42
43+EventUPtr make_start_drag_and_drop_event(frontend::SurfaceId const& surface_id, std::vector<uint8_t> const& handle);
44+void set_drag_and_drop_handle(MirEvent& event, std::vector<uint8_t> const& handle);
45 }
46 }
47
48
49=== added file 'include/client/mir_toolkit/extensions/drag_and_drop.h'
50--- include/client/mir_toolkit/extensions/drag_and_drop.h 1970-01-01 00:00:00 +0000
51+++ include/client/mir_toolkit/extensions/drag_and_drop.h 2017-03-17 15:49:43 +0000
52@@ -0,0 +1,81 @@
53+/*
54+ * Copyright © 2017 Canonical Ltd.
55+ *
56+ * This program is free software: you can redistribute it and/or modify it
57+ * under the terms of the GNU Lesser General Public License version 3,
58+ * as published by the Free Software Foundation.
59+ *
60+ * This program is distributed in the hope that it will be useful,
61+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
62+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
63+ * GNU Lesser General Public License for more details.
64+ *
65+ * You should have received a copy of the GNU Lesser General Public License
66+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
67+ *
68+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
69+ */
70+
71+#ifndef MIR_DRAG_AND_DROP_H
72+#define MIR_DRAG_AND_DROP_H
73+
74+#include "mir_toolkit/mir_extension_core.h"
75+#include "mir_toolkit/client_types.h"
76+
77+#ifdef __cplusplus
78+extern "C" {
79+#endif
80+
81+typedef struct MirDragAndDropEvent MirDragAndDropEvent;
82+
83+typedef struct MirDragAndDropV1
84+{
85+ /**
86+ * Request drag and drop. If the request succeeds a window event with a
87+ * "start drag" handle will be received.
88+ *
89+ * \warning An invalid cookie will terminate the client connection.
90+ *
91+ * \param [in] window The source window
92+ * \param [in] cookie A cookie instance obtained from an input event.
93+ */
94+ void (*request_drag_and_drop)(MirWindow* window, MirCookie const* cookie);
95+
96+ /**
97+ * Set the drag and drop callback. This receives
98+ *
99+ * \param [in] window The window
100+ * \param [in] callback The callback function.
101+ * \param [in] context To be passed to callback
102+ */
103+ void (*set_start_drag_and_drop_callback)(MirWindow* window,
104+ void (*callback)(MirWindow* window, MirDragAndDropEvent const* event, void* context),
105+ void* context);
106+
107+ /**
108+ * Retrieve any "drag & drop" handle associated with the event.
109+ *
110+ * \param [in] event The event
111+ * \return The associated drag handle or NULL
112+ */
113+ MirBlob* (*start_drag_and_drop)(MirDragAndDropEvent const* event);
114+
115+ /**
116+ * Retrieve any "drag & drop" handle associated with the event.
117+ *
118+ * \param [in] event The event
119+ * \return The associated drag handle or NULL
120+ */
121+ MirBlob* (*pointer_drag_and_drop)(MirPointerEvent const* event);
122+
123+} MirDragAndDropV1;
124+
125+static inline MirDragAndDropV1 const* mir_drag_and_drop_v1(MirConnection* connection)
126+{
127+ return (MirDragAndDropV1 const*) mir_connection_request_extension(connection, "mir_drag_and_drop", 1);
128+}
129+
130+#ifdef __cplusplus
131+}
132+#endif
133+#endif //MIR_DRAG_AND_DROP_H
134\ No newline at end of file
135
136=== modified file 'include/server/mir/scene/null_surface_observer.h'
137--- include/server/mir/scene/null_surface_observer.h 2017-02-15 13:36:35 +0000
138+++ include/server/mir/scene/null_surface_observer.h 2017-03-17 15:49:43 +0000
139@@ -47,6 +47,7 @@
140 void cursor_image_removed() override;
141 void placed_relative(geometry::Rectangle const& placement) override;
142 void input_consumed(MirEvent const* event) override;
143+ void start_drag_and_drop(std::vector<uint8_t> const& handle) override;
144
145 protected:
146 NullSurfaceObserver(NullSurfaceObserver const&) = delete;
147
148=== modified file 'include/server/mir/scene/surface.h'
149--- include/server/mir/scene/surface.h 2017-02-15 14:45:41 +0000
150+++ include/server/mir/scene/surface.h 2017-03-17 15:49:43 +0000
151@@ -116,6 +116,7 @@
152 virtual MirPointerConfinementState confine_pointer_state() const = 0;
153
154 virtual void placed_relative(geometry::Rectangle const& placement) = 0;
155+ virtual void start_drag_and_drop(std::vector<uint8_t> const& handle) = 0;
156 };
157 }
158 }
159
160=== modified file 'include/server/mir/scene/surface_observer.h'
161--- include/server/mir/scene/surface_observer.h 2017-02-15 13:36:35 +0000
162+++ include/server/mir/scene/surface_observer.h 2017-03-17 15:49:43 +0000
163@@ -27,6 +27,7 @@
164
165 #include <glm/glm.hpp>
166 #include <string>
167+#include <vector>
168
169 namespace mir
170 {
171@@ -62,6 +63,7 @@
172 virtual void cursor_image_removed() = 0;
173 virtual void placed_relative(geometry::Rectangle const& placement) = 0;
174 virtual void input_consumed(MirEvent const* event) = 0;
175+ virtual void start_drag_and_drop(std::vector<uint8_t> const& handle) = 0;
176
177 protected:
178 SurfaceObserver() = default;
179
180=== modified file 'include/server/mir/shell/abstract_shell.h'
181--- include/server/mir/shell/abstract_shell.h 2017-01-18 02:29:37 +0000
182+++ include/server/mir/shell/abstract_shell.h 2017-03-17 15:49:43 +0000
183@@ -82,6 +82,11 @@
184 std::shared_ptr<scene::Surface> const& surface,
185 uint64_t timestamp) override;
186
187+ void request_drag_and_drop(
188+ std::shared_ptr<scene::Session> const& session,
189+ std::shared_ptr<scene::Surface> const& surface,
190+ uint64_t timestamp) override;
191+
192 std::shared_ptr<scene::PromptSession> start_prompt_session_for(
193 std::shared_ptr<scene::Session> const& session,
194 scene::PromptSessionCreationParameters const& params) override;
195@@ -124,6 +129,9 @@
196
197 void update_focused_surface_confined_region();
198
199+ void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) override;
200+ void clear_drag_and_drop_handle() override;
201+
202 protected:
203 std::shared_ptr<InputTargeter> const input_targeter;
204 std::shared_ptr<SurfaceStack> const surface_stack;
205
206=== modified file 'include/server/mir/shell/focus_controller.h'
207--- include/server/mir/shell/focus_controller.h 2016-01-29 08:18:22 +0000
208+++ include/server/mir/shell/focus_controller.h 2017-03-17 15:49:43 +0000
209@@ -19,8 +19,10 @@
210 #ifndef MIR_SHELL_FOCUS_CONTROLLER_H_
211 #define MIR_SHELL_FOCUS_CONTROLLER_H_
212
213+#include <stddef.h>
214 #include <memory>
215 #include <set>
216+#include <vector>
217
218 namespace mir
219 {
220@@ -54,6 +56,9 @@
221
222 virtual void raise(SurfaceSet const& surfaces) = 0;
223
224+ virtual void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) = 0;
225+ virtual void clear_drag_and_drop_handle() = 0;
226+
227 protected:
228 FocusController() = default;
229 FocusController(FocusController const&) = delete;
230
231=== modified file 'include/server/mir/shell/input_targeter.h'
232--- include/server/mir/shell/input_targeter.h 2015-06-18 02:46:16 +0000
233+++ include/server/mir/shell/input_targeter.h 2017-03-17 15:49:43 +0000
234@@ -19,7 +19,9 @@
235 #ifndef MIR_SHELL_INPUT_TARGETER_H_
236 #define MIR_SHELL_INPUT_TARGETER_H_
237
238+#include <stddef.h>
239 #include <memory>
240+#include <vector>
241
242 namespace mir
243 {
244@@ -40,6 +42,9 @@
245 virtual void set_focus(std::shared_ptr<input::Surface> const& focus_surface) = 0;
246 virtual void clear_focus() = 0;
247
248+ virtual void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) = 0;
249+ virtual void clear_drag_and_drop_handle() = 0;
250+
251 protected:
252 InputTargeter() = default;
253 InputTargeter(InputTargeter const&) = delete;
254
255=== modified file 'include/server/mir/shell/shell.h'
256--- include/server/mir/shell/shell.h 2017-01-19 00:27:55 +0000
257+++ include/server/mir/shell/shell.h 2017-03-17 15:49:43 +0000
258@@ -100,6 +100,10 @@
259 std::shared_ptr<scene::Surface> const& surface,
260 uint64_t timestamp) = 0;
261
262+ virtual void request_drag_and_drop(
263+ std::shared_ptr<scene::Session> const& session,
264+ std::shared_ptr<scene::Surface> const& surface,
265+ uint64_t timestamp) = 0;
266 /** @} */
267 };
268 }
269
270=== modified file 'include/server/mir/shell/shell_wrapper.h'
271--- include/server/mir/shell/shell_wrapper.h 2017-01-18 02:29:37 +0000
272+++ include/server/mir/shell/shell_wrapper.h 2017-03-17 15:49:43 +0000
273@@ -85,11 +85,19 @@
274 std::shared_ptr<scene::Surface> const& surface,
275 uint64_t timestamp) override;
276
277+ void request_drag_and_drop(
278+ std::shared_ptr<scene::Session> const& session,
279+ std::shared_ptr<scene::Surface> const& surface,
280+ uint64_t timestamp) override;
281+
282 void add_display(geometry::Rectangle const& area) override;
283 void remove_display(geometry::Rectangle const& area) override;
284
285 bool handle(MirEvent const& event) override;
286
287+ void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) override;
288+ void clear_drag_and_drop_handle() override;
289+
290 protected:
291 std::shared_ptr<Shell> const wrapped;
292 };
293
294=== modified file 'include/server/mir/shell/system_compositor_window_manager.h'
295--- include/server/mir/shell/system_compositor_window_manager.h 2017-01-18 02:29:37 +0000
296+++ include/server/mir/shell/system_compositor_window_manager.h 2017-03-17 15:49:43 +0000
297@@ -95,6 +95,11 @@
298 std::shared_ptr<scene::Surface> const& surface,
299 uint64_t timestamp) override;
300
301+ void handle_request_drag_and_drop(
302+ std::shared_ptr<scene::Session> const& session,
303+ std::shared_ptr<scene::Surface> const& surface,
304+ uint64_t timestamp) override;
305+
306 int set_surface_attribute(
307 std::shared_ptr<scene::Session> const& session,
308 std::shared_ptr<scene::Surface> const& surface,
309
310=== modified file 'include/server/mir/shell/window_manager.h'
311--- include/server/mir/shell/window_manager.h 2017-01-18 02:29:37 +0000
312+++ include/server/mir/shell/window_manager.h 2017-03-17 15:49:43 +0000
313@@ -76,6 +76,11 @@
314 std::shared_ptr<scene::Surface> const& surface,
315 uint64_t timestamp) = 0;
316
317+ virtual void handle_request_drag_and_drop(
318+ std::shared_ptr<scene::Session> const& session,
319+ std::shared_ptr<scene::Surface> const& surface,
320+ uint64_t timestamp) = 0;
321+
322 virtual ~WindowManager() = default;
323 WindowManager() = default;
324 WindowManager(WindowManager const&) = delete;
325
326=== modified file 'include/test/mir/test/doubles/mock_window_manager.h'
327--- include/test/mir/test/doubles/mock_window_manager.h 2017-01-18 02:29:37 +0000
328+++ include/test/mir/test/doubles/mock_window_manager.h 2017-03-17 15:49:43 +0000
329@@ -58,6 +58,7 @@
330 MOCK_METHOD1(handle_pointer_event, bool(MirPointerEvent const*));
331
332 MOCK_METHOD3(handle_raise_surface, void(std::shared_ptr<scene::Session> const&, std::shared_ptr<scene::Surface> const&, uint64_t));
333+ MOCK_METHOD3(handle_request_drag_and_drop, void(std::shared_ptr<scene::Session> const&, std::shared_ptr<scene::Surface> const&, uint64_t));
334
335 MOCK_METHOD4(set_surface_attribute,
336 int(std::shared_ptr<scene::Session> const& session,
337
338=== modified file 'include/test/mir/test/doubles/stub_surface.h'
339--- include/test/mir/test/doubles/stub_surface.h 2017-02-15 14:45:41 +0000
340+++ include/test/mir/test/doubles/stub_surface.h 2017-03-17 15:49:43 +0000
341@@ -70,6 +70,7 @@
342 void set_confine_pointer_state(MirPointerConfinementState state) override;
343 MirPointerConfinementState confine_pointer_state() const override;
344 void placed_relative(geometry::Rectangle const& placement) override;
345+ void start_drag_and_drop(std::vector<uint8_t> const& handle) override;
346 };
347 }
348 }
349
350=== modified file 'include/test/mir_test_framework/observant_shell.h'
351--- include/test/mir_test_framework/observant_shell.h 2017-01-20 00:01:50 +0000
352+++ include/test/mir_test_framework/observant_shell.h 2017-03-17 15:49:43 +0000
353@@ -96,6 +96,14 @@
354 std::shared_ptr<mir::scene::Surface> const& window,
355 uint64_t timestamp) override;
356
357+ void request_drag_and_drop(
358+ std::shared_ptr<mir::scene::Session> const& session,
359+ std::shared_ptr<mir::scene::Surface> const& window,
360+ uint64_t timestamp) override;
361+
362+ void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) override;
363+ void clear_drag_and_drop_handle() override;
364+
365 private:
366 std::shared_ptr<mir::shell::Shell> const wrapped;
367 std::shared_ptr<mir::scene::SurfaceObserver> const surface_observer;
368
369=== modified file 'src/capnproto/mir_event.capnp'
370--- src/capnproto/mir_event.capnp 2017-02-17 08:46:05 +0000
371+++ src/capnproto/mir_event.capnp 2017-03-17 15:49:43 +0000
372@@ -104,6 +104,8 @@
373
374 buttons @7 :UInt32;
375
376+ dndHandle @8 :List(UInt8);
377+
378 enum PointerAction
379 {
380 up @0;
381@@ -149,6 +151,7 @@
382 id @0 :Int32;
383 attrib @1 :Attrib;
384 value @2 :Int32;
385+ dndHandle @3 :List(UInt8);
386
387 enum Attrib
388 {
389@@ -160,8 +163,9 @@
390 dpi @4;
391 visibility @5;
392 preferredOrientation @6;
393+ startDragAndDrop @7;
394 # Must be last
395- surfaceAttrib @7;
396+ surfaceAttrib @8;
397 }
398 }
399
400
401=== modified file 'src/client/CMakeLists.txt'
402--- src/client/CMakeLists.txt 2017-01-24 13:43:12 +0000
403+++ src/client/CMakeLists.txt 2017-03-17 15:49:43 +0000
404@@ -49,6 +49,7 @@
405 add_library(mirclientobjects OBJECT
406
407 display_configuration.cpp
408+ drag_and_drop.cpp drag_and_drop.h
409 error_connections.cpp
410 event.cpp
411 event_printer.cpp
412@@ -105,6 +106,7 @@
413 ${CMAKE_SOURCE_DIR}/include/client/mir_toolkit/mir_error.h
414 mir_extension_core.cpp
415 ${CMAKE_SOURCE_DIR}/include/client/mir_toolkit/mir_extension_core.h
416+ ${CMAKE_SOURCE_DIR}/include/client/mir_toolkit/extensions/drag_and_drop.h
417 )
418
419 # Ensure protobuf C++ headers have been produced before
420
421=== added file 'src/client/drag_and_drop.cpp'
422--- src/client/drag_and_drop.cpp 1970-01-01 00:00:00 +0000
423+++ src/client/drag_and_drop.cpp 2017-03-17 15:49:43 +0000
424@@ -0,0 +1,85 @@
425+/*
426+ * Copyright © 2017 Canonical Ltd.
427+ *
428+ * This program is free software: you can redistribute it and/or modify it
429+ * under the terms of the GNU Lesser General Public License version 3,
430+ * as published by the Free Software Foundation.
431+ *
432+ * This program is distributed in the hope that it will be useful,
433+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
434+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
435+ * GNU Lesser General Public License for more details.
436+ *
437+ * You should have received a copy of the GNU Lesser General Public License
438+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
439+ *
440+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
441+ */
442+
443+#include "drag_and_drop.h"
444+#include "mir_toolkit/extensions/drag_and_drop.h"
445+
446+#include "mir/uncaught.h"
447+#include "mir/events/surface_event.h"
448+#include "mir/events/pointer_event.h"
449+
450+#include "mir_surface.h"
451+
452+namespace
453+{
454+
455+void request_drag_and_drop(MirWindow* window, MirCookie const* cookie)
456+try
457+{
458+ window->request_drag_and_drop(cookie);
459+}
460+catch (std::exception const& e)
461+{
462+ MIR_LOG_UNCAUGHT_EXCEPTION(e);
463+ abort();
464+}
465+
466+void set_start_drag_and_drop_callback(MirWindow* window,
467+ void (*callback)(MirWindow* window, MirDragAndDropEvent const* event, void* context),
468+ void* context)
469+try
470+{
471+ window->set_drag_and_drop_start_handler([callback,window,context](MirWindowEvent const* event)
472+ { callback(window, reinterpret_cast<MirDragAndDropEvent const*>(event), context); });
473+}
474+catch (std::exception const& e)
475+{
476+ MIR_LOG_UNCAUGHT_EXCEPTION(e);
477+ abort();
478+}
479+
480+MirBlob* start_drag_and_drop(MirDragAndDropEvent const* event)
481+try
482+{
483+ return reinterpret_cast<MirWindowEvent const*>(event)->dnd_handle();
484+}
485+catch (std::exception const& e)
486+{
487+ MIR_LOG_UNCAUGHT_EXCEPTION(e);
488+ abort();
489+}
490+
491+MirBlob* pointer_drag_and_drop(MirPointerEvent const* event)
492+try
493+{
494+ return event->dnd_handle();
495+}
496+catch (std::exception const& e)
497+{
498+ MIR_LOG_UNCAUGHT_EXCEPTION(e);
499+ abort();
500+}
501+
502+MirDragAndDropV1 const impl{
503+ &request_drag_and_drop,
504+ &set_start_drag_and_drop_callback,
505+ &start_drag_and_drop,
506+ &pointer_drag_and_drop};
507+}
508+
509+MirDragAndDropV1 const* const mir::drag_and_drop::v1 = &impl;
510
511=== added file 'src/client/drag_and_drop.h'
512--- src/client/drag_and_drop.h 1970-01-01 00:00:00 +0000
513+++ src/client/drag_and_drop.h 2017-03-17 15:49:43 +0000
514@@ -0,0 +1,32 @@
515+/*
516+ * Copyright © 2017 Canonical Ltd.
517+ *
518+ * This program is free software: you can redistribute it and/or modify it
519+ * under the terms of the GNU Lesser General Public License version 3,
520+ * as published by the Free Software Foundation.
521+ *
522+ * This program is distributed in the hope that it will be useful,
523+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
524+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
525+ * GNU Lesser General Public License for more details.
526+ *
527+ * You should have received a copy of the GNU Lesser General Public License
528+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
529+ *
530+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
531+ */
532+
533+#ifndef MIR_DRAG_AND_DROP_V1_H
534+#define MIR_DRAG_AND_DROP_V1_H
535+
536+typedef struct MirDragAndDropV1 MirDragAndDropV1;
537+
538+namespace mir
539+{
540+namespace drag_and_drop
541+{
542+extern MirDragAndDropV1 const* const v1;
543+}
544+}
545+
546+#endif //MIR_DRAG_AND_DROP_V1_H
547
548=== modified file 'src/client/events/event_builders.cpp'
549--- src/client/events/event_builders.cpp 2017-02-15 13:36:35 +0000
550+++ src/client/events/event_builders.cpp 2017-03-17 15:49:43 +0000
551@@ -110,6 +110,20 @@
552 return make_uptr_event(e);
553 }
554
555+auto mev::make_start_drag_and_drop_event(frontend::SurfaceId const& surface_id, std::vector<uint8_t> const& handle)
556+ -> EventUPtr
557+{
558+ auto e = new_event<MirWindowEvent>();
559+
560+ e->set_id(surface_id.as_value());
561+ e->set_attrib(mir_window_attribs);
562+ e->set_value(0);
563+ e->set_dnd_handle(handle);
564+
565+ return make_uptr_event(e);
566+
567+}
568+
569 mir::EventUPtr mev::make_event(mf::SurfaceId const& surface_id)
570 {
571 auto e = new_event<MirCloseWindowEvent>();
572@@ -450,3 +464,14 @@
573 BOOST_THROW_EXCEPTION(std::invalid_argument("Event has no window id."));
574 }
575 }
576+
577+void mev::set_drag_and_drop_handle(MirEvent& event, std::vector<uint8_t> const& handle)
578+{
579+ if (event.type() == mir_event_type_input)
580+ {
581+ auto const input_event = event.to_input();
582+ if (mir_input_event_get_type(input_event) == mir_input_event_type_pointer)
583+ const_cast<MirPointerEvent*>(mir_input_event_get_pointer_event(input_event))->set_dnd_handle(handle);
584+ }
585+}
586+
587
588=== modified file 'src/client/mir_blob.cpp'
589--- src/client/mir_blob.cpp 2017-01-18 02:29:37 +0000
590+++ src/client/mir_blob.cpp 2017-03-17 15:49:43 +0000
591@@ -16,6 +16,7 @@
592 * Authored by: Alan Griffiths <alan@octopull.co.uk>
593 */
594
595+#include "mir_blob.h"
596 #include "display_configuration.h"
597
598 #include "mir_toolkit/mir_blob.h"
599@@ -25,14 +26,6 @@
600
601 namespace mp = mir::protobuf;
602
603-struct MirBlob
604-{
605- virtual size_t size() const = 0;
606- virtual void const* data() const = 0;
607-
608- virtual ~MirBlob() = default;
609-};
610-
611 namespace
612 {
613 struct MirManagedBlob : MirBlob
614
615=== modified file 'src/client/mir_connection.cpp'
616--- src/client/mir_connection.cpp 2017-03-14 04:41:33 +0000
617+++ src/client/mir_connection.cpp 2017-03-17 15:49:43 +0000
618@@ -17,6 +17,7 @@
619 */
620
621 #include "mir_connection.h"
622+#include "drag_and_drop.h"
623 #include "mir_surface.h"
624 #include "mir_prompt_session.h"
625 #include "mir_toolkit/extensions/graphics_module.h"
626@@ -1451,6 +1452,9 @@
627 if (!strcmp(name, "mir_extension_graphics_module") && (version == 1) && graphics_module_extension.is_set())
628 return &graphics_module_extension.value();
629
630+ if (!strcmp(name, "mir_drag_and_drop") && (version == 1))
631+ return const_cast<MirDragAndDropV1*>(mir::drag_and_drop::v1);
632+
633 return platform->request_interface(name, version);
634 }
635
636
637=== modified file 'src/client/mir_surface.cpp'
638--- src/client/mir_surface.cpp 2017-03-14 04:41:33 +0000
639+++ src/client/mir_surface.cpp 2017-03-17 15:49:43 +0000
640@@ -24,6 +24,7 @@
641 #include "connection_surface_map.h"
642
643 #include "mir_toolkit/mir_client_library.h"
644+#include "mir_toolkit/mir_blob.h"
645 #include "mir/frontend/client_constants.h"
646 #include "mir/client_buffer.h"
647 #include "mir/mir_buffer_stream.h"
648@@ -32,6 +33,7 @@
649 #include "mir/cookie/cookie.h"
650 #include "mir_cookie.h"
651 #include "mir/time/posix_timestamp.h"
652+#include "mir/events/surface_event.h"
653
654 #include <cassert>
655 #include <unistd.h>
656@@ -493,7 +495,14 @@
657 auto sev = mir_event_get_window_event(&e);
658 auto a = mir_window_event_get_attribute(sev);
659 if (a < mir_window_attribs)
660+ {
661 attrib_cache[a] = mir_window_event_get_attribute_value(sev);
662+ }
663+ else
664+ {
665+ handle_drag_and_drop_start_callback(sev);
666+ return;
667+ }
668 break;
669 }
670 case mir_event_type_input:
671@@ -598,6 +607,29 @@
672 google::protobuf::NewCallback(google::protobuf::DoNothing));
673 }
674
675+void MirSurface::request_drag_and_drop(MirCookie const* cookie)
676+{
677+ mp::RequestAuthority authority;
678+
679+ std::unique_lock<decltype(mutex)> lock(mutex);
680+ authority.mutable_surface_id()->set_value(surface->id().value());
681+
682+ auto const event_cookie = authority.mutable_cookie();
683+
684+ event_cookie->set_cookie(cookie->cookie().data(), cookie->size());
685+
686+ server->request_drag_and_drop(
687+ &authority,
688+ void_response.get(),
689+ google::protobuf::NewCallback(google::protobuf::DoNothing));
690+}
691+
692+void MirSurface::set_drag_and_drop_start_handler(std::function<void(MirWindowEvent const*)> const& callback)
693+{
694+ std::lock_guard<decltype(mutex)> lock(mutex);
695+ handle_drag_and_drop_start_callback = callback;
696+}
697+
698 MirBufferStream* MirSurface::get_buffer_stream()
699 {
700 std::lock_guard<decltype(mutex)> lock(mutex);
701
702=== modified file 'src/client/mir_surface.h'
703--- src/client/mir_surface.h 2017-02-15 14:45:41 +0000
704+++ src/client/mir_surface.h 2017-03-17 15:49:43 +0000
705@@ -200,6 +200,8 @@
706 MirWaitHandle* set_preferred_orientation(MirOrientationMode mode);
707
708 void raise_surface(MirCookie const* cookie);
709+ void request_drag_and_drop(MirCookie const* cookie);
710+ void set_drag_and_drop_start_handler(std::function<void(MirWindowEvent const*)> const& callback);
711
712 MirWaitHandle* configure_cursor(MirCursorConfiguration const* cursor);
713
714@@ -262,6 +264,8 @@
715 std::shared_ptr<mir::client::FrameClock> const frame_clock;
716
717 std::function<void(MirEvent const*)> handle_event_callback;
718+ std::function<void(MirWindowEvent const*)> handle_drag_and_drop_start_callback = [](auto){};
719+
720 std::shared_ptr<mir::dispatch::ThreadedDispatcher> input_thread;
721
722 //a bit batty, but the creation handle has to exist for as long as the MirSurface does,
723
724=== modified file 'src/client/rpc/mir_display_server.cpp'
725--- src/client/rpc/mir_display_server.cpp 2017-02-15 07:38:33 +0000
726+++ src/client/rpc/mir_display_server.cpp 2017-03-17 15:49:43 +0000
727@@ -238,6 +238,15 @@
728 {
729 channel->call_method(std::string(__func__), request, response, done);
730 }
731+
732+void mclr::DisplayServer::request_drag_and_drop(
733+ mir::protobuf::RequestAuthority const* request,
734+ mir::protobuf::Void* response,
735+ google::protobuf::Closure* done)
736+{
737+ channel->call_method(std::string(__func__), request, response, done);
738+}
739+
740 void mclr::DisplayServer::apply_input_configuration(
741 mir::protobuf::InputConfigurationRequest const* request,
742 mir::protobuf::Void* response,
743
744=== modified file 'src/client/rpc/mir_display_server.h'
745--- src/client/rpc/mir_display_server.h 2017-02-15 07:38:33 +0000
746+++ src/client/rpc/mir_display_server.h 2017-03-17 15:49:43 +0000
747@@ -155,6 +155,10 @@
748 mir::protobuf::RaiseRequest const* request,
749 mir::protobuf::Void* response,
750 google::protobuf::Closure* done) override;
751+ void request_drag_and_drop(
752+ mir::protobuf::RequestAuthority const* request,
753+ mir::protobuf::Void* response,
754+ google::protobuf::Closure* done) override;
755 void apply_input_configuration(
756 mir::protobuf::InputConfigurationRequest const* request,
757 mir::protobuf::Void* response,
758
759=== modified file 'src/client/symbols.map'
760--- src/client/symbols.map 2017-03-14 04:41:33 +0000
761+++ src/client/symbols.map 2017-03-17 15:49:43 +0000
762@@ -604,6 +604,8 @@
763 global:
764 extern "C++" {
765 mir::events::set_window_id*;
766+ mir::events::make_start_drag_and_drop_event*;
767+ mir::events::set_drag_and_drop_handle*;
768 };
769 } MIR_CLIENT_DETAIL_0.26.1;
770
771
772=== modified file 'src/common/events/pointer_event.cpp'
773--- src/common/events/pointer_event.cpp 2016-11-07 23:02:26 +0000
774+++ src/common/events/pointer_event.cpp 2017-03-17 15:49:43 +0000
775@@ -1,5 +1,5 @@
776 /*
777- * Copyright © 2016 Canonical Ltd.
778+ * Copyright © 2016-2017 Canonical Ltd.
779 *
780 * This program is free software: you can redistribute it and/or modify it
781 * under the terms of the GNU Lesser General Public License version 3,
782@@ -16,10 +16,11 @@
783 * Authored by: Andreas Pokorny <andreas.pokorny@canonical.com>
784 */
785
786+#include "mir/events/pointer_event.h"
787+#include "mir_blob.h"
788+
789 #include <boost/throw_exception.hpp>
790
791-#include "mir/events/pointer_event.h"
792-
793 MirPointerEvent::MirPointerEvent()
794 {
795 event.initInput();
796@@ -132,3 +133,40 @@
797 {
798 event.getInput().getPointer().setAction(static_cast<mir::capnp::PointerEvent::PointerAction>(action));
799 }
800+
801+void MirPointerEvent::set_dnd_handle(std::vector<uint8_t> const& handle)
802+{
803+ event.getInput().getPointer().initDndHandle(handle.size());
804+ event.getInput().getPointer().setDndHandle(::kj::ArrayPtr<uint8_t const>{&*begin(handle), &*end(handle)});
805+}
806+
807+namespace
808+{
809+struct MyMirBlob : MirBlob
810+{
811+
812+ size_t size() const override { return data_.size(); }
813+ virtual void const* data() const override { return data_.data(); }
814+
815+ std::vector<uint8_t> data_;
816+};
817+}
818+
819+MirBlob* MirPointerEvent::dnd_handle() const
820+{
821+ auto const reader = event.asReader().getInput().getPointer();
822+
823+ if (!reader.hasDndHandle())
824+ return nullptr;
825+
826+ auto const dnd_handle = reader.getDndHandle();
827+
828+ auto blob = std::make_unique<MyMirBlob>();
829+ blob->data_.reserve(dnd_handle.size());
830+
831+ // Can't use std::copy() as the CapnP iterators don't provide an iterator category
832+ for (auto p = dnd_handle.begin(); p != dnd_handle.end(); ++p)
833+ blob->data_.push_back(*p);
834+
835+ return blob.release();
836+}
837
838=== modified file 'src/common/events/surface_event.cpp'
839--- src/common/events/surface_event.cpp 2017-01-23 03:38:33 +0000
840+++ src/common/events/surface_event.cpp 2017-03-17 15:49:43 +0000
841@@ -1,5 +1,5 @@
842 /*
843- * Copyright © 2016 Canonical Ltd.
844+ * Copyright © 2016-2017 Canonical Ltd.
845 *
846 * This program is free software: you can redistribute it and/or modify it
847 * under the terms of the GNU Lesser General Public License version 3,
848@@ -17,6 +17,7 @@
849 */
850
851 #include "mir/events/surface_event.h"
852+#include "mir_blob.h"
853
854 // MirSurfaceEvent is a deprecated type, but we need to implement it
855 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
856@@ -55,3 +56,40 @@
857 {
858 event.getSurface().setValue(value);
859 }
860+
861+void MirSurfaceEvent::set_dnd_handle(std::vector<uint8_t> const& handle)
862+{
863+ event.getSurface().initDndHandle(handle.size());
864+ event.getSurface().setDndHandle(::kj::ArrayPtr<uint8_t const>{&*begin(handle), &*end(handle)});
865+}
866+
867+namespace
868+{
869+struct MyMirBlob : MirBlob
870+{
871+
872+ size_t size() const override { return data_.size(); }
873+ virtual void const* data() const override { return data_.data(); }
874+
875+ std::vector<uint8_t> data_;
876+};
877+}
878+
879+MirBlob* MirSurfaceEvent::dnd_handle() const
880+{
881+ if (!event.asReader().getSurface().hasDndHandle())
882+ return nullptr;
883+
884+ auto blob = std::make_unique<MyMirBlob>();
885+
886+ auto reader = event.asReader().getSurface().getDndHandle();
887+
888+ blob->data_.reserve(reader.size());
889+
890+ // Can't use std::copy() as the CapnP iterators don't provide an iterator category
891+ for (auto p = reader.begin(); p != reader.end(); ++p)
892+ blob->data_.push_back(*p);
893+
894+ return blob.release();
895+}
896+
897
898=== modified file 'src/common/symbols.map'
899--- src/common/symbols.map 2017-02-28 08:53:57 +0000
900+++ src/common/symbols.map 2017-03-17 15:49:43 +0000
901@@ -432,5 +432,9 @@
902 MirInputEvent::window_id*;
903 MirKeyboardEvent::set_text*;
904 MirKeyboardEvent::text*;
905+ MirPointerEvent::dnd_handle*;
906+ MirPointerEvent::set_dnd_handle*;
907+ MirSurfaceEvent::dnd_handle*;
908+ MirSurfaceEvent::set_dnd_handle*;
909 };
910 } MIR_COMMON_0.26;
911
912=== modified file 'src/include/common/mir/events/pointer_event.h'
913--- src/include/common/mir/events/pointer_event.h 2016-09-22 19:21:34 +0000
914+++ src/include/common/mir/events/pointer_event.h 2017-03-17 15:49:43 +0000
915@@ -1,5 +1,5 @@
916 /*
917- * Copyright © 2016 Canonical Ltd.
918+ * Copyright © 2016-2017 Canonical Ltd.
919 *
920 * This program is free software: you can redistribute it and/or modify it
921 * under the terms of the GNU Lesser General Public License version 3,
922@@ -21,6 +21,8 @@
923
924 #include "mir/events/input_event.h"
925
926+typedef struct MirBlob MirBlob;
927+
928 struct MirPointerEvent : MirInputEvent
929 {
930 MirPointerEvent();
931@@ -60,6 +62,9 @@
932
933 MirPointerButtons buttons() const;
934 void set_buttons(MirPointerButtons buttons);
935+
936+ void set_dnd_handle(std::vector<uint8_t> const& handle);
937+ MirBlob* dnd_handle() const;
938 private:
939 };
940
941
942=== modified file 'src/include/common/mir/events/surface_event.h'
943--- src/include/common/mir/events/surface_event.h 2017-01-18 02:29:37 +0000
944+++ src/include/common/mir/events/surface_event.h 2017-03-17 15:49:43 +0000
945@@ -1,5 +1,5 @@
946 /*
947- * Copyright © 2016 Canonical Ltd.
948+ * Copyright © 2016-2017 Canonical Ltd.
949 *
950 * This program is free software: you can redistribute it and/or modify it
951 * under the terms of the GNU Lesser General Public License version 3,
952@@ -21,6 +21,8 @@
953
954 #include "mir/events/event.h"
955
956+typedef struct MirBlob MirBlob;
957+
958 struct MirSurfaceEvent : MirEvent
959 {
960 MirSurfaceEvent();
961@@ -33,6 +35,9 @@
962
963 int value() const;
964 void set_value(int value);
965+
966+ void set_dnd_handle(std::vector<uint8_t> const& handle);
967+ MirBlob* dnd_handle() const;
968 };
969
970 #endif /* MIR_COMMON_SURFACE_EVENT_H_ */
971
972=== modified file 'src/include/common/mir/protobuf/display_server.h'
973--- src/include/common/mir/protobuf/display_server.h 2017-02-15 07:38:33 +0000
974+++ src/include/common/mir/protobuf/display_server.h 2017-03-17 15:49:43 +0000
975@@ -151,6 +151,10 @@
976 mir::protobuf::RaiseRequest const* request,
977 mir::protobuf::Void* response,
978 google::protobuf::Closure* done) = 0;
979+ virtual void request_drag_and_drop(
980+ mir::protobuf::RequestAuthority const* request,
981+ mir::protobuf::Void* response,
982+ google::protobuf::Closure* done) = 0;
983 virtual void apply_input_configuration(
984 mir::protobuf::InputConfigurationRequest const* request,
985 mir::protobuf::Void* response,
986
987=== added file 'src/include/common/mir_blob.h'
988--- src/include/common/mir_blob.h 1970-01-01 00:00:00 +0000
989+++ src/include/common/mir_blob.h 2017-03-17 15:49:43 +0000
990@@ -0,0 +1,32 @@
991+/*
992+ * Copyright © 2017 Canonical Ltd.
993+ *
994+ * This program is free software: you can redistribute it and/or modify
995+ * it under the terms of the GNU Lesser General Public License version 3 as
996+ * published by the Free Software Foundation.
997+ *
998+ * This program is distributed in the hope that it will be useful,
999+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1000+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1001+ * GNU Lesser General Public License for more details.
1002+ *
1003+ * You should have received a copy of the GNU Lesser General Public License
1004+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1005+ *
1006+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
1007+ */
1008+
1009+#ifndef MIR_MIR_BLOB_H_H
1010+#define MIR_MIR_BLOB_H_H
1011+
1012+#include <stddef.h>
1013+
1014+struct MirBlob
1015+{
1016+ virtual size_t size() const = 0;
1017+ virtual void const* data() const = 0;
1018+
1019+ virtual ~MirBlob() = default;
1020+};
1021+
1022+#endif //MIR_MIR_BLOB_H_H
1023
1024=== modified file 'src/include/server/mir/frontend/shell.h'
1025--- src/include/server/mir/frontend/shell.h 2017-01-18 02:29:37 +0000
1026+++ src/include/server/mir/frontend/shell.h 2017-03-17 15:49:43 +0000
1027@@ -87,6 +87,11 @@
1028 SurfaceId surface_id,
1029 uint64_t timestamp) = 0;
1030
1031+ virtual void request_drag_and_drop(
1032+ std::shared_ptr<Session> const& session,
1033+ SurfaceId surface_id,
1034+ uint64_t timestamp) = 0;
1035+
1036 protected:
1037 Shell() = default;
1038 Shell(const Shell&) = delete;
1039
1040=== modified file 'src/include/server/mir/scene/surface_event_source.h'
1041--- src/include/server/mir/scene/surface_event_source.h 2017-02-15 13:36:35 +0000
1042+++ src/include/server/mir/scene/surface_event_source.h 2017-03-17 15:49:43 +0000
1043@@ -51,6 +51,7 @@
1044 std::string const& variant, std::string const& options) override;
1045 void placed_relative(geometry::Rectangle const& placement) override;
1046 void input_consumed(MirEvent const* event) override;
1047+ void start_drag_and_drop(std::vector<uint8_t> const& handle) override;
1048
1049 private:
1050 frontend::SurfaceId const id;
1051
1052=== modified file 'src/include/server/mir/scene/surface_observers.h'
1053--- src/include/server/mir/scene/surface_observers.h 2017-02-15 13:36:35 +0000
1054+++ src/include/server/mir/scene/surface_observers.h 2017-03-17 15:49:43 +0000
1055@@ -51,6 +51,7 @@
1056 void cursor_image_removed() override;
1057 void placed_relative(geometry::Rectangle const& placement) override;
1058 void input_consumed(MirEvent const* event) override;
1059+ void start_drag_and_drop(std::vector<uint8_t> const& handle) override;
1060 };
1061
1062 }
1063
1064=== modified file 'src/include/server/mir/shell/basic_window_manager.h'
1065--- src/include/server/mir/shell/basic_window_manager.h 2017-01-18 02:29:37 +0000
1066+++ src/include/server/mir/shell/basic_window_manager.h 2017-03-17 15:49:43 +0000
1067@@ -65,6 +65,9 @@
1068
1069 virtual void raise_tree(std::shared_ptr<scene::Surface> const& root) = 0;
1070
1071+ virtual void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) = 0;
1072+ virtual void clear_drag_and_drop_handle() = 0;
1073+
1074 virtual ~WindowManagerTools() = default;
1075 WindowManagerTools() = default;
1076 WindowManagerTools(WindowManagerTools const&) = delete;
1077@@ -107,6 +110,10 @@
1078 std::shared_ptr<scene::Session> const& session,
1079 std::shared_ptr<scene::Surface> const& surface) = 0;
1080
1081+ virtual void handle_request_drag_and_drop(
1082+ std::shared_ptr<scene::Session> const& session,
1083+ std::shared_ptr<scene::Surface> const& surface) = 0;
1084+
1085 virtual ~WindowManagementPolicy() = default;
1086 WindowManagementPolicy() = default;
1087 WindowManagementPolicy(WindowManagementPolicy const&) = delete;
1088@@ -163,6 +170,11 @@
1089 std::shared_ptr<scene::Surface> const& surface,
1090 uint64_t timestamp) override;
1091
1092+ void handle_request_drag_and_drop(
1093+ std::shared_ptr<scene::Session> const& session,
1094+ std::shared_ptr<scene::Surface> const& surface,
1095+ uint64_t timestamp) override;
1096+
1097 int set_surface_attribute(
1098 std::shared_ptr<scene::Session> const& /*session*/,
1099 std::shared_ptr<scene::Surface> const& surface,
1100@@ -192,6 +204,9 @@
1101
1102 void raise_tree(std::shared_ptr<scene::Surface> const& root) override;
1103
1104+ void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) override;
1105+ void clear_drag_and_drop_handle() override;
1106+
1107 private:
1108 shell::FocusController* const focus_controller;
1109 std::unique_ptr<WindowManagementPolicy> const policy;
1110
1111=== modified file 'src/include/server/mir/shell/canonical_window_manager.h'
1112--- src/include/server/mir/shell/canonical_window_manager.h 2017-01-18 02:29:37 +0000
1113+++ src/include/server/mir/shell/canonical_window_manager.h 2017-03-17 15:49:43 +0000
1114@@ -75,6 +75,10 @@
1115 std::shared_ptr<scene::Session> const& session,
1116 std::shared_ptr<scene::Surface> const& surface) override;
1117
1118+ void handle_request_drag_and_drop(
1119+ std::shared_ptr<scene::Session> const& session,
1120+ std::shared_ptr<scene::Surface> const& surface) override;
1121+
1122 private:
1123 static const int modifier_mask =
1124 mir_input_event_modifier_alt |
1125
1126=== modified file 'src/protobuf/mir_protobuf.proto'
1127--- src/protobuf/mir_protobuf.proto 2017-03-14 04:41:33 +0000
1128+++ src/protobuf/mir_protobuf.proto 2017-03-17 15:49:43 +0000
1129@@ -444,6 +444,11 @@
1130 required SurfaceId surface_id = 2;
1131 }
1132
1133+message RequestAuthority {
1134+ required Cookie cookie = 1;
1135+ required SurfaceId surface_id = 2;
1136+}
1137+
1138 message InputDevices {
1139 repeated InputDeviceInfo device_info = 1;
1140 }
1141
1142=== modified file 'src/protobuf/symbols.map'
1143--- src/protobuf/symbols.map 2017-02-15 07:38:33 +0000
1144+++ src/protobuf/symbols.map 2017-03-17 15:49:43 +0000
1145@@ -1112,3 +1112,29 @@
1146 vtable?for?mir::protobuf::PromptSession;
1147 };
1148 } MIR_PROTOBUF_0.22;
1149+
1150+MIR_PROTOBUF_0.27 {
1151+ global:
1152+ extern "C++" {
1153+ mir::protobuf::RequestAuthority::ByteSize*;
1154+ mir::protobuf::RequestAuthority::CheckTypeAndMergeFrom*;
1155+ mir::protobuf::RequestAuthority::Clear*;
1156+ mir::protobuf::RequestAuthority::CopyFrom*;
1157+ mir::protobuf::RequestAuthority::default_instance*;
1158+ mir::protobuf::RequestAuthority::DiscardUnknownFields*;
1159+ mir::protobuf::RequestAuthority::GetTypeName*;
1160+ mir::protobuf::RequestAuthority::IsInitialized*;
1161+ mir::protobuf::RequestAuthority::kCookieFieldNumber*;
1162+ mir::protobuf::RequestAuthority::kSurfaceIdFieldNumber*;
1163+ mir::protobuf::RequestAuthority::MergeFrom*;
1164+ mir::protobuf::RequestAuthority::MergePartialFromCodedStream*;
1165+ mir::protobuf::RequestAuthority::New*;
1166+ mir::protobuf::RequestAuthority::?RequestAuthority*;
1167+ mir::protobuf::RequestAuthority::RequestAuthority*;
1168+ mir::protobuf::RequestAuthority::SerializeWithCachedSizes*;
1169+ mir::protobuf::RequestAuthority::Swap*;
1170+ non-virtual?thunk?to?mir::protobuf::RequestAuthority::?RequestAuthority*;
1171+ typeinfo?for?mir::protobuf::RequestAuthority;
1172+ vtable?for?mir::protobuf::RequestAuthority;
1173+ };
1174+} MIR_PROTOBUF_0.26;
1175
1176=== modified file 'src/server/frontend/protobuf_message_processor.cpp'
1177--- src/server/frontend/protobuf_message_processor.cpp 2017-02-15 07:38:33 +0000
1178+++ src/server/frontend/protobuf_message_processor.cpp 2017-03-17 15:49:43 +0000
1179@@ -305,6 +305,10 @@
1180 {
1181 invoke(this, display_server.get(), &protobuf::DisplayServer::stop_prompt_session, invocation);
1182 }
1183+ else if ("request_drag_and_drop" == invocation.method_name())
1184+ {
1185+ invoke(this, display_server.get(), &protobuf::DisplayServer::request_drag_and_drop, invocation);
1186+ }
1187 else if ("disconnect" == invocation.method_name())
1188 {
1189 invoke(this, display_server.get(), &DisplayServer::disconnect, invocation);
1190
1191=== modified file 'src/server/frontend/session_mediator.cpp'
1192--- src/server/frontend/session_mediator.cpp 2017-03-14 04:41:33 +0000
1193+++ src/server/frontend/session_mediator.cpp 2017-03-17 15:49:43 +0000
1194@@ -1175,6 +1175,26 @@
1195 done->Run();
1196 }
1197
1198+void mir::frontend::SessionMediator::request_drag_and_drop(mir::protobuf::RequestAuthority const* request,
1199+ mir::protobuf::Void*, google::protobuf::Closure* done)
1200+{
1201+ auto const session = weak_session.lock();
1202+ if (!session)
1203+ BOOST_THROW_EXCEPTION(std::logic_error("Invalid application session"));
1204+
1205+ auto const cookie = request->cookie();
1206+ auto const surface_id = request->surface_id();
1207+
1208+ auto cookie_string = cookie.cookie();
1209+
1210+ std::vector<uint8_t> cookie_bytes(cookie_string.begin(), cookie_string.end());
1211+ auto const cookie_ptr = cookie_authority->make_cookie(cookie_bytes);
1212+
1213+ shell->request_drag_and_drop(session, mf::SurfaceId{surface_id.value()}, cookie_ptr->timestamp());
1214+
1215+ done->Run();
1216+}
1217+
1218 void mf::SessionMediator::apply_input_configuration(
1219 mir::protobuf::InputConfigurationRequest const* request,
1220 mir::protobuf::Void*,
1221@@ -1254,7 +1274,6 @@
1222 screencast_buffer_tracker.remove_session(id);
1223 }
1224
1225-
1226 auto mf::detail::PromptSessionStore::insert(std::shared_ptr<PromptSession> const& session) -> PromptSessionId
1227 {
1228 std::lock_guard<decltype(mutex)> lock{mutex};
1229
1230=== modified file 'src/server/frontend/session_mediator.h'
1231--- src/server/frontend/session_mediator.h 2017-03-14 02:26:28 +0000
1232+++ src/server/frontend/session_mediator.h 2017-03-17 15:49:43 +0000
1233@@ -249,6 +249,10 @@
1234 mir::protobuf::RaiseRequest const* request,
1235 mir::protobuf::Void*,
1236 google::protobuf::Closure* done) override;
1237+ void request_drag_and_drop(
1238+ mir::protobuf::RequestAuthority const* request,
1239+ mir::protobuf::Void*,
1240+ google::protobuf::Closure* done) override;
1241 void apply_input_configuration(
1242 mir::protobuf::InputConfigurationRequest const* request,
1243 mir::protobuf::Void* response,
1244
1245=== modified file 'src/server/frontend/shell_wrapper.cpp'
1246--- src/server/frontend/shell_wrapper.cpp 2017-01-18 02:29:37 +0000
1247+++ src/server/frontend/shell_wrapper.cpp 2017-03-17 15:49:43 +0000
1248@@ -106,3 +106,11 @@
1249 {
1250 wrapped->raise_surface(session, surface_id, timestamp);
1251 }
1252+
1253+void mf::ShellWrapper::request_drag_and_drop(
1254+ std::shared_ptr<Session> const& session,
1255+ SurfaceId surface_id,
1256+ uint64_t timestamp)
1257+{
1258+ wrapped->request_drag_and_drop(session, surface_id, timestamp);
1259+}
1260
1261=== modified file 'src/server/frontend/shell_wrapper.h'
1262--- src/server/frontend/shell_wrapper.h 2017-01-18 02:29:37 +0000
1263+++ src/server/frontend/shell_wrapper.h 2017-03-17 15:49:43 +0000
1264@@ -80,6 +80,11 @@
1265 SurfaceId surface_id,
1266 uint64_t timestamp) override;
1267
1268+ void request_drag_and_drop(
1269+ std::shared_ptr<Session> const& session,
1270+ SurfaceId surface_id,
1271+ uint64_t timestamp) override;
1272+
1273 protected:
1274 std::shared_ptr<Shell> const wrapped;
1275 };
1276
1277=== modified file 'src/server/input/null_input_targeter.h'
1278--- src/server/input/null_input_targeter.h 2017-02-15 14:45:41 +0000
1279+++ src/server/input/null_input_targeter.h 2017-03-17 15:49:43 +0000
1280@@ -38,6 +38,9 @@
1281 void clear_focus() override
1282 {
1283 }
1284+
1285+ void set_drag_and_drop_handle(std::vector<uint8_t> const&) override {}
1286+ void clear_drag_and_drop_handle() override {}
1287 };
1288
1289 }
1290
1291=== modified file 'src/server/input/surface_input_dispatcher.cpp'
1292--- src/server/input/surface_input_dispatcher.cpp 2017-01-18 02:29:37 +0000
1293+++ src/server/input/surface_input_dispatcher.cpp 2017-03-17 15:49:43 +0000
1294@@ -70,7 +70,10 @@
1295 std::function<void(ms::Surface*)> const on_removed;
1296 };
1297
1298-void deliver_without_relative_motion(std::shared_ptr<mi::Surface> const& surface, MirEvent const* ev)
1299+void deliver_without_relative_motion(
1300+ std::shared_ptr<mi::Surface> const& surface,
1301+ MirEvent const* ev,
1302+ std::vector<uint8_t> const& drag_and_drop_handle)
1303 {
1304 auto const* input_ev = mir_event_get_input_event(ev);
1305 auto const* pev = mir_input_event_get_pointer_event(input_ev);
1306@@ -98,12 +101,21 @@
1307 0.0f);
1308
1309 mev::transform_positions(*to_deliver, geom::Displacement{bounds.top_left.x.as_int(), bounds.top_left.y.as_int()});
1310+ if (!drag_and_drop_handle.empty())
1311+ mev::set_drag_and_drop_handle(*to_deliver, drag_and_drop_handle);
1312 surface->consume(to_deliver.get());
1313 }
1314
1315-void deliver(std::shared_ptr<mi::Surface> const& surface, MirEvent const* ev)
1316+void deliver(
1317+ std::shared_ptr<mi::Surface> const& surface,
1318+ MirEvent const* ev,
1319+ std::vector<uint8_t> const& drag_and_drop_handle)
1320 {
1321 auto to_deliver = mev::clone_event(*ev);
1322+
1323+ if (!drag_and_drop_handle.empty())
1324+ mev::set_drag_and_drop_handle(*to_deliver, drag_and_drop_handle);
1325+
1326 auto const& bounds = surface->input_bounds();
1327 mev::transform_positions(*to_deliver, geom::Displacement{bounds.top_left.x.as_int(), bounds.top_left.y.as_int()});
1328 surface->consume(to_deliver.get());
1329@@ -243,6 +255,8 @@
1330 mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_x),
1331 mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_y));
1332
1333+ if (!drag_and_drop_handle.empty())
1334+ mev::set_drag_and_drop_handle(*event, drag_and_drop_handle);
1335 surface->consume(event.get());
1336 }
1337
1338@@ -270,12 +284,17 @@
1339
1340 if (pointer_state.gesture_owner)
1341 {
1342- deliver(pointer_state.gesture_owner, ev);
1343-
1344- if (is_gesture_terminator(pev))
1345+ deliver(pointer_state.gesture_owner, ev, drag_and_drop_handle);
1346+
1347+ auto const gesture_terminated = is_gesture_terminator(pev);
1348+
1349+ if (gesture_terminated)
1350 {
1351 pointer_state.gesture_owner.reset();
1352+ }
1353
1354+ if (gesture_terminated || !drag_and_drop_handle.empty())
1355+ {
1356 auto target = find_target_surface(event_x_y);
1357
1358 if (pointer_state.current_target != target)
1359@@ -286,6 +305,9 @@
1360 pointer_state.current_target = target;
1361 if (target)
1362 send_enter_exit_event(target, pev, mir_pointer_action_enter);
1363+
1364+ if (!gesture_terminated)
1365+ pointer_state.gesture_owner = target;
1366 }
1367 }
1368
1369@@ -323,11 +345,11 @@
1370 if (sent_ev)
1371 {
1372 if (action != mir_pointer_action_motion)
1373- deliver_without_relative_motion(target, ev);
1374+ deliver_without_relative_motion(target, ev, drag_and_drop_handle);
1375 }
1376 else
1377 {
1378- deliver(target, ev);
1379+ deliver(target, ev, drag_and_drop_handle);
1380 }
1381 return true;
1382 }
1383@@ -383,7 +405,7 @@
1384
1385 if (gesture_owner)
1386 {
1387- deliver(gesture_owner, ev);
1388+ deliver(gesture_owner, ev, drag_and_drop_handle);
1389
1390 if (is_gesture_end(tev))
1391 gesture_owner.reset();
1392@@ -450,3 +472,15 @@
1393 set_focus_locked(lg, nullptr);
1394 }
1395
1396+void mir::input::SurfaceInputDispatcher::set_drag_and_drop_handle(std::vector<uint8_t> const& handle)
1397+{
1398+ std::lock_guard<std::mutex> lg(dispatcher_mutex);
1399+ drag_and_drop_handle = handle;
1400+}
1401+
1402+void mir::input::SurfaceInputDispatcher::clear_drag_and_drop_handle()
1403+{
1404+ std::lock_guard<std::mutex> lg(dispatcher_mutex);
1405+ drag_and_drop_handle.clear();
1406+}
1407+
1408
1409=== modified file 'src/server/input/surface_input_dispatcher.h'
1410--- src/server/input/surface_input_dispatcher.h 2016-10-05 13:18:38 +0000
1411+++ src/server/input/surface_input_dispatcher.h 2017-03-17 15:49:43 +0000
1412@@ -54,7 +54,10 @@
1413 // InputTargeter
1414 void set_focus(std::shared_ptr<input::Surface> const& target) override;
1415 void clear_focus() override;
1416-
1417+
1418+ void set_drag_and_drop_handle(std::vector<uint8_t> const& handle) override;
1419+ void clear_drag_and_drop_handle() override;
1420+
1421 private:
1422 void device_reset(MirInputDeviceId reset_device_id, std::chrono::nanoseconds when);
1423 bool dispatch_key(MirEvent const* kev);
1424@@ -92,6 +95,7 @@
1425
1426 std::mutex dispatcher_mutex;
1427 std::weak_ptr<input::Surface> focus_surface;
1428+ std::vector<uint8_t> drag_and_drop_handle;
1429 bool started;
1430 };
1431
1432
1433=== modified file 'src/server/scene/basic_surface.cpp'
1434--- src/server/scene/basic_surface.cpp 2017-02-15 14:45:41 +0000
1435+++ src/server/scene/basic_surface.cpp 2017-03-17 15:49:43 +0000
1436@@ -144,6 +144,12 @@
1437 { observer->input_consumed(event); });
1438 }
1439
1440+void ms::SurfaceObservers::start_drag_and_drop(std::vector<uint8_t> const& handle)
1441+{
1442+ for_each([&](std::shared_ptr<SurfaceObserver> const& observer)
1443+ { observer->start_drag_and_drop(handle); });
1444+}
1445+
1446
1447 struct ms::CursorStreamImageAdapter
1448 {
1449@@ -923,3 +929,8 @@
1450 {
1451 observers.placed_relative(placement);
1452 }
1453+
1454+void mir::scene::BasicSurface::start_drag_and_drop(std::vector<uint8_t> const& handle)
1455+{
1456+ observers.start_drag_and_drop(handle);
1457+}
1458
1459=== modified file 'src/server/scene/basic_surface.h'
1460--- src/server/scene/basic_surface.h 2017-02-15 14:45:41 +0000
1461+++ src/server/scene/basic_surface.h 2017-03-17 15:49:43 +0000
1462@@ -139,6 +139,7 @@
1463 void set_confine_pointer_state(MirPointerConfinementState state) override;
1464 MirPointerConfinementState confine_pointer_state() const override;
1465 void placed_relative(geometry::Rectangle const& placement) override;
1466+ void start_drag_and_drop(std::vector<uint8_t> const& handle) override;
1467
1468 private:
1469 bool visible(std::unique_lock<std::mutex>&) const;
1470
1471=== modified file 'src/server/scene/legacy_surface_change_notification.cpp'
1472--- src/server/scene/legacy_surface_change_notification.cpp 2017-02-15 13:36:35 +0000
1473+++ src/server/scene/legacy_surface_change_notification.cpp 2017-03-17 15:49:43 +0000
1474@@ -108,3 +108,7 @@
1475 void ms::LegacySurfaceChangeNotification::input_consumed(MirEvent const*)
1476 {
1477 }
1478+
1479+void ms::LegacySurfaceChangeNotification::start_drag_and_drop(std::vector<uint8_t> const& /*handle*/)
1480+{
1481+}
1482\ No newline at end of file
1483
1484=== modified file 'src/server/scene/legacy_surface_change_notification.h'
1485--- src/server/scene/legacy_surface_change_notification.h 2017-02-15 13:36:35 +0000
1486+++ src/server/scene/legacy_surface_change_notification.h 2017-03-17 15:49:43 +0000
1487@@ -51,6 +51,7 @@
1488 void cursor_image_removed() override;
1489 void placed_relative(geometry::Rectangle const& placement) override;
1490 void input_consumed(MirEvent const* event) override;
1491+ void start_drag_and_drop(std::vector<uint8_t> const& handle) override;
1492
1493 private:
1494 std::function<void()> const notify_scene_change;
1495
1496=== modified file 'src/server/scene/null_surface_observer.cpp'
1497--- src/server/scene/null_surface_observer.cpp 2017-02-15 13:36:35 +0000
1498+++ src/server/scene/null_surface_observer.cpp 2017-03-17 15:49:43 +0000
1499@@ -41,3 +41,4 @@
1500 void ms::NullSurfaceObserver::cursor_image_removed() {}
1501 void ms::NullSurfaceObserver::placed_relative(geometry::Rectangle const& /*placement*/) {}
1502 void ms::NullSurfaceObserver::input_consumed(MirEvent const* /*event*/) {}
1503+void ms::NullSurfaceObserver::start_drag_and_drop(std::vector<uint8_t> const& /*handle*/) {}
1504
1505=== modified file 'src/server/scene/surface_event_source.cpp'
1506--- src/server/scene/surface_event_source.cpp 2017-02-15 13:36:35 +0000
1507+++ src/server/scene/surface_event_source.cpp 2017-03-17 15:49:43 +0000
1508@@ -101,3 +101,8 @@
1509 mev::set_window_id(*ev, id.as_value());
1510 event_sink->handle_event(*ev);
1511 }
1512+
1513+void ms::SurfaceEventSource::start_drag_and_drop(std::vector<uint8_t> const& handle)
1514+{
1515+ event_sink->handle_event(*mev::make_start_drag_and_drop_event(id, handle));
1516+}
1517
1518=== modified file 'src/server/shell/abstract_shell.cpp'
1519--- src/server/shell/abstract_shell.cpp 2017-03-10 19:47:57 +0000
1520+++ src/server/shell/abstract_shell.cpp 2017-03-17 15:49:43 +0000
1521@@ -251,6 +251,14 @@
1522 window_manager->handle_raise_surface(session, surface, timestamp);
1523 }
1524
1525+void msh::AbstractShell::request_drag_and_drop(
1526+ std::shared_ptr<scene::Session> const& session,
1527+ std::shared_ptr<scene::Surface> const& surface,
1528+ uint64_t timestamp)
1529+{
1530+ window_manager->handle_request_drag_and_drop(session, surface, timestamp);
1531+}
1532+
1533 void msh::AbstractShell::focus_next_session()
1534 {
1535 std::unique_lock<std::mutex> lock(focus_mutex);
1536@@ -401,3 +409,12 @@
1537 report->surfaces_raised(surfaces);
1538 }
1539
1540+void msh::AbstractShell::set_drag_and_drop_handle(std::vector<uint8_t> const& handle)
1541+{
1542+ input_targeter->set_drag_and_drop_handle(handle);
1543+}
1544+
1545+void msh::AbstractShell::clear_drag_and_drop_handle()
1546+{
1547+ input_targeter->clear_drag_and_drop_handle();
1548+}
1549
1550=== modified file 'src/server/shell/basic_window_manager.cpp'
1551--- src/server/shell/basic_window_manager.cpp 2017-01-18 02:29:37 +0000
1552+++ src/server/shell/basic_window_manager.cpp 2017-03-17 15:49:43 +0000
1553@@ -135,6 +135,16 @@
1554 policy->handle_raise_surface(session, surface);
1555 }
1556
1557+void msh::BasicWindowManager::handle_request_drag_and_drop(
1558+ std::shared_ptr<scene::Session> const& session,
1559+ std::shared_ptr<scene::Surface> const& surface,
1560+ uint64_t timestamp)
1561+{
1562+ std::lock_guard<decltype(mutex)> lock(mutex);
1563+ if (timestamp >= last_input_event_timestamp)
1564+ policy->handle_request_drag_and_drop(session, surface);
1565+}
1566+
1567 int msh::BasicWindowManager::set_surface_attribute(
1568 std::shared_ptr<scene::Session> const& /*session*/,
1569 std::shared_ptr<scene::Surface> const& surface,
1570@@ -309,3 +319,14 @@
1571 }
1572 }
1573 }
1574+
1575+void mir::shell::BasicWindowManager::set_drag_and_drop_handle(std::vector<uint8_t> const& handle)
1576+{
1577+ focus_controller->set_drag_and_drop_handle(handle);
1578+}
1579+
1580+void mir::shell::BasicWindowManager::clear_drag_and_drop_handle()
1581+{
1582+ focus_controller->clear_drag_and_drop_handle();
1583+}
1584+
1585
1586=== modified file 'src/server/shell/canonical_window_manager.cpp'
1587--- src/server/shell/canonical_window_manager.cpp 2017-03-10 19:47:57 +0000
1588+++ src/server/shell/canonical_window_manager.cpp 2017-03-17 15:49:43 +0000
1589@@ -24,6 +24,7 @@
1590 #include "mir/shell/surface_ready_observer.h"
1591 #include "mir/shell/display_layout.h"
1592
1593+#include <uuid/uuid.h>
1594 #include <linux/input.h>
1595 #include <csignal>
1596
1597@@ -583,6 +584,18 @@
1598 select_active_surface(surface);
1599 }
1600
1601+void msh::CanonicalWindowManagerPolicy::handle_request_drag_and_drop(
1602+ std::shared_ptr<ms::Session> const& /*session*/,
1603+ std::shared_ptr<ms::Surface> const& surface)
1604+{
1605+ uuid_t uuid;
1606+ uuid_generate(uuid);
1607+ std::vector<uint8_t> const handle{std::begin(uuid), std::end(uuid)};
1608+
1609+ surface->start_drag_and_drop(handle);
1610+ tools->set_drag_and_drop_handle(handle);
1611+}
1612+
1613 bool msh::CanonicalWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
1614 {
1615 auto const action = mir_keyboard_event_action(event);
1616
1617=== modified file 'src/server/shell/frontend_shell.cpp'
1618--- src/server/shell/frontend_shell.cpp 2017-01-18 02:29:37 +0000
1619+++ src/server/shell/frontend_shell.cpp 2017-03-17 15:49:43 +0000
1620@@ -157,3 +157,13 @@
1621 auto const surface = scene_session->surface(surface_id);
1622 wrapped->raise_surface(scene_session, surface, timestamp);
1623 }
1624+
1625+void msh::FrontendShell::request_drag_and_drop(
1626+ std::shared_ptr<mf::Session> const& session,
1627+ mf::SurfaceId surface_id,
1628+ uint64_t timestamp)
1629+{
1630+ auto const scene_session = std::dynamic_pointer_cast<ms::Session>(session);
1631+ auto const surface = scene_session->surface(surface_id);
1632+ wrapped->request_drag_and_drop(scene_session, surface, timestamp);
1633+}
1634
1635=== modified file 'src/server/shell/frontend_shell.h'
1636--- src/server/shell/frontend_shell.h 2017-01-18 02:29:37 +0000
1637+++ src/server/shell/frontend_shell.h 2017-03-17 15:49:43 +0000
1638@@ -91,6 +91,11 @@
1639 std::shared_ptr<mf::Session> const& session,
1640 mf::SurfaceId surface_id,
1641 uint64_t timestamp) override;
1642+
1643+ void request_drag_and_drop(
1644+ std::shared_ptr<mf::Session> const& session,
1645+ mf::SurfaceId surface_id,
1646+ uint64_t timestamp) override;
1647 };
1648 }
1649 }
1650
1651=== modified file 'src/server/shell/shell_wrapper.cpp'
1652--- src/server/shell/shell_wrapper.cpp 2017-01-18 02:29:37 +0000
1653+++ src/server/shell/shell_wrapper.cpp 2017-03-17 15:49:43 +0000
1654@@ -120,6 +120,14 @@
1655 wrapped->raise_surface(session, surface, timestamp);
1656 }
1657
1658+void msh::ShellWrapper::request_drag_and_drop(
1659+ std::shared_ptr<ms::Session> const& session,
1660+ std::shared_ptr<ms::Surface> const& surface,
1661+ uint64_t timestamp)
1662+{
1663+ wrapped->request_drag_and_drop(session, surface, timestamp);
1664+}
1665+
1666 void msh::ShellWrapper::add_display(geometry::Rectangle const& area)
1667 {
1668 wrapped->add_display(area);
1669@@ -149,3 +157,13 @@
1670 {
1671 return wrapped->raise(surfaces);
1672 }
1673+
1674+void msh::ShellWrapper::set_drag_and_drop_handle(std::vector<uint8_t> const& handle)
1675+{
1676+ wrapped->set_drag_and_drop_handle(handle);
1677+}
1678+
1679+void msh::ShellWrapper::clear_drag_and_drop_handle()
1680+{
1681+ wrapped->clear_drag_and_drop_handle();
1682+}
1683
1684=== modified file 'src/server/shell/system_compositor_window_manager.cpp'
1685--- src/server/shell/system_compositor_window_manager.cpp 2017-01-18 02:29:37 +0000
1686+++ src/server/shell/system_compositor_window_manager.cpp 2017-03-17 15:49:43 +0000
1687@@ -201,3 +201,10 @@
1688 uint64_t /*timestamp*/)
1689 {
1690 }
1691+
1692+void msh::SystemCompositorWindowManager::handle_request_drag_and_drop(
1693+ std::shared_ptr<ms::Session> const& /*session*/,
1694+ std::shared_ptr<ms::Surface> const& /*surface*/,
1695+ uint64_t /*timestamp*/)
1696+{
1697+}
1698
1699=== modified file 'src/server/symbols.map'
1700--- src/server/symbols.map 2017-03-15 10:38:02 +0000
1701+++ src/server/symbols.map 2017-03-17 15:49:43 +0000
1702@@ -129,6 +129,7 @@
1703 mir::scene::NullSurfaceObserver::reception_mode_set_to*;
1704 mir::scene::NullSurfaceObserver::renamed*;
1705 mir::scene::NullSurfaceObserver::resized_to*;
1706+ mir::scene::NullSurfaceObserver::start_drag_and_drop*;
1707 mir::scene::NullSurfaceObserver::transformation_set_to*;
1708 mir::scene::Observer::?Observer*;
1709 mir::scene::Observer::Observer*;
1710@@ -293,6 +294,7 @@
1711 mir::shell::ShellReport::ShellReport*;
1712 mir::shell::ShellWrapper::add_display*;
1713 mir::shell::ShellWrapper::add_prompt_provider_for*;
1714+ mir::shell::ShellWrapper::clear_drag_and_drop_handle*;
1715 mir::shell::ShellWrapper::close_session*;
1716 mir::shell::ShellWrapper::create_surface*;
1717 mir::shell::ShellWrapper::destroy_surface*;
1718@@ -306,9 +308,11 @@
1719 mir::shell::ShellWrapper::raise*;
1720 mir::shell::ShellWrapper::raise_surface*;
1721 mir::shell::ShellWrapper::remove_display*;
1722+ mir::shell::ShellWrapper::request_drag_and_drop*;
1723 mir::shell::ShellWrapper::set_focus_to*;
1724 mir::shell::ShellWrapper::set_surface_attribute*;
1725 mir::shell::ShellWrapper::ShellWrapper*;
1726+ mir::shell::ShellWrapper::set_drag_and_drop_handle*;
1727 mir::shell::ShellWrapper::start_prompt_session_for*;
1728 mir::shell::ShellWrapper::stop_prompt_session*;
1729 mir::shell::ShellWrapper::surface_at*;
1730@@ -338,6 +342,7 @@
1731 mir::shell::SystemCompositorWindowManager::remove_display*;
1732 mir::shell::SystemCompositorWindowManager::remove_session*;
1733 mir::shell::SystemCompositorWindowManager::remove_surface*;
1734+ mir::shell::SystemCompositorWindowManager::handle_request_drag_and_drop*;
1735 mir::shell::SystemCompositorWindowManager::set_surface_attribute*;
1736 mir::shell::SystemCompositorWindowManager::SystemCompositorWindowManager*;
1737 mir::shell::WindowManager::operator*;
1738@@ -695,6 +700,7 @@
1739 mir::shell::CanonicalWindowManagerPolicy::handle_touch_event*;
1740 mir::shell::CanonicalWindowManagerPolicy::handle_pointer_event*;
1741 mir::shell::CanonicalWindowManagerPolicy::handle_raise_surface*;
1742+ mir::shell::CanonicalWindowManagerPolicy::handle_request_drag_and_drop*;
1743 typeinfo?for?mir::shell::CanonicalWindowManagerPolicy;
1744 vtable?for?mir::shell::CanonicalWindowManagerPolicy;
1745 VTT?for?mir::shell::CanonicalWindowManagerPolicy;
1746@@ -831,6 +837,7 @@
1747 mir::shell::BasicWindowManager::add_session*;
1748 mir::shell::BasicWindowManager::add_surface*;
1749 mir::shell::BasicWindowManager::BasicWindowManager*;
1750+ mir::shell::BasicWindowManager::clear_drag_and_drop_handle*;
1751 mir::shell::BasicWindowManager::find_session*;
1752 mir::shell::BasicWindowManager::focused_session*;
1753 mir::shell::BasicWindowManager::focused_surface*;
1754@@ -839,6 +846,7 @@
1755 mir::shell::BasicWindowManager::handle_keyboard_event*;
1756 mir::shell::BasicWindowManager::handle_pointer_event*;
1757 mir::shell::BasicWindowManager::handle_raise_surface*;
1758+ mir::shell::BasicWindowManager::handle_request_drag_and_drop*;
1759 mir::shell::BasicWindowManager::handle_touch_event*;
1760 mir::shell::BasicWindowManager::info_for*;
1761 mir::shell::BasicWindowManager::modify_surface*;
1762@@ -846,6 +854,7 @@
1763 mir::shell::BasicWindowManager::remove_display*;
1764 mir::shell::BasicWindowManager::remove_session*;
1765 mir::shell::BasicWindowManager::remove_surface*;
1766+ mir::shell::BasicWindowManager::set_drag_and_drop_handle*;
1767 mir::shell::BasicWindowManager::set_focus_to*;
1768 mir::shell::BasicWindowManager::set_surface_attribute*;
1769 mir::shell::BasicWindowManager::surface_at*;
1770@@ -856,6 +865,7 @@
1771 virtual?thunk?to?mir::shell::BasicWindowManager::handle_keyboard_event*;
1772 virtual?thunk?to?mir::shell::BasicWindowManager::handle_pointer_event*;
1773 virtual?thunk?to?mir::shell::BasicWindowManager::handle_raise_surface*;
1774+ virtual?thunk?to?mir::shell::BasicWindowManager::handle_request_drag_and_drop*;
1775 virtual?thunk?to?mir::shell::BasicWindowManager::handle_touch_event*;
1776 virtual?thunk?to?mir::shell::BasicWindowManager::modify_surface*;
1777 virtual?thunk?to?mir::shell::BasicWindowManager::remove_display*;
1778
1779=== modified file 'tests/acceptance-tests/CMakeLists.txt'
1780--- tests/acceptance-tests/CMakeLists.txt 2017-03-10 19:47:57 +0000
1781+++ tests/acceptance-tests/CMakeLists.txt 2017-03-17 15:49:43 +0000
1782@@ -3,6 +3,8 @@
1783 set(
1784 SOURCES
1785
1786+ drag_and_drop.cpp
1787+
1788 # Catch double-free bugs by wrapping close() and abort()ing on EBADF
1789 strict_close.cpp
1790
1791
1792=== added file 'tests/acceptance-tests/drag_and_drop.cpp'
1793--- tests/acceptance-tests/drag_and_drop.cpp 1970-01-01 00:00:00 +0000
1794+++ tests/acceptance-tests/drag_and_drop.cpp 2017-03-17 15:49:43 +0000
1795@@ -0,0 +1,624 @@
1796+/*
1797+ * Copyright © 2017 Canonical Ltd.
1798+ *
1799+ * This program is free software: you can redistribute it and/or modify it
1800+ * under the terms of the GNU General Public License version 3,
1801+ * as published by the Free Software Foundation.
1802+ *
1803+ * This program is distributed in the hope that it will be useful,
1804+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1805+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1806+ * GNU General Public License for more details.
1807+ *
1808+ * You should have received a copy of the GNU General Public License
1809+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1810+ *
1811+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
1812+ */
1813+
1814+#include <mir_toolkit/extensions/drag_and_drop.h>
1815+#include <mir_toolkit/mir_blob.h>
1816+
1817+#include <mir/geometry/displacement.h>
1818+#include <mir/input/input_device_info.h>
1819+#include <mir/input/device_capability.h>
1820+#include <mir/shell/shell.h>
1821+
1822+#include <mir_test_framework/connected_client_with_a_window.h>
1823+#include <mir_test_framework/fake_input_device.h>
1824+#include <mir_test_framework/stub_server_platform_factory.h>
1825+#include <mir/test/event_factory.h>
1826+#include <mir/test/signal.h>
1827+
1828+#include <gmock/gmock.h>
1829+#include <gtest/gtest.h>
1830+
1831+#include <linux/input.h>
1832+
1833+#include <boost/throw_exception.hpp>
1834+#include <atomic>
1835+
1836+using namespace std::chrono_literals;
1837+using namespace mir::geometry;
1838+using namespace testing;
1839+using mir::test::Signal;
1840+
1841+namespace
1842+{
1843+class Cookie
1844+{
1845+public:
1846+ Cookie() = default;
1847+ explicit Cookie(MirCookie const* cookie) : self{cookie, deleter} {}
1848+
1849+ operator MirCookie const*() const { return self.get(); }
1850+ auto get() const -> MirCookie const* { return self.get(); }
1851+
1852+ void reset() { self.reset(); }
1853+ void reset(MirCookie const* cookie) { self.reset(cookie, deleter); }
1854+
1855+private:
1856+ static void deleter(MirCookie const* cookie) { mir_cookie_release(cookie); }
1857+ std::shared_ptr<MirCookie const> self;
1858+};
1859+
1860+void mir_cookie_release(Cookie const&) = delete;
1861+
1862+class Blob
1863+{
1864+public:
1865+ Blob() = default;
1866+ explicit Blob(MirBlob* blob) : self{blob, deleter} {}
1867+
1868+ operator MirBlob*() const { return self.get(); }
1869+ auto get() const -> MirBlob* { return self.get(); }
1870+
1871+ void reset() { self.reset(); }
1872+ void reset(MirBlob* blob) { self.reset(blob, deleter); }
1873+
1874+private:
1875+ static void deleter(MirBlob* blob) { mir_blob_release(blob); }
1876+ std::shared_ptr<MirBlob> self;
1877+};
1878+
1879+void mir_blob_release(Blob const&) = delete;
1880+
1881+struct MouseMoverAndFaker
1882+{
1883+ void start_dragging_mouse()
1884+ {
1885+ using namespace mir::input::synthesis;
1886+ fake_mouse->emit_event(a_button_down_event().of_button(BTN_LEFT));
1887+ }
1888+
1889+ void move_mouse(Displacement const& displacement)
1890+ {
1891+ using mir::input::synthesis::a_pointer_event;
1892+ fake_mouse->emit_event(a_pointer_event().with_movement(displacement.dx.as_int(), displacement.dy.as_int()));
1893+ }
1894+
1895+ void release_mouse()
1896+ {
1897+ using namespace mir::input::synthesis;
1898+ fake_mouse->emit_event(a_button_up_event().of_button(BTN_LEFT));
1899+ }
1900+
1901+private:
1902+ std::unique_ptr<mir_test_framework::FakeInputDevice> fake_mouse{
1903+ mir_test_framework::add_fake_input_device(
1904+ mir::input::InputDeviceInfo{"mouse", "mouse-uid", mir::input::DeviceCapability::pointer})};
1905+};
1906+
1907+Rectangle const screen_geometry{{0,0}, {800,600}};
1908+auto const receive_event_timeout = 90s;
1909+
1910+struct DragAndDrop : mir_test_framework::ConnectedClientWithAWindow,
1911+ MouseMoverAndFaker
1912+{
1913+ MirDragAndDropV1 const* dnd = nullptr;
1914+
1915+ void SetUp() override
1916+ {
1917+ initial_display_layout({screen_geometry});
1918+ mir_test_framework::ConnectedClientWithAWindow::SetUp();
1919+ dnd = mir_drag_and_drop_v1(connection);
1920+ mir_window_set_event_handler(window, &window_event_handler, this);
1921+ if (dnd) dnd->set_start_drag_and_drop_callback(window, &window_dnd_start_handler, this);
1922+
1923+ create_target_window();
1924+
1925+ paint_window(window);
1926+
1927+ center_mouse();
1928+ }
1929+
1930+ void TearDown() override
1931+ {
1932+ reset_window_event_handler(target_window);
1933+ reset_window_event_handler(window);
1934+ mir_window_release_sync(target_window);
1935+ mir_connection_release(another_connection);
1936+ mir_test_framework::ConnectedClientWithAWindow::TearDown();
1937+ }
1938+
1939+ auto user_initiates_drag() -> Cookie;
1940+ auto client_requests_drag(Cookie const& cookie) -> Blob;
1941+ auto handle_from_mouse_move() -> Blob;
1942+ auto handle_from_mouse_leave() -> Blob;
1943+ auto handle_from_mouse_enter() -> Blob;
1944+ auto handle_from_mouse_release() -> Blob;
1945+ auto count_of_handles_when_moving_mouse() -> int;
1946+
1947+private:
1948+ void center_mouse();
1949+ void paint_window(MirWindow* w);
1950+ void set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler);
1951+ void set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler);
1952+ void reset_window_event_handler(MirWindow* window);
1953+
1954+ void create_target_window()
1955+ {
1956+ another_connection = mir_connect_sync(new_connection().c_str(), "another_connection");
1957+ auto const spec = mir_create_normal_window_spec(
1958+ connection, screen_geometry.size.width.as_int(), screen_geometry.size.height.as_int());
1959+ mir_window_spec_set_pixel_format(spec, mir_pixel_format_abgr_8888);
1960+ mir_window_spec_set_name(spec, "target_window");
1961+ mir_window_spec_set_buffer_usage(spec, mir_buffer_usage_hardware);
1962+ mir_window_spec_set_event_handler(spec, &window_event_handler, this);
1963+
1964+ target_window = mir_create_window_sync(spec);
1965+ mir_window_spec_release(spec);
1966+
1967+ paint_window(target_window);
1968+ }
1969+
1970+ void invoke_window_event_handler(MirWindow* window, MirEvent const* event)
1971+ {
1972+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1973+ if (window == this->window) window_event_handler_(event);
1974+ if (window == target_window) target_window_event_handler_(event);
1975+ }
1976+
1977+ void invoke_window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event)
1978+ {
1979+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1980+ if (window == this->window) window_dnd_start_(event);
1981+ }
1982+
1983+ std::mutex window_event_handler_mutex;
1984+ std::function<void(MirDragAndDropEvent const* event)> window_dnd_start_ = [](MirDragAndDropEvent const*) {};
1985+ std::function<void(MirEvent const* event)> window_event_handler_ = [](MirEvent const*) {};
1986+ std::function<void(MirEvent const* event)> target_window_event_handler_ = [](MirEvent const*) {};
1987+
1988+ static void window_event_handler(MirWindow* window, MirEvent const* event, void* context);
1989+ static void window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context);
1990+
1991+ MirConnection* another_connection{nullptr};
1992+ MirWindow* target_window{nullptr};
1993+};
1994+
1995+void DragAndDrop::set_window_event_handler(MirWindow* window, std::function<void(MirEvent const* event)> const& handler)
1996+{
1997+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
1998+ if (window == this->window) window_event_handler_ = handler;
1999+ if (window == target_window) target_window_event_handler_ = handler;
2000+}
2001+
2002+void DragAndDrop::set_window_dnd_start_handler(MirWindow* window, std::function<void(MirDragAndDropEvent const*)> const& handler)
2003+{
2004+ std::lock_guard<decltype(window_event_handler_mutex)> lock{window_event_handler_mutex};
2005+ if (window == this->window) window_dnd_start_ = handler;
2006+}
2007+
2008+
2009+void DragAndDrop::reset_window_event_handler(MirWindow* window)
2010+{
2011+ if (window == this->window) window_event_handler_ = [](MirEvent const*) {};
2012+ if (window == target_window) target_window_event_handler_ = [](MirEvent const*) {};
2013+}
2014+
2015+void DragAndDrop::paint_window(MirWindow* w)
2016+{
2017+ Signal have_focus;
2018+
2019+ set_window_event_handler(w, [&](MirEvent const* event)
2020+ {
2021+ if (mir_event_get_type(event) != mir_event_type_window)
2022+ return;
2023+
2024+ auto const window_event = mir_event_get_window_event(event);
2025+ if (mir_window_event_get_attribute(window_event) != mir_window_attrib_focus)
2026+ return;
2027+
2028+ if (mir_window_event_get_attribute_value(window_event))
2029+ have_focus.raise();
2030+ });
2031+
2032+ mir_buffer_stream_swap_buffers_sync(mir_window_get_buffer_stream(w));
2033+
2034+ EXPECT_THAT(have_focus.wait_for(receive_event_timeout), Eq(true));
2035+
2036+ reset_window_event_handler(w);
2037+}
2038+
2039+void DragAndDrop::center_mouse()
2040+{
2041+ Signal have_mouseover;
2042+
2043+ set_window_event_handler(window, [&](MirEvent const* event)
2044+ {
2045+ if (mir_event_get_type(event) != mir_event_type_input)
2046+ return;
2047+
2048+ auto const input_event = mir_event_get_input_event(event);
2049+
2050+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2051+ return;
2052+
2053+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2054+
2055+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
2056+ return;
2057+
2058+ have_mouseover.raise();
2059+ });
2060+
2061+ move_mouse(0.5 * as_displacement(screen_geometry.size));
2062+
2063+// We miss the "mouseover" occasionally (with valgrind and heavy stress about 1/20).
2064+// But it isn't essential for the test and we've probably waited long enough
2065+// for the mouse-down needed by the test to reach the window.
2066+// EXPECT_THAT(have_mouseover.wait_for(receive_event_timeout), Eq(true));
2067+ have_mouseover.wait_for(receive_event_timeout);
2068+
2069+ reset_window_event_handler(window);
2070+}
2071+
2072+void DragAndDrop::window_event_handler(MirWindow* window, MirEvent const* event, void* context)
2073+{
2074+ static_cast<DragAndDrop*>(context)->invoke_window_event_handler(window, event);
2075+}
2076+
2077+void DragAndDrop::window_dnd_start_handler(MirWindow* window, MirDragAndDropEvent const* event, void* context)
2078+{
2079+ static_cast<DragAndDrop*>(context)->invoke_window_dnd_start_handler(window, event);
2080+}
2081+
2082+
2083+auto DragAndDrop::user_initiates_drag() -> Cookie
2084+{
2085+ Cookie cookie;
2086+ Signal have_cookie;
2087+
2088+ set_window_event_handler(window, [&](MirEvent const* event)
2089+ {
2090+ if (mir_event_get_type(event) != mir_event_type_input)
2091+ return;
2092+
2093+ auto const input_event = mir_event_get_input_event(event);
2094+
2095+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2096+ return;
2097+
2098+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2099+
2100+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_down)
2101+ return;
2102+
2103+ cookie = Cookie{mir_input_event_get_cookie(input_event)};
2104+ have_cookie.raise();
2105+ });
2106+
2107+ start_dragging_mouse();
2108+
2109+ EXPECT_THAT(have_cookie.wait_for(receive_event_timeout), Eq(true));
2110+
2111+ reset_window_event_handler(window);
2112+ return cookie;
2113+}
2114+
2115+auto DragAndDrop::client_requests_drag(Cookie const& cookie) -> Blob
2116+{
2117+ Blob blob;
2118+ Signal initiated;
2119+
2120+ set_window_dnd_start_handler(window, [&](MirDragAndDropEvent const* event)
2121+ {
2122+ if (dnd)
2123+ blob.reset(dnd->start_drag_and_drop(event));
2124+
2125+ if (blob)
2126+ initiated.raise();
2127+ });
2128+
2129+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
2130+
2131+ if (dnd)
2132+ dnd->request_drag_and_drop(window, cookie);
2133+
2134+ EXPECT_TRUE(initiated.wait_for(receive_event_timeout));
2135+
2136+ reset_window_event_handler(window);
2137+ return blob;
2138+}
2139+
2140+auto DragAndDrop::handle_from_mouse_move() -> Blob
2141+{
2142+ Blob blob;
2143+ Signal have_blob;
2144+
2145+ set_window_event_handler(window, [&](MirEvent const* event)
2146+ {
2147+ if (mir_event_get_type(event) != mir_event_type_input)
2148+ return;
2149+
2150+ auto const input_event = mir_event_get_input_event(event);
2151+
2152+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2153+ return;
2154+
2155+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2156+
2157+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
2158+
2159+ if (dnd)
2160+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
2161+
2162+ if (blob)
2163+ have_blob.raise();
2164+ });
2165+
2166+ move_mouse({1,1});
2167+
2168+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
2169+
2170+ reset_window_event_handler(window);
2171+ return blob;
2172+}
2173+
2174+auto DragAndDrop::handle_from_mouse_leave() -> Blob
2175+{
2176+ Blob blob;
2177+ Signal have_blob;
2178+
2179+ set_window_event_handler(window, [&](MirEvent const* event)
2180+ {
2181+ if (mir_event_get_type(event) != mir_event_type_input)
2182+ return;
2183+
2184+ auto const input_event = mir_event_get_input_event(event);
2185+
2186+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2187+ return;
2188+
2189+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2190+
2191+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_leave)
2192+ return;
2193+
2194+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
2195+
2196+ if (dnd)
2197+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
2198+
2199+ if (blob)
2200+ have_blob.raise();
2201+ });
2202+
2203+ move_mouse({1,1});
2204+ move_mouse(0.5 * as_displacement(surface_size));
2205+
2206+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
2207+
2208+ reset_window_event_handler(window);
2209+ return blob;
2210+}
2211+
2212+auto DragAndDrop::handle_from_mouse_enter() -> Blob
2213+{
2214+ Blob blob;
2215+ Signal have_blob;
2216+
2217+ set_window_event_handler(target_window, [&](MirEvent const* event)
2218+ {
2219+ if (mir_event_get_type(event) != mir_event_type_input)
2220+ return;
2221+
2222+ auto const input_event = mir_event_get_input_event(event);
2223+
2224+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2225+ return;
2226+
2227+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2228+
2229+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_enter)
2230+ return;
2231+
2232+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
2233+
2234+ if (dnd)
2235+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
2236+
2237+ if (blob)
2238+ have_blob.raise();
2239+ });
2240+
2241+ move_mouse({1,1});
2242+ move_mouse(0.5 * as_displacement(surface_size));
2243+
2244+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
2245+
2246+ reset_window_event_handler(target_window);
2247+ return blob;
2248+}
2249+
2250+auto DragAndDrop::handle_from_mouse_release() -> Blob
2251+{
2252+ Blob blob;
2253+ Signal have_blob;
2254+
2255+ set_window_event_handler(target_window, [&](MirEvent const* event)
2256+ {
2257+ if (mir_event_get_type(event) != mir_event_type_input)
2258+ return;
2259+
2260+ auto const input_event = mir_event_get_input_event(event);
2261+
2262+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2263+ return;
2264+
2265+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2266+
2267+ if (mir_pointer_event_action(pointer_event) != mir_pointer_action_button_up)
2268+ return;
2269+
2270+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
2271+
2272+ if (dnd)
2273+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
2274+
2275+ if (blob)
2276+ have_blob.raise();
2277+ });
2278+
2279+ move_mouse({1,1});
2280+ move_mouse(0.5 * as_displacement(surface_size));
2281+ release_mouse();
2282+
2283+ EXPECT_TRUE(have_blob.wait_for(receive_event_timeout));
2284+
2285+ reset_window_event_handler(target_window);
2286+ return blob;
2287+}
2288+
2289+auto DragAndDrop::count_of_handles_when_moving_mouse() -> int
2290+{
2291+ Signal have_3_events;
2292+ std::atomic<int> events{0};
2293+ std::atomic<int> handles{0};
2294+
2295+ auto counter = [&](MirEvent const* event)
2296+ {
2297+ if (mir_event_get_type(event) != mir_event_type_input)
2298+ return;
2299+
2300+ auto const input_event = mir_event_get_input_event(event);
2301+
2302+ if (mir_input_event_get_type(input_event) != mir_input_event_type_pointer)
2303+ return;
2304+
2305+ auto const pointer_event = mir_input_event_get_pointer_event(input_event);
2306+
2307+ EXPECT_THAT(dnd, Ne(nullptr)) << "No Drag and Drop extension";
2308+
2309+ Blob blob;
2310+ if (dnd)
2311+ blob.reset(dnd->pointer_drag_and_drop(pointer_event));
2312+
2313+ if (blob)
2314+ handles.fetch_add(1);
2315+
2316+ if (events.fetch_add(1) == 2)
2317+ have_3_events.raise();
2318+ };
2319+
2320+ set_window_event_handler(window, counter);
2321+ set_window_event_handler(target_window, counter);
2322+
2323+ start_dragging_mouse();
2324+ move_mouse({1,1});
2325+ release_mouse();
2326+
2327+ EXPECT_TRUE(have_3_events.wait_for(receive_event_timeout));
2328+
2329+ reset_window_event_handler(window);
2330+ reset_window_event_handler(target_window);
2331+ return handles;
2332+}
2333+
2334+MATCHER_P(BlobContentEq, p, "")
2335+{
2336+ if (!arg || !p)
2337+ return false;
2338+ if (mir_blob_size(arg) != mir_blob_size(p))
2339+ return false;
2340+ return !memcmp(mir_blob_data(arg), mir_blob_data(p), mir_blob_size(p));
2341+}
2342+}
2343+
2344+TEST_F(DragAndDrop, when_user_initiates_drag_client_receives_cookie)
2345+{
2346+ auto const cookie = user_initiates_drag();
2347+
2348+ EXPECT_THAT(cookie.get(), NotNull());
2349+}
2350+
2351+TEST_F(DragAndDrop, when_client_requests_drags_it_receives_handle)
2352+{
2353+ auto const cookie = user_initiates_drag();
2354+ ASSERT_THAT(cookie.get(), NotNull());
2355+
2356+ auto const handle = client_requests_drag(cookie);
2357+
2358+ EXPECT_THAT(handle.get(), NotNull());
2359+}
2360+
2361+TEST_F(DragAndDrop, during_drag_when_user_moves_mouse_client_receives_handle)
2362+{
2363+ auto const cookie = user_initiates_drag();
2364+ ASSERT_THAT(cookie.get(), NotNull());
2365+ auto const handle_from_request = client_requests_drag(cookie);
2366+
2367+ auto const handle = handle_from_mouse_move();
2368+
2369+ EXPECT_THAT(handle.get(), NotNull());
2370+ EXPECT_THAT(handle.get(), BlobContentEq(handle_from_request.get()));
2371+}
2372+
2373+TEST_F(DragAndDrop, when_drag_moves_from_window_leave_event_contains_handle)
2374+{
2375+ auto const cookie = user_initiates_drag();
2376+ ASSERT_THAT(cookie.get(), NotNull());
2377+ auto const handle_from_request = client_requests_drag(cookie);
2378+
2379+ auto const handle = handle_from_mouse_leave();
2380+
2381+ EXPECT_THAT(handle.get(), NotNull());
2382+ EXPECT_THAT(handle.get(), BlobContentEq(handle_from_request.get()));
2383+}
2384+
2385+TEST_F(DragAndDrop, when_drag_enters_target_window_enter_event_contains_handle)
2386+{
2387+ auto const cookie = user_initiates_drag();
2388+ ASSERT_THAT(cookie.get(), NotNull());
2389+ auto const handle_from_request = client_requests_drag(cookie);
2390+
2391+ auto const handle = handle_from_mouse_enter();
2392+
2393+ EXPECT_THAT(handle.get(), NotNull());
2394+ EXPECT_THAT(handle.get(), BlobContentEq(handle_from_request.get()));
2395+}
2396+
2397+TEST_F(DragAndDrop, when_drag_releases_target_window_release_event_contains_handle)
2398+{
2399+ auto const cookie = user_initiates_drag();
2400+ ASSERT_THAT(cookie.get(), NotNull());
2401+ auto const handle_from_request = client_requests_drag(cookie);
2402+
2403+ auto const handle = handle_from_mouse_release();
2404+
2405+ EXPECT_THAT(handle.get(), NotNull());
2406+ EXPECT_THAT(handle.get(), BlobContentEq(handle_from_request.get()));
2407+}
2408+
2409+TEST_F(DragAndDrop, after_drag_finishes_pointer_events_no_longer_contain_handle)
2410+{
2411+ auto const cookie = user_initiates_drag();
2412+ ASSERT_THAT(cookie.get(), NotNull());
2413+ client_requests_drag(cookie);
2414+ handle_from_mouse_release();
2415+
2416+ server.the_shell()->clear_drag_and_drop_handle();
2417+
2418+ EXPECT_THAT(count_of_handles_when_moving_mouse(), Eq(0));
2419+}
2420
2421=== modified file 'tests/acceptance-tests/test_client_cursor_api.cpp'
2422--- tests/acceptance-tests/test_client_cursor_api.cpp 2017-02-28 08:53:57 +0000
2423+++ tests/acceptance-tests/test_client_cursor_api.cpp 2017-03-17 15:49:43 +0000
2424@@ -82,6 +82,7 @@
2425 MOCK_METHOD0(cursor_image_removed, void());
2426 MOCK_METHOD1(placed_relative, void(geom::Rectangle const& placement));
2427 MOCK_METHOD1(input_consumed, void(MirEvent const*));
2428+ MOCK_METHOD1(start_drag_and_drop, void(std::vector<uint8_t> const& handle));
2429 };
2430
2431
2432
2433=== modified file 'tests/include/mir/test/doubles/mock_input_targeter.h'
2434--- tests/include/mir/test/doubles/mock_input_targeter.h 2015-06-18 02:46:16 +0000
2435+++ tests/include/mir/test/doubles/mock_input_targeter.h 2017-03-17 15:49:43 +0000
2436@@ -35,6 +35,9 @@
2437 virtual ~MockInputTargeter() noexcept(true) {}
2438 MOCK_METHOD1(set_focus, void(std::shared_ptr<input::Surface> const&));
2439 MOCK_METHOD0(clear_focus, void());
2440+
2441+ void set_drag_and_drop_handle(std::vector<uint8_t> const&) override {}
2442+ void clear_drag_and_drop_handle() override {}
2443 };
2444
2445 }
2446
2447=== modified file 'tests/include/mir/test/doubles/mock_shell.h'
2448--- tests/include/mir/test/doubles/mock_shell.h 2017-01-18 02:29:37 +0000
2449+++ tests/include/mir/test/doubles/mock_shell.h 2017-03-17 15:49:43 +0000
2450@@ -72,6 +72,9 @@
2451
2452 MOCK_METHOD3(raise_surface, void(std::shared_ptr<frontend::Session> const& session,
2453 frontend::SurfaceId surface_id, uint64_t timestamp));
2454+
2455+ MOCK_METHOD3(request_drag_and_drop, void(std::shared_ptr<frontend::Session> const& session,
2456+ frontend::SurfaceId surface_id, uint64_t timestamp));
2457 };
2458
2459 }
2460
2461=== modified file 'tests/include/mir/test/doubles/stub_display_server.h'
2462--- tests/include/mir/test/doubles/stub_display_server.h 2017-02-15 07:38:33 +0000
2463+++ tests/include/mir/test/doubles/stub_display_server.h 2017-03-17 15:49:43 +0000
2464@@ -158,6 +158,10 @@
2465 mir::protobuf::RaiseRequest const* /*request*/,
2466 mir::protobuf::Void* /*response*/,
2467 google::protobuf::Closure* /*done*/) {}
2468+ void request_drag_and_drop(
2469+ mir::protobuf::RequestAuthority const* /*request*/,
2470+ mir::protobuf::Void* /*response*/,
2471+ google::protobuf::Closure* /*done*/) {}
2472 void apply_input_configuration(
2473 mir::protobuf::InputConfigurationRequest const* /*request*/,
2474 mir::protobuf::Void* /*response*/,
2475
2476=== modified file 'tests/include/mir/test/doubles/stub_input_targeter.h'
2477--- tests/include/mir/test/doubles/stub_input_targeter.h 2015-06-18 02:46:16 +0000
2478+++ tests/include/mir/test/doubles/stub_input_targeter.h 2017-03-17 15:49:43 +0000
2479@@ -36,6 +36,9 @@
2480 void clear_focus()
2481 {
2482 }
2483+
2484+ void set_drag_and_drop_handle(std::vector<uint8_t> const&) override {}
2485+ void clear_drag_and_drop_handle() override {}
2486 };
2487
2488 }
2489
2490=== modified file 'tests/include/mir/test/doubles/stub_scene_surface.h'
2491--- tests/include/mir/test/doubles/stub_scene_surface.h 2017-02-15 14:45:41 +0000
2492+++ tests/include/mir/test/doubles/stub_scene_surface.h 2017-03-17 15:49:43 +0000
2493@@ -94,6 +94,7 @@
2494 void set_confine_pointer_state(MirPointerConfinementState /*state*/) override {}
2495 MirPointerConfinementState confine_pointer_state() const override { return {}; }
2496 void placed_relative(geometry::Rectangle const& /*placement*/) override {}
2497+ void start_drag_and_drop(std::vector<uint8_t> const& /*handle*/) override {}
2498 };
2499
2500 }
2501
2502=== modified file 'tests/mir_test_framework/observant_shell.cpp'
2503--- tests/mir_test_framework/observant_shell.cpp 2017-01-20 00:01:50 +0000
2504+++ tests/mir_test_framework/observant_shell.cpp 2017-03-17 15:49:43 +0000
2505@@ -155,7 +155,26 @@
2506 void mtf::ObservantShell::raise_surface(
2507 std::shared_ptr<msc::Session> const& session,
2508 std::shared_ptr<msc::Surface> const& window,
2509+ uint64_t timestamp)
2510+{
2511+ return wrapped->raise_surface(session, window, timestamp);
2512+}
2513+
2514+void mtf::ObservantShell::request_drag_and_drop(
2515+ std::shared_ptr<msc::Session> const& session,
2516+ std::shared_ptr<msc::Surface> const& window,
2517 uint64_t timestamp)
2518 {
2519- return wrapped->raise_surface(session, window, timestamp);
2520-}
2521+ return wrapped->request_drag_and_drop(session, window, timestamp);
2522+}
2523+
2524+void mtf::ObservantShell::set_drag_and_drop_handle(std::vector<uint8_t> const& handle)
2525+{
2526+ wrapped->set_drag_and_drop_handle(handle);
2527+}
2528+
2529+void mtf::ObservantShell::clear_drag_and_drop_handle()
2530+{
2531+ wrapped->clear_drag_and_drop_handle();
2532+}
2533+
2534
2535=== modified file 'tests/mir_test_framework/stub_surface.cpp'
2536--- tests/mir_test_framework/stub_surface.cpp 2017-02-15 14:45:41 +0000
2537+++ tests/mir_test_framework/stub_surface.cpp 2017-03-17 15:49:43 +0000
2538@@ -198,6 +198,10 @@
2539 {
2540 }
2541
2542+void mtd::StubSurface::start_drag_and_drop(std::vector<uint8_t> const& /*handle*/)
2543+{
2544+}
2545+
2546 namespace
2547 {
2548 // Ensure we don't accidentally have an abstract class

Subscribers

People subscribed via source and target branches