Mir

Merge lp:~brandontschaefer/mir/pointer-confinement into lp:mir

Proposed by Brandon Schaefer
Status: Merged
Approved by: Cemil Azizoglu
Approved revision: no longer in the source branch.
Merged at revision: 3560
Proposed branch: lp:~brandontschaefer/mir/pointer-confinement
Merge into: lp:mir
Diff against target: 1159 lines (+429/-17)
36 files modified
examples/server_example_canonical_window_manager.cpp (+5/-0)
include/server/mir/scene/surface.h (+3/-0)
include/server/mir/scene/surface_creation_parameters.h (+1/-0)
include/server/mir/shell/abstract_shell.h (+7/-1)
include/test/mir/test/doubles/stub_surface.h (+2/-0)
src/client/mir_connection.cpp (+1/-0)
src/include/server/mir/input/seat.h (+3/-0)
src/include/server/mir/shell/window_management_info.h (+1/-1)
src/protobuf/mir_protobuf.proto (+1/-0)
src/server/frontend/session_mediator.cpp (+1/-0)
src/server/input/basic_seat.cpp (+10/-0)
src/server/input/basic_seat.h (+2/-0)
src/server/input/seat_input_device_tracker.cpp (+29/-7)
src/server/input/seat_input_device_tracker.h (+6/-0)
src/server/scene/basic_surface.cpp (+14/-1)
src/server/scene/basic_surface.h (+6/-0)
src/server/scene/surface_allocator.cpp (+2/-0)
src/server/shell/abstract_shell.cpp (+23/-1)
src/server/shell/canonical_window_manager.cpp (+5/-0)
src/server/shell/default_configuration.cpp (+2/-1)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_confined_pointer.cpp (+214/-0)
tests/acceptance-tests/test_surface_placement.cpp (+1/-0)
tests/include/mir/test/doubles/mock_input_seat.h (+3/-0)
tests/include/mir/test/doubles/mock_surface.h (+1/-0)
tests/include/mir/test/doubles/stub_scene_surface.h (+3/-0)
tests/integration-tests/surface_composition.cpp (+1/-0)
tests/integration-tests/test_surface_stack_with_compositor.cpp (+1/-0)
tests/mir_test_framework/stub_surface.cpp (+10/-0)
tests/unit-tests/input/test_seat_input_device_tracker.cpp (+36/-0)
tests/unit-tests/scene/test_abstract_shell.cpp (+4/-1)
tests/unit-tests/scene/test_basic_surface.cpp (+8/-2)
tests/unit-tests/scene/test_session_manager.cpp (+1/-0)
tests/unit-tests/scene/test_surface.cpp (+4/-1)
tests/unit-tests/scene/test_surface_impl.cpp (+3/-1)
tests/unit-tests/scene/test_surface_stack.cpp (+14/-0)
To merge this branch: bzr merge lp:~brandontschaefer/mir/pointer-confinement
Reviewer Review Type Date Requested Status
Mir CI Bot continuous-integration Approve
Cemil Azizoglu (community) Approve
Kevin DuBois (community) Approve
Chris Halse Rogers Approve
Andreas Pokorny (community) Approve
Review via email: mp+297941@code.launchpad.net

Commit message

Implements pointer confinement for a surface as a spec.

Only works for a focus windows. The window manager is in charge of actually setting the surface to have pointer confinement if the spec is updated. Right now we need to depend on setting the seat in the abstract shell since its where we need to update the mouse events.

Description of the change

Implements pointer confinement for a surface as a spec.

Only works for a focus windows. The window manager is in charge of actually setting the surface to have pointer confinement if the spec is updated. Right now we need to depend on setting the seat in the abstract shell since its where we need to update the mouse events.

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

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

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

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

Need to implement this for SurfaceSpecParams (ie. the creation parms which are different then the normal SurfaceSpec struct!)

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

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

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

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

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

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

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

Not blocking: A piece is missing here: the handling of surface movements... I.e. imagine a window manager short cut that changes the window position on screen i.e. toggles form full screen to windows or back - without loosing focus.

I am not sure who should be in charge of that. In the past in mirserver we would use a surface change observer. With AbstractShell we might want to have a move_resize_surface_to(...) that handles that and more things...

Codewise a suggestion:

