Mir

Merge lp:~mir-team/mir/cursor-spike-phase-2-resubmit into lp:mir

Proposed by Robert Carr
Status: Merged
Approved by: Robert Carr
Approved revision: no longer in the source branch.
Merged at revision: 1621
Proposed branch: lp:~mir-team/mir/cursor-spike-phase-2-resubmit
Merge into: lp:mir
Prerequisite: lp:~mir-team/mir/cursor-spike-phase-1-resubmit-resubmit-resubmit-hello-out-there
Diff against target: 926 lines (+685/-10)
18 files modified
include/client/mir_toolkit/mir_cursor_configuration.h (+68/-0)
include/client/mir_toolkit/mir_surface.h (+13/-0)
include/platform/mir/graphics/cursor.h (+3/-1)
src/client/CMakeLists.txt (+1/-0)
src/client/cursor_configuration.h (+31/-0)
src/client/mir_cursor_api.cpp (+44/-0)
src/client/mir_surface.cpp (+19/-0)
src/client/mir_surface.h (+4/-0)
src/client/mir_surface_api.cpp (+16/-0)
src/platform/graphics/mesa/cursor.cpp (+2/-2)
src/platform/graphics/mesa/cursor.h (+2/-2)
src/server/frontend/protobuf_message_processor.cpp (+4/-0)
src/server/frontend/session_mediator.cpp (+10/-0)
src/server/frontend/session_mediator.h (+6/-1)
src/shared/protobuf/mir_protobuf.proto (+8/-0)
tests/acceptance-tests/CMakeLists.txt (+1/-0)
tests/acceptance-tests/test_client_cursor_api.cpp (+449/-0)
tests/unit-tests/graphics/mesa/test_cursor.cpp (+4/-4)
To merge this branch: bzr merge lp:~mir-team/mir/cursor-spike-phase-2-resubmit
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Daniel van Vugt Abstain
Alberto Aguirre (community) Approve
Chris Halse Rogers Approve
Andreas Pokorny (community) Approve
Alan Griffiths Approve
Kevin DuBois (community) Needs Information
Review via email: mp+217685@code.launchpad.net

Commit message

Acceptance tests and client machinery implementation for client cursor API.

Description of the change

Acceptance tests and client machinery implementation for client cursor API. Much rework of API since last submission. If there is positive feedback ill rebase the implementation on this (impl also depends on introduce-scene-observer)

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Robert Carr (robertcarr) wrote :

Failure looks unrelated.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

> Failure looks unrelated.

There's been a few of those floating around, haven't been able to figure out why

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

mostly "needs info":

1) +MirWaitHandle* mir_surface_configure_cursor(MirSurface *surface, MirCursor const* parameters);
We submit the cursor and the surface, but don't tell what we want (I assume its like "make an hourglass over this surface", or "hide cursor over this surface"). Is a 3rd parameter coming in the future?
2) is it difficult to add a mir_available_cursor_names() sort of function, so we know what strings will get a cursor? this could condense the 'default' and 'disabled' cursor acquisition too.
3) MirCursor is c++ code, why not std::string?
4) include guards on cursor_representation.h

review: Needs Information
Revision history for this message
Robert Carr (robertcarr) wrote :

Thanks!

>> We submit the cursor and the surface, but don't tell what we want (I assume its like "make an hourglass
>> over this surface", or "hide cursor over this surface"). Is a 3rd parameter coming in the future?

MirCursor was in fact this struct. I've renamed it to MirCursorConfiguration to be more explicit.

>> 2) is it difficult to add a mir_available_cursor_names() sort of function, so we know what strings will get >> a cursor? this could condense the 'default' and 'disabled' cursor acquisition too.

I'll add this with the loader.

>> 3) MirCursor is c++ code, why not std::string?
>> 4) include guards on cursor_representation.h

Fixed!

Revision history for this message
Robert Carr (robertcarr) wrote :

>> 2) is it difficult to add a mir_available_cursor_names() sort of function, so we know what strings will get >> a cursor? this could condense the 'default' and 'disabled' cursor acquisition too.

>> I'll add this with the loader.

Or something of the sort at least.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

43 +void mir_cursor_configuration_destroy(MirCursorConfiguration *parameters);
44 +
45 +/**
46 + * Returns a new MirCursorConfiguration representing a disabled cursor
47 + * \return A cursor parameters object which must be passed
48 + * to mir_cursor_configuration_destroy
49 + */
50 +MirCursorConfiguration* mir_cursor_configuration_disabled();
51 +
52 +/**
53 + * Returns a new MirCursorConfiguration representing the default cursor
54 + * request.
55 + * \return A cursor parameters object which must be passed
56 + * to_mir_cursor_configuration_destroy
57 + */
58 +MirCursorConfiguration *mir_cursor_configuration_default();
59 +
60 +/**
61 + * Returns a new MirCursorConfiguration representing a named cursor
62 + * from the system cursor theme. At present all implementations
63 + * follow XCursor naming conventions.
64 + * \param [in] name The cursor name
65 + * \return A cursor parameters object which must be passed
66 + * to_mir_cursor_configuration_destroy
67 + */
68 +MirCursorConfiguration *mir_cursor_configuration_from_name(char const* name);

Is it really worth having 3 functions?

Couldn't we have one function and some default names? Vis:

extern char const *const mir_default_cursor_name;
extern char const *const mir_disabled_cursor_name;

MirCursorConfiguration *mir_cursor_configuration_from_name(char const* name);

...

mir_cursor_configuration_from_name(mir_disabled_cursor_name);

~~~~

200 +MirCursorConfiguration* mir_cursor_configuration_from_name(char const* name)
201 +{
202 + auto c = new MirCursorConfiguration;
203 + c->name = std::string(name);
204 +
205 + return c;
206 +}

This is a C API - but C++ memory allocations can throw.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Alan: Addressed concerns.

Also made some changes to mg::Cursor API, since set_image was changed to accept references, had to stop using nullptr to disable cursor, api is now show(image)/hide()

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

233 + {
234 + std::unique_lock<decltype(mutex)> lock(mutex);
235 + setting.mutable_surfaceid()->CopyFrom(surface.id());
236 + if (cursor->name != "")
237 + setting.set_name(cursor->name.c_str());
238 + }

indentation

~~~~

374 + BOOST_THROW_EXCEPTION(std::runtime_error("Cursor API not yet implemented"));

We have a tradition of having a "TODO" comment to mark pending work. (Also, getting very picky, I think this is more of a logic_error than a runtime_error.)

~~~~

406 + // No name is interpreted as disabled cursor.
407 + optional string name = 2;

Is a comment really clearer than a boolean field?

~~~~

528 + if (image->cursor_name != "default")
529 + return false;
530 + return true;

Overly verbose: return image->cursor_name == "default";

~~~~

