Merge lp:~mir-team/mir/cursor-spike-phase-2-resubmit into lp:mir
- cursor-spike-phase-2-resubmit
- Merge into development-branch
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 |
Related bugs: |
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-
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1499
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
Failure looks unrelated.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1500
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Kevin DuBois (kdub) wrote : | # |
> Failure looks unrelated.
There's been a few of those floating around, haven't been able to figure out why
Kevin DuBois (kdub) wrote : | # |
mostly "needs info":
1) +MirWaitHandle* mir_surface_
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_
3) MirCursor is c++ code, why not std::string?
4) include guards on cursor_
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 MirCursorConfig
>> 2) is it difficult to add a mir_available_
I'll add this with the loader.
>> 3) MirCursor is c++ code, why not std::string?
>> 4) include guards on cursor_
Fixed!
Robert Carr (robertcarr) wrote : | # |
>> 2) is it difficult to add a mir_available_
>> I'll add this with the loader.
Or something of the sort at least.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1501
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
43 +void mir_cursor_
44 +
45 +/**
46 + * Returns a new MirCursorConfig
47 + * \return A cursor parameters object which must be passed
48 + * to mir_cursor_
49 + */
50 +MirCursorConfi
51 +
52 +/**
53 + * Returns a new MirCursorConfig
54 + * request.
55 + * \return A cursor parameters object which must be passed
56 + * to_mir_
57 + */
58 +MirCursorConfi
59 +
60 +/**
61 + * Returns a new MirCursorConfig
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_
67 + */
68 +MirCursorConfi
Is it really worth having 3 functions?
Couldn't we have one function and some default names? Vis:
extern char const *const mir_default_
extern char const *const mir_disabled_
MirCursorConfig
...
mir_cursor_
~~~~
200 +MirCursorConfi
201 +{
202 + auto c = new MirCursorConfig
203 + c->name = std::string(name);
204 +
205 + return c;
206 +}
This is a C API - but C++ memory allocations can throw.
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()
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1504
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1505
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1506
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
233 + {
234 + std::unique_
235 + setting.
236 + if (cursor->name != "")
237 + setting.
238 + }
indentation
~~~~
374 + BOOST_THROW_
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(
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.
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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1511
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1512
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alan Griffiths (alan-griffiths) wrote : | # |
OK for now
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_
287 + MirWaitHandle *result = NULL;
~~~
Use nullptr
~~~
487 +namespace
488 +{
489 + char const* const mir_test_socket = mtf::test_
490 +}
~~~
Perhaps define that as a static member variable of CursorSettingCl
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..
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.
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.
Daniel van Vugt (vanvugt) wrote : | # |
(1) I think "Configuration" is a vague word with respect to MirCursorConfig
158 +// Cursor parameterization. May grow to include RGBA cursors, etc...
159 +struct MirCursorConfig
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_
64 +MirCursorConfi
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_
The verb "configure" implies you're changing the cursor object, which is not true. More accurate would be "mir_surface_
(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 "MirCursorConfi
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-
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:
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-
> 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:
> 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-
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 :)
Robert Carr (robertcarr) wrote : | # |
Alberto/Andreas: Addressed comments.
>> (1) I think "Configuration" is a vague word with respect to MirCursorConfig
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_
>> (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_
I disagree. Perhaps more accurate would be mir_surface_
>> (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-
>> 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.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1516
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1520
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
(1) Reading again, I still believe there's no justification for MirCursorConfig
(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:/
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:
Robert Carr (robertcarr) wrote : | # |
>> (1) Reading again, I still believe there's no justification for MirCursorConfig
>> 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!)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1522
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
(1) Alright, MirCursorConfig
(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.
Robert Carr (robertcarr) wrote : | # |
Jenkins failure is intermittent and unrelated. Landing.
PS Jenkins bot (ps-jenkins) : | # |
Preview Diff
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 |
FAILED: Continuous integration, rev:1498 jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- ci/1437/ jenkins. qa.ubuntu. com/job/ mir-android- trusty- i386-build/ 1755/console jenkins. qa.ubuntu. com/job/ mir-clang- trusty- amd64-build/ 1753/console jenkins. qa.ubuntu. com/job/ mir-mediumtests -trusty- touch/1325/ console jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- amd64-ci/ 1169/console jenkins. qa.ubuntu. com/job/ mir-team- mir-development -branch- trusty- armhf-ci/ 1174/console jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- trusty- armhf/1328/ console
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- team-mir- development- branch- ci/1437/ rebuild
http://