Merge lp:~vanvugt/mir/managed-surface into lp:mir
- managed-surface
- Merge into development-branch
Status: | Work in progress |
---|---|
Proposed branch: | lp:~vanvugt/mir/managed-surface |
Merge into: | lp:mir |
Diff against target: |
1248 lines (+909/-47) 17 files modified
doc/demo_shell_controls.md (+5/-0) examples/eglapp.c (+11/-42) playground/demo-shell/window_manager.cpp (+41/-0) playground/demo-shell/window_manager.h (+4/-0) src/include/server/mir/scene/surface_wrapper.h (+83/-0) src/server/scene/CMakeLists.txt (+2/-0) src/server/scene/basic_surface.cpp (+3/-2) src/server/scene/default_configuration.cpp (+1/-0) src/server/scene/managed_surface.cpp (+161/-0) src/server/scene/managed_surface.h (+50/-0) src/server/scene/surface_controller.cpp (+11/-1) src/server/scene/surface_controller.h (+19/-0) src/server/scene/surface_wrapper.cpp (+229/-0) tests/include/mir_test_doubles/mock_surface.h (+5/-0) tests/unit-tests/scene/CMakeLists.txt (+1/-0) tests/unit-tests/scene/test_managed_surface.cpp (+249/-0) tests/unit-tests/scene/test_surface_controller.cpp (+34/-2) |
To merge this branch: | bzr merge lp:~vanvugt/mir/managed-surface |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Approve | |
Alan Griffiths | Disapprove | ||
Robert Carr (community) | Disapprove | ||
Alberto Aguirre (community) | Needs Fixing | ||
Review via email: mp+244542@code.launchpad.net |
Commit message
Introduce surface wrapping and ManagedSurface as a default wrapper to
implement common window management policy.
As a first example this branch implements all the surface states.
Now client calls to mir_surface_
have been added to the demo shell for minimize, maximize, fullscreen,
vertmaximize and restoring.
Description of the change
For those who do manual testing, beware you will run into some pre-existing bugs:
https:/
- 2181. By Daniel van Vugt
-
Fix clang SIGILL crash by avoiding int-enum pointer casting.
- 2182. By Daniel van Vugt
-
Merge latest trunk and fix a conflict.
- 2183. By Daniel van Vugt
-
Fix conflict properly.
PS Jenkins bot (ps-jenkins) wrote : | # |
Robert Carr (robertcarr) wrote : | # |
I wonder if it would be easier (avoiding "SurfaceWrapper"), to use a controller pattern,
e.g. ms::SurfaceCont
Eventually implementation of these methods will require access to a lot of system state. At present, the display layout is pulled in, you can see obviously how it blossoms though, e.g. choosing placement requires looking at existing surfaces.
Likewise it seems as if these managed surface instances may have to share state? for example if you wanted to implement behavior like only one surface can be fullscreen at a time.
I have the thought that it may be easier to encode all this in a centralized controller object. This would make the responsibilities more clear, surface stores the data, the controller object acts as a way to access surface which injects policy as well. What do you think?
Daniel van Vugt (vanvugt) wrote : | # |
Robert:
I spent over a week trying out the existing controller pattern and discussed it at several team meetings. The result was something that worked, but so ugly that I decided not to propose it (lp:~vanvugt/mir/states). So I spent another week on this one which actually solves a lot more problems, more elegantly.
This branch has added advantages over SurfaceController:
* You can't call a_scene_
* Any surface functionality can be modified by the shell in future this way.
* Any surface metadata can be neatly attached to a surface instance by the shell, without touching the core BasicSurface. e.g. the restore location and soon a lot more like decoration state, minimize location, animation state etc.
So this approach solves a whole bunch of problems. And admittedly even during my first prototype where everything was in SurfaceController, I was aware that these other problems would necessitate a surface wrapping approach eventually anyway.
P.S. Any number of surfaces can be full screen on normal desktops right now. While a shell can choose to restrict that, it would be unusual.
- 2184. By Daniel van Vugt
-
Merge latest trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2184
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
* You can't call a_scene_
Hmm true...
* Any surface functionality can be modified by the shell in future this way.
Kind of a blessing and a curse on this one though right? in the sense that total flexibility means you provide nothing.
* Any surface metadata can be neatly attached to a surface instance by the shell, without touching the core BasicSurface. e.g. the restore location and soon a lot more like decoration state, minimize location, animation state etc.
This could be solved by association...
I'll investigate your states branch though...still yet to form a full opinion.
Daniel van Vugt (vanvugt) wrote : | # |
This branch will conflict with lp:mir right now. But that should be resolved by:
https:/
Alan Griffiths (alan-griffiths) wrote : | # |
I'm uneasy about this approach without yet having clear ideas of whether alternatives are any better.
I'm not convinced that the scene surface should be wrapped nor that the wrapper belongs in scene. Wrapping suggests that there is more behavior to Surface than I feel is appropriate. Maybe some of the multiple "Surface" roles need splitting out.
Anyway, I'm rambling. I really don't have an opinion yet.
Robert Carr (robertcarr) wrote : | # |
I've looked through the states branch some.
I'm still inclined to lean towards the controller approach. The biggest wart I saw in the states branch was the drag_surface method on SurfaceController. I would have chosen an approach where the shell code implements what you've put in the surface controller (the shell would then just invoke surface_
In lack of any clear differentiator for controller v. wrapping I put the following arguments in favor of controller:
1. Easy to start with a smaller interface, e.g. no wrapper of "client_input_fd". Of course, you could call this a blessing or a curse (e.g. is the wrapper interface flexible or vague?).
2. Provides a nice level of decoupling, if shell code is using this controller interface then we gain some freedom on most of the surface interface (much of which could probably be removed.
3. I speculate (with a fair amount of confidence) that these wrapper objects will end up having to share data with eachother...however in the controller approach the data can remain as private controller state as opposed to psuedo-public data exposed through either the surface interface or the wrapper implementation class (and I guess the shell would dynamic_cast from the interface to wrapper impl).
As for the arguments in favor of surface wrapper
* You can't call a_scene_
I think this can be solved by API if we mandate use of a controller object by the shell.
* Any surface functionality can be modified by the shell in future this way.
This is the one which is discussed above as a "blessing/curse".
* Any surface metadata can be neatly attached to a surface instance by the shell, without touching the core BasicSurface. e.g. the restore location and soon a lot more like decoration state, minimize location, animation state etc.
I "prefer association and non member methods".
Daniel van Vugt (vanvugt) wrote : | # |
This branch has additional benefits you missed, over the controller branch:
* No inconsistent hacking of some state information (but not all) and policy into BasicSurface. The other branch required strange bespoke hacks in BasicSurface.
* This branch solves significantly more future problems which require attaching extra variables to each surface. Oh, maybe I mentioned that.
* This branch is dramatically cleaner in design than the other one. I've spent several weeks on both approaches and the other one was so ugly it made me depressed frankly. So I'm much more excited about this one and don't really want to think about the other branch.
Alan Griffiths (alan-griffiths) wrote : | # |
I definitely don't want to suggest that lp:~vanvugt/mir/states is better than this. But I don't like this either.
At the moment I feel that if this really is the best approach then there's something fundamental wrong with the Mir design. I think I'm going to have to try writing some code to get a feel for why more "obvious" solutions don't actually work.
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
It may work well for WM policy affecting only a single surface (e.g. minimize, maximize, etc) but for things that involve state(s) to be applied across a number of surfaces, I imagine there will have to be an encompassing "coordinator" object to oversee the policy is enforced.
I can see the ManagedSurface class morphing into that in the future. Of course, the name would have to change and it should no longer inherit from ms::surface.
I guess my question is how would then a policy involving multiple windows/surfaces be enforced with this approach?
Daniel van Vugt (vanvugt) wrote : | # |
Actually it's worked out happening backwards to how you describe. We already have a "coordinator" in SurfaceController. And I did start using just that in the previous "states" branch. However I found it to be inadequate for the reasons outlined above.
SurfaceController remains and is still used in this branch. But regardless of how much responsibility you want to give it, at some stage you do need to attach metadata to each surface, beyond what we call BasicSurface. And given we can't predict what every future shell will want to "attach" to each surface, it needs to be something extendible.
The absence of this feature has long been a problem for Unity8. They've had to resort to storing surface information in relatively convoluted ways. Having spoken to Gerry in the past he expressed a preference for this kind of solution. And even within Mir you can see a shell like mir_proving_server needs to attach some extra data to each surface if it wanted (for example) to implement animations (not something we want implemented in Mir but living in the shell code).
So you need to keep in mind two major goals here:
1. libmirserver implements some sane default window management policy, so that shells don't need to reinvent the wheel each time.
2. libmirserver needs to provide shells with a convenient mechanism of attaching their own member variables to each surface. Without the overhead of extra association containers to keep in sync.
The only reason Mir isn't yet superior to Compiz is because we haven't yet agreed on how to solve this problem (despite several solutions having been proposed). This being my third or fourth attempt at landing a solution to this problem, I believe this is by far the nicest solution yet. It addresses the main goals and is sufficiently elegant that I'm excited about building on it in future (instead of depressed as the "states" branch made me).
I'm always happy to accept a superior alternate solution. But it needs to be superior to what already exists.
Cemil Azizoglu (cemil-azizoglu) wrote : | # |
It's not a matter of having a superior solution. I'm trying to convince myself (or be convinced) that this is a solution at all for complex use cases involving metadata affecting multiple surfaces.
I don't disagree with shells wanting to attach per-surface metadata. I am more worried about metadata that is NOT per-surface. With this approach, such data would need to be replicated in each ManagedSurface instance. Also, there would have to be a "coordinator" to get the job done, which seems to be missing in this approach (or at least I'm not seeing it).
Perhaps you can provide a concrete example (code would be nice) of how such a complex policy involving multiple surfaces would be enforced. Perhaps a carousel WM that you can slide your finger to turn it to browse various app windows you have open. The surfaces would be rendered on the sides of the carousel. As the carousel is turning, I imagine you'd need to keep track of how much it has turned so you can continue to render your windows in their respective positions around the carousel's perimeter. But how much it has turned is not a per-surface info, and it's awkward to treat it as such.
Daniel van Vugt (vanvugt) wrote : | # |
> I don't disagree with shells wanting to attach per-surface metadata. I am more
> worried about metadata that is NOT per-surface. With this approach, such data
> would need to be replicated in each ManagedSurface instance. Also, there would
> have to be a "coordinator" to get the job done, which seems to be missing in
> this approach (or at least I'm not seeing it).
Absolutely that kind of thing will be required for some features. The example I like to think of is snapping ("magnetic") windows that like to align and attach to each other. But I don't intend to use ManagedSurface for everything, and don't intend on any "replication"
We do have a "coordinator" already in the pre-existing class "SurfaceControl
https:/
I think when the time comes that we need inter-surface behaviours, some of the wiring I prototyped in the above branch will be introduced.
Daniel van Vugt (vanvugt) wrote : | # |
We do many effects like a "carousel" in Compiz and the Mir equivalent of that is already planned in the form of Surface/
When more people make use of Mir's 3D transformations I suspect the class design might change to make it easier to use. Although a shell might decide to ignore it completely and transform everything itself (like Unity8).
Alan Griffiths (alan-griffiths) wrote : | # |
*Needs Discussion*
I think that my dislike of this and related branches comes from inconsistencies that have crept into the design of Mir.
We started Mir with the intent to have an MVC structure to our system with the model being the shared data representation of the system state, the views being subsystems like compositor, frontend and input and the controller being where the the shell plugs in.
But through a series of pragmatic decisions we've got to a point that is far away from there. There are items missing from the "model" we have in scene (like the output devices), there are things in the model that belong to the controller (like the surface state), there are interactions that bypass the controller and update the model (like the frontend creating surfaces on a session instead of forwarding the request to the shell).
It sort-of-works but it gets increasingly clunky and I think we can usefully rework towards the original intent.
This MP continues in this misguided direction by putting logic and data to handle surface state changes into the model (as ManagedSurface). It is like saying that because the walls of a building are not sound we'll fit a shelf by building a scaffold outside and mount the shelf on a pair of horizontals that come through the window.
I think the right approach is to fix the current architecture rather that trying to work around it. Clearly that implies we should first agree what needs fixing and how.
Robert Carr (robertcarr) wrote : | # |
I've tried to outline in improved-
I hate to feel like I am repeating myself a fourth or fifth time on the same topic but I will try and summarize my thoughts again for the benefit of EU/AUS time discussion:
1. Model-View-
2. As I've outlined in the tiling-
Robert Carr (robertcarr) wrote : | # |
3. I believe all the arguments in favor of this branch are bunk. I'll outline them trying to skip repetitions of the same argument:
3a. "You can't call a_scene_
This can be solved by API.
3b * Any surface functionality can be modified by the shell in future this way.
This is a tautology as an absolutism (define a C++ interface which prevents someone from doing something), and bad interface design as an ideal (maximal flexibility = doing nothing).
3c * Any surface metadata can be neatly attached to a surface instance by the shell, without touching the core BasicSurface. e.g. the restore location and soon a lot more like decoration state, minimize location, animation state etc.
This seems to be inventing a problem to justify a design. We all know this can be trivially solved by association.
3d * So I'm much more excited about this one and don't really want to think about the other branch.
I'm very excited about the improved tiling wm branch and don't really want to think about this branch.
Daniel van Vugt (vanvugt) wrote : | # |
Alan:
I share your concerns and agree with your statements. If there's a way for us to get to a cleaner design, then I don't mind implementing surface management logic a 4th/5th/6th time. I would like us to be able to land some small first steps, somehow.
Robert:
I think your arguments lack objective justification:
3. OK, demonstrate it in a way that does not make things more complex, again.
3b. Flexibility is a good thing. I aim for code reuse to make things easier in the future. And this has been proven successful in the code I have landed in Mir over the past 2 years.
3c. I am not "inventing a problem". The problem is real as firstly demonstrated below (ManagedSurface contains restore_rect, and in future more such data like minimize_rect). Secondly, it's been clear for a long time that one cannot implement animations for example without storing animation state somewhere. And you agree "somewhere" is required. Your suggested "somewhere" of association is a hack that is inelegant and difficult to maintain. Because a separate associative container not only needs to be kept in sync with the real list of surfaces, but also notified of all surface changes (ManagedSurface solves both these problems easily).
3d. The tiling branch shouldn't come into the discussion yet. It doesn't solve the same problems as this branch does. I remind you of the requirements I seek to fulfil:
1. libmirserver implements some sane default window management policy, so that shells don't need to reinvent the wheel each time.
2. libmirserver needs to provide shells with a convenient mechanism of attaching their own member variables to each surface. Without the overhead of extra association containers to keep in sync.
This branch solves both while the tiling branches solve neither (yet).
But a single branch doesn't need to solve both. If it makes people happier, I can divide this one up into smaller pieces.
Daniel van Vugt (vanvugt) wrote : | # |
It's worth noting that the window management logic is almost trivial in the grand scheme of things. It's 100 lines long (see 347-450), so 10% of the proposal. Everything else we're talking about here is architectural improvement and making Mir a better codebase for the future...
One can trivially implement those 100 lines of WM logic almost anywhere. The challenge is to make it elegant and reusable to solve other problems too. That's what I'm trying to do here.
- 2185. By Daniel van Vugt
-
Merge latest trunk
- 2186. By Daniel van Vugt
-
Merge latest trunk
- 2187. By Daniel van Vugt
-
Merge latest trunk
- 2188. By Daniel van Vugt
-
Merge latest trunk
- 2189. By Daniel van Vugt
-
Merge latest trunk
- 2190. By Daniel van Vugt
-
Merge latest trunk
- 2191. By Daniel van Vugt
-
Merge latest trunk
- 2192. By Daniel van Vugt
-
Merge latest trunk
- 2193. By Daniel van Vugt
-
Merge latest trunk
- 2194. By Daniel van Vugt
-
Merge latest trunk
- 2195. By Daniel van Vugt
-
Merge latest trunk
- 2196. By Daniel van Vugt
-
Merge latest trunk
- 2197. By Daniel van Vugt
-
Merge latest trunk and fix a conflict.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2197
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 2198. By Daniel van Vugt
-
Merge latest trunk
- 2199. By Daniel van Vugt
-
Merge latest trunk
- 2200. By Daniel van Vugt
-
Merge latest trunk and fix conflicts.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2200
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Alberto Aguirre (albaguirre) wrote : | # |
Surface wrapper looks like a convenient class to have for mir server clients (though a client could of course roll their own as it's a straightforward wrapper), though that implies the client is willing to use dynamic_cast for downcasting, instead of using other means like associative containers.
As for managed surface, I would prefer a cleaner delineation between model and window management code (which should mostly be in the controller side of things) instead of combining both controller+model data into one object.
Daniel van Vugt (vanvugt) wrote : | # |
I didn't choose the name SurfaceWrapper. I forget what I had before but Alan insisted on that name :)
As for pure MVC vs conventional object orientation, yeah I partially agree for some things, but not others. You can see elsewhere I'm trying to make the controller approach available too:
https:/
which works, and pre-dates this branch. Although I don't think everything would use that.
For example, we still need SurfaceWrapper (or something like it) to attach state to each surface from a bespoke shell, without needing external containers.
I did mention above three days ago that I'm willing to split this into smaller pieces that might better satisfy people in part. The only risk with smaller branches is that people (do) complain about not seeing the bigger picture to understand the intent.
- 2201. By Daniel van Vugt
-
Merge latest trunk
- 2202. By Daniel van Vugt
-
Add support for the newly introduced mir_surface_
state_horizmaxi mized - 2203. By Daniel van Vugt
-
More tests to cover the new horizmaximized semantics
- 2204. By Daniel van Vugt
-
Update docs
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2204
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Robert Carr (robertcarr) wrote : | # |
>> For example, we still need SurfaceWrapper (or something like it) to attach state to each surface
>> from a bespoke shell, without needing external containers.
I prefer this sentence backwards.
"For example, we still need a hash table (or something like it) to attach state to each surface from
a bespoke shell, without needing surface wrappers"
Daniel van Vugt (vanvugt) wrote : | # |
I think all this metadata can still be pointed to by a single instance of each surface object instead. We don't need to add new containers for that.
A hash table would obviously work, but it's neither the most efficient nor elegant solution. If you delete a surface for example, you'd also need to add all the logic to ensure the hash tables/maps you're maintaining in parallel get updated. Not to mention other methods during runtime. For example, if someone does set_state(foo) and your bespoke shell has an animation for foo, you need to have some wiring to initiate that. These are the future problems the approach proposed here solves.
While this branch is one example of achieving the goal without adding any new containers, there must be other ways too...
- 2205. By Daniel van Vugt
-
Merge latest trunk
- 2206. By Daniel van Vugt
-
Fix logical conflict between the new "hidden" state and default
minimize behaviour.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2206
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: 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 : | # |
BTW, if you want a pure controller approach please help me to make progress on that front too:
https:/
- 2207. By Daniel van Vugt
-
Merge latest trunk
- 2208. By Daniel van Vugt
-
Merge latest trunk
- 2209. By Daniel van Vugt
-
Merge latest trunk
- 2210. By Daniel van Vugt
-
Merge latest trunk
- 2211. By Daniel van Vugt
-
Fix a conflict from upstream changes.
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2211
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 2212. By Daniel van Vugt
-
Ping Jenkins
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2212
http://
Executed test runs:
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 2213. By Daniel van Vugt
-
Merge latest trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2213
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Daniel van Vugt (vanvugt) wrote : | # |
Bug 1413748 again. Unrelated to the proposal -- it's happening on all branches.
Alan Griffiths (alan-griffiths) wrote : | # |
> *Needs Discussion*
>
> I think that my dislike of this and related branches comes from
> inconsistencies that have crept into the design of Mir.
>
> We started Mir with the intent to have an MVC structure to our system with the
> model being the shared data representation of the system state, the views
> being subsystems like compositor, frontend and input and the controller being
> where the the shell plugs in.
>
> But through a series of pragmatic decisions we've got to a point that is far
> away from there. There are items missing from the "model" we have in scene
> (like the output devices), there are things in the model that belong to the
> controller (like the surface state), there are interactions that bypass the
> controller and update the model (like the frontend creating surfaces on a
> session instead of forwarding the request to the shell).
>
> It sort-of-works but it gets increasingly clunky and I think we can usefully
> rework towards the original intent.
>
> This MP continues in this misguided direction by putting logic and data to
> handle surface state changes into the model (as ManagedSurface). It is like
> saying that because the walls of a building are not sound we'll fit a shelf by
> building a scaffold outside and mount the shelf on a pair of horizontals that
> come through the window.
>
> I think the right approach is to fix the current architecture rather that
> trying to work around it. Clearly that implies we should first agree what
> needs fixing and how.
Discussions have been had and we're working ong fixing the architecture
- 2214. By Daniel van Vugt
-
Merge latest trunk.
Daniel van Vugt (vanvugt) wrote : | # |
OK, well more of the team might prefer MVC (although that's not yet certain). I'm a bit disappointed this branch isn't going anywhere after being up for review for 7 weeks.
Still, parts of this branch can be easily salvaged for future proposals.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:2214
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Unmerged revisions
- 2214. By Daniel van Vugt
-
Merge latest trunk.
- 2213. By Daniel van Vugt
-
Merge latest trunk
- 2212. By Daniel van Vugt
-
Ping Jenkins
- 2211. By Daniel van Vugt
-
Fix a conflict from upstream changes.
- 2210. By Daniel van Vugt
-
Merge latest trunk
- 2209. By Daniel van Vugt
-
Merge latest trunk
- 2208. By Daniel van Vugt
-
Merge latest trunk
- 2207. By Daniel van Vugt
-
Merge latest trunk
- 2206. By Daniel van Vugt
-
Fix logical conflict between the new "hidden" state and default
minimize behaviour. - 2205. By Daniel van Vugt
-
Merge latest trunk
Preview Diff
1 | === modified file 'doc/demo_shell_controls.md' |
2 | --- doc/demo_shell_controls.md 2015-01-16 02:57:31 +0000 |
3 | +++ doc/demo_shell_controls.md 2015-01-27 03:20:51 +0000 |
4 | @@ -46,5 +46,10 @@ |
5 | - Reset display mode to default, on focussed monitor: *Ctrl-Alt-0* |
6 | - Adjust window opacity/alpha: *Alt-mousewheel* |
7 | - Zoom in/out: *Super-mousewheel* |
8 | + - Minimize/restore the focussed window: *Alt-F9* |
9 | + - Maximize/restore the focussed window: *Alt-F10* |
10 | + - Fullscreen/restore the focussed window: *Alt-F11* |
11 | + - Vertically maximize/restore the focussed window: *Alt-F12* |
12 | + - Horizontally maximize/restore the focussed window: *Shift-Alt-F12* |
13 | |
14 | Want more? Log your requests at: https://bugs.launchpad.net/mir/+filebug |
15 | |
16 | === modified file 'examples/eglapp.c' |
17 | --- examples/eglapp.c 2015-01-21 00:49:28 +0000 |
18 | +++ examples/eglapp.c 2015-01-27 03:20:51 +0000 |
19 | @@ -155,29 +155,6 @@ |
20 | } |
21 | } |
22 | |
23 | -static const MirDisplayOutput *find_active_output( |
24 | - const MirDisplayConfiguration *conf) |
25 | -{ |
26 | - const MirDisplayOutput *output = NULL; |
27 | - int d; |
28 | - |
29 | - for (d = 0; d < (int)conf->num_outputs; d++) |
30 | - { |
31 | - const MirDisplayOutput *out = conf->outputs + d; |
32 | - |
33 | - if (out->used && |
34 | - out->connected && |
35 | - out->num_modes && |
36 | - out->current_mode < out->num_modes) |
37 | - { |
38 | - output = out; |
39 | - break; |
40 | - } |
41 | - } |
42 | - |
43 | - return output; |
44 | -} |
45 | - |
46 | mir_eglapp_bool mir_eglapp_init(int argc, char *argv[], |
47 | unsigned int *width, unsigned int *height) |
48 | { |
49 | @@ -199,6 +176,7 @@ |
50 | unsigned int output_id = mir_display_output_id_invalid; |
51 | char *mir_socket = NULL; |
52 | char const* cursor_name = mir_default_cursor_name; |
53 | + MirSurfaceState initial_state = mir_surface_state_restored; |
54 | |
55 | if (argc > 1) |
56 | { |
57 | @@ -256,8 +234,7 @@ |
58 | } |
59 | break; |
60 | case 'f': |
61 | - *width = 0; |
62 | - *height = 0; |
63 | + initial_state = mir_surface_state_fullscreen; |
64 | break; |
65 | case 's': |
66 | { |
67 | @@ -329,16 +306,6 @@ |
68 | MirDisplayConfiguration* display_config = |
69 | mir_connection_create_display_config(connection); |
70 | |
71 | - const MirDisplayOutput *output = find_active_output(display_config); |
72 | - |
73 | - if (output == NULL) |
74 | - { |
75 | - printf("No active outputs found.\n"); |
76 | - return 0; |
77 | - } |
78 | - |
79 | - const MirDisplayMode *mode = &output->modes[output->current_mode]; |
80 | - |
81 | unsigned int format[mir_pixel_formats]; |
82 | unsigned int nformats; |
83 | |
84 | @@ -360,14 +327,15 @@ |
85 | } |
86 | } |
87 | |
88 | - printf("Current active output is %dx%d %+d%+d\n", |
89 | - mode->horizontal_resolution, mode->vertical_resolution, |
90 | - output->position_x, output->position_y); |
91 | + if (*width == 0 && *height == 0) |
92 | + { |
93 | + initial_state = mir_surface_state_fullscreen; |
94 | |
95 | - if (*width == 0) |
96 | - *width = mode->horizontal_resolution; |
97 | - if (*height == 0) |
98 | - *height = mode->vertical_resolution; |
99 | + // These should be ignored due to the initial_state, but it's |
100 | + // unfortunately required that they're never zero: |
101 | + *width = 1; |
102 | + *height = 1; |
103 | + } |
104 | |
105 | mir_display_config_destroy(display_config); |
106 | |
107 | @@ -397,6 +365,7 @@ |
108 | |
109 | CHECK(mir_surface_is_valid(surface), "Can't create a surface"); |
110 | |
111 | + mir_surface_set_state(surface, initial_state); |
112 | mir_surface_set_event_handler(surface, &delegate); |
113 | |
114 | MirCursorConfiguration *conf = mir_cursor_configuration_from_name(cursor_name); |
115 | |
116 | === modified file 'playground/demo-shell/window_manager.cpp' |
117 | --- playground/demo-shell/window_manager.cpp 2015-01-21 21:08:50 +0000 |
118 | +++ playground/demo-shell/window_manager.cpp 2015-01-27 03:20:51 +0000 |
119 | @@ -130,6 +130,23 @@ |
120 | |
121 | } // namespace |
122 | |
123 | +void me::WindowManager::toggle(MirSurfaceState state) |
124 | +{ |
125 | + if (auto const app = focus_controller->focussed_application().lock()) |
126 | + { |
127 | + if (auto const surf = app->default_surface()) |
128 | + toggle(*surf, state); |
129 | + } |
130 | +} |
131 | + |
132 | +void me::WindowManager::toggle(scene::Surface& surface, MirSurfaceState state) |
133 | +{ |
134 | + auto new_state = state; |
135 | + if (new_state == surface.state()) |
136 | + new_state = mir_surface_state_restored; |
137 | + |
138 | + surface.configure(mir_surface_attrib_state, new_state); |
139 | +} |
140 | |
141 | void me::WindowManager::toggle(ColourEffect which) |
142 | { |
143 | @@ -217,6 +234,30 @@ |
144 | return true; |
145 | } |
146 | else if (event.key.modifiers & mir_key_modifier_alt && |
147 | + event.key.scan_code == KEY_F9) |
148 | + { |
149 | + toggle(mir_surface_state_minimized); |
150 | + } |
151 | + else if (event.key.modifiers & mir_key_modifier_alt && |
152 | + event.key.scan_code == KEY_F10) |
153 | + { |
154 | + toggle(mir_surface_state_maximized); |
155 | + } |
156 | + else if (event.key.modifiers & mir_key_modifier_alt && |
157 | + event.key.scan_code == KEY_F11) |
158 | + { |
159 | + toggle(mir_surface_state_fullscreen); |
160 | + } |
161 | + else if (event.key.modifiers & mir_key_modifier_alt && |
162 | + event.key.scan_code == KEY_F12) |
163 | + { |
164 | + MirSurfaceState state = |
165 | + event.key.modifiers & mir_key_modifier_shift ? |
166 | + mir_surface_state_horizmaximized : |
167 | + mir_surface_state_vertmaximized; |
168 | + toggle(state); |
169 | + } |
170 | + else if (event.key.modifiers & mir_key_modifier_alt && |
171 | event.key.scan_code == KEY_F4) |
172 | { |
173 | auto const app = focus_controller->focussed_application().lock(); |
174 | |
175 | === modified file 'playground/demo-shell/window_manager.h' |
176 | --- playground/demo-shell/window_manager.h 2015-01-20 10:07:15 +0000 |
177 | +++ playground/demo-shell/window_manager.h 2015-01-27 03:20:51 +0000 |
178 | @@ -23,6 +23,7 @@ |
179 | #include "mir/input/scene.h" |
180 | #include "mir/geometry/displacement.h" |
181 | #include "mir/geometry/size.h" |
182 | +#include "mir/scene/surface.h" |
183 | #include "demo_renderer.h" |
184 | |
185 | #include <memory> |
186 | @@ -66,6 +67,9 @@ |
187 | WindowManager& operator=(const WindowManager&) = delete; |
188 | |
189 | private: |
190 | + void toggle(MirSurfaceState state); |
191 | + void toggle(scene::Surface& surface, MirSurfaceState state); |
192 | + |
193 | std::shared_ptr<shell::FocusController> focus_controller; |
194 | std::shared_ptr<graphics::Display> display; |
195 | std::shared_ptr<compositor::Compositor> compositor; |
196 | |
197 | === added file 'src/include/server/mir/scene/surface_wrapper.h' |
198 | --- src/include/server/mir/scene/surface_wrapper.h 1970-01-01 00:00:00 +0000 |
199 | +++ src/include/server/mir/scene/surface_wrapper.h 2015-01-27 03:20:51 +0000 |
200 | @@ -0,0 +1,83 @@ |
201 | +/* |
202 | + * Copyright © 2014 Canonical Ltd. |
203 | + * |
204 | + * This program is free software: you can redistribute it and/or modify it |
205 | + * under the terms of the GNU General Public License version 3, |
206 | + * as published by the Free Software Foundation. |
207 | + * |
208 | + * This program is distributed in the hope that it will be useful, |
209 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
210 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
211 | + * GNU General Public License for more details. |
212 | + * |
213 | + * You should have received a copy of the GNU General Public License |
214 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
215 | + * |
216 | + * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> |
217 | + */ |
218 | + |
219 | +#ifndef MIR_SCENE_SURFACE_WRAPPER_H_ |
220 | +#define MIR_SCENE_SURFACE_WRAPPER_H_ |
221 | + |
222 | +#include "mir/scene/surface.h" |
223 | +#include <memory> |
224 | + |
225 | +namespace mir { namespace scene { |
226 | + |
227 | +class SurfaceWrapper : public scene::Surface |
228 | +{ |
229 | +public: |
230 | + SurfaceWrapper(std::shared_ptr<Surface> const& impl); |
231 | + virtual ~SurfaceWrapper(); |
232 | + |
233 | + // This might look like a long list, but the beauty of this is that once |
234 | + // you inherit from SurfaceWrapper, you will never have to remember |
235 | + // or maintain all these. Just override the function you need... |
236 | + |
237 | + virtual std::shared_ptr<mir::input::InputChannel> input_channel() const override; |
238 | + virtual mir::input::InputReceptionMode reception_mode() const override; |
239 | + virtual std::string name() const override; |
240 | + virtual geometry::Point top_left() const override; |
241 | + virtual geometry::Size client_size() const override; |
242 | + virtual geometry::Size size() const override; |
243 | + virtual geometry::Rectangle input_bounds() const override; |
244 | + virtual bool input_area_contains(mir::geometry::Point const&) const override; |
245 | + virtual std::unique_ptr<graphics::Renderable> compositor_snapshot(void const*) const override; |
246 | + virtual float alpha() const override; |
247 | + virtual MirSurfaceType type() const override; |
248 | + virtual MirSurfaceState state() const override; |
249 | + virtual void hide() override; |
250 | + virtual void show() override; |
251 | + virtual bool visible() const override; |
252 | + virtual void move_to(geometry::Point const&) override; |
253 | + virtual void take_input_focus(std::shared_ptr<shell::InputTargeter> const&) override; |
254 | + virtual void set_input_region(std::vector<geometry::Rectangle> const&) override; |
255 | + virtual void allow_framedropping(bool) override; |
256 | + virtual void resize(geometry::Size const&) override; |
257 | + virtual void set_transformation(glm::mat4 const&) override; |
258 | + virtual void set_alpha(float) override; |
259 | + virtual void set_orientation(MirOrientation) override; |
260 | + virtual void force_requests_to_complete() override; |
261 | + virtual void add_observer(std::shared_ptr<SurfaceObserver> const&) override; |
262 | + virtual void remove_observer(std::weak_ptr<SurfaceObserver> const&) override; |
263 | + virtual void set_reception_mode(input::InputReceptionMode mode) override; |
264 | + virtual void consume(MirEvent const&) override; |
265 | + virtual void set_cursor_image(std::shared_ptr<graphics::CursorImage> const&) override; |
266 | + virtual std::shared_ptr<graphics::CursorImage> cursor_image() const override; |
267 | + virtual void request_client_surface_close() override; |
268 | + virtual MirPixelFormat pixel_format() const override; |
269 | + virtual void swap_buffers(graphics::Buffer*, std::function<void(graphics::Buffer*)>) override; |
270 | + virtual bool supports_input() const override; |
271 | + virtual int client_input_fd() const override; |
272 | + virtual int configure(MirSurfaceAttrib, int) override; |
273 | + virtual int query(MirSurfaceAttrib) override; |
274 | + virtual void with_most_recent_buffer_do(std::function<void(graphics::Buffer&)> const& ) override; |
275 | + virtual std::shared_ptr<Surface> parent() const override; |
276 | + |
277 | +protected: |
278 | + std::shared_ptr<Surface> const raw_surface; |
279 | +}; |
280 | + |
281 | +}} // namespace mir::scene |
282 | + |
283 | +#endif // MIR_SCENE_SURFACE_WRAPPER_H_ |
284 | |
285 | === modified file 'src/server/scene/CMakeLists.txt' |
286 | --- src/server/scene/CMakeLists.txt 2015-01-14 06:39:13 +0000 |
287 | +++ src/server/scene/CMakeLists.txt 2015-01-27 03:20:51 +0000 |
288 | @@ -25,4 +25,6 @@ |
289 | prompt_session_manager_impl.cpp |
290 | rendering_tracker.cpp |
291 | default_coordinate_translator.cpp |
292 | + surface_wrapper.cpp |
293 | + managed_surface.cpp |
294 | ) |
295 | |
296 | === modified file 'src/server/scene/basic_surface.cpp' |
297 | --- src/server/scene/basic_surface.cpp 2015-01-22 03:10:13 +0000 |
298 | +++ src/server/scene/basic_surface.cpp 2015-01-27 03:20:51 +0000 |
299 | @@ -367,7 +367,9 @@ |
300 | |
301 | bool ms::BasicSurface::visible(std::unique_lock<std::mutex>&) const |
302 | { |
303 | - return !hidden && first_frame_posted; |
304 | + return !hidden |
305 | + && first_frame_posted |
306 | + && state_ != mir_surface_state_hidden; // not overridable (?) |
307 | } |
308 | |
309 | mi::InputReceptionMode ms::BasicSurface::reception_mode() const |
310 | @@ -435,7 +437,6 @@ |
311 | { |
312 | state_ = s; |
313 | lg.unlock(); |
314 | - set_hidden(s == mir_surface_state_hidden); |
315 | |
316 | observers.attrib_changed(mir_surface_attrib_state, s); |
317 | } |
318 | |
319 | === modified file 'src/server/scene/default_configuration.cpp' |
320 | --- src/server/scene/default_configuration.cpp 2015-01-19 17:20:10 +0000 |
321 | +++ src/server/scene/default_configuration.cpp 2015-01-27 03:20:51 +0000 |
322 | @@ -90,6 +90,7 @@ |
323 | std::make_shared<ms::SurfaceController>( |
324 | the_surface_factory(), |
325 | the_placement_strategy(), |
326 | + the_shell_display_layout(), |
327 | the_surface_stack_model())); |
328 | }); |
329 | } |
330 | |
331 | === added file 'src/server/scene/managed_surface.cpp' |
332 | --- src/server/scene/managed_surface.cpp 1970-01-01 00:00:00 +0000 |
333 | +++ src/server/scene/managed_surface.cpp 2015-01-27 03:20:51 +0000 |
334 | @@ -0,0 +1,161 @@ |
335 | +/* |
336 | + * Copyright © 2014 Canonical Ltd. |
337 | + * |
338 | + * This program is free software: you can redistribute it and/or modify it |
339 | + * under the terms of the GNU General Public License version 3, |
340 | + * as published by the Free Software Foundation. |
341 | + * |
342 | + * This program is distributed in the hope that it will be useful, |
343 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
344 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
345 | + * GNU General Public License for more details. |
346 | + * |
347 | + * You should have received a copy of the GNU General Public License |
348 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
349 | + * |
350 | + * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> |
351 | + */ |
352 | + |
353 | +#include "managed_surface.h" |
354 | + |
355 | +namespace mir { namespace scene { |
356 | + |
357 | +ManagedSurface::ManagedSurface(std::shared_ptr<Surface> const& raw, |
358 | + std::shared_ptr<shell::DisplayLayout> const& layout) |
359 | + : SurfaceWrapper(raw) |
360 | + , display_layout(layout) |
361 | + , restore_rect{raw->top_left(), raw->size()} |
362 | +{ |
363 | +} |
364 | + |
365 | +ManagedSurface::~ManagedSurface() |
366 | +{ |
367 | +} |
368 | + |
369 | +int ManagedSurface::configure(MirSurfaceAttrib attrib, int value) |
370 | +{ |
371 | + int new_value = value; |
372 | + |
373 | + if (attrib == mir_surface_attrib_state) |
374 | + new_value = set_state(static_cast<MirSurfaceState>(value)); |
375 | + |
376 | + return SurfaceWrapper::configure(attrib, new_value); |
377 | +} |
378 | + |
379 | +MirSurfaceState ManagedSurface::set_state(MirSurfaceState desired) |
380 | +{ |
381 | + // TODO: Eventually this whole function should be atomic (LP: #1395957) |
382 | + geometry::Rectangle old_win{top_left(), size()}, new_win = old_win; |
383 | + |
384 | + auto fullscreen = old_win; |
385 | + display_layout->size_to_output(fullscreen); |
386 | + |
387 | + // TODO: Shell should define workarea to exclude panels/launchers/docks |
388 | + auto workarea = fullscreen; |
389 | + |
390 | + auto old_state = state(); |
391 | + if (old_state != desired) |
392 | + { |
393 | + switch (old_state) |
394 | + { |
395 | + case mir_surface_state_minimized: |
396 | + show(); |
397 | + break; |
398 | + case mir_surface_state_restored: |
399 | + restore_rect = old_win; |
400 | + break; |
401 | + default: |
402 | + break; |
403 | + } |
404 | + } |
405 | + |
406 | + switch (desired) |
407 | + { |
408 | + case mir_surface_state_fullscreen: |
409 | + new_win = fullscreen; |
410 | + break; |
411 | + case mir_surface_state_maximized: |
412 | + new_win = workarea; |
413 | + break; |
414 | + case mir_surface_state_vertmaximized: |
415 | + new_win.top_left.y = workarea.top_left.y; |
416 | + new_win.size.height = workarea.size.height; |
417 | + break; |
418 | + case mir_surface_state_horizmaximized: |
419 | + new_win.top_left.x = workarea.top_left.x; |
420 | + new_win.size.width = workarea.size.width; |
421 | + break; |
422 | + case mir_surface_state_restored: |
423 | + new_win = restore_rect; |
424 | + break; |
425 | + case mir_surface_state_minimized: |
426 | + hide(); |
427 | + break; |
428 | + default: |
429 | + break; |
430 | + } |
431 | + |
432 | + if (new_win != old_win) |
433 | + { |
434 | + /* |
435 | + * Important: Call to parent class move_to/resize functions. |
436 | + * We don't want policy enforcement of this class getting in the way |
437 | + * here because our own versions will switch based on current state(), |
438 | + * which is incorrect behaviour for the 'desired' new state. |
439 | + */ |
440 | + SurfaceWrapper::move_to(new_win.top_left); |
441 | + SurfaceWrapper::resize(new_win.size); |
442 | + } |
443 | + |
444 | + // TODO: In future the desired state may be rejected based on other |
445 | + // factors such as surface type. |
446 | + return desired; |
447 | +} |
448 | + |
449 | +void ManagedSurface::move_to(geometry::Point const& desired) |
450 | +{ |
451 | + // TODO: Eventually this whole function should be atomic (LP: #1395957) |
452 | + auto new_pos = desired; |
453 | + |
454 | + switch (state()) |
455 | + { |
456 | + case mir_surface_state_fullscreen: |
457 | + case mir_surface_state_maximized: |
458 | + return; |
459 | + case mir_surface_state_vertmaximized: |
460 | + new_pos.y = top_left().y; |
461 | + break; |
462 | + case mir_surface_state_horizmaximized: |
463 | + new_pos.x = top_left().x; |
464 | + break; |
465 | + default: |
466 | + break; |
467 | + } |
468 | + |
469 | + SurfaceWrapper::move_to(new_pos); |
470 | +} |
471 | + |
472 | +void ManagedSurface::resize(geometry::Size const& desired) |
473 | +{ |
474 | + // TODO: Eventually this whole function should be atomic (LP: #1395957) |
475 | + auto new_size = desired; |
476 | + |
477 | + switch (state()) |
478 | + { |
479 | + case mir_surface_state_fullscreen: |
480 | + case mir_surface_state_maximized: |
481 | + return; |
482 | + case mir_surface_state_vertmaximized: |
483 | + new_size.height = size().height; |
484 | + break; |
485 | + case mir_surface_state_horizmaximized: |
486 | + new_size.width = size().width; |
487 | + break; |
488 | + default: |
489 | + break; |
490 | + } |
491 | + |
492 | + SurfaceWrapper::resize(new_size); |
493 | +} |
494 | + |
495 | +}} // namespace mir::scene |
496 | |
497 | === added file 'src/server/scene/managed_surface.h' |
498 | --- src/server/scene/managed_surface.h 1970-01-01 00:00:00 +0000 |
499 | +++ src/server/scene/managed_surface.h 2015-01-27 03:20:51 +0000 |
500 | @@ -0,0 +1,50 @@ |
501 | +/* |
502 | + * Copyright © 2014 Canonical Ltd. |
503 | + * |
504 | + * This program is free software: you can redistribute it and/or modify it |
505 | + * under the terms of the GNU General Public License version 3, |
506 | + * as published by the Free Software Foundation. |
507 | + * |
508 | + * This program is distributed in the hope that it will be useful, |
509 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
510 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
511 | + * GNU General Public License for more details. |
512 | + * |
513 | + * You should have received a copy of the GNU General Public License |
514 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
515 | + * |
516 | + * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> |
517 | + */ |
518 | + |
519 | +#ifndef MIR_SCENE_MANAGED_SURFACE_H_ |
520 | +#define MIR_SCENE_MANAGED_SURFACE_H_ |
521 | + |
522 | +#include "mir/scene/surface_wrapper.h" |
523 | +#include "mir/shell/display_layout.h" |
524 | +#include "mir/geometry/rectangle.h" |
525 | +#include "mir_toolkit/common.h" |
526 | +#include <memory> |
527 | + |
528 | +namespace mir { namespace scene { |
529 | + |
530 | +class ManagedSurface : public SurfaceWrapper |
531 | +{ |
532 | +public: |
533 | + ManagedSurface(std::shared_ptr<Surface> const&, |
534 | + std::shared_ptr<shell::DisplayLayout> const&); |
535 | + virtual ~ManagedSurface(); |
536 | + |
537 | + int configure(MirSurfaceAttrib attrib, int value) override; |
538 | + void move_to(geometry::Point const&) override; |
539 | + void resize(geometry::Size const&) override; |
540 | + |
541 | +private: |
542 | + std::shared_ptr<shell::DisplayLayout> const display_layout; |
543 | + geometry::Rectangle restore_rect; |
544 | + |
545 | + MirSurfaceState set_state(MirSurfaceState state); |
546 | +}; |
547 | + |
548 | +}} // namespace mir::scene |
549 | + |
550 | +#endif // MIR_SCENE_MANAGED_SURFACE_H_ |
551 | |
552 | === modified file 'src/server/scene/surface_controller.cpp' |
553 | --- src/server/scene/surface_controller.cpp 2015-01-14 06:39:13 +0000 |
554 | +++ src/server/scene/surface_controller.cpp 2015-01-27 03:20:51 +0000 |
555 | @@ -21,26 +21,36 @@ |
556 | #include "mir/scene/surface_factory.h" |
557 | #include "mir/scene/surface.h" |
558 | #include "mir/scene/placement_strategy.h" |
559 | +#include "mir/shell/display_layout.h" |
560 | +#include "managed_surface.h" |
561 | |
562 | namespace ms = mir::scene; |
563 | |
564 | ms::SurfaceController::SurfaceController( |
565 | std::shared_ptr<SurfaceFactory> const& surface_factory, |
566 | std::shared_ptr<ms::PlacementStrategy> const& placement_strategy, |
567 | + std::shared_ptr<shell::DisplayLayout> const& display_layout, |
568 | std::shared_ptr<SurfaceStackModel> const& surface_stack) : |
569 | surface_factory(surface_factory), |
570 | placement_strategy(placement_strategy), |
571 | + display_layout(display_layout), |
572 | surface_stack(surface_stack) |
573 | { |
574 | } |
575 | |
576 | +std::shared_ptr<ms::Surface> ms::SurfaceController::wrap_surface( |
577 | + std::shared_ptr<ms::Surface> const& raw) |
578 | +{ |
579 | + return std::make_shared<ManagedSurface>(raw, display_layout); |
580 | +} |
581 | + |
582 | std::shared_ptr<ms::Surface> ms::SurfaceController::add_surface( |
583 | SurfaceCreationParameters const& params, |
584 | Session* session) |
585 | { |
586 | auto placed_params = placement_strategy->place(*session, params); |
587 | |
588 | - auto const surface = surface_factory->create_surface(placed_params); |
589 | + auto const surface = wrap_surface(surface_factory->create_surface(placed_params)); |
590 | surface_stack->add_surface(surface, placed_params.depth, placed_params.input_mode); |
591 | return surface; |
592 | } |
593 | |
594 | === modified file 'src/server/scene/surface_controller.h' |
595 | --- src/server/scene/surface_controller.h 2015-01-14 06:39:13 +0000 |
596 | +++ src/server/scene/surface_controller.h 2015-01-27 03:20:51 +0000 |
597 | @@ -24,6 +24,9 @@ |
598 | |
599 | namespace mir |
600 | { |
601 | + |
602 | +namespace shell { class DisplayLayout; } |
603 | + |
604 | namespace scene |
605 | { |
606 | class PlacementStrategy; |
607 | @@ -37,6 +40,7 @@ |
608 | SurfaceController( |
609 | std::shared_ptr<SurfaceFactory> const& surface_factory, |
610 | std::shared_ptr<PlacementStrategy> const& placement_strategy, |
611 | + std::shared_ptr<shell::DisplayLayout> const& display_layout, |
612 | std::shared_ptr<SurfaceStackModel> const& surface_stack); |
613 | |
614 | std::shared_ptr<Surface> add_surface( |
615 | @@ -47,9 +51,24 @@ |
616 | |
617 | void raise(std::weak_ptr<Surface> const& surface) override; |
618 | |
619 | + /** |
620 | + * wrap_surface allows you wrap ("decorate") a surface in your own |
621 | + * window management policy-enforcing class. It's important to keep |
622 | + * the wrapping stage out of SurfaceFactory, so that a shell may choose |
623 | + * to both partly reuse the default wrapper (ManagedSurface) for |
624 | + * window management logic while also retaining access to the underlying |
625 | + * BasicSurface if it wants to do anything that would be disallowed by |
626 | + * the default policy of ManagedSurface. |
627 | + * TODO: Move this up to SurfaceCoordinator (or some bespoke factory) |
628 | + * in the public API later. |
629 | + */ |
630 | + virtual std::shared_ptr<Surface> |
631 | + wrap_surface(std::shared_ptr<Surface> const& raw); |
632 | + |
633 | private: |
634 | std::shared_ptr<SurfaceFactory> const surface_factory; |
635 | std::shared_ptr<PlacementStrategy> const placement_strategy; |
636 | + std::shared_ptr<shell::DisplayLayout> const display_layout; |
637 | std::shared_ptr<SurfaceStackModel> const surface_stack; |
638 | }; |
639 | |
640 | |
641 | === added file 'src/server/scene/surface_wrapper.cpp' |
642 | --- src/server/scene/surface_wrapper.cpp 1970-01-01 00:00:00 +0000 |
643 | +++ src/server/scene/surface_wrapper.cpp 2015-01-27 03:20:51 +0000 |
644 | @@ -0,0 +1,229 @@ |
645 | +/* |
646 | + * Copyright © 2014 Canonical Ltd. |
647 | + * |
648 | + * This program is free software: you can redistribute it and/or modify it |
649 | + * under the terms of the GNU General Public License version 3, |
650 | + * as published by the Free Software Foundation. |
651 | + * |
652 | + * This program is distributed in the hope that it will be useful, |
653 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
654 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
655 | + * GNU General Public License for more details. |
656 | + * |
657 | + * You should have received a copy of the GNU General Public License |
658 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
659 | + * |
660 | + * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> |
661 | + */ |
662 | + |
663 | +#include "mir/scene/surface_wrapper.h" |
664 | + |
665 | +namespace mir { namespace scene { |
666 | + |
667 | +SurfaceWrapper::SurfaceWrapper(std::shared_ptr<Surface> const& impl) |
668 | + : raw_surface(impl) |
669 | +{ |
670 | +} |
671 | + |
672 | +SurfaceWrapper::~SurfaceWrapper() |
673 | +{ |
674 | +} |
675 | + |
676 | +std::shared_ptr<mir::input::InputChannel> SurfaceWrapper::input_channel() const |
677 | +{ |
678 | + return raw_surface->input_channel(); |
679 | +} |
680 | + |
681 | +mir::input::InputReceptionMode SurfaceWrapper::reception_mode() const |
682 | +{ |
683 | + return raw_surface->reception_mode(); |
684 | +} |
685 | + |
686 | +std::string SurfaceWrapper::name() const |
687 | +{ |
688 | + return raw_surface->name(); |
689 | +} |
690 | + |
691 | +geometry::Point SurfaceWrapper::top_left() const |
692 | +{ |
693 | + return raw_surface->top_left(); |
694 | +} |
695 | + |
696 | +geometry::Size SurfaceWrapper::client_size() const |
697 | +{ |
698 | + return raw_surface->client_size(); |
699 | +} |
700 | + |
701 | +geometry::Size SurfaceWrapper::size() const |
702 | +{ |
703 | + return raw_surface->size(); |
704 | +} |
705 | + |
706 | +geometry::Rectangle SurfaceWrapper::input_bounds() const |
707 | +{ |
708 | + return raw_surface->input_bounds(); |
709 | +} |
710 | + |
711 | +bool SurfaceWrapper::input_area_contains(mir::geometry::Point const& p) const |
712 | +{ |
713 | + return raw_surface->input_area_contains(p); |
714 | +} |
715 | + |
716 | +std::unique_ptr<graphics::Renderable> SurfaceWrapper::compositor_snapshot(void const* id) const |
717 | +{ |
718 | + return raw_surface->compositor_snapshot(id); |
719 | +} |
720 | + |
721 | +float SurfaceWrapper::alpha() const |
722 | +{ |
723 | + return raw_surface->alpha(); |
724 | +} |
725 | + |
726 | +MirSurfaceType SurfaceWrapper::type() const |
727 | +{ |
728 | + return raw_surface->type(); |
729 | +} |
730 | + |
731 | +MirSurfaceState SurfaceWrapper::state() const |
732 | +{ |
733 | + return raw_surface->state(); |
734 | +} |
735 | + |
736 | +void SurfaceWrapper::hide() |
737 | +{ |
738 | + raw_surface->hide(); |
739 | +} |
740 | + |
741 | +void SurfaceWrapper::show() |
742 | +{ |
743 | + raw_surface->show(); |
744 | +} |
745 | + |
746 | +bool SurfaceWrapper::visible() const |
747 | +{ |
748 | + return raw_surface->visible(); |
749 | +} |
750 | + |
751 | +void SurfaceWrapper::move_to(geometry::Point const& p) |
752 | +{ |
753 | + raw_surface->move_to(p); |
754 | +} |
755 | + |
756 | +void SurfaceWrapper::take_input_focus(std::shared_ptr<shell::InputTargeter> const& t) |
757 | +{ |
758 | + raw_surface->take_input_focus(t); |
759 | +} |
760 | + |
761 | +void SurfaceWrapper::set_input_region(std::vector<geometry::Rectangle> const& r) |
762 | +{ |
763 | + raw_surface->set_input_region(r); |
764 | +} |
765 | + |
766 | +void SurfaceWrapper::allow_framedropping(bool b) |
767 | +{ |
768 | + raw_surface->allow_framedropping(b); |
769 | +} |
770 | + |
771 | +void SurfaceWrapper::resize(geometry::Size const& s) |
772 | +{ |
773 | + raw_surface->resize(s); |
774 | +} |
775 | + |
776 | +void SurfaceWrapper::set_transformation(glm::mat4 const& t) |
777 | +{ |
778 | + raw_surface->set_transformation(t); |
779 | +} |
780 | + |
781 | +void SurfaceWrapper::set_alpha(float a) |
782 | +{ |
783 | + raw_surface->set_alpha(a); |
784 | +} |
785 | + |
786 | +void SurfaceWrapper::set_orientation(MirOrientation orientation) |
787 | +{ |
788 | + raw_surface->set_orientation(orientation); |
789 | +} |
790 | + |
791 | +void SurfaceWrapper::force_requests_to_complete() |
792 | +{ |
793 | + raw_surface->force_requests_to_complete(); |
794 | +} |
795 | + |
796 | +void SurfaceWrapper::add_observer(std::shared_ptr<SurfaceObserver> const& ob) |
797 | +{ |
798 | + raw_surface->add_observer(ob); |
799 | +} |
800 | + |
801 | +void SurfaceWrapper::remove_observer(std::weak_ptr<SurfaceObserver> const& ob) |
802 | +{ |
803 | + raw_surface->remove_observer(ob); |
804 | +} |
805 | + |
806 | +void SurfaceWrapper::set_reception_mode(input::InputReceptionMode mode) |
807 | +{ |
808 | + raw_surface->set_reception_mode(mode); |
809 | +} |
810 | + |
811 | +void SurfaceWrapper::consume(MirEvent const& e) |
812 | +{ |
813 | + raw_surface->consume(e); |
814 | +} |
815 | + |
816 | +void SurfaceWrapper::set_cursor_image(std::shared_ptr<graphics::CursorImage> const& i) |
817 | +{ |
818 | + raw_surface->set_cursor_image(i); |
819 | +} |
820 | + |
821 | +std::shared_ptr<graphics::CursorImage> SurfaceWrapper::cursor_image() const |
822 | +{ |
823 | + return raw_surface->cursor_image(); |
824 | +} |
825 | + |
826 | +void SurfaceWrapper::request_client_surface_close() |
827 | +{ |
828 | + raw_surface->request_client_surface_close(); |
829 | +} |
830 | + |
831 | +MirPixelFormat SurfaceWrapper::pixel_format() const |
832 | +{ |
833 | + return raw_surface->pixel_format(); |
834 | +} |
835 | + |
836 | +void SurfaceWrapper::swap_buffers(graphics::Buffer* old_buffer, |
837 | + std::function<void(graphics::Buffer*)> callback) |
838 | +{ |
839 | + raw_surface->swap_buffers(old_buffer, callback); |
840 | +} |
841 | + |
842 | +bool SurfaceWrapper::supports_input() const |
843 | +{ |
844 | + return raw_surface->supports_input(); |
845 | +} |
846 | + |
847 | +int SurfaceWrapper::client_input_fd() const |
848 | +{ |
849 | + return raw_surface->client_input_fd(); |
850 | +} |
851 | + |
852 | +int SurfaceWrapper::configure(MirSurfaceAttrib a, int v) |
853 | +{ |
854 | + return raw_surface->configure(a, v); |
855 | +} |
856 | + |
857 | +int SurfaceWrapper::query(MirSurfaceAttrib a) |
858 | +{ |
859 | + return raw_surface->query(a); |
860 | +} |
861 | + |
862 | +void SurfaceWrapper::with_most_recent_buffer_do( |
863 | + std::function<void(graphics::Buffer&)> const& callback) |
864 | +{ |
865 | + raw_surface->with_most_recent_buffer_do(callback); |
866 | +} |
867 | + |
868 | +std::shared_ptr<Surface> SurfaceWrapper::parent() const |
869 | +{ |
870 | + return raw_surface->parent(); |
871 | +} |
872 | + |
873 | +}} // namespace mir::scene |
874 | |
875 | === modified file 'tests/include/mir_test_doubles/mock_surface.h' |
876 | --- tests/include/mir_test_doubles/mock_surface.h 2015-01-14 06:39:13 +0000 |
877 | +++ tests/include/mir_test_doubles/mock_surface.h 2015-01-27 03:20:51 +0000 |
878 | @@ -57,6 +57,11 @@ |
879 | MOCK_METHOD0(force_requests_to_complete, void()); |
880 | MOCK_METHOD0(advance_client_buffer, std::shared_ptr<graphics::Buffer>()); |
881 | |
882 | + MOCK_CONST_METHOD0(state, MirSurfaceState()); |
883 | + |
884 | + MOCK_METHOD1(move_to, void(geometry::Point const&)); |
885 | + MOCK_CONST_METHOD0(top_left, geometry::Point()); |
886 | + MOCK_METHOD1(resize, void(geometry::Size const&)); |
887 | MOCK_CONST_METHOD0(size, geometry::Size()); |
888 | MOCK_CONST_METHOD0(pixel_format, MirPixelFormat()); |
889 | |
890 | |
891 | === modified file 'tests/unit-tests/scene/CMakeLists.txt' |
892 | --- tests/unit-tests/scene/CMakeLists.txt 2015-01-14 06:39:13 +0000 |
893 | +++ tests/unit-tests/scene/CMakeLists.txt 2015-01-27 03:20:51 +0000 |
894 | @@ -15,6 +15,7 @@ |
895 | ${CMAKE_CURRENT_SOURCE_DIR}/test_surface.cpp |
896 | ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_impl.cpp |
897 | ${CMAKE_CURRENT_SOURCE_DIR}/test_basic_surface.cpp |
898 | + ${CMAKE_CURRENT_SOURCE_DIR}/test_managed_surface.cpp |
899 | ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_stack.cpp |
900 | ${CMAKE_CURRENT_SOURCE_DIR}/test_surface_controller.cpp |
901 | ${CMAKE_CURRENT_SOURCE_DIR}/test_legacy_scene_change_notification.cpp |
902 | |
903 | === added file 'tests/unit-tests/scene/test_managed_surface.cpp' |
904 | --- tests/unit-tests/scene/test_managed_surface.cpp 1970-01-01 00:00:00 +0000 |
905 | +++ tests/unit-tests/scene/test_managed_surface.cpp 2015-01-27 03:20:51 +0000 |
906 | @@ -0,0 +1,249 @@ |
907 | +/* |
908 | + * Copyright © 2014 Canonical Ltd. |
909 | + * |
910 | + * This program is free software: you can redistribute it and/or modify |
911 | + * it under the terms of the GNU General Public License version 3 as |
912 | + * published by the Free Software Foundation. |
913 | + * |
914 | + * This program is distributed in the hope that it will be useful, |
915 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
916 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
917 | + * GNU General Public License for more details. |
918 | + * |
919 | + * You should have received a copy of the GNU General Public License |
920 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
921 | + * |
922 | + * Authored by: Daniel van Vugt <daniel.van.vugt@canonical.com> |
923 | + */ |
924 | + |
925 | +#include "src/server/scene/managed_surface.h" |
926 | +#include "mir_test_doubles/mock_surface.h" |
927 | +#include "mir_test_doubles/mock_display_layout.h" |
928 | +#include <gtest/gtest.h> |
929 | +#include <gmock/gmock.h> |
930 | + |
931 | +using namespace testing; |
932 | +using namespace mir::scene; |
933 | +using namespace mir::geometry; |
934 | +using namespace mir::test::doubles; |
935 | + |
936 | +struct ManagedSurfaceTest : public Test |
937 | +{ |
938 | + std::shared_ptr<MockDisplayLayout> |
939 | + mock_display_layout{std::make_shared<NiceMock<MockDisplayLayout>>()}; |
940 | + |
941 | + std::shared_ptr<MockSurface> |
942 | + mock_basic_surface{std::make_shared<NiceMock<MockSurface>>()}; |
943 | + MirSurfaceState state_ = mir_surface_state_restored; |
944 | + Rectangle position{{12,34}, {56,78}}; |
945 | + |
946 | + Rectangle const random_place{{31,41}, {59,26}}; |
947 | + Rectangle const fullscreen{{0,0}, {1366,768}}; |
948 | + Rectangle const maximized{fullscreen}; // will be different in future |
949 | + |
950 | + void SetUp() |
951 | + { |
952 | + ON_CALL(*mock_display_layout, size_to_output(_)) |
953 | + .WillByDefault(SetArgReferee<0>(fullscreen)); |
954 | + |
955 | + // This could be served better by a FakeSurface later, but this |
956 | + // requires less code for now. |
957 | + ON_CALL(*mock_basic_surface, size()) |
958 | + .WillByDefault(ReturnPointee(&position.size)); |
959 | + ON_CALL(*mock_basic_surface, resize(_)) |
960 | + .WillByDefault(SaveArg<0>(&position.size)); |
961 | + |
962 | + ON_CALL(*mock_basic_surface, top_left()) |
963 | + .WillByDefault(ReturnPointee(&position.top_left)); |
964 | + ON_CALL(*mock_basic_surface, move_to(_)) |
965 | + .WillByDefault(SaveArg<0>(&position.top_left)); |
966 | + |
967 | + ON_CALL(*mock_basic_surface, state()) |
968 | + .WillByDefault(ReturnPointee(&state_)); |
969 | + ON_CALL(*mock_basic_surface, configure(mir_surface_attrib_state, _)) |
970 | + .WillByDefault(Invoke( |
971 | + [this](MirSurfaceAttrib, int v) -> int |
972 | + { |
973 | + state_ = (MirSurfaceState)v; |
974 | + return v; |
975 | + } |
976 | + )); |
977 | + } |
978 | +}; |
979 | + |
980 | +TEST_F(ManagedSurfaceTest, goes_fullscreen_and_restores) |
981 | +{ |
982 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
983 | + |
984 | + Rectangle const restored{surf.top_left(), surf.size()}; |
985 | + ASSERT_NE(fullscreen, restored); |
986 | + |
987 | + InSequence seq; |
988 | + EXPECT_CALL(*mock_basic_surface, move_to(fullscreen.top_left)); |
989 | + EXPECT_CALL(*mock_basic_surface, resize(fullscreen.size)); |
990 | + EXPECT_CALL(*mock_basic_surface, move_to(restored.top_left)); |
991 | + EXPECT_CALL(*mock_basic_surface, resize(restored.size)); |
992 | + |
993 | + surf.configure(mir_surface_attrib_state, mir_surface_state_fullscreen); |
994 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
995 | +} |
996 | + |
997 | +TEST_F(ManagedSurfaceTest, goes_maximized_and_restores) |
998 | +{ |
999 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1000 | + |
1001 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1002 | + ASSERT_NE(maximized, restored); |
1003 | + |
1004 | + InSequence seq; |
1005 | + EXPECT_CALL(*mock_basic_surface, move_to(maximized.top_left)); |
1006 | + EXPECT_CALL(*mock_basic_surface, resize(maximized.size)); |
1007 | + EXPECT_CALL(*mock_basic_surface, move_to(restored.top_left)); |
1008 | + EXPECT_CALL(*mock_basic_surface, resize(restored.size)); |
1009 | + |
1010 | + surf.configure(mir_surface_attrib_state, mir_surface_state_maximized); |
1011 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
1012 | +} |
1013 | + |
1014 | +TEST_F(ManagedSurfaceTest, goes_vertmaximized_and_restores) |
1015 | +{ |
1016 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1017 | + |
1018 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1019 | + ASSERT_NE(maximized, restored); |
1020 | + |
1021 | + Rectangle const vertmaximized{ |
1022 | + {restored.top_left.x, maximized.top_left.y}, |
1023 | + {restored.size.width, maximized.size.height} }; |
1024 | + |
1025 | + InSequence seq; |
1026 | + EXPECT_CALL(*mock_basic_surface, move_to(vertmaximized.top_left)); |
1027 | + EXPECT_CALL(*mock_basic_surface, resize(vertmaximized.size)); |
1028 | + EXPECT_CALL(*mock_basic_surface, move_to(restored.top_left)); |
1029 | + EXPECT_CALL(*mock_basic_surface, resize(restored.size)); |
1030 | + |
1031 | + surf.configure(mir_surface_attrib_state, mir_surface_state_vertmaximized); |
1032 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
1033 | +} |
1034 | + |
1035 | +TEST_F(ManagedSurfaceTest, goes_horizmaximized_and_restores) |
1036 | +{ |
1037 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1038 | + |
1039 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1040 | + ASSERT_NE(maximized, restored); |
1041 | + |
1042 | + Rectangle const horizmaximized{ |
1043 | + {maximized.top_left.x, restored.top_left.y}, |
1044 | + {maximized.size.width, restored.size.height} }; |
1045 | + |
1046 | + InSequence seq; |
1047 | + EXPECT_CALL(*mock_basic_surface, move_to(horizmaximized.top_left)); |
1048 | + EXPECT_CALL(*mock_basic_surface, resize(horizmaximized.size)); |
1049 | + EXPECT_CALL(*mock_basic_surface, move_to(restored.top_left)); |
1050 | + EXPECT_CALL(*mock_basic_surface, resize(restored.size)); |
1051 | + |
1052 | + surf.configure(mir_surface_attrib_state, mir_surface_state_horizmaximized); |
1053 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
1054 | +} |
1055 | + |
1056 | +TEST_F(ManagedSurfaceTest, goes_minimized_and_restores) |
1057 | +{ |
1058 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1059 | + |
1060 | + InSequence seq; |
1061 | + EXPECT_CALL(*mock_basic_surface, hide()); |
1062 | + EXPECT_CALL(*mock_basic_surface, show()); |
1063 | + |
1064 | + surf.configure(mir_surface_attrib_state, mir_surface_state_minimized); |
1065 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
1066 | +} |
1067 | + |
1068 | +TEST_F(ManagedSurfaceTest, multistate_restores_to_original) |
1069 | +{ |
1070 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1071 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1072 | + |
1073 | + surf.configure(mir_surface_attrib_state, mir_surface_state_vertmaximized); |
1074 | + surf.move_to({surf.top_left().x.as_int() + 5, surf.top_left().y}); |
1075 | + EXPECT_NE(restored.top_left, surf.top_left()); |
1076 | + |
1077 | + surf.configure(mir_surface_attrib_state, mir_surface_state_fullscreen); |
1078 | + EXPECT_EQ(fullscreen.top_left, surf.top_left()); |
1079 | + EXPECT_EQ(fullscreen.size, surf.size()); |
1080 | + |
1081 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
1082 | + EXPECT_EQ(restored.top_left, surf.top_left()); |
1083 | + EXPECT_EQ(restored.size, surf.size()); |
1084 | +} |
1085 | + |
1086 | +TEST_F(ManagedSurfaceTest, fullscreen_cant_move) |
1087 | +{ |
1088 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1089 | + |
1090 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1091 | + ASSERT_NE(fullscreen, restored); |
1092 | + |
1093 | + surf.configure(mir_surface_attrib_state, mir_surface_state_fullscreen); |
1094 | + surf.move_to(random_place.top_left); |
1095 | + surf.resize(random_place.size); |
1096 | + ASSERT_NE(random_place, fullscreen); |
1097 | + EXPECT_EQ(fullscreen.top_left, surf.top_left()); |
1098 | + EXPECT_EQ(fullscreen.size, surf.size()); |
1099 | +} |
1100 | + |
1101 | +TEST_F(ManagedSurfaceTest, maximized_cant_move) |
1102 | +{ |
1103 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1104 | + |
1105 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1106 | + ASSERT_NE(maximized, restored); |
1107 | + |
1108 | + surf.configure(mir_surface_attrib_state, mir_surface_state_maximized); |
1109 | + surf.move_to(random_place.top_left); |
1110 | + surf.resize(random_place.size); |
1111 | + ASSERT_NE(random_place, maximized); |
1112 | + EXPECT_EQ(maximized.top_left, surf.top_left()); |
1113 | + EXPECT_EQ(maximized.size, surf.size()); |
1114 | +} |
1115 | + |
1116 | +TEST_F(ManagedSurfaceTest, vertmaximized_can_move_only_horizontally) |
1117 | +{ |
1118 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1119 | + |
1120 | + surf.configure(mir_surface_attrib_state, mir_surface_state_vertmaximized); |
1121 | + surf.move_to(random_place.top_left); |
1122 | + surf.resize(random_place.size); |
1123 | + EXPECT_EQ(random_place.top_left.x, surf.top_left().x); |
1124 | + EXPECT_EQ(maximized.top_left.y, surf.top_left().y); |
1125 | + EXPECT_EQ(random_place.size.width, surf.size().width); |
1126 | + EXPECT_EQ(maximized.size.height, surf.size().height); |
1127 | +} |
1128 | + |
1129 | +TEST_F(ManagedSurfaceTest, horizmaximized_can_move_only_vertically) |
1130 | +{ |
1131 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1132 | + |
1133 | + surf.configure(mir_surface_attrib_state, mir_surface_state_horizmaximized); |
1134 | + surf.move_to(random_place.top_left); |
1135 | + surf.resize(random_place.size); |
1136 | + EXPECT_EQ(maximized.top_left.x, surf.top_left().x); |
1137 | + EXPECT_EQ(random_place.top_left.y, surf.top_left().y); |
1138 | + EXPECT_EQ(maximized.size.width, surf.size().width); |
1139 | + EXPECT_EQ(random_place.size.height, surf.size().height); |
1140 | +} |
1141 | + |
1142 | +TEST_F(ManagedSurfaceTest, restored_can_move_freely) |
1143 | +{ |
1144 | + ManagedSurface surf(mock_basic_surface, mock_display_layout); |
1145 | + |
1146 | + Rectangle const restored{surf.top_left(), surf.size()}; |
1147 | + EXPECT_NE(restored, random_place); |
1148 | + |
1149 | + surf.configure(mir_surface_attrib_state, mir_surface_state_restored); |
1150 | + surf.move_to(random_place.top_left); |
1151 | + surf.resize(random_place.size); |
1152 | + |
1153 | + EXPECT_EQ(random_place.top_left, surf.top_left()); |
1154 | + EXPECT_EQ(random_place.size, surf.size()); |
1155 | +} |
1156 | |
1157 | === modified file 'tests/unit-tests/scene/test_surface_controller.cpp' |
1158 | --- tests/unit-tests/scene/test_surface_controller.cpp 2015-01-14 06:39:13 +0000 |
1159 | +++ tests/unit-tests/scene/test_surface_controller.cpp 2015-01-27 03:20:51 +0000 |
1160 | @@ -24,6 +24,7 @@ |
1161 | #include "mir_test_doubles/stub_scene_session.h" |
1162 | |
1163 | #include "mir_test_doubles/mock_surface.h" |
1164 | +#include "mir_test_doubles/mock_display_layout.h" |
1165 | #include "mir_test/fake_shared.h" |
1166 | |
1167 | #include <gtest/gtest.h> |
1168 | @@ -62,7 +63,8 @@ |
1169 | struct SurfaceController : testing::Test |
1170 | { |
1171 | MockPlacementStrategy placement_strategy; |
1172 | - mtd::MockSurface mock_surface; |
1173 | + testing::NiceMock<mtd::MockDisplayLayout> mock_display_layout; |
1174 | + testing::NiceMock<mtd::MockSurface> mock_surface; |
1175 | std::shared_ptr<ms::Surface> const expect_surface = mt::fake_shared(mock_surface); |
1176 | testing::NiceMock<MockSurfaceAllocator> mock_surface_allocator; |
1177 | MockSurfaceStackModel model; |
1178 | @@ -73,6 +75,10 @@ |
1179 | using namespace ::testing; |
1180 | ON_CALL(mock_surface_allocator, create_surface(_)).WillByDefault(Return(expect_surface)); |
1181 | ON_CALL(placement_strategy, place(_, _)).WillByDefault(ReturnArg<1>()); |
1182 | + ON_CALL(mock_surface, size()) |
1183 | + .WillByDefault(Return(geom::Size{12,34})); |
1184 | + ON_CALL(mock_surface, top_left()) |
1185 | + .WillByDefault(Return(geom::Point{56,78})); |
1186 | } |
1187 | }; |
1188 | } |
1189 | @@ -81,11 +87,34 @@ |
1190 | { |
1191 | using namespace ::testing; |
1192 | |
1193 | - ms::SurfaceController controller( |
1194 | + class MockSurfaceController : public ms::SurfaceController |
1195 | + { |
1196 | + public: |
1197 | + MockSurfaceController( |
1198 | + std::shared_ptr<ms::SurfaceFactory> const& surface_factory, |
1199 | + std::shared_ptr<ms::PlacementStrategy> const& placement_strategy, |
1200 | + std::shared_ptr<msh::DisplayLayout> const& display_layout, |
1201 | + std::shared_ptr<ms::SurfaceStackModel> const& surface_stack) |
1202 | + : ms::SurfaceController(surface_factory, |
1203 | + placement_strategy, |
1204 | + display_layout, |
1205 | + surface_stack) |
1206 | + { |
1207 | + } |
1208 | + |
1209 | + MOCK_METHOD1(wrap_surface, std::shared_ptr<ms::Surface>( |
1210 | + std::shared_ptr<ms::Surface> const&)); |
1211 | + }; |
1212 | + |
1213 | + MockSurfaceController controller( |
1214 | mt::fake_shared(mock_surface_allocator), |
1215 | mt::fake_shared(placement_strategy), |
1216 | + mt::fake_shared(mock_display_layout), |
1217 | mt::fake_shared(model)); |
1218 | |
1219 | + EXPECT_CALL(controller, wrap_surface(expect_surface)) |
1220 | + .WillOnce(Return(expect_surface)); |
1221 | + |
1222 | InSequence seq; |
1223 | EXPECT_CALL(placement_strategy, place(_, _)).Times(1); |
1224 | EXPECT_CALL(mock_surface_allocator, create_surface(_)).Times(1).WillOnce(Return(expect_surface)); |
1225 | @@ -105,6 +134,7 @@ |
1226 | ms::SurfaceController controller( |
1227 | mt::fake_shared(mock_surface_allocator), |
1228 | mt::fake_shared(placement_strategy), |
1229 | + mt::fake_shared(mock_display_layout), |
1230 | mt::fake_shared(model)); |
1231 | |
1232 | EXPECT_CALL(model, raise(_)).Times(1); |
1233 | @@ -121,6 +151,7 @@ |
1234 | ms::SurfaceController controller( |
1235 | mt::fake_shared(mock_surface_allocator), |
1236 | mt::fake_shared(placement_strategy), |
1237 | + mt::fake_shared(mock_display_layout), |
1238 | mt::fake_shared(model)); |
1239 | |
1240 | auto params = ms::a_surface(); |
1241 | @@ -139,6 +170,7 @@ |
1242 | ms::SurfaceController controller( |
1243 | mt::fake_shared(mock_surface_allocator), |
1244 | mt::fake_shared(placement_strategy), |
1245 | + mt::fake_shared(mock_display_layout), |
1246 | mt::fake_shared(model)); |
1247 | |
1248 | auto params = ms::a_surface(); |
FAILED: Continuous integration, rev:2183 jenkins. qa.ubuntu. com/job/ mir-ci/ 2393/ jenkins. qa.ubuntu. com/job/ mir-android- vivid-i386- build/504 jenkins. qa.ubuntu. com/job/ mir-clang- vivid-amd64- build/504 jenkins. qa.ubuntu. com/job/ mir-mediumtests -vivid- touch/478/ console jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 390 jenkins. qa.ubuntu. com/job/ mir-vivid- amd64-ci/ 390/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 478 jenkins. qa.ubuntu. com/job/ mir-mediumtests -builder- vivid-armhf/ 478/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ mir-mediumtests -runner- mako/3662/ console s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 16553
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
deb: http://
FAILURE: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/mir- ci/2393/ rebuild
http://