662 +// In this set we create a 1x1 client surface at the point (1,0). The client requests to disable the cursor
663 +// over this surface. Since the cursor starts at (0,0) we when we move the cursor by (1,0) thus causing it
664 +// to enter the bounds of the first surface, we should observe it being disabled.
665 +TEST_F(TestClientCursorAPI, DISABLED_client_may_disable_cursor_over_surface)

We have a tradition of having a "TODO" comment to mark pending work.

Also with this and other tests it looks like a lot of the setup could be moved into the test fixture - which would make toe behavior under test a lot clearer.

review: Needs Fixing
Revision history for this message
Robert Carr (robertcarr) wrote :

Fixed indentation, verbose and TODOs. Though, the branch removing the TODOs (which now has to be updated to remove the TODOs) is ready to propose when this one lands ;)

>> Is a comment really clearer than a boolean field?

In this case I think it is. If you use a field there are two more questions:
What if the enabled field is true but there is no cursor image?
What if the enabled field is false but there is a cursor image?

Not only are there multiple possible behaviors but it requires documentation as well.

>> Also with this and other tests it looks like a lot of the setup could be moved into the test fixture -
>> which would make toe behavior under test a lot clearer.

I'll do another round of abstraction on the tests once I have a little more time to deal with the input tests as well and arrive at the grand unified input test fixture.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

OK for now

review: Approve
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

~~~
208 + catch (std::exception const&)
294 + catch (std::exception const&)
~~~
Shouldn't you be catching all exceptions with catch(...) ?

~~~
191 +char const *const mir_disabled_cursor_name = NULL;
287 + MirWaitHandle *result = NULL;
~~~

Use nullptr

~~~
487 +namespace
488 +{
489 + char const* const mir_test_socket = mtf::test_socket_file().c_str();
490 +}
~~~

Perhaps define that as a static member variable of CursorSettingClient?

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

while you are at it and fix the stuff above, please have a look at:
159 + Brace Style

apart from that looks good so..

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

Looks broadly fine to me.

I suspect that the tests would be simpler to write and easier to debug if they used InProcessServer.

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

Note that in Alberto's comment:

287 + MirWaitHandle *result = NULL;
~~~
Use nullptr

The result is being returned from a C API, not C++. So NULL is more correct than nullptr, because the caller will only understand NULL, not nullptr.

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

(1) I think "Configuration" is a vague word with respect to MirCursorConfiguration, and find it confusing. Looking to the source for an explanation, I find a comment which is also confusing:

158 +// Cursor parameterization. May grow to include RGBA cursors, etc...
159 +struct MirCursorConfiguration {
160 + std::string name;
161 +};

We already support colour cursors(!?), so can you explain what the struct might grow to represent? Then it might be easier to come up with a more appropriate name/structure.

(2) Very minor issue: This function name is inconsistent with the existing convention of:
     mir_OBJECT_OPERATION(OBJECT x, ...)
64 +MirCursorConfiguration *mir_cursor_configuration_from_name(char const* name);
Although maintaining complete consistency can yield even more awkward names. So I'm not sure if it should be changed at all.

(3) This inline documentation needs elaboration (like a URL of X docs). There's presently not enough information provided in the header/docs to really use the function...
59 + * follow XCursor naming conventions.

(4) Inappropriate function name:
98 +MirWaitHandle* mir_surface_configure_cursor(MirSurface *surface, MirCursorConfiguration const* parameters);
The verb "configure" implies you're changing the cursor object, which is not true. More accurate would be "mir_surface_set_cursor".

(5) Per previous discussions I don't think this is correct for common toolkit usage:
405 +message CursorSetting {
406 + required SurfaceId surfaceid = 1;
407 + // No name is interpreted as disabled cursor.
408 + optional string name = 2;
409 +}
Hiding the cursor should probably not change its appearance for when it becomes visible again. And simply making the cursor invisible will lead to bugs where it sounds like the cursor still exists and is clickable on things while invisible.

The big issue is (1). We either need some explanation of what a "MirCursorConfiguration" is, or a clearer name like "MirCursor" which would already be understood.

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

(6) The API implication that a surface has a single cursor setting is misleading. Obviously the cursor will vary over the surface depending on the widget. So although the one-cursor-set-per-surface sounds wrong, I now realize it does work so long as you make the app/toolkit do the hard work of changing the cursor as it moves.

On the other hand, Mir does already have the concept internally of input regions (still needs work). But since we already have multiple input rectangle support per-surface in libmirserver (BasicSurface::input_rectangles), wouldn't it be better to expose that to the client API and simply attach a MirCursor to each input rectangle? Then the server could do the cursor changes for you. I think that's what X and Win32 does... And it certainly eliminates the redundancy of asking every toolkit to re-implement cursor tracking.

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

On Thu, May 8, 2014 at 2:02 PM, Daniel van Vugt
<email address hidden> wrote:
> (6) The API implication that a surface has a single cursor setting is
> misleading. Obviously the cursor will vary over the surface depending
> on the widget. So although the one-cursor-set-per-surface sounds
> wrong, I now realize it does work so long as you make the app/toolkit
> do the hard work of changing the cursor as it moves.
>
> On the other hand, Mir does already have the concept internally of
> input regions (still needs work). But since we already have multiple
> input rectangle support per-surface in libmirserver
> (BasicSurface::input_rectangles), wouldn't it be better to expose
> that to the client API and simply attach a MirCursor to each input
> rectangle? Then the server could do the cursor changes for you. I
> think that's what X and Win32 does... And it certainly eliminates the
> redundancy of asking every toolkit to re-implement cursor tracking.

While that used to be true, I don't think it is still the case - GTK at
least moved away from multiple X11-protocol-windows per window some
time ago. Sometime late in the 2.x series, IIRC.

While exposing subrectangles is appealing for other reasons, I think
the toolkit boat sailed some time ago :)

Revision history for this message
Robert Carr (robertcarr) wrote :

Alberto/Andreas: Addressed comments.

>> (1) I think "Configuration" is a vague word with respect to MirCursorConfiguration.

I disagree. I've updated the comments though. If you look up in the review you can see it was MirCursor and why it was changed.

>> (2) Very minor issue: This function name is inconsistent with the existing convention of:

It's because there are no existing named constructors which do not require an existing mir object. A possible name is mir_cursor_name_create_configuration(const char*) but this is the more familiar pattern.

>> (3) (3) This inline documentation needs elaboration (like a URL of X docs). There's presently not enough
>> information provided in the header/docs to really use the function...

I've removed the suggestion that you can set arbitrary cursor and am starting with just default/disabled for the XMir use case. Will elaborate on additional cursors in cursor spike phase 5 (XCursor theme loader...)

>> (4) The verb "configure" implies you're changing the cursor object, which is not true. More accurate would be "mir_surface_set_cursor".