void mi::SeatInputDeviceTracker::set_confinement_regions(geometry::Rectangles const& regions)
+{
+ confine_function = [regions,this](mir::geometry::Point& pos)
+ {
+ regions.confine(pos);
+ input_region->confine(pos);
+ };
+}
+
+void mi::SeatInputDeviceTracker::reset_confinement_regions()
+{
+ confine_function = [this](geometry::Point& pos) {input_region->confine(pos);};
+}
+
+void mi::SeatInputDeviceTracker::confine_pointer()
+{
     mir::geometry::Point const old{cursor_x, cursor_y};
     auto confined = old;
- input_region->confine(confined);
+ confine_function(confined);
     if (confined.x != old.x) cursor_x = confined.x.as_int();
     if (confined.y != old.y) cursor_y = confined.y.as_int();
+}
+
+void mi::SeatInputDeviceTracker::update_cursor(MirPointerEvent const* event)
+{
+ cursor_x += mir_pointer_event_axis_value(event, mir_pointer_axis_relative_x);
+ cursor_y += mir_pointer_event_axis_value(event, mir_pointer_axis_relative_y);
+
+ confine_pointer();

and just let confine_function be a std::function<void(geometry::Point&)> confine_function initialized like reset_confinement_regions.

hmm is that a needs fixing..

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

O i like that!

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

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

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

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

lgtm

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

747 -#include <gmock/gmock.h>
747 +#include "mir/input/device.h"
748 +#include "mir/test/gmock_fixes.h"

You've accidentally removed <gmock/gmock.h> and replaced it with (the unnecessary, I think?) gmock_fixes.h.

Not blocking, but if you need to update it for any other reason, please fix.

Other than that, looks fine.

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

lgtm too

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

25 + virtual void set_confine_pointer(MirPointerConfinementState state) = 0;

set_confine_pointer_state would be more consistent, IMO.
-----------------------------------------------------

285 + geometry::Rectangles pointer_confinment_regions;

s/pointer_confinment_regions/pointer_confinement_regions

review: Needs Fixing
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

Looks good

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

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

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

review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/server_example_canonical_window_manager.cpp'
2--- examples/server_example_canonical_window_manager.cpp 2016-05-04 01:48:16 +0000
3+++ examples/server_example_canonical_window_manager.cpp 2016-06-27 20:45:54 +0000
4@@ -416,6 +416,11 @@
5 auto const state = handle_set_state(surface, modifications.state.value());
6 surface->configure(mir_surface_attrib_state, state);
7 }
8+
9+ if (modifications.confine_pointer.is_set())
10+ {
11+ surface->set_confine_pointer_state(modifications.confine_pointer.value());
12+ }
13 }
14
15 void me::CanonicalWindowManagerPolicyCopy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
16
17=== modified file 'include/server/mir/scene/surface.h'
18--- include/server/mir/scene/surface.h 2016-05-03 06:55:25 +0000
19+++ include/server/mir/scene/surface.h 2016-06-27 20:45:54 +0000
20@@ -115,6 +115,9 @@
21 std::string const& variant, std::string const& options) = 0;
22 virtual void rename(std::string const& title) = 0;
23 virtual void set_streams(std::list<StreamInfo> const& streams) = 0;
24+
25+ virtual void set_confine_pointer_state(MirPointerConfinementState state) = 0;
26+ virtual MirPointerConfinementState confine_pointer_state() const = 0;
27 };
28 }
29 }
30
31=== modified file 'include/server/mir/scene/surface_creation_parameters.h'
32--- include/server/mir/scene/surface_creation_parameters.h 2016-04-13 01:27:36 +0000
33+++ include/server/mir/scene/surface_creation_parameters.h 2016-06-27 20:45:54 +0000
34@@ -102,6 +102,7 @@
35 mir::optional_value<std::vector<geometry::Rectangle>> input_shape;
36 mir::optional_value<MirShellChrome> shell_chrome;
37 mir::optional_value<std::vector<shell::StreamSpecification>> streams;
38+ mir::optional_value<MirPointerConfinementState> confine_pointer;
39 };
40
41 bool operator==(const SurfaceCreationParameters& lhs, const SurfaceCreationParameters& rhs);
42
43=== modified file 'include/server/mir/shell/abstract_shell.h'
44--- include/server/mir/shell/abstract_shell.h 2016-01-29 08:18:22 +0000
45+++ include/server/mir/shell/abstract_shell.h 2016-06-27 20:45:54 +0000
46@@ -26,6 +26,10 @@
47
48 namespace mir
49 {
50+namespace input
51+{
52+class Seat;
53+}
54 namespace shell
55 {
56 class ShellReport;
57@@ -41,7 +45,8 @@
58 std::shared_ptr<scene::SessionCoordinator> const& session_coordinator,
59 std::shared_ptr<scene::PromptSessionManager> const& prompt_session_manager,
60 std::shared_ptr<ShellReport> const& report,
61- WindowManagerBuilder const& wm_builder);
62+ WindowManagerBuilder const& wm_builder,
63+ std::shared_ptr<input::Seat> const& seat);
64
65 ~AbstractShell() noexcept;
66
67@@ -122,6 +127,7 @@
68 std::shared_ptr<scene::SessionCoordinator> const session_coordinator;
69 std::shared_ptr<scene::PromptSessionManager> const prompt_session_manager;
70 std::shared_ptr<WindowManager> const window_manager;
71+ std::shared_ptr<input::Seat> const seat;
72
73 private:
74 std::shared_ptr<ShellReport> const report;
75
76=== modified file 'include/test/mir/test/doubles/stub_surface.h'
77--- include/test/mir/test/doubles/stub_surface.h 2016-01-29 08:18:22 +0000
78+++ include/test/mir/test/doubles/stub_surface.h 2016-06-27 20:45:54 +0000
79@@ -70,6 +70,8 @@
80 void set_keymap(MirInputDeviceId id, std::string const& model, std::string const& layout,
81 std::string const& variant, std::string const& options) override;
82 void rename(std::string const& title) override;
83+ void set_confine_pointer_state(MirPointerConfinementState state) override;
84+ MirPointerConfinementState confine_pointer_state() const override;
85 };
86 }
87 }
88
89=== modified file 'src/client/mir_connection.cpp'
90--- src/client/mir_connection.cpp 2016-06-24 01:19:33 +0000
91+++ src/client/mir_connection.cpp 2016-06-27 20:45:54 +0000
92@@ -129,6 +129,7 @@
93 SERIALIZE_OPTION_IF_SET(width_inc);
94 SERIALIZE_OPTION_IF_SET(height_inc);
95 SERIALIZE_OPTION_IF_SET(shell_chrome);
96+ SERIALIZE_OPTION_IF_SET(confine_pointer);
97 // min_aspect is a special case (below)
98 // max_aspect is a special case (below)
99
100
101=== modified file 'src/include/server/mir/input/seat.h'
102--- src/include/server/mir/input/seat.h 2016-05-03 06:55:25 +0000
103+++ src/include/server/mir/input/seat.h 2016-06-27 20:45:54 +0000
104@@ -21,6 +21,7 @@
105 #define MIR_INPUT_SEAT_H_
106
107 #include "mir/geometry/rectangle.h"
108+#include "mir/geometry/rectangles.h"
109 #include "mir_toolkit/event.h"
110
111 #include <memory>
112@@ -39,6 +40,8 @@
113 virtual void remove_device(Device const& device) = 0;
114 virtual void dispatch_event(MirEvent& event) = 0;
115 virtual geometry::Rectangle get_rectangle_for(Device const& dev) = 0;
116+ virtual void set_confinement_regions(geometry::Rectangles const& regions) = 0;
117+ virtual void reset_confinement_regions() = 0;
118 private:
119 Seat(Seat const&) = delete;
120 Seat& operator=(Seat const&) = delete;
121
122=== modified file 'src/include/server/mir/shell/window_management_info.h'
123--- src/include/server/mir/shell/window_management_info.h 2016-06-07 19:23:52 +0000
124+++ src/include/server/mir/shell/window_management_info.h 2016-06-27 20:45:54 +0000
125@@ -75,7 +75,7 @@
126 mir::optional_value<shell::SurfaceAspectRatio> min_aspect;
127 mir::optional_value<shell::SurfaceAspectRatio> max_aspect;
128 mir::optional_value<graphics::DisplayConfigurationOutputId> output_id;
129- mir::optional_value<bool> confine_pointer;
130+ mir::optional_value<MirPointerConfinementState> confine_pointer;
131
132 void init_titlebar(std::shared_ptr <scene::Surface> const& surface);
133
134
135=== modified file 'src/protobuf/mir_protobuf.proto'
136--- src/protobuf/mir_protobuf.proto 2016-06-08 13:49:11 +0000
137+++ src/protobuf/mir_protobuf.proto 2016-06-27 20:45:54 +0000
138@@ -45,6 +45,7 @@
139 repeated Rectangle input_shape = 22;
140 optional int32 shell_chrome = 24;
141 repeated StreamConfiguration stream = 25;
142+ optional int32 confine_pointer = 26;
143 }
144
145 message SurfaceAspectRatio
146
147=== modified file 'src/server/frontend/session_mediator.cpp'
148--- src/server/frontend/session_mediator.cpp 2016-06-07 19:23:52 +0000
149+++ src/server/frontend/session_mediator.cpp 2016-06-27 20:45:54 +0000
150@@ -284,6 +284,7 @@
151 COPY_IF_SET(width_inc);
152 COPY_IF_SET(height_inc);
153 COPY_IF_SET(shell_chrome);
154+ COPY_IF_SET(confine_pointer);
155
156 #undef COPY_IF_SET
157
158
159=== modified file 'src/server/input/basic_seat.cpp'
160--- src/server/input/basic_seat.cpp 2016-05-03 06:55:25 +0000
161+++ src/server/input/basic_seat.cpp 2016-06-27 20:45:54 +0000
162@@ -57,3 +57,13 @@
163 // assumes that only the first output may have a touch screen associated to it.
164 return input_region->bounding_rectangle();
165 }
166+
167+void mi::BasicSeat::set_confinement_regions(geometry::Rectangles const& regions)
168+{
169+ input_state_tracker.set_confinement_regions(regions);
170+}
171+
172+void mi::BasicSeat::reset_confinement_regions()
173+{
174+ input_state_tracker.reset_confinement_regions();
175+}
176
177=== modified file 'src/server/input/basic_seat.h'
178--- src/server/input/basic_seat.h 2016-05-03 06:55:25 +0000
179+++ src/server/input/basic_seat.h 2016-06-27 20:45:54 +0000
180@@ -47,6 +47,8 @@
181 void remove_device(Device const& device) override;
182 void dispatch_event(MirEvent& event) override;
183 geometry::Rectangle get_rectangle_for(Device const& dev) override;
184+ virtual void set_confinement_regions(geometry::Rectangles const& regions) override;
185+ virtual void reset_confinement_regions() override;
186
187 private:
188 SeatInputDeviceTracker input_state_tracker;
189
190=== modified file 'src/server/input/seat_input_device_tracker.cpp'
191--- src/server/input/seat_input_device_tracker.cpp 2016-06-02 08:19:38 +0000
192+++ src/server/input/seat_input_device_tracker.cpp 2016-06-27 20:45:54 +0000
193@@ -37,13 +37,16 @@
194
195 namespace mi = mir::input;
196 namespace mev = mir::events;
197+namespace geom = mir::geometry;
198
199 mi::SeatInputDeviceTracker::SeatInputDeviceTracker(std::shared_ptr<InputDispatcher> const& dispatcher,
200 std::shared_ptr<TouchVisualizer> const& touch_visualizer,
201 std::shared_ptr<CursorListener> const& cursor_listener,
202 std::shared_ptr<InputRegion> const& input_region)
203 : dispatcher{dispatcher}, touch_visualizer{touch_visualizer}, cursor_listener{cursor_listener},
204- input_region{input_region}, modifier{0}, buttons{0}
205+ input_region{input_region}, modifier{0}, buttons{0},
206+ confine_function{[input_region](mir::geometry::Point& pos) { input_region->confine(pos); }}
207+
208 {
209 }
210
211@@ -210,16 +213,35 @@
212 return buttons;
213 }
214
215-void mi::SeatInputDeviceTracker::update_cursor(MirPointerEvent const* event)
216-{
217- cursor_x += mir_pointer_event_axis_value(event, mir_pointer_axis_relative_x);
218- cursor_y += mir_pointer_event_axis_value(event, mir_pointer_axis_relative_y);
219-
220+void mi::SeatInputDeviceTracker::set_confinement_regions(geometry::Rectangles const& regions)
221+{
222+ confine_function = [regions, this](mir::geometry::Point& pos)
223+ {
224+ input_region->confine(pos);
225+ regions.confine(pos);
226+ };
227+}
228+
229+void mi::SeatInputDeviceTracker::reset_confinement_regions()
230+{
231+ confine_function = [this](mir::geometry::Point& pos) { input_region->confine(pos); };
232+}
233+
234+void mi::SeatInputDeviceTracker::confine_pointer()
235+{
236 mir::geometry::Point const old{cursor_x, cursor_y};
237 auto confined = old;
238- input_region->confine(confined);
239+ confine_function(confined);
240 if (confined.x != old.x) cursor_x = confined.x.as_int();
241 if (confined.y != old.y) cursor_y = confined.y.as_int();
242+}
243+
244+void mi::SeatInputDeviceTracker::update_cursor(MirPointerEvent const* event)
245+{
246+ cursor_x += mir_pointer_event_axis_value(event, mir_pointer_axis_relative_x);
247+ cursor_y += mir_pointer_event_axis_value(event, mir_pointer_axis_relative_y);
248+
249+ confine_pointer();
250
251 cursor_listener->cursor_moved_to(cursor_x, cursor_y);
252 }
253
254=== modified file 'src/server/input/seat_input_device_tracker.h'
255--- src/server/input/seat_input_device_tracker.h 2016-05-03 06:55:25 +0000
256+++ src/server/input/seat_input_device_tracker.h 2016-06-27 20:45:54 +0000
257@@ -22,6 +22,7 @@
258
259 #include "mir/input/touch_visualizer.h"
260 #include "mir/geometry/point.h"
261+#include "mir/geometry/rectangles.h"
262 #include "mir_toolkit/event.h"
263 #include <unordered_map>
264 #include <memory>
265@@ -57,11 +58,15 @@
266 geometry::Point cursor_position() const;
267 MirInputEventModifiers event_modifier() const;
268 MirInputEventModifiers event_modifier(MirInputDeviceId) const;
269+
270+ void set_confinement_regions(geometry::Rectangles const& region);
271+ void reset_confinement_regions();
272 private:
273 void update_seat_properties(MirInputEvent const* event);
274 void update_cursor(MirPointerEvent const* event);
275 void update_spots();
276 void update_states();
277+ void confine_pointer();
278
279 std::shared_ptr<InputDispatcher> const dispatcher;
280 std::shared_ptr<TouchVisualizer> const touch_visualizer;
281@@ -88,6 +93,7 @@
282 MirPointerButtons buttons;
283 std::unordered_map<MirInputDeviceId, DeviceData> device_data;
284 std::vector<TouchVisualizer::Spot> spots;
285+ std::function<void(mir::geometry::Point&)> confine_function;
286 };
287
288 }
289
290=== modified file 'src/server/scene/basic_surface.cpp'
291--- src/server/scene/basic_surface.cpp 2016-05-03 06:55:25 +0000
292+++ src/server/scene/basic_surface.cpp 2016-06-27 20:45:54 +0000
293@@ -217,6 +217,7 @@
294 std::string const& name,
295 geometry::Rectangle rect,
296 std::weak_ptr<Surface> const& parent,
297+ MirPointerConfinementState state,
298 bool nonrectangular,
299 std::list<StreamInfo> const& layers,
300 std::shared_ptr<mi::InputChannel> const& input_channel,
301@@ -237,6 +238,7 @@
302 report(report),
303 parent_(parent),
304 layers(layers),
305+ confine_pointer_state_(state),
306 cursor_stream_adapter{std::make_unique<ms::CursorStreamImageAdapter>(*this)},
307 input_validator([this](MirEvent const& ev) { this->input_sender->send_event(ev, server_input_channel); })
308 {
309@@ -246,13 +248,14 @@
310 ms::BasicSurface::BasicSurface(
311 std::string const& name,
312 geometry::Rectangle rect,
313+ MirPointerConfinementState state,
314 bool nonrectangular,
315 std::list<StreamInfo> const& layers,
316 std::shared_ptr<mi::InputChannel> const& input_channel,
317 std::shared_ptr<input::InputSender> const& input_sender,
318 std::shared_ptr<mg::CursorImage> const& cursor_image,
319 std::shared_ptr<SceneReport> const& report) :
320- BasicSurface(name, rect, std::shared_ptr<Surface>{nullptr}, nonrectangular, layers,
321+ BasicSurface(name, rect, std::shared_ptr<Surface>{nullptr}, state, nonrectangular, layers,
322 input_channel, input_sender, cursor_image, report)
323 {
324 }
325@@ -916,3 +919,13 @@
326 }
327 return list;
328 }
329+
330+void ms::BasicSurface::set_confine_pointer_state(MirPointerConfinementState state)
331+{
332+ confine_pointer_state_ = state;
333+}
334+
335+MirPointerConfinementState ms::BasicSurface::confine_pointer_state() const
336+{
337+ return confine_pointer_state_;
338+}
339
340=== modified file 'src/server/scene/basic_surface.h'
341--- src/server/scene/basic_surface.h 2016-04-19 06:29:53 +0000
342+++ src/server/scene/basic_surface.h 2016-06-27 20:45:54 +0000
343@@ -64,6 +64,7 @@
344 BasicSurface(
345 std::string const& name,
346 geometry::Rectangle rect,
347+ MirPointerConfinementState state,
348 bool nonrectangular,
349 std::list<scene::StreamInfo> const& streams,
350 std::shared_ptr<input::InputChannel> const& input_channel,
351@@ -75,6 +76,7 @@
352 std::string const& name,
353 geometry::Rectangle rect,
354 std::weak_ptr<Surface> const& parent,
355+ MirPointerConfinementState state,
356 bool nonrectangular,
357 std::list<scene::StreamInfo> const& streams,
358 std::shared_ptr<input::InputChannel> const& input_channel,
359@@ -148,6 +150,9 @@
360
361 void rename(std::string const& title) override;
362
363+ void set_confine_pointer_state(MirPointerConfinementState state) override;
364+ MirPointerConfinementState confine_pointer_state() const override;
365+
366 private:
367 bool visible(std::unique_lock<std::mutex>&) const;
368 MirSurfaceType set_type(MirSurfaceType t); // Use configure() to make public changes
369@@ -184,6 +189,7 @@
370 int dpi_ = 0;
371 MirSurfaceVisibility visibility_ = mir_surface_visibility_occluded;
372 MirOrientationMode pref_orientation_mode = mir_orientation_mode_any;
373+ MirPointerConfinementState confine_pointer_state_ = mir_pointer_unconfined;
374
375 std::unique_ptr<CursorStreamImageAdapter> const cursor_stream_adapter;
376
377
378=== modified file 'src/server/scene/surface_allocator.cpp'
379--- src/server/scene/surface_allocator.cpp 2016-04-19 06:29:53 +0000
380+++ src/server/scene/surface_allocator.cpp 2016-06-27 20:45:54 +0000
381@@ -53,10 +53,12 @@
382 {
383 bool nonrectangular = has_alpha(params.pixel_format);
384 auto input_channel = input_factory->make_input_channel();
385+ auto confine = params.confine_pointer.is_set() ? params.confine_pointer.value() : mir_pointer_unconfined;
386 auto const surface = std::make_shared<BasicSurface>(
387 params.name,
388 geom::Rectangle{params.top_left, params.size},
389 params.parent,
390+ confine,
391 nonrectangular,
392 streams,
393 input_channel,
394
395=== modified file 'src/server/shell/abstract_shell.cpp'
396--- src/server/shell/abstract_shell.cpp 2016-01-29 08:18:22 +0000
397+++ src/server/shell/abstract_shell.cpp 2016-06-27 20:45:54 +0000
398@@ -27,9 +27,11 @@
399 #include "mir/scene/session_coordinator.h"
400 #include "mir/scene/session.h"
401 #include "mir/scene/surface.h"
402+#include "mir/input/seat.h"
403
404 namespace mf = mir::frontend;
405 namespace ms = mir::scene;
406+namespace mi = mir::input;
407 namespace msh = mir::shell;
408
409 msh::AbstractShell::AbstractShell(
410@@ -38,12 +40,14 @@
411 std::shared_ptr<ms::SessionCoordinator> const& session_coordinator,
412 std::shared_ptr<ms::PromptSessionManager> const& prompt_session_manager,
413 std::shared_ptr<ShellReport> const& report,
414- std::function<std::shared_ptr<shell::WindowManager>(FocusController* focus_controller)> const& wm_builder) :
415+ std::function<std::shared_ptr<shell::WindowManager>(FocusController* focus_controller)> const& wm_builder,
416+ std::shared_ptr<mi::Seat> const& seat) :
417 input_targeter(input_targeter),
418 surface_stack(surface_stack),
419 session_coordinator(session_coordinator),
420 prompt_session_manager(prompt_session_manager),
421 window_manager(wm_builder(this)),
422+ seat(seat),
423 report(report)
424 {
425 }
426@@ -113,6 +117,18 @@
427 {
428 window_manager->modify_surface(session, surface, wm_relevant_mods);
429 }
430+
431+ if (modifications.confine_pointer.is_set())
432+ {
433+ if (surface->confine_pointer_state() == mir_pointer_confined_to_surface)
434+ {
435+ seat->set_confinement_regions({surface->input_bounds()});
436+ }
437+ else
438+ {
439+ seat->reset_confinement_regions();
440+ }
441+ }
442 }
443
444 void msh::AbstractShell::destroy_surface(
445@@ -224,12 +240,18 @@
446 if (surface != current_focus)
447 {
448 focus_surface = surface;
449+ seat->reset_confinement_regions();
450
451 if (current_focus)
452 current_focus->configure(mir_surface_attrib_focus, mir_surface_unfocused);
453
454 if (surface)
455 {
456+ if (surface->confine_pointer_state() == mir_pointer_confined_to_surface)
457+ {
458+ seat->set_confinement_regions({surface->input_bounds()});
459+ }
460+
461 // Ensure the surface has really taken the focus before notifying it that it is focused
462 input_targeter->set_focus(surface);
463 surface->configure(mir_surface_attrib_focus, mir_surface_focused);
464
465=== modified file 'src/server/shell/canonical_window_manager.cpp'
466--- src/server/shell/canonical_window_manager.cpp 2016-06-22 14:52:57 +0000
467+++ src/server/shell/canonical_window_manager.cpp 2016-06-27 20:45:54 +0000
468@@ -408,6 +408,11 @@
469 auto const state = handle_set_state(surface, modifications.state.value());
470 surface->configure(mir_surface_attrib_state, state);
471 }
472+
473+ if (modifications.confine_pointer.is_set())
474+ {
475+ surface->set_confine_pointer_state(modifications.confine_pointer.value());
476+ }
477 }
478
479 void msh::CanonicalWindowManagerPolicy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
480
481=== modified file 'src/server/shell/default_configuration.cpp'
482--- src/server/shell/default_configuration.cpp 2016-06-09 19:29:52 +0000
483+++ src/server/shell/default_configuration.cpp 2016-06-27 20:45:54 +0000
484@@ -41,7 +41,8 @@
485 the_session_coordinator(),
486 the_prompt_session_manager(),
487 the_shell_report(),
488- the_window_manager_builder()));
489+ the_window_manager_builder(),
490+ the_seat()));
491
492 the_composite_event_filter()->prepend(result);
493
494
495=== modified file 'tests/acceptance-tests/CMakeLists.txt'
496--- tests/acceptance-tests/CMakeLists.txt 2016-06-09 22:54:09 +0000
497+++ tests/acceptance-tests/CMakeLists.txt 2016-06-27 20:45:54 +0000
498@@ -19,6 +19,7 @@
499 test_client_surface_events.cpp
500 test_client_surface_swap_buffers.cpp
501 test_command_line_handling.cpp
502+ test_confined_pointer.cpp
503 test_client_surfaces.cpp
504 test_client_logging.cpp
505 test_custom_window_management.cpp
506
507=== added file 'tests/acceptance-tests/test_confined_pointer.cpp'
508--- tests/acceptance-tests/test_confined_pointer.cpp 1970-01-01 00:00:00 +0000
509+++ tests/acceptance-tests/test_confined_pointer.cpp 2016-06-27 20:45:54 +0000
510@@ -0,0 +1,214 @@
511+/*
512+ * Copyright © 2016 Canonical Ltd.
513+ *
514+ * This program is free software: you can redistribute it and/or modify
515+ * it under the terms of the GNU General Public License version 3 as
516+ * published by the Free Software Foundation.
517+ *
518+ * This program is distributed in the hope that it will be useful,
519+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
520+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
521+ * GNU General Public License for more details.
522+ *
523+ * You should have received a copy of the GNU General Public License
524+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
525+ *
526+ * Authored by: Brandon Schaefer <brandon.schaefer@canonical.com>
527+ */
528+
529+#include "mir/test/event_matchers.h"
530+#include "mir/input/input_device_info.h"
531+#include "mir_test_framework/fake_input_device.h"
532+#include "mir_test_framework/stub_server_platform_factory.h"
533+
534+#include "mir/test/signal.h"
535+#include "mir_test_framework/placement_applying_shell.h"
536+#include "mir_test_framework/headless_in_process_server.h"
537+
538+#include "mir_toolkit/mir_client_library.h"
539+
540+#include <gtest/gtest.h>
541+#include <gmock/gmock.h>
542+
543+#include <boost/throw_exception.hpp>
544+
545+
546+namespace mt = mir::test;
547+namespace mi = mir::input;
548+namespace mis = mir::input::synthesis;
549+namespace mtf = mir_test_framework;
550+namespace geom = mir::geometry;
551+
552+using namespace std::chrono_literals;
553+
554+namespace
555+{
556+int const surface_width = 100;
557+int const surface_height = 100;
558+}
559+
560+void null_event_handler(MirSurface*, MirEvent const*, void*)
561+{
562+}
563+
564+struct Client
565+{
566+ MirSurface* surface{nullptr};
567+
568+ MOCK_METHOD1(handle_input, void(MirEvent const*));
569+
570+ Client(std::string const& con, std::string const& name)
571+ {
572+ connection = mir_connect_sync(con.c_str(), name.c_str());
573+
574+ if (!mir_connection_is_valid(connection))
575+ {
576+ BOOST_THROW_EXCEPTION(
577+ std::runtime_error{std::string{"Failed to connect to test server: "} +
578+ mir_connection_get_error_message(connection)});
579+ }
580+ auto spec = mir_connection_create_spec_for_normal_surface(connection, surface_width,
581+ surface_height, mir_pixel_format_abgr_8888);
582+ mir_surface_spec_set_pointer_confinement(spec, mir_pointer_confined_to_surface);
583+ mir_surface_spec_set_name(spec, name.c_str());
584+ surface = mir_surface_create_sync(spec);
585+ mir_surface_spec_release(spec);
586+ if (!mir_surface_is_valid(surface))
587+ {
588+ BOOST_THROW_EXCEPTION(std::runtime_error{std::string{"Failed creating a surface: "}+
589+ mir_surface_get_error_message(surface)});
590+ }
591+
592+ mir_surface_set_event_handler(surface, handle_event, this);
593+ mir_buffer_stream_swap_buffers_sync(
594+ mir_surface_get_buffer_stream(surface));
595+
596+ ready_to_accept_events.wait_for(4s);
597+ if (!ready_to_accept_events.raised())
598+ {
599+ BOOST_THROW_EXCEPTION(std::runtime_error("Timeout waiting for surface to become focused and exposed"));
600+ }
601+ }
602+
603+ void handle_surface_event(MirSurfaceEvent const* event)
604+ {
605+ auto const attrib = mir_surface_event_get_attribute(event);
606+ auto const value = mir_surface_event_get_attribute_value(event);
607+
608+ if (mir_surface_attrib_visibility == attrib &&
609+ mir_surface_visibility_exposed == value)
610+ exposed = true;
611+
612+ if (mir_surface_attrib_focus == attrib &&
613+ mir_surface_focused == value)
614+ focused = true;
615+
616+ if (exposed && focused)
617+ ready_to_accept_events.raise();
618+ }
619+
620+ static void handle_event(MirSurface*, MirEvent const* ev, void* context)
621+ {
622+ auto const client = static_cast<Client*>(context);
623+ auto type = mir_event_get_type(ev);
624+ if (type == mir_event_type_surface)
625+ {
626+ auto surface_event = mir_event_get_surface_event(ev);
627+ client->handle_surface_event(surface_event);
628+
629+ }
630+ if (type == mir_event_type_input)
631+ {
632+ client->handle_input(ev);
633+ }
634+ }
635+ ~Client()
636+ {
637+ // Remove the event handler to avoid handling spurious events unrelated
638+ // to the tests (e.g. pointer leave events when the surface is destroyed),
639+ // which can cause test expectations to fail.
640+ mir_surface_set_event_handler(surface, null_event_handler, nullptr);
641+ mir_surface_release_sync(surface);
642+ mir_connection_release(connection);
643+ }
644+
645+ MirConnection* connection;
646+ mir::test::Signal ready_to_accept_events;
647+ mir::test::Signal all_events_received;
648+ bool exposed = false;
649+ bool focused = false;
650+};
651+
652+struct PointerConfinement : mtf::HeadlessInProcessServer
653+{
654+ void SetUp() override
655+ {
656+ initial_display_layout({screen_geometry});
657+
658+ server.wrap_shell(
659+ [this](std::shared_ptr<mir::shell::Shell> const& wrapped)
660+ {
661+ //return wrapped;
662+ shell = std::make_shared<mtf::PlacementApplyingShell>(wrapped, input_regions, positions);
663+ return shell;
664+ });
665+
666+ HeadlessInProcessServer::SetUp();
667+
668+ positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}};
669+ }
670+
671+ std::string const mouse_name = "mouse";
672+ std::string const mouse_unique_id = "mouse-uid";
673+ std::unique_ptr<mtf::FakeInputDevice> fake_mouse{
674+ mtf::add_fake_input_device(mi::InputDeviceInfo{mouse_name, mouse_unique_id, mi::DeviceCapability::pointer})};
675+
676+ std::shared_ptr<mtf::PlacementApplyingShell> shell;
677+ geom::Rectangle screen_geometry{{0,0}, {800,600}};
678+ mtf::ClientInputRegions input_regions;
679+ std::string first{"first"};
680+ mtf::ClientPositions positions;
681+ mtf::TemporaryEnvironmentValue disable_batching{"MIR_CLIENT_INPUT_RATE", "0"};
682+};
683+
684+TEST_F(PointerConfinement, test_we_hit_pointer_confined_boundary)
685+{
686+ using namespace ::testing;
687+
688+ positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}};
689+ Client client(new_connection(), first);
690+
691+ InSequence seq;
692+ EXPECT_CALL(client, handle_input(mt::PointerEnterEvent()));
693+ EXPECT_CALL(client, handle_input(mt::PointerEventWithPosition(surface_width / 2, 0)));
694+ EXPECT_CALL(client, handle_input(AllOf(mt::PointerEventWithPosition(surface_width - 1, 0),
695+ mt::PointerEventWithDiff(surface_width * 2, 0))));
696+ EXPECT_CALL(client, handle_input(AllOf(mt::PointerEventWithPosition(surface_width - 1, 0),
697+ mt::PointerEventWithDiff(10, 0))))
698+ .WillOnce(mt::WakeUp(&client.all_events_received));
699+
700+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(surface_width / 2, 0));
701+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(surface_width * 2, 0));
702+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(10, 0));
703+
704+ client.all_events_received.wait_for(10s);
705+}
706+
707+TEST_F(PointerConfinement, test_we_generate_relative_after_boundary)
708+{
709+ using namespace ::testing;
710+
711+ positions[first] = geom::Rectangle{{0,0}, {surface_width, surface_height}};
712+ Client client(new_connection(), first);
713+
714+ InSequence seq;
715+ EXPECT_CALL(client, handle_input(mt::PointerEnterEvent()));
716+ EXPECT_CALL(client, handle_input(mt::PointerEventWithPosition(surface_width - 1, 0)));
717+ EXPECT_CALL(client, handle_input(AllOf(mt::PointerEventWithPosition(surface_width - 1, 0), mt::PointerEventWithDiff(10, 0))))
718+ .WillOnce(mt::WakeUp(&client.all_events_received));
719+
720+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(surface_width + 1, 0));
721+ fake_mouse->emit_event(mis::a_pointer_event().with_movement(10, 0));
722+
723+ client.all_events_received.wait_for(10s);
724+}
725
726=== modified file 'tests/acceptance-tests/test_surface_placement.cpp'
727--- tests/acceptance-tests/test_surface_placement.cpp 2016-04-18 09:29:26 +0000
728+++ tests/acceptance-tests/test_surface_placement.cpp 2016-06-27 20:45:54 +0000
729@@ -19,6 +19,7 @@
730 #include "mir/events/event_builders.h"
731 #include "mir/scene/surface.h"
732
733+#include "mir_test_framework/temporary_environment_value.h"
734 #include "mir/test/doubles/wrap_shell_to_track_latest_surface.h"
735 #include "mir_test_framework/connected_client_headless_server.h"
736 #include "mir/test/fake_shared.h"
737
738=== modified file 'tests/include/mir/test/doubles/mock_input_seat.h'
739--- tests/include/mir/test/doubles/mock_input_seat.h 2016-05-03 06:55:25 +0000
740+++ tests/include/mir/test/doubles/mock_input_seat.h 2016-06-27 20:45:54 +0000
741@@ -22,6 +22,7 @@
742 #include "mir/input/seat.h"
743
744 #include <gmock/gmock.h>
745+#include "mir/input/device.h"
746
747 namespace mir
748 {
749@@ -35,6 +36,8 @@
750 MOCK_METHOD1(remove_device, void(input::Device const& device));
751 MOCK_METHOD1(dispatch_event, void(MirEvent& event));
752 MOCK_METHOD1(get_rectangle_for, geometry::Rectangle(input::Device const& dev));
753+ MOCK_METHOD1(set_confinement_regions, void(geometry::Rectangles const&));
754+ MOCK_METHOD0(reset_confinement_regions, void());
755 };
756 }
757 }
758
759=== modified file 'tests/include/mir/test/doubles/mock_surface.h'
760--- tests/include/mir/test/doubles/mock_surface.h 2016-04-19 06:29:53 +0000
761+++ tests/include/mir/test/doubles/mock_surface.h 2016-06-27 20:45:54 +0000
762@@ -42,6 +42,7 @@
763 scene::BasicSurface(
764 {},
765 {{},{}},
766+ mir_pointer_unconfined,
767 true,
768 { { std::make_shared<testing::NiceMock<MockBufferStream>>(), {0, 0}, {} } },
769 std::make_shared<StubInputChannel>(),
770
771=== modified file 'tests/include/mir/test/doubles/stub_scene_surface.h'
772--- tests/include/mir/test/doubles/stub_scene_surface.h 2016-01-29 08:18:22 +0000
773+++ tests/include/mir/test/doubles/stub_scene_surface.h 2016-06-27 20:45:54 +0000
774@@ -105,6 +105,9 @@
775 void set_cursor_stream(std::shared_ptr<frontend::BufferStream> const&, geometry::Displacement const&) {}
776 void rename(std::string const&) {}
777 std::shared_ptr<frontend::BufferStream> primary_buffer_stream() const override { return nullptr; }
778+
779+ void set_confine_pointer_state(MirPointerConfinementState /*state*/) override {}
780+ MirPointerConfinementState confine_pointer_state() const override { return {}; }
781 };
782
783 }
784
785=== modified file 'tests/integration-tests/surface_composition.cpp'
786--- tests/integration-tests/surface_composition.cpp 2016-04-19 06:29:53 +0000
787+++ tests/integration-tests/surface_composition.cpp 2016-06-27 20:45:54 +0000
788@@ -48,6 +48,7 @@
789 return std::make_shared<ms::BasicSurface>(
790 std::string("SurfaceComposition"),
791 geom::Rectangle{{},{}},
792+ mir_pointer_unconfined,
793 false,
794 std::list<ms::StreamInfo> { { stream, {0,0}, {} } },
795 create_input_channel(),
796
797=== modified file 'tests/integration-tests/test_surface_stack_with_compositor.cpp'
798--- tests/integration-tests/test_surface_stack_with_compositor.cpp 2016-04-19 06:29:53 +0000
799+++ tests/integration-tests/test_surface_stack_with_compositor.cpp 2016-06-27 20:45:54 +0000
800@@ -127,6 +127,7 @@
801 stub_surface{std::make_shared<ms::BasicSurface>(
802 std::string("stub"),
803 geom::Rectangle{{0,0},{1,1}},
804+ mir_pointer_unconfined,
805 false,
806 streams,
807 std::shared_ptr<mir::input::InputChannel>(),
808
809=== modified file 'tests/mir_test_framework/stub_surface.cpp'
810--- tests/mir_test_framework/stub_surface.cpp 2016-01-29 08:18:22 +0000
811+++ tests/mir_test_framework/stub_surface.cpp 2016-06-27 20:45:54 +0000
812@@ -200,6 +200,16 @@
813 {
814 }
815
816+void mtd::StubSurface::set_confine_pointer_state(MirPointerConfinementState /*state*/)
817+{
818+}
819+
820+MirPointerConfinementState mtd::StubSurface::confine_pointer_state() const
821+{
822+ return {};
823+}
824+
825+
826 namespace
827 {
828 // Ensure we don't accidentally have an abstract class
829
830=== modified file 'tests/unit-tests/input/test_seat_input_device_tracker.cpp'
831--- tests/unit-tests/input/test_seat_input_device_tracker.cpp 2016-04-19 06:29:53 +0000
832+++ tests/unit-tests/input/test_seat_input_device_tracker.cpp 2016-06-27 20:45:54 +0000
833@@ -37,6 +37,7 @@
834 namespace mt = mir::test;
835 namespace mtd = mt::doubles;
836 namespace mi = mir::input;
837+namespace geom = mir::geometry;
838 namespace
839 {
840
841@@ -292,3 +293,38 @@
842 tracker.dispatch(*another_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion,
843 mir_pointer_button_secondary, 0, 0, 0, 0));
844 }
845+
846+TEST_F(SeatInputDeviceTracker, pointer_confinement_bounds_mouse_inside)
847+{
848+ auto const move_x = 20.0f, move_y = 40.0f;
849+ auto const max_w_h = 100;
850+ EXPECT_CALL(mock_cursor_listener, cursor_moved_to(move_x, move_y)).Times(1);
851+ EXPECT_CALL(mock_cursor_listener, cursor_moved_to(max_w_h - 1, max_w_h - 1)).Times(1);
852+
853+ geom::Rectangle rec{{0, 0}, {max_w_h, max_w_h}};
854+ tracker.set_confinement_regions({rec});
855+ tracker.add_device(some_device);
856+ tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f,
857+ move_x, move_y));
858+ tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f,
859+ max_w_h * 2, max_w_h * 2));
860+}
861+
862+TEST_F(SeatInputDeviceTracker, reset_pointer_confinement_allows_movement_past)
863+{
864+ auto const move_x = 20.0f, move_y = 40.0f;
865+ auto const max_w_h = 100;
866+ EXPECT_CALL(mock_cursor_listener, cursor_moved_to(move_x, move_y)).Times(1);
867+ EXPECT_CALL(mock_cursor_listener, cursor_moved_to(move_x + max_w_h * 2,
868+ move_y + max_w_h * 2)).Times(1);
869+
870+ geom::Rectangle rec{{0, 0}, {max_w_h, max_w_h}};
871+ tracker.set_confinement_regions({rec});
872+ tracker.add_device(some_device);
873+ tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f,
874+ move_x, move_y));
875+
876+ tracker.reset_confinement_regions();
877+ tracker.dispatch(*some_device_builder.pointer_event(arbitrary_timestamp, mir_pointer_action_motion, 0, 0.0f, 0.0f,
878+ max_w_h * 2, max_w_h * 2));
879+}
880
881=== modified file 'tests/unit-tests/scene/test_abstract_shell.cpp'
882--- tests/unit-tests/scene/test_abstract_shell.cpp 2016-05-04 01:48:16 +0000
883+++ tests/unit-tests/scene/test_abstract_shell.cpp 2016-06-27 20:45:54 +0000
884@@ -41,6 +41,7 @@
885 #include "mir/test/doubles/stub_buffer_allocator.h"
886 #include "mir/test/doubles/null_application_not_responding_detector.h"
887 #include "mir/test/doubles/stub_display.h"
888+#include "mir/test/doubles/mock_input_seat.h"
889
890 #include "mir/test/fake_shared.h"
891
892@@ -108,6 +109,7 @@
893 NiceMock<MockSessionContainer> session_container;
894 NiceMock<MockSessionEventSink> session_event_sink;
895 NiceMock<MockSurfaceFactory> surface_factory;
896+ NiceMock<mtd::MockInputSeat> seat;
897 mtd::StubDisplay display{3};
898
899 NiceMock<MockSessionManager> session_manager{
900@@ -131,7 +133,8 @@
901 mt::fake_shared(session_manager),
902 std::make_shared<mtd::NullPromptSessionManager>(),
903 std::make_shared<mir::report::null::ShellReport>(),
904- [this](msh::FocusController*) { return wm = std::make_shared<NiceMockWindowManager>(); }};
905+ [this](msh::FocusController*) { return wm = std::make_shared<NiceMockWindowManager>(); },
906+ mt::fake_shared(seat)};
907
908 void SetUp() override
909 {
910
911=== modified file 'tests/unit-tests/scene/test_basic_surface.cpp'
912--- tests/unit-tests/scene/test_basic_surface.cpp 2016-05-03 06:55:25 +0000
913+++ tests/unit-tests/scene/test_basic_surface.cpp 2016-06-27 20:45:54 +0000
914@@ -93,6 +93,7 @@
915 ms::BasicSurface surface{
916 name,
917 rect,
918+ mir_pointer_unconfined,
919 false,
920 streams,
921 std::shared_ptr<mi::InputChannel>(),
922@@ -121,7 +122,7 @@
923 for (auto& surface : surfaces)
924 {
925 surface = std::make_unique<ms::BasicSurface>(
926- name, rect, false,
927+ name, rect, mir_pointer_unconfined, false,
928 std::list<ms::StreamInfo> {
929 { std::make_shared<testing::NiceMock<mtd::MockBufferStream>>(), {}, {} } },
930 std::shared_ptr<mi::InputChannel>(), stub_input_sender,
931@@ -142,7 +143,7 @@
932 for (auto& surface : surfaces)
933 {
934 surface = std::make_unique<ms::BasicSurface>(
935- name, rect, false, streams,
936+ name, rect, mir_pointer_unconfined, false, streams,
937 std::shared_ptr<mi::InputChannel>(), stub_input_sender,
938 std::shared_ptr<mg::CursorImage>(), report);
939
940@@ -265,6 +266,7 @@
941 ms::BasicSurface surface{
942 name,
943 rect,
944+ mir_pointer_unconfined,
945 false,
946 streams,
947 std::shared_ptr<mi::InputChannel>(),
948@@ -305,6 +307,7 @@
949 ms::BasicSurface surface{
950 name,
951 geom::Rectangle{pt, one_by_one},
952+ mir_pointer_unconfined,
953 false,
954 streams,
955 std::shared_ptr<mi::InputChannel>(),
956@@ -346,6 +349,7 @@
957 ms::BasicSurface surface{
958 name,
959 geom::Rectangle{{0,0}, {100,100}},
960+ mir_pointer_unconfined,
961 false,
962 streams,
963 std::shared_ptr<mi::InputChannel>(),
964@@ -486,6 +490,7 @@
965 name,
966 geom::Rectangle{{0,0}, {100,100}},
967 parent,
968+ mir_pointer_unconfined,
969 false,
970 streams,
971 std::shared_ptr<mi::InputChannel>(),
972@@ -673,6 +678,7 @@
973 ms::BasicSurface surface{
974 name,
975 rect,
976+ mir_pointer_unconfined,
977 false,
978 streams,
979 std::shared_ptr<mi::InputChannel>(),
980
981=== modified file 'tests/unit-tests/scene/test_session_manager.cpp'
982--- tests/unit-tests/scene/test_session_manager.cpp 2016-05-04 01:48:16 +0000
983+++ tests/unit-tests/scene/test_session_manager.cpp 2016-06-27 20:45:54 +0000
984@@ -83,6 +83,7 @@
985 std::shared_ptr<ms::Surface> dummy_surface = std::make_shared<ms::BasicSurface>(
986 std::string("stub"),
987 geom::Rectangle{{},{}},
988+ mir_pointer_unconfined,
989 false,
990 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
991 std::shared_ptr<mi::InputChannel>(),
992
993=== modified file 'tests/unit-tests/scene/test_surface.cpp'
994--- tests/unit-tests/scene/test_surface.cpp 2016-05-03 06:55:25 +0000
995+++ tests/unit-tests/scene/test_surface.cpp 2016-06-27 20:45:54 +0000
996@@ -193,7 +193,8 @@
997 {
998 SurfaceCreation()
999 : surface(surface_name,
1000- rect, false, streams,
1001+ rect, mir_pointer_unconfined,
1002+ false, streams,
1003 std::make_shared<mtd::StubInputChannel>(),
1004 std::make_shared<mtd::StubInputSender>(),
1005 nullptr /* cursor_image */, report)
1006@@ -377,6 +378,7 @@
1007 ms::BasicSurface input_surf(
1008 surface_name,
1009 rect,
1010+ mir_pointer_unconfined,
1011 false,
1012 streams,
1013 mt::fake_shared(channel),
1014@@ -395,6 +397,7 @@
1015 ms::BasicSurface surface(
1016 surface_name,
1017 rect,
1018+ mir_pointer_unconfined,
1019 false,
1020 streams,
1021 std::make_shared<mtd::StubInputChannel>(),
1022
1023=== modified file 'tests/unit-tests/scene/test_surface_impl.cpp'
1024--- tests/unit-tests/scene/test_surface_impl.cpp 2016-05-03 06:55:25 +0000
1025+++ tests/unit-tests/scene/test_surface_impl.cpp 2016-06-27 20:45:54 +0000
1026@@ -83,7 +83,8 @@
1027 .WillByDefault(InvokeArgument<0>(nullptr));
1028
1029 surface = std::make_shared<ms::BasicSurface>(
1030- std::string("stub"), geom::Rectangle{{},{}}, false,
1031+ std::string("stub"), geom::Rectangle{{},{}},
1032+ mir_pointer_unconfined, false,
1033 std::list<ms::StreamInfo> { { buffer_stream, {}, {} } },
1034 nullptr, stub_input_sender, nullptr, report);
1035 }
1036@@ -280,6 +281,7 @@
1037 ms::BasicSurface surf(
1038 std::string("stub"),
1039 geom::Rectangle{{},{}},
1040+ mir_pointer_unconfined,
1041 false,
1042 std::list<ms::StreamInfo> { { buffer_stream, {}, {} } },
1043 std::shared_ptr<mi::InputChannel>(),
1044
1045=== modified file 'tests/unit-tests/scene/test_surface_stack.cpp'
1046--- tests/unit-tests/scene/test_surface_stack.cpp 2016-05-03 06:55:25 +0000
1047+++ tests/unit-tests/scene/test_surface_stack.cpp 2016-06-27 20:45:54 +0000
1048@@ -127,6 +127,7 @@
1049 stub_surface1 = std::make_shared<ms::BasicSurface>(
1050 std::string("stub"),
1051 geom::Rectangle{{},{}},
1052+ mir_pointer_unconfined,
1053 false,
1054 std::list<ms::StreamInfo> { { stub_buffer_stream1, {}, {} } },
1055 std::shared_ptr<mir::input::InputChannel>(),
1056@@ -137,6 +138,7 @@
1057 stub_surface2 = std::make_shared<ms::BasicSurface>(
1058 std::string("stub"),
1059 geom::Rectangle{{},{}},
1060+ mir_pointer_unconfined,
1061 false,
1062 std::list<ms::StreamInfo> { { stub_buffer_stream2, {}, {} } },
1063 std::shared_ptr<mir::input::InputChannel>(),
1064@@ -148,6 +150,7 @@
1065 stub_surface3 = std::make_shared<ms::BasicSurface>(
1066 std::string("stub"),
1067 geom::Rectangle{{},{}},
1068+ mir_pointer_unconfined,
1069 false,
1070 std::list<ms::StreamInfo> { { stub_buffer_stream3, {}, {} } },
1071 std::shared_ptr<mir::input::InputChannel>(),
1072@@ -159,6 +162,7 @@
1073 invisible_stub_surface = std::make_shared<ms::BasicSurface>(
1074 std::string("stub"),
1075 geom::Rectangle{{},{}},
1076+ mir_pointer_unconfined,
1077 false,
1078 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
1079 std::shared_ptr<mir::input::InputChannel>(),
1080@@ -274,6 +278,7 @@
1081 auto surface = std::make_shared<ms::BasicSurface>(
1082 std::string("Mary had a little lamb"),
1083 geom::Rectangle{{},{}},
1084+ mir_pointer_unconfined,
1085 false,
1086 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
1087 std::shared_ptr<mir::input::InputChannel>(),
1088@@ -303,6 +308,7 @@
1089 auto surface = std::make_shared<ms::BasicSurface>(
1090 std::string("username@hostname: /"),
1091 geom::Rectangle{{},{}},
1092+ mir_pointer_unconfined,
1093 false,
1094 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
1095 std::shared_ptr<mir::input::InputChannel>(),
1096@@ -346,6 +352,7 @@
1097 auto surface = std::make_shared<ms::BasicSurface>(
1098 std::string("stub"),
1099 geom::Rectangle{{},{}},
1100+ mir_pointer_unconfined,
1101 false,
1102 std::list<ms::StreamInfo> { { stream, {}, {} } },
1103 std::shared_ptr<mir::input::InputChannel>(),
1104@@ -383,6 +390,7 @@
1105 auto surface = std::make_shared<ms::BasicSurface>(
1106 std::string("stub"),
1107 geom::Rectangle{{},{}},
1108+ mir_pointer_unconfined,
1109 false,
1110 std::list<ms::StreamInfo> { { stream, {}, {} } },
1111 std::shared_ptr<mir::input::InputChannel>(),
1112@@ -417,6 +425,7 @@
1113 auto surface = std::make_shared<ms::BasicSurface>(
1114 std::string("stub"),
1115 geom::Rectangle{{},{}},
1116+ mir_pointer_unconfined,
1117 false,
1118 std::list<ms::StreamInfo> { { stream, {}, {} } },
1119 std::shared_ptr<mir::input::InputChannel>(),
1120@@ -531,6 +540,7 @@
1121 auto const surface = std::make_shared<ms::BasicSurface>(
1122 std::string("stub"),
1123 geom::Rectangle{geom::Point{3 * i, 4 * i},geom::Size{1 * i, 2 * i}},
1124+ mir_pointer_unconfined,
1125 true,
1126 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
1127 std::shared_ptr<mir::input::InputChannel>(),
1128@@ -716,6 +726,7 @@
1129 auto const surface = std::make_shared<ms::BasicSurface>(
1130 std::string("stub"),
1131 geom::Rectangle{geom::Point{3 * i, 4 * i},geom::Size{1 * i, 2 * i}},
1132+ mir_pointer_unconfined,
1133 true,
1134 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
1135 std::shared_ptr<mir::input::InputChannel>(),
1136@@ -749,6 +760,7 @@
1137 auto const surface = std::make_shared<ms::BasicSurface>(
1138 std::string("stub"),
1139 geom::Rectangle{geom::Point{3, 4},geom::Size{1, 2}},
1140+ mir_pointer_unconfined,
1141 true,
1142 std::list<ms::StreamInfo> { { mock_stream, {}, {} } },
1143 std::shared_ptr<mir::input::InputChannel>(),
1144@@ -779,6 +791,7 @@
1145 auto const surface = std::make_shared<ms::BasicSurface>(
1146 std::string("stub"),
1147 geom::Rectangle{geom::Point{3, 4},geom::Size{1, 2}},
1148+ mir_pointer_unconfined,
1149 true,
1150 std::list<ms::StreamInfo> { { mock_stream, {}, {} } },
1151 std::shared_ptr<mir::input::InputChannel>(),
1152@@ -802,6 +815,7 @@
1153 ms::BasicSurface(
1154 {},
1155 {{},{}},
1156+ mir_pointer_unconfined,
1157 true,
1158 std::list<ms::StreamInfo> { { std::make_shared<mtd::StubBufferStream>(), {}, {} } },
1159 {},

Subscribers

People subscribed via source and target branches