Mir

Merge lp:~vanvugt/mir/managed-surface into lp:mir

Proposed by Daniel van Vugt
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
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_set_state actually work. And key combos
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://bugs.launchpad.net/mir/+bugs?field.tag=fullscreen

To post a comment you must log in.
lp:~vanvugt/mir/managed-surface updated
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Robert Carr (robertcarr) wrote :

I wonder if it would be easier (avoiding "SurfaceWrapper"), to use a controller pattern,

e.g. ms::SurfaceController::configure(ms::Surface const&, ...) ms::SurfaceController::resize...etc

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?

Revision history for this message
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_surface->some_method() and accidentally bypass the management policy.
 * 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.

lp:~vanvugt/mir/managed-surface updated
2184. By Daniel van Vugt

Merge latest trunk

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

* You can't call a_scene_surface->some_method() and accidentally bypass the management policy.

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.

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

This branch will conflict with lp:mir right now. But that should be resolved by:
https://code.launchpad.net/~vanvugt/mir/revert-r2165/+merge/245063

Revision history for this message
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.

Revision history for this message
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_controller->move)...perhaps SurfaceObservers play some role too (reactive approach).

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...especially if we switch to something like surface->input_target() rather than surface IS a input_target)
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_surface->some_method() and accidentally bypass the management policy.

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".

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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?

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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"/duplication.

We do have a "coordinator" already in the pre-existing class "SurfaceController". You don't see it much in this diff though because I didn't have to use it much for the surface states. You do see it used extensively in my previous adventure:
https://code.launchpad.net/~vanvugt/mir/states/+merge/243960

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.

Revision history for this message
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/Renderable::transformation() for arbitrary 3D transformations of surfaces. But we don't really demonstrate it at all other than in mir_demo_standalone_render_surfaces.

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).

Revision history for this message
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.

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

I've tried to outline in improved-tiling-window-management MR some of the ways I'd like to see the architecture improve and how we can really get the window manager as an elegant controller. In light of being convinced it's fatally flawed, I am definitely inclined to lean towards continue working on the controller based approach than add this surface wrapper.

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-Controller is a well known and understood pattern which has several additional benefits for us: Decoupling of model object (e.g.) surface from interface used to control it, and an easier way to store intra-surface state.
2. As I've outlined in the tiling-window-management MR I think untangling the controller may be able to provide a significant simplification to our current factory interfaces and the path from session mediator to scene.

Revision history for this message
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_surface->some_method() and accidentally bypass the management policy."

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.

Revision history for this message
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.

Revision history for this message
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.

lp:~vanvugt/mir/managed-surface updated
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
lp:~vanvugt/mir/managed-surface updated
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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.

review: Needs Fixing
Revision history for this message
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://code.launchpad.net/~vanvugt/mir/wire-up-SurfaceController/+merge/246281
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.

lp:~vanvugt/mir/managed-surface updated
2201. By Daniel van Vugt

Merge latest trunk

2202. By Daniel van Vugt

Add support for the newly introduced mir_surface_state_horizmaximized

2203. By Daniel van Vugt

More tests to cover the new horizmaximized semantics

2204. By Daniel van Vugt

Update docs

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
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"

review: Disapprove
Revision history for this message
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...

lp:~vanvugt/mir/managed-surface updated
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.

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

BTW, if you want a pure controller approach please help me to make progress on that front too:
https://code.launchpad.net/~vanvugt/mir/wire-up-SurfaceController/+merge/246281

lp:~vanvugt/mir/managed-surface updated
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.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~vanvugt/mir/managed-surface updated
2212. By Daniel van Vugt

Ping Jenkins

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~vanvugt/mir/managed-surface updated
2213. By Daniel van Vugt

Merge latest trunk

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

Bug 1413748 again. Unrelated to the proposal -- it's happening on all branches.

Revision history for this message
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

review: Disapprove
lp:~vanvugt/mir/managed-surface updated
2214. By Daniel van Vugt

Merge latest trunk.

Revision history for this message
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.

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

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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();

Subscribers

People subscribed via source and target branches