I disagree. Perhaps more accurate would be mir_surface_configure_"cursor_request" but everything is a request so I dropped the suffix.

>> (5) Hiding the cursor should probably not change its appearance for when it becomes visible again

The cursor can only be made visable again by requesting a specific issue.

>> (5) And simply making the cursor invisible will lead to bugs where it sounds like the cursor still exists and is clickable on things while invisible.

Yes this is the intended use of the API, for apps which have no cursor (i.e. perhaps a racing game) but still respond to mouse events or apps which really want to draw their own cursor (virtualization software, rooted XMir).

>> (6)
>> (6) The API implication that a surface has a single cursor setting is misleading. Obviously the cursor
>> will vary over the surface depending on the widget. So although the one-cursor-set-per-surface sounds
>> wrong, I now realize it does work so long as you make the app/toolkit do the hard work of changing the
>> cursor as it moves.

In my experience with Qt, Chromium, and GTK it would be more effort (Qt, Chromium) or about the same (GTK) to require the toolkit to assemble these rectangles. In the case of QML and Chromium I am not even sure you can accurately assemble these rectangles and they would likely use a full surface rectangle that they changed the cursor name on.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(1) Reading again, I still believe there's no justification for MirCursorConfiguration. All the relevant info would be contained in a MirCursor (image + hotspot... what else is there?).

(3) You should refer to them by identifier name in the docs. Not all readers will be looking at the header file and immediately see which identifiers to use (if any):
58 + * from the system cursor theme. Currently only the default cursor
59 + * and a disabled cursor are supported.

(4) OK, assuming the verb "configure" describes something more complex than "setting" a cursor, what's the extra complexity? What is it doing other than setting a cursor?

(5) Your justification for having invisible cursors is not valid, I believe. Games with no visible cursor require relative movement data, which is unrelated to any "cursor" location. See: https://bugs.launchpad.net/mir/+bug/1276322
In particular, in your "racing game" or FPS you don't want mouse movement to suddenly hit an invisible wall like a cursor would at the edge of the screen. Thus, you need to turn the cursor "off" (including events) and not just make it invisible. Per bug 1276322.

(6) I don't really mind letting it be a toolkit/app problem. I was particularly curious since I noticed we already have BasicSurface::input_rectangles in the server. It would be nice if we avoided libmirserver having to maintain any knowledge of the sub-regions of the client surface.

review: Needs Fixing
Revision history for this message
Alberto Aguirre (albaguirre) wrote :

ok.

review: Approve
Revision history for this message
Robert Carr (robertcarr) wrote :

>> (1) Reading again, I still believe there's no justification for MirCursorConfiguration. All the relevant '
>> info would be contained in a MirCursor (image + hotspot... what else is there?).

The idea in changing it was that MirCursor could be meant to refer to a particular on-screen cursor. RAther what this struct contains is the parameters used to configure an on screen cursor.

>> (3) You should refer to them by identifier name in the docs.

Ok.

>> (4) OK, assuming the verb "configure" describes something more complex than "setting" a cursor, what's the extra complexity? What is it doing other than setting a cursor?

I am thinking of the cursor as the onscreen buffer with a particular location. In this sense you can't set a cursor on a surface, rather you set the cursor configuration which a surface requests.

>> (5) Your justification for having invisible cursors is not valid, I believe. Games with no visible cursor require relative movement data, which is unrelated to any "cursor" location

I guess that's true for racing games which would need some sort of mouse grab...I think there are still use cases though.

1. Virtual Machine/VNC type apps operating without mouse grab
2. Apps which may wish to use crazy cursors (i.e. maybe a fullscreen painting app has some crazy paint brush cursor which interacts with the surface in animations and its easiest to just do it on the client side).
3. Xmir (arguably, but its quite convenient!)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

(1) Alright, MirCursorConfiguration is a separate object to a specific MirCursor instance (which moves). I get that now, but suggest we try to come up with a noun less clumsy than the (slightly too long) "Configuration".

(5) OK, so there are indeed invisible cursor use cases, but I think they're less common than needing to hide the cursor (and not get any absolute location events). Games need grabs, certainly.

review: Abstain
Revision history for this message
Robert Carr (robertcarr) wrote :

Jenkins failure is intermittent and unrelated. Landing.

Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'include/client/mir_toolkit/mir_cursor_configuration.h'
2--- include/client/mir_toolkit/mir_cursor_configuration.h 1970-01-01 00:00:00 +0000
3+++ include/client/mir_toolkit/mir_cursor_configuration.h 2014-05-09 20:09:14 +0000
4@@ -0,0 +1,68 @@
5+/*
6+ * Copyright © 2014 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU Lesser General Public License version 3,
10+ * as published by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful,
13+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+ * GNU Lesser General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU Lesser General Public License
18+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
19+ *
20+ */
21+
22+#ifndef MIR_TOOLKIT_MIR_CURSOR_H_
23+#define MIR_TOOLKIT_MIR_CURSOR_H_
24+
25+/**
26+ * Opaque structure containing cursor parameterization. Create with mir_cursor* family.
27+ * Used with mir_surface_configure_cursor.
28+ */
29+typedef struct MirCursorConfiguration MirCursorConfiguration;
30+
31+#ifdef __cplusplus
32+/**
33+ * \addtogroup mir_toolkit
34+ * @{
35+ */
36+extern "C" {
37+#endif
38+
39+/**
40+ * A special cursor name for use with mir_cursor_configuration_from_name
41+ * representing the system default cursor.
42+ */
43+extern char const *const mir_default_cursor_name;
44+/**
45+ * A special cursor name for use with mir_cursor_configuration_from_name
46+ * representing a disabled cursor image.
47+ */
48+extern char const *const mir_disabled_cursor_name;
49+
50+/**
51+ * Release resources assosciated with cursor parameters
52+ * \param [in] parameters The operand
53+ */
54+void mir_cursor_configuration_destroy(MirCursorConfiguration *parameters);
55+
56+/**
57+ * Returns a new MirCursorConfiguration representing a named cursor
58+ * from the system cursor theme. Currently only the symbolic values,
59+ * mir_default_cursor_name and mir_disabled_cursor_name are supported
60+ * as input.
61+ * \param [in] name The cursor name
62+ * \return A cursor parameters object which must be passed
63+ * to_mir_cursor_configuration_destroy
64+ */
65+MirCursorConfiguration *mir_cursor_configuration_from_name(char const* name);
66+
67+#ifdef __cplusplus
68+}
69+/**@}*/
70+#endif
71+
72+#endif /* MIR_TOOLKIT_MIR_CURSOR_H_ */
73
74=== modified file 'include/client/mir_toolkit/mir_surface.h'
75--- include/client/mir_toolkit/mir_surface.h 2014-03-31 14:36:08 +0000
76+++ include/client/mir_toolkit/mir_surface.h 2014-05-09 20:09:14 +0000
77@@ -21,6 +21,7 @@
78 #include <mir_toolkit/mir_native_buffer.h>
79 #include <mir_toolkit/client_types.h>
80 #include <mir_toolkit/common.h>
81+#include <mir_toolkit/mir_cursor_configuration.h>
82
83 #ifdef __cplusplus
84 /**
85@@ -246,6 +247,18 @@
86 */
87 int mir_surface_get_swapinterval(MirSurface* surface);
88
89+/**
90+ * Choose the cursor state for a surface: whether a cursor is shown,
91+ * and which cursor if so.
92+ * \param [in] surface The surface to operate on
93+ * \param [in] parameters The configuration parameters obtained
94+ * from mir_cursor* family of functions.
95+ * \return A wait handle that can be passed to mir_wait_for,
96+ * or NULL if parameters is invalid.
97+ *
98+ */
99+MirWaitHandle* mir_surface_configure_cursor(MirSurface *surface, MirCursorConfiguration const* parameters);
100+
101 #ifdef __cplusplus
102 }
103 /**@}*/
104
105=== modified file 'include/platform/mir/graphics/cursor.h'
106--- include/platform/mir/graphics/cursor.h 2014-05-06 03:33:05 +0000
107+++ include/platform/mir/graphics/cursor.h 2014-05-09 20:09:14 +0000
108@@ -33,7 +33,9 @@
109 class Cursor
110 {
111 public:
112- virtual void set_image(CursorImage const& cursor_image) = 0;
113+ virtual void show(CursorImage const& cursor_image) = 0;
114+ virtual void hide() = 0;
115+
116 virtual void move_to(geometry::Point position) = 0;
117
118 protected:
119
120=== modified file 'src/client/CMakeLists.txt'
121--- src/client/CMakeLists.txt 2014-03-31 14:36:08 +0000
122+++ src/client/CMakeLists.txt 2014-05-09 20:09:14 +0000
123@@ -47,6 +47,7 @@
124 private.cpp
125 mir_screencast.cpp
126 mir_screencast_api.cpp
127+ mir_cursor_api.cpp
128 )
129
130 add_library(
131
132=== added file 'src/client/cursor_configuration.h'
133--- src/client/cursor_configuration.h 1970-01-01 00:00:00 +0000
134+++ src/client/cursor_configuration.h 2014-05-09 20:09:14 +0000
135@@ -0,0 +1,31 @@
136+/*
137+ * Copyright © 2014 Canonical Ltd.
138+ *
139+ * This program is free software: you can redistribute it and/or modify
140+ * it under the terms of the GNU Lesser General Public License version 3 as
141+ * published by the Free Software Foundation.
142+ *
143+ * This program is distributed in the hope that it will be useful,
144+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
145+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
146+ * GNU Lesser General Public License for more details.
147+ *
148+ * You should have received a copy of the GNU Lesser General Public License
149+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
150+ *
151+ * Authored by: Robert Carr <robert.carr@canonical.com>
152+ */
153+
154+#ifndef MIR_CLIENT_CURSOR_CONFIGURATION_H_
155+#define MIR_CLIENT_CURSOR_CONFIGURATION_H_
156+
157+#include <string>
158+
159+// Parameters for configuring the apperance and behavior of the system cursor.
160+// Will grow to include cursors specified by raw RGBA data, hotspots, etc...
161+struct MirCursorConfiguration
162+{
163+ std::string name;
164+};
165+
166+#endif // MIR_CLIENT_CURSOR_CONFIGURATION_H_
167
168=== added file 'src/client/mir_cursor_api.cpp'
169--- src/client/mir_cursor_api.cpp 1970-01-01 00:00:00 +0000
170+++ src/client/mir_cursor_api.cpp 2014-05-09 20:09:14 +0000
171@@ -0,0 +1,44 @@
172+/*
173+ * Copyright © 2014 Canonical Ltd.
174+ *
175+ * This program is free software: you can redistribute it and/or modify it
176+ * under the terms of the GNU Lesser General Public License version 3,
177+ * as published by the Free Software Foundation.
178+ *
179+ * This program is distributed in the hope that it will be useful,
180+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
181+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
182+ * GNU Lesser General Public License for more details.
183+ *
184+ * You should have received a copy of the GNU Lesser General Public License
185+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
186+ *
187+ * Authored by: Robert Carr <robert.carr@canonical.com>
188+ */
189+
190+#include "mir_toolkit/mir_cursor_configuration.h"
191+#include "cursor_configuration.h"
192+
193+char const *const mir_default_cursor_name = "default";
194+char const *const mir_disabled_cursor_name = nullptr;
195+
196+
197+void mir_cursor_configuration_destroy(MirCursorConfiguration *cursor)
198+{
199+ delete cursor;
200+}
201+
202+MirCursorConfiguration* mir_cursor_configuration_from_name(char const* name)
203+{
204+ try
205+ {
206+ auto c = new MirCursorConfiguration;
207+ c->name = std::string(name);
208+
209+ return c;
210+ }
211+ catch (...)
212+ {
213+ return nullptr;
214+ }
215+}
216
217=== modified file 'src/client/mir_surface.cpp'
218--- src/client/mir_surface.cpp 2014-03-06 06:05:17 +0000
219+++ src/client/mir_surface.cpp 2014-05-09 20:09:14 +0000
220@@ -20,6 +20,7 @@
221 #include "mir/frontend/client_constants.h"
222 #include "client_buffer.h"
223 #include "mir_surface.h"
224+#include "cursor_configuration.h"
225 #include "mir_connection.h"
226 #include "mir/input/input_receiver_thread.h"
227 #include "mir/input/input_platform.h"
228@@ -297,6 +298,24 @@
229 return *accelerated_window;
230 }
231
232+MirWaitHandle* MirSurface::configure_cursor(MirCursorConfiguration const* cursor)
233+{
234+ mp::CursorSetting setting;
235+
236+ {
237+ std::unique_lock<decltype(mutex)> lock(mutex);
238+ setting.mutable_surfaceid()->CopyFrom(surface.id());
239+ if (cursor->name != "")
240+ setting.set_name(cursor->name.c_str());
241+ }
242+
243+ configure_cursor_wait_handle.expect_result();
244+ server.configure_cursor(0, &setting, &void_response,
245+ google::protobuf::NewCallback(this, &MirSurface::on_configured));
246+
247+ return &configure_cursor_wait_handle;
248+}
249+
250 MirWaitHandle* MirSurface::configure(MirSurfaceAttrib at, int value)
251 {
252 std::unique_lock<decltype(mutex)> lock(mutex);
253
254=== modified file 'src/client/mir_surface.h'
255--- src/client/mir_surface.h 2014-03-11 15:46:03 +0000
256+++ src/client/mir_surface.h 2014-05-09 20:09:14 +0000
257@@ -87,6 +87,8 @@
258
259 MirWaitHandle* configure(MirSurfaceAttrib a, int value);
260 int attrib(MirSurfaceAttrib a) const;
261+
262+ MirWaitHandle* configure_cursor(MirCursorConfiguration const* cursor);
263
264 void set_event_handler(MirEventDelegate const* delegate);
265 void handle_event(MirEvent const& e);
266@@ -109,11 +111,13 @@
267 mir::protobuf::DisplayServer::Stub & server;
268 mir::protobuf::Surface surface;
269 std::string error_message;
270+ mir::protobuf::Void void_response;
271
272 MirConnection* const connection;
273 MirWaitHandle create_wait_handle;
274 MirWaitHandle next_buffer_wait_handle;
275 MirWaitHandle configure_wait_handle;
276+ MirWaitHandle configure_cursor_wait_handle;
277
278 std::shared_ptr<mir::client::MemoryRegion> secured_region;
279 std::shared_ptr<mir::client::ClientBufferDepository> buffer_depository;
280
281=== modified file 'src/client/mir_surface_api.cpp'
282--- src/client/mir_surface_api.cpp 2014-03-31 14:36:08 +0000
283+++ src/client/mir_surface_api.cpp 2014-05-09 20:09:14 +0000
284@@ -245,6 +245,22 @@
285 return surf ? surf->attrib(mir_surface_attrib_swapinterval) : -1;
286 }
287
288+MirWaitHandle* mir_surface_configure_cursor(MirSurface* surface, MirCursorConfiguration const* cursor)
289+{
290+ MirWaitHandle *result = nullptr;
291+
292+ try
293+ {
294+ if (surface)
295+ result = surface->configure_cursor(cursor);
296+ }
297+ catch (...)
298+ {
299+ }
300+
301+ return result;
302+}
303+
304 /* Debug functions */
305
306 uint32_t mir_debug_surface_current_buffer_id(MirSurface* surface)
307
308=== modified file 'src/platform/graphics/mesa/cursor.cpp'
309--- src/platform/graphics/mesa/cursor.cpp 2014-05-06 03:33:05 +0000
310+++ src/platform/graphics/mesa/cursor.cpp 2014-05-09 20:09:14 +0000
311@@ -80,7 +80,7 @@
312 buffer(gbm),
313 current_configuration(current_configuration)
314 {
315- set_image(*initial_image);
316+ show(*initial_image);
317
318 show_at_last_known_position();
319 }
320@@ -90,7 +90,7 @@
321 hide();
322 }
323
324-void mgm::Cursor::set_image(CursorImage const& cursor_image)
325+void mgm::Cursor::show(CursorImage const& cursor_image)
326 {
327 auto const& size = cursor_image.size();
328
329
330=== modified file 'src/platform/graphics/mesa/cursor.h'
331--- src/platform/graphics/mesa/cursor.h 2014-05-06 03:33:05 +0000
332+++ src/platform/graphics/mesa/cursor.h 2014-05-09 20:09:14 +0000
333@@ -68,12 +68,12 @@
334
335 ~Cursor() noexcept;
336
337- void set_image(CursorImage const& cursor_image) override;
338+ void show(CursorImage const& cursor_image) override;
339+ void hide() override;
340
341 void move_to(geometry::Point position);
342
343 void show_at_last_known_position();
344- void hide();
345
346 private:
347 enum ForceCursorState { UpdateState, ForceState };
348
349=== modified file 'src/server/frontend/protobuf_message_processor.cpp'
350--- src/server/frontend/protobuf_message_processor.cpp 2014-05-06 16:54:43 +0000
351+++ src/server/frontend/protobuf_message_processor.cpp 2014-05-09 20:09:14 +0000
352@@ -174,6 +174,10 @@
353 {
354 invoke(this, display_server.get(), &protobuf::DisplayServer::release_screencast, invocation);
355 }
356+ else if ("configure_cursor" == invocation.method_name())
357+ {
358+ invoke(this, display_server.get(), &protobuf::DisplayServer::configure_cursor, invocation);
359+ }
360 else if ("new_fds_for_trusted_clients" == invocation.method_name())
361 {
362 invoke(this, display_server.get(), &protobuf::DisplayServer::new_fds_for_trusted_clients, invocation);
363
364=== modified file 'src/server/frontend/session_mediator.cpp'
365--- src/server/frontend/session_mediator.cpp 2014-05-09 11:01:53 +0000
366+++ src/server/frontend/session_mediator.cpp 2014-05-09 20:09:14 +0000
367@@ -416,6 +416,16 @@
368 done->Run();
369 }
370
371+void mf::SessionMediator::configure_cursor(
372+ google::protobuf::RpcController*,
373+ mir::protobuf::CursorSetting const* /* cursor_request */,
374+ mir::protobuf::Void* /* void_response */,
375+ google::protobuf::Closure* /* done */)
376+{
377+ // TODO: Pass cursor settings down to surface.
378+ BOOST_THROW_EXCEPTION(std::logic_error("Cursor API not yet implemented"));
379+}
380+
381 void mf::SessionMediator::new_fds_for_trusted_clients(
382 ::google::protobuf::RpcController* ,
383 ::mir::protobuf::SocketFDRequest const* parameters,
384
385=== modified file 'src/server/frontend/session_mediator.h'
386--- src/server/frontend/session_mediator.h 2014-05-06 16:54:43 +0000
387+++ src/server/frontend/session_mediator.h 2014-05-09 20:09:14 +0000
388@@ -123,7 +123,12 @@
389 void screencast_buffer(google::protobuf::RpcController*,
390 const mir::protobuf::ScreencastId*,
391 mir::protobuf::Buffer*,
392- google::protobuf::Closure* done) override;
393+ google::protobuf::Closure* done);
394+
395+ void configure_cursor(google::protobuf::RpcController*,
396+ mir::protobuf::CursorSetting const*,
397+ mir::protobuf::Void*,
398+ google::protobuf::Closure* done);
399
400 /* Platform specific requests */
401 void drm_auth_magic(google::protobuf::RpcController* controller,
402
403=== modified file 'src/shared/protobuf/mir_protobuf.proto'
404--- src/shared/protobuf/mir_protobuf.proto 2014-05-06 16:54:43 +0000
405+++ src/shared/protobuf/mir_protobuf.proto 2014-05-09 20:09:14 +0000
406@@ -175,6 +175,12 @@
407 optional string error = 127;
408 }
409
410+message CursorSetting {
411+ required SurfaceId surfaceid = 1;
412+ // No name is interpreted as disabled cursor.
413+ optional string name = 2;
414+}
415+
416 message SocketFDRequest {
417 required int32 number = 1;
418 }
419@@ -206,5 +212,7 @@
420 rpc create_screencast(ScreencastParameters) returns (Screencast);
421 rpc screencast_buffer(ScreencastId) returns (Buffer);
422 rpc release_screencast(ScreencastId) returns (Void);
423+
424+ rpc configure_cursor(CursorSetting) returns (Void);
425 rpc new_fds_for_trusted_clients(SocketFDRequest) returns (SocketFD);
426 }
427
428=== modified file 'tests/acceptance-tests/CMakeLists.txt'
429--- tests/acceptance-tests/CMakeLists.txt 2014-05-07 14:00:57 +0000
430+++ tests/acceptance-tests/CMakeLists.txt 2014-05-09 20:09:14 +0000
431@@ -31,6 +31,7 @@
432 test_protobuf.cpp
433 test_client_screencast.cpp
434 test_client_surface_swap_buffers.cpp
435+ test_client_cursor_api.cpp
436 test_trust_session_helper.cpp
437 ${GENERATED_PROTOBUF_SRCS}
438 ${GENERATED_PROTOBUF_HDRS}
439
440=== added file 'tests/acceptance-tests/test_client_cursor_api.cpp'
441--- tests/acceptance-tests/test_client_cursor_api.cpp 1970-01-01 00:00:00 +0000
442+++ tests/acceptance-tests/test_client_cursor_api.cpp 2014-05-09 20:09:14 +0000
443@@ -0,0 +1,449 @@
444+/*
445+ * Copyright © 2013 Canonical Ltd.
446+ *
447+ * This program is free software: you can redistribute it and/or modify
448+ * it under the terms of the GNU General Public License version 3 as
449+ * published by the Free Software Foundation.
450+ *
451+ * This program is distributed in the hope that it will be useful,
452+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
453+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
454+ * GNU General Public License for more details.
455+ *
456+ * You should have received a copy of the GNU General Public License
457+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
458+ *
459+ * Authored by: Robert Carr <robert.carr@canonical.com>
460+ */
461+
462+#include "mir/graphics/cursor.h"
463+#include "mir/graphics/cursor_image.h"
464+#include "mir/graphics/cursor_images.h"
465+
466+#include "mir_toolkit/mir_client_library.h"
467+
468+#include "mir_test/fake_event_hub.h"
469+#include "mir_test/fake_shared.h"
470+#include "mir_test/event_factory.h"
471+#include "mir_test/wait_condition.h"
472+#include "mir_test_framework/display_server_test_fixture.h"
473+#include "mir_test_framework/input_testing_server_configuration.h"
474+#include "mir_test_framework/input_testing_client_configuration.h"
475+#include "mir_test_framework/declarative_placement_strategy.h"
476+#include "mir_test_framework/cross_process_sync.h"
477+
478+#include <gtest/gtest.h>
479+#include <gmock/gmock.h>
480+
481+#include <assert.h>
482+
483+namespace mg = mir::graphics;
484+namespace ms = mir::scene;
485+namespace msh = mir::shell;
486+namespace mis = mir::input::synthesis;
487+namespace geom = mir::geometry;
488+
489+namespace mt = mir::test;
490+namespace mtf = mir_test_framework;
491+
492+namespace
493+{
494+
495+struct MockCursor : public mg::Cursor
496+{
497+ MOCK_METHOD1(show, void(mg::CursorImage const&));
498+ MOCK_METHOD0(hide, void());
499+
500+ MOCK_METHOD1(move_to, void(geom::Point));
501+};
502+
503+struct NamedCursorImage : public mg::CursorImage
504+{
505+ NamedCursorImage(std::string const& name)
506+ : cursor_name(name)
507+ {
508+ }
509+
510+ void const* as_argb_8888() const { return nullptr; }
511+ geom::Size size() const { return geom::Size{}; }
512+
513+ std::string const cursor_name;
514+};
515+
516+struct StubCursorImages : public mg::CursorImages
517+{
518+ std::shared_ptr<mg::CursorImage> image(std::string const& name, geom::Size const& /* size */)
519+ {
520+ return std::make_shared<NamedCursorImage>(name);
521+ }
522+};
523+
524+bool cursor_is_named(mg::CursorImage const& i, std::string const& name)
525+{
526+ auto image = dynamic_cast<NamedCursorImage const*>(&i);
527+ assert(image);
528+
529+ return image->cursor_name == name;
530+}
531+
532+MATCHER(DefaultCursorImage, "")
533+{
534+ return cursor_is_named(arg, "default");
535+}
536+
537+MATCHER_P(CursorNamed, name, "")
538+{
539+ return cursor_is_named(arg, name);
540+}
541+
542+struct CursorSettingClient : mtf::TestingClientConfiguration
543+{
544+ static char const* const mir_test_socket;
545+
546+ std::string const client_name;
547+
548+ mtf::CrossProcessSync set_cursor_complete;
549+ mtf::CrossProcessSync client_may_exit;
550+
551+ std::function<void(MirSurface*)> const set_cursor;
552+
553+ CursorSettingClient(std::string const& client_name,
554+ mtf::CrossProcessSync const& cursor_ready_fence,
555+ mtf::CrossProcessSync const& client_may_exit_fence,
556+ std::function<void(MirSurface*)> const& set_cursor)
557+ : client_name(client_name),
558+ set_cursor_complete(cursor_ready_fence),
559+ client_may_exit(client_may_exit_fence),
560+ set_cursor(set_cursor)
561+ {
562+ }
563+
564+ void exec() override
565+ {
566+ auto connection = mir_connect_sync(mir_test_socket,
567+ client_name.c_str());
568+
569+ ASSERT_TRUE(connection != NULL);
570+ MirSurfaceParameters const request_params =
571+ {
572+ client_name.c_str(),
573+ // For this fixture, we force geometry on server side
574+ 0, 0,
575+ mir_pixel_format_abgr_8888,
576+ mir_buffer_usage_hardware,
577+ mir_display_output_id_invalid
578+ };
579+ auto surface = mir_connection_create_surface_sync(connection, &request_params);
580+
581+ set_cursor(surface);
582+ set_cursor_complete.signal_ready();
583+
584+ client_may_exit.wait_for_signal_ready_for();
585+
586+ mir_surface_release_sync(surface);
587+ mir_connection_release(connection);
588+ }
589+};
590+
591+char const* const CursorSettingClient::mir_test_socket = mtf::test_socket_file().c_str();
592+
593+
594+typedef unsigned ClientCount;
595+struct CursorTestServerConfiguration : mtf::InputTestingServerConfiguration
596+{
597+ std::shared_ptr<ms::PlacementStrategy> placement_strategy;
598+ mtf::CrossProcessSync client_ready_fence;
599+ mtf::CrossProcessSync client_may_exit_fence;
600+ int const number_of_clients;
601+
602+ std::function<void(MockCursor&, mt::WaitCondition&)> const expect_cursor_states;
603+ std::function<void(CursorTestServerConfiguration*)> const synthesize_cursor_motion;
604+
605+ MockCursor cursor;
606+
607+ CursorTestServerConfiguration(mtf::SurfaceGeometries surface_geometries_by_name,
608+ mtf::SurfaceDepths surface_depths_by_name,
609+ mtf::CrossProcessSync client_ready_fence,
610+ mtf::CrossProcessSync client_may_exit_fence,
611+ ClientCount const number_of_clients,
612+ std::function<void(MockCursor&, mt::WaitCondition&)> const& expect_cursor_states,
613+ std::function<void(CursorTestServerConfiguration*)> const& synthesize_cursor_motion)
614+ : placement_strategy(
615+ std::make_shared<mtf::DeclarativePlacementStrategy>(InputTestingServerConfiguration::the_placement_strategy(),
616+ surface_geometries_by_name, surface_depths_by_name)),
617+ client_ready_fence(client_ready_fence),
618+ client_may_exit_fence(client_may_exit_fence),
619+ number_of_clients(number_of_clients),
620+ expect_cursor_states(expect_cursor_states),
621+ synthesize_cursor_motion(synthesize_cursor_motion)
622+ {
623+ }
624+
625+ std::shared_ptr<ms::PlacementStrategy> the_placement_strategy() override
626+ {
627+ return placement_strategy;
628+ }
629+
630+ std::shared_ptr<mg::Cursor> the_cursor() override
631+ {
632+ return mt::fake_shared(cursor);
633+ }
634+
635+ std::shared_ptr<mg::CursorImages> the_cursor_images() override
636+ {
637+ return std::make_shared<StubCursorImages>();
638+ }
639+
640+ void inject_input()
641+ {
642+ using namespace ::testing;
643+
644+ for (int i = 1; i < number_of_clients + 1; i++)
645+ EXPECT_EQ(i, client_ready_fence.wait_for_signal_ready_for());
646+
647+ mt::WaitCondition expectations_satisfied;
648+
649+ // Clear any states applied during server initialization.
650+ Mock::VerifyAndClearExpectations(&cursor);
651+ expect_cursor_states(cursor, expectations_satisfied);
652+
653+ synthesize_cursor_motion(this);
654+ expectations_satisfied.wait_for_at_most_seconds(60);
655+
656+ EXPECT_CALL(cursor, show(_)).Times(AnyNumber()); // Client shutdown
657+ for (int i = 0; i < number_of_clients; i++)
658+ client_may_exit_fence.signal_ready();
659+ }
660+};
661+
662+}
663+
664+// TODO: A lot of common code setup in these tests could be moved to
665+// a fixture.
666+using TestClientCursorAPI = BespokeDisplayServerTestFixture;
667+
668+// In this set we create a 1x1 client surface at the point (1,0). The client requests to disable the cursor
669+// over this surface. Since the cursor starts at (0,0) we when we move the cursor by (1,0) thus causing it
670+// to enter the bounds of the first surface, we should observe it being disabled.
671+// TODO: Enable
672+TEST_F(TestClientCursorAPI, DISABLED_client_may_disable_cursor_over_surface)
673+{
674+ using namespace ::testing;
675+
676+ std::string const test_client_name = "1";
677+ mtf::SurfaceGeometries client_geometries;
678+ client_geometries[test_client_name] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
679+ geom::Size{geom::Width{1}, geom::Height{1}}};
680+
681+ mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;
682+
683+ CursorTestServerConfiguration server_conf(
684+ client_geometries, mtf::SurfaceDepths(),
685+ client_ready_fence, client_may_exit_fence,
686+ ClientCount{1},
687+ [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
688+ {
689+ EXPECT_CALL(cursor, hide()).Times(1)
690+ .WillOnce(mt::WakeUp(&expectations_satisfied));
691+ },
692+ [](CursorTestServerConfiguration *server)
693+ {
694+ server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
695+ });
696+ launch_server_process(server_conf);
697+
698+ CursorSettingClient client_conf(test_client_name, client_ready_fence, client_may_exit_fence,
699+ [](MirSurface *surface)
700+ {
701+ // Disable cursor
702+ mir_wait_for(mir_surface_configure_cursor(surface,
703+ mir_cursor_configuration_from_name(mir_disabled_cursor_name)));
704+ });
705+ launch_client_process(client_conf);
706+}
707+
708+// TODO: Enable
709+TEST_F(TestClientCursorAPI, DISABLED_cursor_restored_when_leaving_surface)
710+{
711+ using namespace ::testing;
712+
713+ std::string const test_client_name = "1";
714+ mtf::SurfaceGeometries client_geometries;
715+ client_geometries[test_client_name] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
716+ geom::Size{geom::Width{1}, geom::Height{1}}};
717+
718+ mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;
719+
720+ CursorTestServerConfiguration server_conf(
721+ client_geometries, mtf::SurfaceDepths(),
722+ client_ready_fence, client_may_exit_fence,
723+ ClientCount{1},
724+ [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
725+ {
726+ InSequence seq;
727+ EXPECT_CALL(cursor, hide()).Times(1);
728+ EXPECT_CALL(cursor, show(DefaultCursorImage())).Times(1)
729+ .WillOnce(mt::WakeUp(&expectations_satisfied));
730+ },
731+ [](CursorTestServerConfiguration *server)
732+ {
733+ server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
734+ server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(2,0));
735+ });
736+ launch_server_process(server_conf);
737+
738+ CursorSettingClient client_conf(test_client_name, client_ready_fence, client_may_exit_fence,
739+ [](MirSurface *surface)
740+ {
741+ // Disable cursor
742+ mir_wait_for(mir_surface_configure_cursor(surface,
743+ mir_cursor_configuration_from_name(mir_disabled_cursor_name)));
744+ });
745+ launch_client_process(client_conf);
746+}
747+
748+// TODO: Enable
749+TEST_F(TestClientCursorAPI, DISABLED_cursor_changed_when_crossing_surface_boundaries)
750+{
751+ using namespace ::testing;
752+
753+ static std::string const test_client_name_1 = "1";
754+ static std::string const test_client_name_2 = "2";
755+ static std::string const client_1_cursor = test_client_name_1;
756+ static std::string const client_2_cursor = test_client_name_2;
757+
758+ mtf::SurfaceGeometries client_geometries;
759+ client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
760+ geom::Size{geom::Width{1}, geom::Height{1}}};
761+ client_geometries[test_client_name_2] = geom::Rectangle{geom::Point{geom::X{2}, geom::Y{0}},
762+ geom::Size{geom::Width{1}, geom::Height{1}}};
763+
764+ mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;
765+
766+ CursorTestServerConfiguration server_conf(
767+ client_geometries, mtf::SurfaceDepths(),
768+ client_ready_fence, client_may_exit_fence,
769+ ClientCount{2},
770+ [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
771+ {
772+ InSequence seq;
773+ EXPECT_CALL(cursor, show(CursorNamed(client_1_cursor))).Times(1);
774+ EXPECT_CALL(cursor, show(CursorNamed(client_2_cursor))).Times(1)
775+ .WillOnce(mt::WakeUp(&expectations_satisfied));
776+ },
777+ [](CursorTestServerConfiguration *server)
778+ {
779+ server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
780+ server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
781+ });
782+ launch_server_process(server_conf);
783+
784+ CursorSettingClient client1_conf(test_client_name_1, client_ready_fence, client_may_exit_fence,
785+ [](MirSurface *surface)
786+ {
787+ mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str())));
788+ });
789+ launch_client_process(client1_conf);
790+ CursorSettingClient client2_conf(test_client_name_2, client_ready_fence, client_may_exit_fence,
791+ [](MirSurface *surface)
792+ {
793+ // Disable cursor
794+ mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_2_cursor.c_str())));
795+ });
796+ launch_client_process(client2_conf);
797+}
798+
799+// TODO: Enable
800+TEST_F(TestClientCursorAPI, DISABLED_cursor_request_taken_from_top_surface)
801+{
802+ using namespace ::testing;
803+
804+ static std::string const test_client_name_1 = "1";
805+ static std::string const test_client_name_2 = "2";
806+ static std::string const client_1_cursor = test_client_name_1;
807+ static std::string const client_2_cursor = test_client_name_2;
808+
809+ mtf::SurfaceGeometries client_geometries;
810+ client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
811+ geom::Size{geom::Width{1}, geom::Height{1}}};
812+ client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{1}, geom::Y{0}},
813+ geom::Size{geom::Width{1}, geom::Height{1}}};
814+ mtf::SurfaceDepths client_depths;
815+ client_depths[test_client_name_1] = ms::DepthId{0};
816+ client_depths[test_client_name_2] = ms::DepthId{1};
817+
818+ mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;
819+
820+ CursorTestServerConfiguration server_conf(
821+ client_geometries, client_depths,
822+ client_ready_fence, client_may_exit_fence,
823+ ClientCount{2},
824+ [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
825+ {
826+ InSequence seq;
827+ EXPECT_CALL(cursor, show(CursorNamed(client_2_cursor))).Times(1)
828+ .WillOnce(mt::WakeUp(&expectations_satisfied));
829+ },
830+ [](CursorTestServerConfiguration *server)
831+ {
832+ server->fake_event_hub->synthesize_event(mis::a_motion_event().with_movement(1, 0));
833+ });
834+ launch_server_process(server_conf);
835+
836+ CursorSettingClient client1_conf(test_client_name_1, client_ready_fence, client_may_exit_fence,
837+ [](MirSurface *surface)
838+ {
839+ mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str())));
840+ });
841+ launch_client_process(client1_conf);
842+ CursorSettingClient client2_conf(test_client_name_2, client_ready_fence, client_may_exit_fence,
843+ [](MirSurface *surface)
844+ {
845+ mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str())));
846+ });
847+
848+ launch_client_process(client2_conf);
849+}
850+
851+// TODO: Enable
852+TEST_F(TestClientCursorAPI, DISABLED_cursor_request_applied_without_cursor_motion)
853+{
854+ using namespace ::testing;
855+ static std::string const test_client_name_1 = "1";
856+ static std::string const client_1_cursor = test_client_name_1;
857+
858+ mtf::SurfaceGeometries client_geometries;
859+ client_geometries[test_client_name_1] = geom::Rectangle{geom::Point{geom::X{0}, geom::Y{0}},
860+ geom::Size{geom::Width{1}, geom::Height{1}}};
861+
862+ mtf::CrossProcessSync client_ready_fence, client_may_exit_fence;
863+ static mtf::CrossProcessSync client_may_change_cursor;
864+
865+ CursorTestServerConfiguration server_conf(
866+ client_geometries, mtf::SurfaceDepths(),
867+ client_ready_fence, client_may_exit_fence,
868+ ClientCount{1},
869+ [](MockCursor& cursor, mt::WaitCondition& expectations_satisfied)
870+ {
871+ InSequence seq;
872+ EXPECT_CALL(cursor, show(CursorNamed(client_1_cursor))).Times(1);
873+ EXPECT_CALL(cursor, hide()).Times(1)
874+ .WillOnce(mt::WakeUp(&expectations_satisfied));
875+ },
876+ [](CursorTestServerConfiguration * /* server */)
877+ {
878+ client_may_change_cursor.signal_ready();
879+ });
880+ launch_server_process(server_conf);
881+
882+ CursorSettingClient client1_conf(test_client_name_1, client_ready_fence, client_may_exit_fence,
883+ [&client_ready_fence](MirSurface *surface)
884+ {
885+ client_ready_fence.signal_ready();
886+ client_may_change_cursor.wait_for_signal_ready_for();
887+ mir_wait_for(mir_surface_configure_cursor(surface, mir_cursor_configuration_from_name(client_1_cursor.c_str())));
888+ mir_wait_for(mir_surface_configure_cursor(surface,
889+ mir_cursor_configuration_from_name(mir_disabled_cursor_name)));
890+ });
891+ launch_client_process(client1_conf);
892+}
893
894=== modified file 'tests/unit-tests/graphics/mesa/test_cursor.cpp'
895--- tests/unit-tests/graphics/mesa/test_cursor.cpp 2014-05-06 03:33:05 +0000
896+++ tests/unit-tests/graphics/mesa/test_cursor.cpp 2014-05-09 20:09:14 +0000
897@@ -246,7 +246,7 @@
898 std::make_shared<StubCursorImage>()};
899 }
900
901-TEST_F(MesaCursorTest, set_cursor_writes_to_bo)
902+TEST_F(MesaCursorTest, show_cursor_writes_to_bo)
903 {
904 using namespace testing;
905
906@@ -257,10 +257,10 @@
907
908 EXPECT_CALL(mock_gbm, gbm_bo_write(mock_gbm.fake_gbm.bo, StubCursorImage::image_data, cursor_size_bytes));
909
910- cursor.set_image(image);
911+ cursor.show(image);
912 }
913
914-TEST_F(MesaCursorTest, set_cursor_throws_on_incorrect_size)
915+TEST_F(MesaCursorTest, show_cursor_throws_on_incorrect_size)
916 {
917 using namespace testing;
918
919@@ -275,7 +275,7 @@
920 };
921
922 EXPECT_THROW(
923- cursor.set_image(InvalidlySizedCursorImage());
924+ cursor.show(InvalidlySizedCursorImage());
925 , std::logic_error);
926 }
927

Subscribers

People subscribed via source and target branches