Merge lp:~alan-griffiths/qtmir/import-WindowManagement-code-from-Mir-examples into lp:qtmir

Proposed by Alan Griffiths
Status: Merged
Approved by: Gerry Boland
Approved revision: 422
Merged at revision: 454
Proposed branch: lp:~alan-griffiths/qtmir/import-WindowManagement-code-from-Mir-examples
Merge into: lp:qtmir
Diff against target: 3203 lines (+3132/-0)
13 files modified
src/platforms/mirserver/CMakeLists.txt (+2/-0)
src/platforms/mirserver/wm-wip/CMakeLists.txt (+22/-0)
src/platforms/mirserver/wm-wip/README (+5/-0)
src/platforms/mirserver/wm-wip/server_example_basic_window_manager.cpp (+312/-0)
src/platforms/mirserver/wm-wip/server_example_basic_window_manager.h (+252/-0)
src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.cpp (+946/-0)
src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.h (+132/-0)
src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.cpp (+640/-0)
src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.h (+124/-0)
src/platforms/mirserver/wm-wip/server_example_window_management.cpp (+155/-0)
src/platforms/mirserver/wm-wip/server_example_window_management.h (+33/-0)
src/platforms/mirserver/wm-wip/server_example_window_management_info.cpp (+406/-0)
src/platforms/mirserver/wm-wip/server_example_window_management_info.h (+103/-0)
To merge this branch: bzr merge lp:~alan-griffiths/qtmir/import-WindowManagement-code-from-Mir-examples
Reviewer Review Type Date Requested Status
Unity8 CI Bot (community) continuous-integration Needs Fixing
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+278719@code.launchpad.net

Commit message

Copy the Window Management work-in-progress from Mir examples.

Description of the change

Copy the Window Management work-in-progress from Mir examples. (Just builds the library, no attempt to use it yet.)

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

Why Wily?

(Failures are unrelated to MP)

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

In src/platforms/mirserver/wm-wip/server_example_window_management_info.h

"""
+ std::shared_ptr <scene::Surface> titlebar;
""

A QML shell won't be using mir surfaces for placing title bars. They will be QML items in the compositor's qml scene. Mir will be oblivious to that.

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

"""
    void init_titlebar(std::shared_ptr <scene::Surface> const& surface);

    void paint_titlebar(int intensity);
"""

Likewise

Revision history for this message
Daniel d'Andrada (dandrader) wrote :

I don't get the point of dumping this code, unmodified, here in qtmir.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> I don't get the point of dumping this code, unmodified, here in qtmir.

This code is a snapshot of the different Mir examples of Window Management ("canonical", "tiling", "fullscreen"). While these are simplistic examples they are also helpful reference cases to getting generic WM support right.

The the code isn't published by libmirserver as mir-team do not want to publish the API until we get it stable, and we've been using the feedback from the Mir examples to evolve it. It is a work-in-progress and now want feedback from integration into Qtmir.

"Dumping" it here accomplishes two goals:

/1/ it makes it available to Qtmir without exposing it in the Mir API
/2/ feedback is easier to accomplish with the code here, so that we can change it without tying it to the Mir release cycle.

In the medium term changes will be fed back into Mir, published as a supported API and the code deleted from Qtmir.

To aid the tracking of changes for feedback to Mir this MI is just the unmodified code. Any tailoring for the needs of Qtmir or Unity8 will be done in subsequent MPs.

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:418
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/8/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/8/rebuild

review: Needs Fixing (continuous-integration)
419. By Alan Griffiths

merge lp:qtmir

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:419
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/16/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/16/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
420. By Alan Griffiths

Re-sync with mir/examples

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:420
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/17/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/17/rebuild

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

FAILURE: http://jenkins.qa.ubuntu.com/job/qtmir-wily-amd64-ci/372/console

The following packages have unmet dependencies:
 pbuilder-satisfydepends-dummy : Depends: libmirclient-dev (>= 0.18.0) but 0.17.0+15.10.20151008.2-0ubuntu1 is to be installed.
                                 Depends: libmircommon-dev (>= 0.18.0) but 0.17.0+15.10.20151008.2-0ubuntu1 is to be installed.
                                 Depends: libmirserver-dev (>= 0.18.0) but it is not going to be installed.
                                 Depends: libunity-api-dev (>= 7.104) but it is not going to be installed.

Do we care about wily (and Mir 0,17 support) here?

Revision history for this message
Gerry Boland (gerboland) wrote :

Can you please have this be only built when a cmake flag is set? Just to avoid building it unnecessarily.

review: Needs Fixing
421. By Alan Griffiths

Only build the mir example Window Managers with cmake -DEXAMPLE_MIR_WINDOW_MANAGERS=on

Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:421
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/23/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/23/rebuild

review: Needs Fixing (continuous-integration)
422. By Alan Griffiths

Fix compile options

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

FAILED: Continuous integration, rev:422
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/24/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/24/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

LGTM

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

FAILED: Continuous integration, rev:422
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/51/
Executed test runs:

Click here to trigger a rebuild:
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-1-ci/51/rebuild

review: Needs Fixing (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/platforms/mirserver/CMakeLists.txt'
2--- src/platforms/mirserver/CMakeLists.txt 2015-12-07 10:51:03 +0000
3+++ src/platforms/mirserver/CMakeLists.txt 2016-01-20 19:52:54 +0000
4@@ -36,6 +36,8 @@
5 ${APPLICATION_API_INCLUDE_DIRS}
6 )
7
8+add_subdirectory(wm-wip)
9+
10 # We have to remove -pedantic for tracepoints.c
11 string (REPLACE " -pedantic " " " CMAKE_C_FLAGS ${CMAKE_C_FLAGS})
12 # Needed to compile tracepoints in C99 mode.
13
14=== added directory 'src/platforms/mirserver/wm-wip'
15=== added file 'src/platforms/mirserver/wm-wip/CMakeLists.txt'
16--- src/platforms/mirserver/wm-wip/CMakeLists.txt 1970-01-01 00:00:00 +0000
17+++ src/platforms/mirserver/wm-wip/CMakeLists.txt 2016-01-20 19:52:54 +0000
18@@ -0,0 +1,22 @@
19+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -fPIC")
20+
21+add_library(experimentalwindowmanager STATIC
22+ server_example_basic_window_manager.cpp
23+ server_example_basic_window_manager.h
24+ server_example_window_management_info.cpp
25+ server_example_window_management_info.h
26+)
27+
28+
29+if (EXAMPLE_MIR_WINDOW_MANAGERS)
30+
31+add_library(examplewindowmanager STATIC
32+ server_example_canonical_window_manager.h
33+ server_example_tiling_window_manager.h
34+ server_example_window_management.h
35+ server_example_canonical_window_manager.cpp
36+ server_example_tiling_window_manager.cpp
37+ server_example_window_management.cpp
38+)
39+
40+endif()
41
42=== added file 'src/platforms/mirserver/wm-wip/README'
43--- src/platforms/mirserver/wm-wip/README 1970-01-01 00:00:00 +0000
44+++ src/platforms/mirserver/wm-wip/README 2016-01-20 19:52:54 +0000
45@@ -0,0 +1,5 @@
46+examples for mir clients. you should have package 'libmirclient-dev' installed
47+
48+you can compile with a command like:
49+ g++ -std=c++0x -o mir_demo_client_scroll `pkg-config --libs --cflags mirclient` demo_client_scroll.cpp graphics_utils.cpp
50+ gcc -o mir_demo_client_flicker `pkg-config --libs --cflags mirclient` demo_client_flicker.c
51
52=== added file 'src/platforms/mirserver/wm-wip/server_example_basic_window_manager.cpp'
53--- src/platforms/mirserver/wm-wip/server_example_basic_window_manager.cpp 1970-01-01 00:00:00 +0000
54+++ src/platforms/mirserver/wm-wip/server_example_basic_window_manager.cpp 2016-01-20 19:52:54 +0000
55@@ -0,0 +1,312 @@
56+/*
57+ * Copyright © 2015 Canonical Ltd.
58+ *
59+ * This program is free software: you can redistribute it and/or modify it
60+ * under the terms of the GNU General Public License version 3,
61+ * as published by the Free Software Foundation.
62+ *
63+ * This program is distributed in the hope that it will be useful,
64+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
65+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
66+ * GNU General Public License for more details.
67+ *
68+ * You should have received a copy of the GNU General Public License
69+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
70+ *
71+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
72+ */
73+
74+#include "server_example_basic_window_manager.h"
75+
76+#include "mir/scene/session.h"
77+#include "mir/scene/surface.h"
78+#include "mir/scene/surface_creation_parameters.h"
79+
80+namespace me = mir::examples;
81+
82+me::BasicWindowManager::BasicWindowManager(
83+ shell::FocusController* focus_controller,
84+ std::unique_ptr<WindowManagementPolicy> policy) :
85+ focus_controller(focus_controller),
86+ policy(std::move(policy))
87+{
88+}
89+
90+void me::BasicWindowManager::add_session(std::shared_ptr<scene::Session> const& session)
91+{
92+ std::lock_guard<decltype(mutex)> lock(mutex);
93+ session_info[session] = SessionInfo();
94+ policy->handle_session_info_updated(session_info, displays);
95+}
96+
97+void me::BasicWindowManager::remove_session(std::shared_ptr<scene::Session> const& session)
98+{
99+ std::lock_guard<decltype(mutex)> lock(mutex);
100+ session_info.erase(session);
101+ policy->handle_session_info_updated(session_info, displays);
102+}
103+
104+auto me::BasicWindowManager::add_surface(
105+ std::shared_ptr<scene::Session> const& session,
106+ scene::SurfaceCreationParameters const& params,
107+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
108+-> frontend::SurfaceId
109+{
110+ std::lock_guard<decltype(mutex)> lock(mutex);
111+ scene::SurfaceCreationParameters const placed_params = policy->handle_place_new_surface(session, params);
112+ auto const result = build(session, placed_params);
113+ auto const surface = session->surface(result);
114+ surface_info.emplace(surface, SurfaceInfo{session, surface, placed_params});
115+ policy->handle_new_surface(session, surface);
116+ policy->generate_decorations_for(session, surface, surface_info, build);
117+ return result;
118+}
119+
120+void me::BasicWindowManager::modify_surface(
121+ std::shared_ptr<scene::Session> const& session,
122+ std::shared_ptr<scene::Surface> const& surface,
123+ shell::SurfaceSpecification const& modifications)
124+{
125+ std::lock_guard<decltype(mutex)> lock(mutex);
126+ policy->handle_modify_surface(session, surface, modifications);
127+}
128+
129+void me::BasicWindowManager::remove_surface(
130+ std::shared_ptr<scene::Session> const& session,
131+ std::weak_ptr<scene::Surface> const& surface)
132+{
133+ std::lock_guard<decltype(mutex)> lock(mutex);
134+ policy->handle_delete_surface(session, surface);
135+
136+ surface_info.erase(surface);
137+}
138+
139+void me::BasicWindowManager::forget(std::weak_ptr<scene::Surface> const& surface)
140+{
141+ surface_info.erase(surface);
142+}
143+
144+void me::BasicWindowManager::add_display(geometry::Rectangle const& area)
145+{
146+ std::lock_guard<decltype(mutex)> lock(mutex);
147+ displays.add(area);
148+ policy->handle_displays_updated(session_info, displays);
149+}
150+
151+void me::BasicWindowManager::remove_display(geometry::Rectangle const& area)
152+{
153+ std::lock_guard<decltype(mutex)> lock(mutex);
154+ displays.remove(area);
155+ policy->handle_displays_updated(session_info, displays);
156+}
157+
158+bool me::BasicWindowManager::handle_keyboard_event(MirKeyboardEvent const* event)
159+{
160+ std::lock_guard<decltype(mutex)> lock(mutex);
161+ update_event_timestamp(event);
162+ return policy->handle_keyboard_event(event);
163+}
164+
165+bool me::BasicWindowManager::handle_touch_event(MirTouchEvent const* event)
166+{
167+ std::lock_guard<decltype(mutex)> lock(mutex);
168+ update_event_timestamp(event);
169+ return policy->handle_touch_event(event);
170+}
171+
172+bool me::BasicWindowManager::handle_pointer_event(MirPointerEvent const* event)
173+{
174+ std::lock_guard<decltype(mutex)> lock(mutex);
175+ update_event_timestamp(event);
176+
177+ cursor = {
178+ mir_pointer_event_axis_value(event, mir_pointer_axis_x),
179+ mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
180+
181+ return policy->handle_pointer_event(event);
182+}
183+
184+void me::BasicWindowManager::handle_raise_surface(
185+ std::shared_ptr<scene::Session> const& session,
186+ std::shared_ptr<scene::Surface> const& surface,
187+ uint64_t timestamp)
188+{
189+ std::lock_guard<decltype(mutex)> lock(mutex);
190+ if (timestamp >= last_input_event_timestamp)
191+ policy->handle_raise_surface(session, surface);
192+}
193+
194+int me::BasicWindowManager::set_surface_attribute(
195+ std::shared_ptr<scene::Session> const& /*session*/,
196+ std::shared_ptr<scene::Surface> const& surface,
197+ MirSurfaceAttrib attrib,
198+ int value)
199+{
200+ std::lock_guard<decltype(mutex)> lock(mutex);
201+ switch (attrib)
202+ {
203+ case mir_surface_attrib_state:
204+ {
205+ auto const state = policy->handle_set_state(surface, MirSurfaceState(value));
206+ return surface->configure(attrib, state);
207+ }
208+ default:
209+ return surface->configure(attrib, value);
210+ }
211+}
212+
213+auto me::BasicWindowManager::find_session(std::function<bool(SessionInfo const& info)> const& predicate)
214+-> std::shared_ptr<scene::Session>
215+ {
216+ for(auto& info : session_info)
217+ {
218+ if (predicate(info.second))
219+ {
220+ return info.first.lock();
221+ }
222+ }
223+
224+ return std::shared_ptr<scene::Session>{};
225+ }
226+
227+auto me::BasicWindowManager::info_for(std::weak_ptr<scene::Session> const& session) const
228+-> SessionInfo&
229+{
230+ return const_cast<SessionInfo&>(session_info.at(session));
231+}
232+
233+auto me::BasicWindowManager::info_for(std::weak_ptr<scene::Surface> const& surface) const
234+-> SurfaceInfo&
235+{
236+ return const_cast<SurfaceInfo&>(surface_info.at(surface));
237+}
238+
239+auto me::BasicWindowManager::focused_session() const
240+-> std::shared_ptr<scene::Session>
241+{
242+ return focus_controller->focused_session();
243+}
244+
245+auto me::BasicWindowManager::focused_surface() const
246+->std::shared_ptr<scene::Surface>
247+{
248+ return focus_controller->focused_surface();
249+}
250+
251+void me::BasicWindowManager::focus_next_session()
252+{
253+ focus_controller->focus_next_session();
254+}
255+
256+void me::BasicWindowManager::set_focus_to(
257+ std::shared_ptr<scene::Session> const& focus,
258+ std::shared_ptr<scene::Surface> const& surface)
259+{
260+ focus_controller->set_focus_to(focus, surface);
261+}
262+
263+auto me::BasicWindowManager::surface_at(geometry::Point cursor) const
264+-> std::shared_ptr<scene::Surface>
265+{
266+ return focus_controller->surface_at(cursor);
267+}
268+
269+auto me::BasicWindowManager::active_display()
270+-> geometry::Rectangle const
271+{
272+ geometry::Rectangle result;
273+
274+ // 1. If a window has input focus, whichever display contains the largest
275+ // proportion of the area of that window.
276+ if (auto const surface = focused_surface())
277+ {
278+ auto const surface_rect = surface->input_bounds();
279+ int max_overlap_area = -1;
280+
281+ for (auto const& display : displays)
282+ {
283+ auto const intersection = surface_rect.intersection_with(display).size;
284+ if (intersection.width.as_int()*intersection.height.as_int() > max_overlap_area)
285+ {
286+ max_overlap_area = intersection.width.as_int()*intersection.height.as_int();
287+ result = display;
288+ }
289+ }
290+ return result;
291+ }
292+
293+ // 2. Otherwise, if any window previously had input focus, for the window that had
294+ // it most recently, the display that contained the largest proportion of the
295+ // area of that window at the moment it closed, as long as that display is still
296+ // available.
297+
298+ // 3. Otherwise, the display that contains the pointer, if there is one.
299+ for (auto const& display : displays)
300+ {
301+ if (display.contains(cursor))
302+ {
303+ // Ignore the (unspecified) possiblity of overlapping displays
304+ return display;
305+ }
306+ }
307+
308+ // 4. Otherwise, the primary display, if there is one (for example, the laptop display).
309+
310+ // 5. Otherwise, the first display.
311+ if (displays.size())
312+ result = *displays.begin();
313+
314+ return result;
315+}
316+
317+void me::BasicWindowManager::raise_tree(std::shared_ptr<scene::Surface> const& root)
318+{
319+ SurfaceSet surfaces;
320+ std::function<void(std::weak_ptr<scene::Surface> const& surface)> const add_children =
321+ [&,this](std::weak_ptr<scene::Surface> const& surface)
322+ {
323+ auto const& info = info_for(surface);
324+ surfaces.insert(begin(info.children), end(info.children));
325+ for (auto const& child : info.children)
326+ add_children(child);
327+ };
328+
329+ surfaces.insert(root);
330+ add_children(root);
331+
332+ focus_controller->raise(surfaces);
333+}
334+
335+void me::BasicWindowManager::update_event_timestamp(MirKeyboardEvent const* kev)
336+{
337+ auto iev = mir_keyboard_event_input_event(kev);
338+ last_input_event_timestamp = mir_input_event_get_event_time(iev);
339+}
340+
341+void me::BasicWindowManager::update_event_timestamp(MirPointerEvent const* pev)
342+{
343+ auto iev = mir_pointer_event_input_event(pev);
344+ auto pointer_action = mir_pointer_event_action(pev);
345+
346+ if (pointer_action == mir_pointer_action_button_up ||
347+ pointer_action == mir_pointer_action_button_down)
348+ {
349+ last_input_event_timestamp = mir_input_event_get_event_time(iev);
350+ }
351+}
352+
353+void me::BasicWindowManager::update_event_timestamp(MirTouchEvent const* tev)
354+{
355+ auto iev = mir_touch_event_input_event(tev);
356+ auto touch_count = mir_touch_event_point_count(tev);
357+ for (unsigned i = 0; i < touch_count; i++)
358+ {
359+ auto touch_action = mir_touch_event_action(tev, i);
360+ if (touch_action == mir_touch_action_up ||
361+ touch_action == mir_touch_action_down)
362+ {
363+ last_input_event_timestamp = mir_input_event_get_event_time(iev);
364+ break;
365+ }
366+ }
367+}
368
369=== added file 'src/platforms/mirserver/wm-wip/server_example_basic_window_manager.h'
370--- src/platforms/mirserver/wm-wip/server_example_basic_window_manager.h 1970-01-01 00:00:00 +0000
371+++ src/platforms/mirserver/wm-wip/server_example_basic_window_manager.h 2016-01-20 19:52:54 +0000
372@@ -0,0 +1,252 @@
373+/*
374+ * Copyright © 2015 Canonical Ltd.
375+ *
376+ * This program is free software: you can redistribute it and/or modify it
377+ * under the terms of the GNU General Public License version 3,
378+ * as published by the Free Software Foundation.
379+ *
380+ * This program is distributed in the hope that it will be useful,
381+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
382+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
383+ * GNU General Public License for more details.
384+ *
385+ * You should have received a copy of the GNU General Public License
386+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
387+ *
388+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
389+ */
390+
391+#ifndef MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
392+#define MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_
393+
394+#include "server_example_window_management_info.h"
395+
396+#include "mir/geometry/rectangles.h"
397+#include "mir/shell/abstract_shell.h"
398+#include "mir/shell/window_manager.h"
399+
400+#include <map>
401+#include <mutex>
402+
403+///\example server_example_basic_window_manager.h
404+/// A generic policy-based window manager implementation
405+
406+namespace mir
407+{
408+namespace examples
409+{
410+using shell::SurfaceSet;
411+
412+/// The interface through which the policy instructs the controller.
413+/// These functions assume that the BasicWindowManager data structures can be accessed freely.
414+/// I.e. should only be invoked by the policy handle_... methods (where any necessary locks are held).
415+class WindowManagerTools
416+{
417+public:
418+ using SurfaceInfoMap = std::map<std::weak_ptr<scene::Surface>, SurfaceInfo, std::owner_less<std::weak_ptr<scene::Surface>>>;
419+ using SessionInfoMap = std::map<std::weak_ptr<scene::Session>, SessionInfo, std::owner_less<std::weak_ptr<scene::Session>>>;
420+
421+ virtual auto find_session(std::function<bool(SessionInfo const& info)> const& predicate)
422+ -> std::shared_ptr<scene::Session> = 0;
423+
424+ virtual auto info_for(std::weak_ptr<scene::Session> const& session) const -> SessionInfo& = 0;
425+
426+ virtual auto info_for(std::weak_ptr<scene::Surface> const& surface) const -> SurfaceInfo& = 0;
427+
428+ virtual std::shared_ptr<scene::Session> focused_session() const = 0;
429+
430+ virtual std::shared_ptr<scene::Surface> focused_surface() const = 0;
431+
432+ virtual void focus_next_session() = 0;
433+
434+ virtual void set_focus_to(
435+ std::shared_ptr<scene::Session> const& focus,
436+ std::shared_ptr<scene::Surface> const& surface) = 0;
437+
438+ virtual auto surface_at(geometry::Point cursor) const -> std::shared_ptr<scene::Surface> = 0;
439+
440+ virtual auto active_display() -> geometry::Rectangle const = 0;
441+
442+ virtual void forget(std::weak_ptr<scene::Surface> const& surface) = 0;
443+
444+ virtual void raise_tree(std::shared_ptr<scene::Surface> const& root) = 0;
445+
446+ virtual ~WindowManagerTools() = default;
447+ WindowManagerTools() = default;
448+ WindowManagerTools(WindowManagerTools const&) = delete;
449+ WindowManagerTools& operator=(WindowManagerTools const&) = delete;
450+};
451+
452+class WindowManagementPolicy
453+{
454+public:
455+ using SessionInfoMap = typename WindowManagerTools::SessionInfoMap;
456+ using SurfaceInfoMap = typename WindowManagerTools::SurfaceInfoMap;
457+
458+ virtual void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0;
459+
460+ virtual void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays) = 0;
461+
462+ virtual auto handle_place_new_surface(
463+ std::shared_ptr<scene::Session> const& session,
464+ scene::SurfaceCreationParameters const& request_parameters)
465+ -> scene::SurfaceCreationParameters = 0;
466+
467+ virtual void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface) = 0;
468+
469+ virtual void handle_modify_surface(
470+ std::shared_ptr<scene::Session> const& session,
471+ std::shared_ptr<scene::Surface> const& surface,
472+ shell::SurfaceSpecification const& modifications) = 0;
473+
474+ virtual void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface) = 0;
475+
476+ virtual int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirSurfaceState value) = 0;
477+
478+ virtual void generate_decorations_for(
479+ std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface,
480+ SurfaceInfoMap& surface_info,
481+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const&, scene::SurfaceCreationParameters const&)> const& build) = 0;
482+
483+ virtual bool handle_keyboard_event(MirKeyboardEvent const* event) = 0;
484+
485+ virtual bool handle_touch_event(MirTouchEvent const* event) = 0;
486+
487+ virtual bool handle_pointer_event(MirPointerEvent const* event) = 0;
488+
489+ virtual void handle_raise_surface(
490+ std::shared_ptr<scene::Session> const& session,
491+ std::shared_ptr<scene::Surface> const& surface) = 0;
492+
493+ virtual ~WindowManagementPolicy() = default;
494+ WindowManagementPolicy() = default;
495+ WindowManagementPolicy(WindowManagementPolicy const&) = delete;
496+ WindowManagementPolicy& operator=(WindowManagementPolicy const&) = delete;
497+};
498+
499+/// A policy based window manager.
500+/// This takes care of the management of any meta implementation held for the sessions and surfaces.
501+class BasicWindowManager : public virtual shell::WindowManager,
502+ protected WindowManagerTools
503+{
504+protected:
505+ BasicWindowManager(
506+ shell::FocusController* focus_controller,
507+ std::unique_ptr<WindowManagementPolicy> policy);
508+
509+public:
510+ using typename WindowManagerTools::SurfaceInfoMap;
511+ using typename WindowManagerTools::SessionInfoMap;
512+
513+ void add_session(std::shared_ptr<scene::Session> const& session) override;
514+
515+ void remove_session(std::shared_ptr<scene::Session> const& session) override;
516+
517+ auto add_surface(
518+ std::shared_ptr<scene::Session> const& session,
519+ scene::SurfaceCreationParameters const& params,
520+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
521+ -> frontend::SurfaceId override;
522+
523+ void modify_surface(
524+ std::shared_ptr<scene::Session> const& session,
525+ std::shared_ptr<scene::Surface> const& surface,
526+ shell::SurfaceSpecification const& modifications) override;
527+
528+ void remove_surface(
529+ std::shared_ptr<scene::Session> const& session,
530+ std::weak_ptr<scene::Surface> const& surface) override;
531+
532+ void forget(std::weak_ptr<scene::Surface> const& surface) override;
533+
534+ void add_display(geometry::Rectangle const& area) override;
535+
536+ void remove_display(geometry::Rectangle const& area) override;
537+
538+ bool handle_keyboard_event(MirKeyboardEvent const* event) override;
539+
540+ bool handle_touch_event(MirTouchEvent const* event) override;
541+
542+ bool handle_pointer_event(MirPointerEvent const* event) override;
543+
544+ void handle_raise_surface(
545+ std::shared_ptr<scene::Session> const& session,
546+ std::shared_ptr<scene::Surface> const& surface,
547+ uint64_t timestamp) override;
548+
549+ int set_surface_attribute(
550+ std::shared_ptr<scene::Session> const& /*session*/,
551+ std::shared_ptr<scene::Surface> const& surface,
552+ MirSurfaceAttrib attrib,
553+ int value) override;
554+
555+ auto find_session(std::function<bool(SessionInfo const& info)> const& predicate)
556+ -> std::shared_ptr<scene::Session> override;
557+
558+ auto info_for(std::weak_ptr<scene::Session> const& session) const -> SessionInfo& override;
559+
560+ auto info_for(std::weak_ptr<scene::Surface> const& surface) const -> SurfaceInfo& override;
561+
562+ std::shared_ptr<scene::Session> focused_session() const override;
563+
564+ std::shared_ptr<scene::Surface> focused_surface() const override;
565+
566+ void focus_next_session() override;
567+
568+ void set_focus_to(
569+ std::shared_ptr<scene::Session> const& focus,
570+ std::shared_ptr<scene::Surface> const& surface) override;
571+
572+ auto surface_at(geometry::Point cursor) const -> std::shared_ptr<scene::Surface> override;
573+
574+ auto active_display() -> geometry::Rectangle const override;
575+
576+ void raise_tree(std::shared_ptr<scene::Surface> const& root) override;
577+
578+private:
579+ shell::FocusController* const focus_controller;
580+ std::unique_ptr<WindowManagementPolicy> const policy;
581+
582+ std::mutex mutex;
583+ SessionInfoMap session_info;
584+ SurfaceInfoMap surface_info;
585+ geometry::Rectangles displays;
586+ geometry::Point cursor;
587+ uint64_t last_input_event_timestamp{0};
588+
589+ void update_event_timestamp(MirKeyboardEvent const* kev);
590+ void update_event_timestamp(MirPointerEvent const* pev);
591+ void update_event_timestamp(MirTouchEvent const* tev);
592+};
593+
594+/// A policy based window manager. This exists to initialize BasicWindowManager and
595+/// the WMPolicy (in an awkward manner).
596+/// TODO revisit this initialization sequence.
597+template<typename WMPolicy>
598+class WindowManagerBuilder : public BasicWindowManager
599+{
600+public:
601+
602+ template <typename... PolicyArgs>
603+ WindowManagerBuilder(
604+ shell::FocusController* focus_controller,
605+ PolicyArgs&&... policy_args) :
606+ BasicWindowManager(
607+ focus_controller,
608+ build_policy(std::forward<PolicyArgs>(policy_args)...))
609+ {
610+ }
611+
612+private:
613+ template <typename... PolicyArgs>
614+ auto build_policy(PolicyArgs&&... policy_args)
615+ -> std::unique_ptr<WMPolicy>
616+ {
617+ return std::unique_ptr<WMPolicy>(
618+ new WMPolicy(this, std::forward<PolicyArgs>(policy_args)...));
619+ }
620+};
621+}
622+}
623+
624+#endif /* MIR_EXAMPLE_BASIC_WINDOW_MANAGER_H_ */
625
626=== added file 'src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.cpp'
627--- src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.cpp 1970-01-01 00:00:00 +0000
628+++ src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.cpp 2016-01-20 19:52:54 +0000
629@@ -0,0 +1,946 @@
630+/*
631+ * Copyright © 2015 Canonical Ltd.
632+ *
633+ * This program is free software: you can redistribute it and/or modify it
634+ * under the terms of the GNU General Public License version 3,
635+ * as published by the Free Software Foundation.
636+ *
637+ * This program is distributed in the hope that it will be useful,
638+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
639+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
640+ * GNU General Public License for more details.
641+ *
642+ * You should have received a copy of the GNU General Public License
643+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
644+ *
645+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
646+ */
647+
648+#include "server_example_canonical_window_manager.h"
649+
650+#include "mir/scene/session.h"
651+#include "mir/scene/surface.h"
652+#include "mir/scene/surface_creation_parameters.h"
653+#include "mir/shell/surface_ready_observer.h"
654+#include "mir/shell/display_layout.h"
655+
656+#include <linux/input.h>
657+#include <csignal>
658+#include <algorithm>
659+
660+namespace me = mir::examples;
661+namespace ms = mir::scene;
662+using namespace mir::geometry;
663+
664+///\example server_example_canonical_window_manager.cpp
665+// Based on "Mir and Unity: Surfaces, input, and displays (v0.3)"
666+
667+namespace
668+{
669+int const title_bar_height = 10;
670+Size titlebar_size_for_window(Size window_size)
671+{
672+ return {window_size.width, Height{title_bar_height}};
673+}
674+
675+Point titlebar_position_for_window(Point window_position)
676+{
677+ return {
678+ window_position.x,
679+ window_position.y - DeltaY(title_bar_height)
680+ };
681+}
682+}
683+
684+me::CanonicalWindowManagerPolicyCopy::CanonicalWindowManagerPolicyCopy(
685+ WindowManagerTools* const tools,
686+ std::shared_ptr<shell::DisplayLayout> const& display_layout) :
687+ tools{tools},
688+ display_layout{display_layout}
689+{
690+}
691+
692+void me::CanonicalWindowManagerPolicyCopy::click(Point cursor)
693+{
694+ if (auto const surface = tools->surface_at(cursor))
695+ select_active_surface(surface);
696+}
697+
698+void me::CanonicalWindowManagerPolicyCopy::handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/)
699+{
700+}
701+
702+void me::CanonicalWindowManagerPolicyCopy::handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& displays)
703+{
704+ display_area = displays.bounding_rectangle();
705+
706+ for (auto const weak_surface : fullscreen_surfaces)
707+ {
708+ if (auto const surface = weak_surface.lock())
709+ {
710+ auto const& info = tools->info_for(weak_surface);
711+ Rectangle rect{surface->top_left(), surface->size()};
712+
713+ display_layout->place_in_output(info.output_id.value(), rect);
714+ surface->move_to(rect.top_left);
715+ surface->resize(rect.size);
716+ }
717+ }
718+}
719+
720+void me::CanonicalWindowManagerPolicyCopy::resize(Point cursor)
721+{
722+ select_active_surface(tools->surface_at(old_cursor));
723+ resize(active_surface(), cursor, old_cursor, display_area);
724+}
725+
726+auto me::CanonicalWindowManagerPolicyCopy::handle_place_new_surface(
727+ std::shared_ptr<ms::Session> const& session,
728+ ms::SurfaceCreationParameters const& request_parameters)
729+-> ms::SurfaceCreationParameters
730+{
731+ auto parameters = request_parameters;
732+ auto surf_type = parameters.type.is_set() ? parameters.type.value() : mir_surface_type_normal;
733+ bool const needs_titlebar = SurfaceInfo::needs_titlebar(surf_type);
734+
735+ if (needs_titlebar)
736+ parameters.size.height = parameters.size.height + DeltaY{title_bar_height};
737+
738+ if (!parameters.state.is_set())
739+ parameters.state = mir_surface_state_restored;
740+
741+ auto const active_display = tools->active_display();
742+
743+ auto const width = parameters.size.width.as_int();
744+ auto const height = parameters.size.height.as_int();
745+
746+ bool positioned = false;
747+
748+ auto const parent = parameters.parent.lock();
749+
750+ if (parameters.output_id != mir::graphics::DisplayConfigurationOutputId{0})
751+ {
752+ Rectangle rect{parameters.top_left, parameters.size};
753+ display_layout->place_in_output(parameters.output_id, rect);
754+ parameters.top_left = rect.top_left;
755+ parameters.size = rect.size;
756+ parameters.state = mir_surface_state_fullscreen;
757+ positioned = true;
758+ }
759+ else if (!parent) // No parent => client can't suggest positioning
760+ {
761+ if (auto const default_surface = session->default_surface())
762+ {
763+ static Displacement const offset{title_bar_height, title_bar_height};
764+
765+ parameters.top_left = default_surface->top_left() + offset;
766+
767+ geometry::Rectangle display_for_app{default_surface->top_left(), default_surface->size()};
768+
769+ display_layout->size_to_output(display_for_app);
770+
771+ positioned = display_for_app.overlaps(Rectangle{parameters.top_left, parameters.size});
772+ }
773+ }
774+
775+ if (parent && parameters.aux_rect.is_set() && parameters.edge_attachment.is_set())
776+ {
777+ auto const edge_attachment = parameters.edge_attachment.value();
778+ auto const aux_rect = parameters.aux_rect.value();
779+ auto const parent_top_left = parent->top_left();
780+ auto const top_left = aux_rect.top_left -Point{} + parent_top_left;
781+ auto const top_right= aux_rect.top_right() -Point{} + parent_top_left;
782+ auto const bot_left = aux_rect.bottom_left()-Point{} + parent_top_left;
783+
784+ if (edge_attachment & mir_edge_attachment_vertical)
785+ {
786+ if (active_display.contains(top_right + Displacement{width, height}))
787+ {
788+ parameters.top_left = top_right;
789+ positioned = true;
790+ }
791+ else if (active_display.contains(top_left + Displacement{-width, height}))
792+ {
793+ parameters.top_left = top_left + Displacement{-width, 0};
794+ positioned = true;
795+ }
796+ }
797+
798+ if (edge_attachment & mir_edge_attachment_horizontal)
799+ {
800+ if (active_display.contains(bot_left + Displacement{width, height}))
801+ {
802+ parameters.top_left = bot_left;
803+ positioned = true;
804+ }
805+ else if (active_display.contains(top_left + Displacement{width, -height}))
806+ {
807+ parameters.top_left = top_left + Displacement{0, -height};
808+ positioned = true;
809+ }
810+ }
811+ }
812+ else if (parent)
813+ {
814+ // o Otherwise, if the dialog is not the same as any previous dialog for the
815+ // same parent window, and/or it does not have user-customized position:
816+ // o It should be optically centered relative to its parent, unless this
817+ // would overlap or cover the title bar of the parent.
818+ // o Otherwise, it should be cascaded vertically (but not horizontally)
819+ // relative to its parent, unless, this would cause at least part of
820+ // it to extend into shell space.
821+ auto const parent_top_left = parent->top_left();
822+ auto const centred = parent_top_left
823+ + 0.5*(as_displacement(parent->size()) - as_displacement(parameters.size))
824+ - DeltaY{(parent->size().height.as_int()-height)/6};
825+
826+ parameters.top_left = centred;
827+ positioned = true;
828+ }
829+
830+ if (!positioned)
831+ {
832+ auto const centred = active_display.top_left
833+ + 0.5*(as_displacement(active_display.size) - as_displacement(parameters.size))
834+ - DeltaY{(active_display.size.height.as_int()-height)/6};
835+
836+ switch (parameters.state.value())
837+ {
838+ case mir_surface_state_fullscreen:
839+ case mir_surface_state_maximized:
840+ parameters.top_left = active_display.top_left;
841+ parameters.size = active_display.size;
842+ break;
843+
844+ case mir_surface_state_vertmaximized:
845+ parameters.top_left = centred;
846+ parameters.top_left.y = active_display.top_left.y;
847+ parameters.size.height = active_display.size.height;
848+ break;
849+
850+ case mir_surface_state_horizmaximized:
851+ parameters.top_left = centred;
852+ parameters.top_left.x = active_display.top_left.x;
853+ parameters.size.width = active_display.size.width;
854+ break;
855+
856+ default:
857+ parameters.top_left = centred;
858+ }
859+
860+ if (parameters.top_left.y < display_area.top_left.y)
861+ parameters.top_left.y = display_area.top_left.y;
862+ }
863+
864+ if (parameters.state != mir_surface_state_fullscreen && needs_titlebar)
865+ {
866+ parameters.top_left.y = parameters.top_left.y + DeltaY{title_bar_height};
867+ parameters.size.height = parameters.size.height - DeltaY{title_bar_height};
868+ }
869+
870+ return parameters;
871+}
872+
873+void me::CanonicalWindowManagerPolicyCopy::generate_decorations_for(
874+ std::shared_ptr<scene::Session> const& session,
875+ std::shared_ptr<scene::Surface> const& surface,
876+ SurfaceInfoMap& surface_map,
877+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build)
878+{
879+ if (!SurfaceInfo::needs_titlebar(surface->type()))
880+ return;
881+
882+ auto format = mir_pixel_format_xrgb_8888;
883+ ms::SurfaceCreationParameters params;
884+ params.of_size(titlebar_size_for_window(surface->size()))
885+ .of_name("decoration")
886+ .of_pixel_format(format)
887+ .of_buffer_usage(mir::graphics::BufferUsage::software)
888+ .of_position(titlebar_position_for_window(surface->top_left()))
889+ .of_type(mir_surface_type_gloss);
890+ auto id = build(session, params);
891+ auto titlebar = session->surface(id);
892+ titlebar->set_alpha(0.9);
893+
894+ auto& surface_info = tools->info_for(surface);
895+ surface_info.titlebar = titlebar;
896+ surface_info.titlebar_id = id;
897+ surface_info.children.push_back(titlebar);
898+
899+ SurfaceInfo& titlebar_info =
900+ surface_map.emplace(titlebar, SurfaceInfo{session, titlebar, {}}).first->second;
901+ titlebar_info.is_titlebar = true;
902+ titlebar_info.parent = surface;
903+ titlebar_info.init_titlebar(titlebar);
904+}
905+
906+void me::CanonicalWindowManagerPolicyCopy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
907+{
908+ auto& surface_info = tools->info_for(surface);
909+ if (auto const parent = surface_info.parent.lock())
910+ {
911+ tools->info_for(parent).children.push_back(surface);
912+ }
913+
914+ tools->info_for(session).surfaces.push_back(surface);
915+
916+ if (surface_info.can_be_active())
917+ {
918+ surface->add_observer(std::make_shared<shell::SurfaceReadyObserver>(
919+ [this](std::shared_ptr<scene::Session> const& /*session*/,
920+ std::shared_ptr<scene::Surface> const& surface)
921+ {
922+ select_active_surface(surface);
923+ },
924+ session,
925+ surface));
926+ }
927+
928+ if (surface_info.state == mir_surface_state_fullscreen)
929+ fullscreen_surfaces.insert(surface);
930+}
931+
932+void me::CanonicalWindowManagerPolicyCopy::handle_modify_surface(
933+ std::shared_ptr<scene::Session> const& session,
934+ std::shared_ptr<scene::Surface> const& surface,
935+ shell::SurfaceSpecification const& modifications)
936+{
937+ auto& surface_info_old = tools->info_for(surface);
938+
939+ auto surface_info = surface_info_old;
940+
941+ if (modifications.parent.is_set())
942+ surface_info.parent = modifications.parent.value();
943+
944+ if (modifications.type.is_set() &&
945+ surface_info.type != modifications.type.value())
946+ {
947+ auto const new_type = modifications.type.value();
948+
949+ if (!surface_info.can_morph_to(new_type))
950+ {
951+ throw std::runtime_error("Unsupported surface type change");
952+ }
953+
954+ surface_info.type = new_type;
955+
956+ if (surface_info.must_not_have_parent())
957+ {
958+ if (modifications.parent.is_set())
959+ throw std::runtime_error("Target surface type does not support parent");
960+
961+ surface_info.parent.reset();
962+ }
963+ else if (surface_info.must_have_parent())
964+ {
965+ if (!surface_info.parent.lock())
966+ throw std::runtime_error("Target surface type requires parent");
967+ }
968+
969+ surface->configure(mir_surface_attrib_type, new_type);
970+ }
971+
972+ #define COPY_IF_SET(field)\
973+ if (modifications.field.is_set())\
974+ surface_info.field = modifications.field.value()
975+
976+ COPY_IF_SET(min_width);
977+ COPY_IF_SET(min_height);
978+ COPY_IF_SET(max_width);
979+ COPY_IF_SET(max_height);
980+ COPY_IF_SET(min_width);
981+ COPY_IF_SET(width_inc);
982+ COPY_IF_SET(height_inc);
983+ COPY_IF_SET(min_aspect);
984+ COPY_IF_SET(max_aspect);
985+ COPY_IF_SET(output_id);
986+
987+ #undef COPY_IF_SET
988+
989+ std::swap(surface_info, surface_info_old);
990+
991+ if (modifications.name.is_set())
992+ surface->rename(modifications.name.value());
993+
994+ if (modifications.streams.is_set())
995+ {
996+ auto v = modifications.streams.value();
997+ std::vector<shell::StreamSpecification> l (v.begin(), v.end());
998+ session->configure_streams(*surface, l);
999+ }
1000+
1001+ if (modifications.input_shape.is_set())
1002+ {
1003+ surface->set_input_region(modifications.input_shape.value());
1004+ }
1005+
1006+ if (modifications.width.is_set() || modifications.height.is_set())
1007+ {
1008+ auto new_size = surface->size();
1009+
1010+ if (modifications.width.is_set())
1011+ new_size.width = modifications.width.value();
1012+
1013+ if (modifications.height.is_set())
1014+ new_size.height = modifications.height.value();
1015+
1016+ auto top_left = surface->top_left();
1017+
1018+ surface_info.constrain_resize(
1019+ surface,
1020+ top_left,
1021+ new_size,
1022+ false,
1023+ false,
1024+ display_area);
1025+
1026+ apply_resize(surface, surface_info.titlebar, top_left, new_size);
1027+ }
1028+
1029+ if (modifications.state.is_set())
1030+ {
1031+ auto const state = handle_set_state(surface, modifications.state.value());
1032+ surface->configure(mir_surface_attrib_state, state);
1033+ }
1034+}
1035+
1036+void me::CanonicalWindowManagerPolicyCopy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
1037+{
1038+ fullscreen_surfaces.erase(surface);
1039+
1040+ auto& info = tools->info_for(surface);
1041+
1042+ if (auto const parent = info.parent.lock())
1043+ {
1044+ auto& siblings = tools->info_for(parent).children;
1045+
1046+ for (auto i = begin(siblings); i != end(siblings); ++i)
1047+ {
1048+ if (surface.lock() == i->lock())
1049+ {
1050+ siblings.erase(i);
1051+ break;
1052+ }
1053+ }
1054+ }
1055+
1056+ session->destroy_surface(surface);
1057+ if (info.titlebar)
1058+ {
1059+ session->destroy_surface(info.titlebar_id);
1060+ tools->forget(info.titlebar);
1061+ }
1062+
1063+ auto& surfaces = tools->info_for(session).surfaces;
1064+
1065+ for (auto i = begin(surfaces); i != end(surfaces); ++i)
1066+ {
1067+ if (surface.lock() == i->lock())
1068+ {
1069+ surfaces.erase(i);
1070+ break;
1071+ }
1072+ }
1073+
1074+ if (surfaces.empty() && session == tools->focused_session())
1075+ {
1076+ active_surface_.reset();
1077+ tools->focus_next_session();
1078+ select_active_surface(tools->focused_surface());
1079+ }
1080+}
1081+
1082+int me::CanonicalWindowManagerPolicyCopy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirSurfaceState value)
1083+{
1084+ auto& info = tools->info_for(surface);
1085+
1086+ switch (value)
1087+ {
1088+ case mir_surface_state_restored:
1089+ case mir_surface_state_maximized:
1090+ case mir_surface_state_vertmaximized:
1091+ case mir_surface_state_horizmaximized:
1092+ case mir_surface_state_fullscreen:
1093+ case mir_surface_state_hidden:
1094+ case mir_surface_state_minimized:
1095+ break;
1096+
1097+ default:
1098+ return info.state;
1099+ }
1100+
1101+ if (info.state == mir_surface_state_restored)
1102+ {
1103+ info.restore_rect = {surface->top_left(), surface->size()};
1104+ }
1105+
1106+ if (info.state != mir_surface_state_fullscreen)
1107+ {
1108+ info.output_id = decltype(info.output_id){};
1109+ fullscreen_surfaces.erase(surface);
1110+ }
1111+ else
1112+ {
1113+ fullscreen_surfaces.insert(surface);
1114+ }
1115+
1116+ if (info.state == value)
1117+ {
1118+ return info.state;
1119+ }
1120+
1121+ auto const old_pos = surface->top_left();
1122+ Displacement movement;
1123+
1124+ switch (value)
1125+ {
1126+ case mir_surface_state_restored:
1127+ movement = info.restore_rect.top_left - old_pos;
1128+ surface->resize(info.restore_rect.size);
1129+ if (info.titlebar)
1130+ {
1131+ info.titlebar->resize(titlebar_size_for_window(info.restore_rect.size));
1132+ info.titlebar->show();
1133+ }
1134+ break;
1135+
1136+ case mir_surface_state_maximized:
1137+ movement = display_area.top_left - old_pos;
1138+ surface->resize(display_area.size);
1139+ if (info.titlebar)
1140+ info.titlebar->hide();
1141+ break;
1142+
1143+ case mir_surface_state_horizmaximized:
1144+ movement = Point{display_area.top_left.x, info.restore_rect.top_left.y} - old_pos;
1145+ surface->resize({display_area.size.width, info.restore_rect.size.height});
1146+ if (info.titlebar)
1147+ {
1148+ info.titlebar->resize(titlebar_size_for_window({display_area.size.width, info.restore_rect.size.height}));
1149+ info.titlebar->show();
1150+ }
1151+ break;
1152+
1153+ case mir_surface_state_vertmaximized:
1154+ movement = Point{info.restore_rect.top_left.x, display_area.top_left.y} - old_pos;
1155+ surface->resize({info.restore_rect.size.width, display_area.size.height});
1156+ if (info.titlebar)
1157+ info.titlebar->hide();
1158+ break;
1159+
1160+ case mir_surface_state_fullscreen:
1161+ {
1162+ Rectangle rect{old_pos, surface->size()};
1163+
1164+ if (info.output_id.is_set())
1165+ {
1166+ display_layout->place_in_output(info.output_id.value(), rect);
1167+ }
1168+ else
1169+ {
1170+ display_layout->size_to_output(rect);
1171+ }
1172+
1173+ movement = rect.top_left - old_pos;
1174+ surface->resize(rect.size);
1175+ break;
1176+ }
1177+
1178+ case mir_surface_state_hidden:
1179+ case mir_surface_state_minimized:
1180+ if (info.titlebar)
1181+ info.titlebar->hide();
1182+ surface->hide();
1183+ return info.state = value;
1184+
1185+ default:
1186+ break;
1187+ }
1188+
1189+ // TODO It is rather simplistic to move a tree WRT the top_left of the root
1190+ // TODO when resizing. But for more sophistication we would need to encode
1191+ // TODO some sensible layout rules.
1192+ move_tree(surface, movement);
1193+
1194+ info.state = value;
1195+
1196+ if (info.is_visible())
1197+ surface->show();
1198+
1199+ return info.state;
1200+}
1201+
1202+void me::CanonicalWindowManagerPolicyCopy::drag(Point cursor)
1203+{
1204+ select_active_surface(tools->surface_at(old_cursor));
1205+ drag(active_surface(), cursor, old_cursor, display_area);
1206+}
1207+
1208+void me::CanonicalWindowManagerPolicyCopy::handle_raise_surface(
1209+ std::shared_ptr<ms::Session> const& /*session*/,
1210+ std::shared_ptr<ms::Surface> const& surface)
1211+{
1212+ select_active_surface(surface);
1213+}
1214+
1215+bool me::CanonicalWindowManagerPolicyCopy::handle_keyboard_event(MirKeyboardEvent const* event)
1216+{
1217+ auto const action = mir_keyboard_event_action(event);
1218+ auto const scan_code = mir_keyboard_event_scan_code(event);
1219+ auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
1220+
1221+ if (action == mir_keyboard_action_down && scan_code == KEY_F11)
1222+ {
1223+ switch (modifiers)
1224+ {
1225+ case mir_input_event_modifier_alt:
1226+ toggle(mir_surface_state_maximized);
1227+ return true;
1228+
1229+ case mir_input_event_modifier_shift:
1230+ toggle(mir_surface_state_vertmaximized);
1231+ return true;
1232+
1233+ case mir_input_event_modifier_ctrl:
1234+ toggle(mir_surface_state_horizmaximized);
1235+ return true;
1236+
1237+ default:
1238+ break;
1239+ }
1240+ }
1241+ else if (action == mir_keyboard_action_down && scan_code == KEY_F4)
1242+ {
1243+ if (auto const session = tools->focused_session())
1244+ {
1245+ switch (modifiers)
1246+ {
1247+ case mir_input_event_modifier_alt:
1248+ kill(session->process_id(), SIGTERM);
1249+ return true;
1250+
1251+ case mir_input_event_modifier_ctrl:
1252+ if (auto const surf = session->default_surface())
1253+ {
1254+ surf->request_client_surface_close();
1255+ return true;
1256+ }
1257+
1258+ default:
1259+ break;
1260+ }
1261+ }
1262+ }
1263+ else if (action == mir_keyboard_action_down &&
1264+ modifiers == mir_input_event_modifier_alt &&
1265+ scan_code == KEY_TAB)
1266+ {
1267+ tools->focus_next_session();
1268+ if (auto const surface = tools->focused_surface())
1269+ select_active_surface(surface);
1270+
1271+ return true;
1272+ }
1273+ else if (action == mir_keyboard_action_down &&
1274+ modifiers == mir_input_event_modifier_alt &&
1275+ scan_code == KEY_GRAVE)
1276+ {
1277+ if (auto const prev = tools->focused_surface())
1278+ {
1279+ if (auto const app = tools->focused_session())
1280+ select_active_surface(app->surface_after(prev));
1281+ }
1282+
1283+ return true;
1284+ }
1285+
1286+ return false;
1287+}
1288+
1289+bool me::CanonicalWindowManagerPolicyCopy::handle_touch_event(MirTouchEvent const* event)
1290+{
1291+ auto const count = mir_touch_event_point_count(event);
1292+
1293+ long total_x = 0;
1294+ long total_y = 0;
1295+
1296+ for (auto i = 0U; i != count; ++i)
1297+ {
1298+ total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x);
1299+ total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y);
1300+ }
1301+
1302+ Point const cursor{total_x/count, total_y/count};
1303+
1304+ bool is_drag = true;
1305+ for (auto i = 0U; i != count; ++i)
1306+ {
1307+ switch (mir_touch_event_action(event, i))
1308+ {
1309+ case mir_touch_action_up:
1310+ return false;
1311+
1312+ case mir_touch_action_down:
1313+ is_drag = false;
1314+
1315+ case mir_touch_action_change:
1316+ continue;
1317+ }
1318+ }
1319+
1320+ bool consumes_event = false;
1321+ if (is_drag)
1322+ {
1323+ switch (count)
1324+ {
1325+ case 2:
1326+ resize(cursor);
1327+ consumes_event = true;
1328+ break;
1329+
1330+ case 3:
1331+ drag(cursor);
1332+ consumes_event = true;
1333+ break;
1334+ }
1335+ }
1336+
1337+ old_cursor = cursor;
1338+ return consumes_event;
1339+}
1340+
1341+bool me::CanonicalWindowManagerPolicyCopy::handle_pointer_event(MirPointerEvent const* event)
1342+{
1343+ auto const action = mir_pointer_event_action(event);
1344+ auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask;
1345+ Point const cursor{
1346+ mir_pointer_event_axis_value(event, mir_pointer_axis_x),
1347+ mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
1348+
1349+ bool consumes_event = false;
1350+
1351+ if (action == mir_pointer_action_button_down)
1352+ {
1353+ click(cursor);
1354+ }
1355+ else if (action == mir_pointer_action_motion &&
1356+ modifiers == mir_input_event_modifier_alt)
1357+ {
1358+ if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
1359+ {
1360+ drag(cursor);
1361+ consumes_event = true;
1362+ }
1363+
1364+ if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary))
1365+ {
1366+ resize(cursor);
1367+ consumes_event = true;
1368+ }
1369+ }
1370+ else if (action == mir_pointer_action_motion && !modifiers)
1371+ {
1372+ if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
1373+ {
1374+ if (auto const possible_titlebar = tools->surface_at(old_cursor))
1375+ {
1376+ if (tools->info_for(possible_titlebar).is_titlebar)
1377+ {
1378+ drag(cursor);
1379+ consumes_event = true;
1380+ }
1381+ }
1382+ }
1383+ }
1384+
1385+ old_cursor = cursor;
1386+ return consumes_event;
1387+}
1388+
1389+void me::CanonicalWindowManagerPolicyCopy::toggle(MirSurfaceState state)
1390+{
1391+ if (auto const surface = active_surface())
1392+ {
1393+ auto& info = tools->info_for(surface);
1394+
1395+ if (info.state == state)
1396+ state = mir_surface_state_restored;
1397+
1398+ auto const value = handle_set_state(surface, MirSurfaceState(state));
1399+ surface->configure(mir_surface_attrib_state, value);
1400+ }
1401+}
1402+
1403+void me::CanonicalWindowManagerPolicyCopy::select_active_surface(std::shared_ptr<ms::Surface> const& surface)
1404+{
1405+ if (surface == active_surface_.lock())
1406+ return;
1407+
1408+ if (!surface)
1409+ {
1410+ if (auto const active_surface = active_surface_.lock())
1411+ {
1412+ if (auto const titlebar = tools->info_for(active_surface).titlebar)
1413+ {
1414+ tools->info_for(titlebar).paint_titlebar(0x3F);
1415+ }
1416+ }
1417+
1418+ if (active_surface_.lock())
1419+ tools->set_focus_to({}, {});
1420+
1421+ active_surface_.reset();
1422+ return;
1423+ }
1424+
1425+ auto const& info_for = tools->info_for(surface);
1426+
1427+ if (info_for.can_be_active())
1428+ {
1429+ if (auto const active_surface = active_surface_.lock())
1430+ {
1431+ if (auto const titlebar = tools->info_for(active_surface).titlebar)
1432+ {
1433+ tools->info_for(titlebar).paint_titlebar(0x3F);
1434+ }
1435+ }
1436+ if (auto const titlebar = tools->info_for(surface).titlebar)
1437+ {
1438+ tools->info_for(titlebar).paint_titlebar(0xFF);
1439+ }
1440+ tools->set_focus_to(info_for.session.lock(), surface);
1441+ tools->raise_tree(surface);
1442+ active_surface_ = surface;
1443+ }
1444+ else
1445+ {
1446+ // Cannot have input focus - try the parent
1447+ if (auto const parent = info_for.parent.lock())
1448+ select_active_surface(parent);
1449+ }
1450+}
1451+
1452+auto me::CanonicalWindowManagerPolicyCopy::active_surface() const
1453+-> std::shared_ptr<ms::Surface>
1454+{
1455+ if (auto const surface = active_surface_.lock())
1456+ return surface;
1457+
1458+ if (auto const session = tools->focused_session())
1459+ {
1460+ if (auto const surface = session->default_surface())
1461+ return surface;
1462+ }
1463+
1464+ return std::shared_ptr<ms::Surface>{};
1465+}
1466+
1467+bool me::CanonicalWindowManagerPolicyCopy::resize(std::shared_ptr<ms::Surface> const& surface, Point cursor, Point old_cursor, Rectangle bounds)
1468+{
1469+ if (!surface || !surface->input_area_contains(old_cursor))
1470+ return false;
1471+
1472+ auto const top_left = surface->top_left();
1473+ Rectangle const old_pos{top_left, surface->size()};
1474+
1475+ auto anchor = top_left;
1476+
1477+ for (auto const& corner : {
1478+ old_pos.top_right(),
1479+ old_pos.bottom_left(),
1480+ old_pos.bottom_right()})
1481+ {
1482+ if ((old_cursor - anchor).length_squared() <
1483+ (old_cursor - corner).length_squared())
1484+ {
1485+ anchor = corner;
1486+ }
1487+ }
1488+
1489+ bool const left_resize = anchor.x != top_left.x;
1490+ bool const top_resize = anchor.y != top_left.y;
1491+ int const x_sign = left_resize? -1 : 1;
1492+ int const y_sign = top_resize? -1 : 1;
1493+
1494+ auto const delta = cursor-old_cursor;
1495+
1496+ Size new_size{old_pos.size.width + x_sign*delta.dx, old_pos.size.height + y_sign*delta.dy};
1497+
1498+ Point new_pos = top_left + left_resize*delta.dx + top_resize*delta.dy;
1499+
1500+
1501+ auto const& surface_info = tools->info_for(surface);
1502+
1503+ surface_info.constrain_resize(surface, new_pos, new_size, left_resize, top_resize, bounds);
1504+
1505+ apply_resize(surface, surface_info.titlebar, new_pos, new_size);
1506+
1507+ return true;
1508+}
1509+
1510+void me::CanonicalWindowManagerPolicyCopy::apply_resize(
1511+ std::shared_ptr<ms::Surface> const& surface,
1512+ std::shared_ptr<ms::Surface> const& titlebar,
1513+ Point const& new_pos,
1514+ Size const& new_size) const
1515+{
1516+ if (titlebar)
1517+ titlebar->resize({new_size.width, Height{title_bar_height}});
1518+
1519+ surface->resize(new_size);
1520+
1521+ move_tree(surface, new_pos-surface->top_left());
1522+}
1523+
1524+bool me::CanonicalWindowManagerPolicyCopy::drag(std::shared_ptr<ms::Surface> surface, Point to, Point from, Rectangle /*bounds*/)
1525+{
1526+ if (!surface)
1527+ return false;
1528+
1529+ if (!surface->input_area_contains(from) && !tools->info_for(surface).titlebar)
1530+ return false;
1531+
1532+ auto movement = to - from;
1533+
1534+ // placeholder - constrain onscreen
1535+
1536+ switch (tools->info_for(surface).state)
1537+ {
1538+ case mir_surface_state_restored:
1539+ break;
1540+
1541+ // "A vertically maximised surface is anchored to the top and bottom of
1542+ // the available workspace and can have any width."
1543+ case mir_surface_state_vertmaximized:
1544+ movement.dy = DeltaY(0);
1545+ break;
1546+
1547+ // "A horizontally maximised surface is anchored to the left and right of
1548+ // the available workspace and can have any height"
1549+ case mir_surface_state_horizmaximized:
1550+ movement.dx = DeltaX(0);
1551+ break;
1552+
1553+ // "A maximised surface is anchored to the top, bottom, left and right of the
1554+ // available workspace. For example, if the launcher is always-visible then
1555+ // the left-edge of the surface is anchored to the right-edge of the launcher."
1556+ case mir_surface_state_maximized:
1557+ case mir_surface_state_fullscreen:
1558+ default:
1559+ return true;
1560+ }
1561+
1562+ move_tree(surface, movement);
1563+
1564+ return true;
1565+}
1566+
1567+void me::CanonicalWindowManagerPolicyCopy::move_tree(std::shared_ptr<ms::Surface> const& root, Displacement movement) const
1568+{
1569+ root->move_to(root->top_left() + movement);
1570+
1571+ for (auto const& child: tools->info_for(root).children)
1572+ {
1573+ move_tree(child.lock(), movement);
1574+ }
1575+}
1576
1577=== added file 'src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.h'
1578--- src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.h 1970-01-01 00:00:00 +0000
1579+++ src/platforms/mirserver/wm-wip/server_example_canonical_window_manager.h 2016-01-20 19:52:54 +0000
1580@@ -0,0 +1,132 @@
1581+/*
1582+ * Copyright © 2015 Canonical Ltd.
1583+ *
1584+ * This program is free software: you can redistribute it and/or modify it
1585+ * under the terms of the GNU General Public License version 3,
1586+ * as published by the Free Software Foundation.
1587+ *
1588+ * This program is distributed in the hope that it will be useful,
1589+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1590+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1591+ * GNU General Public License for more details.
1592+ *
1593+ * You should have received a copy of the GNU General Public License
1594+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1595+ *
1596+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
1597+ */
1598+
1599+#ifndef MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_
1600+#define MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_
1601+
1602+#include "server_example_basic_window_manager.h"
1603+
1604+#include "mir/geometry/displacement.h"
1605+
1606+#include <atomic>
1607+#include <set>
1608+
1609+///\example server_example_canonical_window_manager.h
1610+// Based on "Mir and Unity: Surfaces, input, and displays (v0.3)"
1611+
1612+namespace mir
1613+{
1614+namespace shell { class DisplayLayout; }
1615+namespace examples
1616+{
1617+// standard window management algorithm:
1618+// o Switch apps: tap or click on the corresponding tile
1619+// o Move window: Alt-leftmousebutton drag (three finger drag)
1620+// o Resize window: Alt-middle_button drag (two finger drag)
1621+// o Maximize/restore current window (to display size): Alt-F11
1622+// o Maximize/restore current window (to display height): Shift-F11
1623+// o Maximize/restore current window (to display width): Ctrl-F11
1624+// o client requests to maximize, vertically maximize & restore
1625+class CanonicalWindowManagerPolicyCopy : public WindowManagementPolicy
1626+{
1627+public:
1628+
1629+ explicit CanonicalWindowManagerPolicyCopy(
1630+ WindowManagerTools* const tools,
1631+ std::shared_ptr<shell::DisplayLayout> const& display_layout);
1632+
1633+ void click(geometry::Point cursor);
1634+
1635+ void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays);
1636+
1637+ void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays);
1638+
1639+ void resize(geometry::Point cursor);
1640+
1641+ auto handle_place_new_surface(
1642+ std::shared_ptr<scene::Session> const& session,
1643+ scene::SurfaceCreationParameters const& request_parameters)
1644+ -> scene::SurfaceCreationParameters;
1645+
1646+ void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface);
1647+
1648+ void handle_modify_surface(
1649+ std::shared_ptr<scene::Session> const& session,
1650+ std::shared_ptr<scene::Surface> const& surface,
1651+ shell::SurfaceSpecification const& modifications);
1652+
1653+ void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface);
1654+
1655+ int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirSurfaceState value);
1656+
1657+ void drag(geometry::Point cursor);
1658+
1659+ bool handle_keyboard_event(MirKeyboardEvent const* event);
1660+
1661+ bool handle_touch_event(MirTouchEvent const* event);
1662+
1663+ bool handle_pointer_event(MirPointerEvent const* event);
1664+
1665+ void handle_raise_surface(
1666+ std::shared_ptr<scene::Session> const& session,
1667+ std::shared_ptr<scene::Surface> const& surface);
1668+
1669+ void generate_decorations_for(
1670+ std::shared_ptr<scene::Session> const& session,
1671+ std::shared_ptr<scene::Surface> const& surface,
1672+ SurfaceInfoMap& surface_map,
1673+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const& session, scene::SurfaceCreationParameters const& params)> const& build);
1674+
1675+private:
1676+ static const int modifier_mask =
1677+ mir_input_event_modifier_alt |
1678+ mir_input_event_modifier_shift |
1679+ mir_input_event_modifier_sym |
1680+ mir_input_event_modifier_ctrl |
1681+ mir_input_event_modifier_meta;
1682+
1683+ void toggle(MirSurfaceState state);
1684+
1685+ // "Mir and Unity: Surfaces, input, and displays (v0.3)" talks about active
1686+ // *window*,but Mir really only understands surfaces
1687+ void select_active_surface(std::shared_ptr<scene::Surface> const& surface);
1688+ auto active_surface() const -> std::shared_ptr<scene::Surface>;
1689+
1690+ bool resize(std::shared_ptr<scene::Surface> const& surface, geometry::Point cursor, geometry::Point old_cursor, geometry::Rectangle bounds);
1691+ bool drag(std::shared_ptr<scene::Surface> surface, geometry::Point to, geometry::Point from, geometry::Rectangle bounds);
1692+ void move_tree(std::shared_ptr<scene::Surface> const& root, geometry::Displacement movement) const;
1693+ void apply_resize(
1694+ std::shared_ptr<mir::scene::Surface> const& surface,
1695+ std::shared_ptr<mir::scene::Surface> const& titlebar,
1696+ geometry::Point const& new_pos,
1697+ geometry::Size const& new_size) const;
1698+
1699+ WindowManagerTools* const tools;
1700+ std::shared_ptr<shell::DisplayLayout> const display_layout;
1701+
1702+ geometry::Rectangle display_area;
1703+ geometry::Point old_cursor{};
1704+ std::weak_ptr<scene::Surface> active_surface_;
1705+ using FullscreenSurfaces = std::set<std::weak_ptr<scene::Surface>, std::owner_less<std::weak_ptr<scene::Surface>>>;
1706+
1707+ FullscreenSurfaces fullscreen_surfaces;
1708+};
1709+}
1710+}
1711+
1712+#endif /* MIR_EXAMPLE_CANONICAL_WINDOW_MANAGER_H_ */
1713
1714=== added file 'src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.cpp'
1715--- src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.cpp 1970-01-01 00:00:00 +0000
1716+++ src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.cpp 2016-01-20 19:52:54 +0000
1717@@ -0,0 +1,640 @@
1718+/*
1719+ * Copyright © 2015 Canonical Ltd.
1720+ *
1721+ * This program is free software: you can redistribute it and/or modify it
1722+ * under the terms of the GNU General Public License version 3,
1723+ * as published by the Free Software Foundation.
1724+ *
1725+ * This program is distributed in the hope that it will be useful,
1726+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1727+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1728+ * GNU General Public License for more details.
1729+ *
1730+ * You should have received a copy of the GNU General Public License
1731+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1732+ *
1733+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
1734+ */
1735+
1736+#include "server_example_tiling_window_manager.h"
1737+
1738+#include "mir/scene/session.h"
1739+#include "mir/scene/surface.h"
1740+#include "mir/scene/surface_creation_parameters.h"
1741+#include "mir/shell/surface_specification.h"
1742+#include "mir/shell/surface_stack.h"
1743+#include "mir/shell/surface_ready_observer.h"
1744+#include "mir/geometry/displacement.h"
1745+
1746+#include <linux/input.h>
1747+#include <csignal>
1748+
1749+namespace me = mir::examples;
1750+namespace ms = mir::scene;
1751+namespace mf = mir::frontend;
1752+using namespace mir::geometry;
1753+
1754+///\example server_example_tiling_window_manager.cpp
1755+/// Demonstrate implementing a simple tiling algorithm
1756+
1757+me::TilingWindowManagerPolicy::TilingWindowManagerPolicy(WindowManagerTools* const tools) :
1758+ tools{tools}
1759+{
1760+}
1761+
1762+void me::TilingWindowManagerPolicy::click(Point cursor)
1763+{
1764+ auto const session = session_under(cursor);
1765+ auto const surface = tools->surface_at(cursor);
1766+ select_active_surface(session, surface);
1767+}
1768+
1769+void me::TilingWindowManagerPolicy::handle_session_info_updated(SessionInfoMap& session_info, Rectangles const& displays)
1770+{
1771+ update_tiles(session_info, displays);
1772+}
1773+
1774+void me::TilingWindowManagerPolicy::handle_displays_updated(SessionInfoMap& session_info, Rectangles const& displays)
1775+{
1776+ update_tiles(session_info, displays);
1777+}
1778+
1779+void me::TilingWindowManagerPolicy::resize(Point cursor)
1780+{
1781+ if (auto const session = session_under(cursor))
1782+ {
1783+ if (session == session_under(old_cursor))
1784+ {
1785+ if (auto const surface = select_active_surface(session, tools->surface_at(old_cursor)))
1786+ {
1787+ resize(surface, cursor, old_cursor, tools->info_for(session).tile);
1788+ }
1789+ }
1790+ }
1791+}
1792+
1793+auto me::TilingWindowManagerPolicy::handle_place_new_surface(
1794+ std::shared_ptr<ms::Session> const& session,
1795+ ms::SurfaceCreationParameters const& request_parameters)
1796+-> ms::SurfaceCreationParameters
1797+{
1798+ auto parameters = request_parameters;
1799+
1800+ Rectangle const& tile = tools->info_for(session).tile;
1801+ parameters.top_left = parameters.top_left + (tile.top_left - Point{0, 0});
1802+
1803+ if (auto const parent = parameters.parent.lock())
1804+ {
1805+ auto const width = parameters.size.width.as_int();
1806+ auto const height = parameters.size.height.as_int();
1807+
1808+ if (parameters.aux_rect.is_set() && parameters.edge_attachment.is_set())
1809+ {
1810+ auto const edge_attachment = parameters.edge_attachment.value();
1811+ auto const aux_rect = parameters.aux_rect.value();
1812+ auto const parent_top_left = parent->top_left();
1813+ auto const top_left = aux_rect.top_left -Point{} + parent_top_left;
1814+ auto const top_right= aux_rect.top_right() -Point{} + parent_top_left;
1815+ auto const bot_left = aux_rect.bottom_left()-Point{} + parent_top_left;
1816+
1817+ if (edge_attachment & mir_edge_attachment_vertical)
1818+ {
1819+ if (tile.contains(top_right + Displacement{width, height}))
1820+ {
1821+ parameters.top_left = top_right;
1822+ }
1823+ else if (tile.contains(top_left + Displacement{-width, height}))
1824+ {
1825+ parameters.top_left = top_left + Displacement{-width, 0};
1826+ }
1827+ }
1828+
1829+ if (edge_attachment & mir_edge_attachment_horizontal)
1830+ {
1831+ if (tile.contains(bot_left + Displacement{width, height}))
1832+ {
1833+ parameters.top_left = bot_left;
1834+ }
1835+ else if (tile.contains(top_left + Displacement{width, -height}))
1836+ {
1837+ parameters.top_left = top_left + Displacement{0, -height};
1838+ }
1839+ }
1840+ }
1841+ else
1842+ {
1843+ auto const parent_top_left = parent->top_left();
1844+ auto const centred = parent_top_left
1845+ + 0.5*(as_displacement(parent->size()) - as_displacement(parameters.size))
1846+ - DeltaY{(parent->size().height.as_int()-height)/6};
1847+
1848+ parameters.top_left = centred;
1849+ }
1850+ }
1851+
1852+ clip_to_tile(parameters, tile);
1853+ return parameters;
1854+}
1855+
1856+void me::TilingWindowManagerPolicy::generate_decorations_for(
1857+ std::shared_ptr<ms::Session> const&,
1858+ std::shared_ptr<ms::Surface> const&,
1859+ SurfaceInfoMap&,
1860+ std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const&, ms::SurfaceCreationParameters const&)> const&)
1861+{
1862+}
1863+
1864+void me::TilingWindowManagerPolicy::handle_new_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<ms::Surface> const& surface)
1865+{
1866+ tools->info_for(session).surfaces.push_back(surface);
1867+
1868+ auto& surface_info = tools->info_for(surface);
1869+ if (auto const parent = surface_info.parent.lock())
1870+ {
1871+ tools->info_for(parent).children.push_back(surface);
1872+ }
1873+
1874+ if (surface_info.can_be_active())
1875+ {
1876+ surface->add_observer(std::make_shared<shell::SurfaceReadyObserver>(
1877+ [this](std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface)
1878+ { select_active_surface(session, surface); },
1879+ session,
1880+ surface));
1881+ }
1882+}
1883+
1884+void me::TilingWindowManagerPolicy::handle_modify_surface(
1885+ std::shared_ptr<scene::Session> const& /*session*/,
1886+ std::shared_ptr<scene::Surface> const& surface,
1887+ shell::SurfaceSpecification const& modifications)
1888+{
1889+ if (modifications.name.is_set())
1890+ surface->rename(modifications.name.value());
1891+}
1892+
1893+void me::TilingWindowManagerPolicy::handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
1894+{
1895+ auto& info = tools->info_for(surface);
1896+
1897+ if (auto const parent = info.parent.lock())
1898+ {
1899+ auto& siblings = tools->info_for(parent).children;
1900+
1901+ for (auto i = begin(siblings); i != end(siblings); ++i)
1902+ {
1903+ if (surface.lock() == i->lock())
1904+ {
1905+ siblings.erase(i);
1906+ break;
1907+ }
1908+ }
1909+ }
1910+
1911+ auto& surfaces = tools->info_for(session).surfaces;
1912+
1913+ for (auto i = begin(surfaces); i != end(surfaces); ++i)
1914+ {
1915+ if (surface.lock() == i->lock())
1916+ {
1917+ surfaces.erase(i);
1918+ break;
1919+ }
1920+ }
1921+
1922+ session->destroy_surface(surface);
1923+
1924+ if (surfaces.empty() && session == tools->focused_session())
1925+ {
1926+ tools->focus_next_session();
1927+ select_active_surface(tools->focused_session(), tools->focused_surface());
1928+ }
1929+}
1930+
1931+int me::TilingWindowManagerPolicy::handle_set_state(std::shared_ptr<ms::Surface> const& surface, MirSurfaceState value)
1932+{
1933+ auto& info = tools->info_for(surface);
1934+
1935+ switch (value)
1936+ {
1937+ case mir_surface_state_restored:
1938+ case mir_surface_state_maximized:
1939+ case mir_surface_state_vertmaximized:
1940+ case mir_surface_state_horizmaximized:
1941+ break;
1942+
1943+ default:
1944+ return info.state;
1945+ }
1946+
1947+ if (info.state == mir_surface_state_restored)
1948+ {
1949+ info.restore_rect = {surface->top_left(), surface->size()};
1950+ }
1951+
1952+ if (info.state == value)
1953+ {
1954+ return info.state;
1955+ }
1956+
1957+ auto const& tile = tools->info_for(info.session).tile;
1958+
1959+ switch (value)
1960+ {
1961+ case mir_surface_state_restored:
1962+ surface->resize(info.restore_rect.size);
1963+ drag(surface, info.restore_rect.top_left, surface->top_left(), tile);
1964+ break;
1965+
1966+ case mir_surface_state_maximized:
1967+ surface->resize(tile.size);
1968+ drag(surface, tile.top_left, surface->top_left(), tile);
1969+ break;
1970+
1971+ case mir_surface_state_horizmaximized:
1972+ surface->resize({tile.size.width, info.restore_rect.size.height});
1973+ drag(surface, {tile.top_left.x, info.restore_rect.top_left.y}, surface->top_left(), tile);
1974+ break;
1975+
1976+ case mir_surface_state_vertmaximized:
1977+ surface->resize({info.restore_rect.size.width, tile.size.height});
1978+ drag(surface, {info.restore_rect.top_left.x, tile.top_left.y}, surface->top_left(), tile);
1979+ break;
1980+
1981+ default:
1982+ break;
1983+ }
1984+
1985+ return info.state = value;
1986+}
1987+
1988+void me::TilingWindowManagerPolicy::drag(Point cursor)
1989+{
1990+ if (auto const session = session_under(cursor))
1991+ {
1992+ if (session == session_under(old_cursor))
1993+ {
1994+ if (auto const surface = select_active_surface(session, tools->surface_at(old_cursor)))
1995+ {
1996+ drag(surface, cursor, old_cursor, tools->info_for(session).tile);
1997+ }
1998+ }
1999+ }
2000+}
2001+
2002+void me::TilingWindowManagerPolicy::handle_raise_surface(
2003+ std::shared_ptr<ms::Session> const& session,
2004+ std::shared_ptr<ms::Surface> const& surface)
2005+{
2006+ select_active_surface(session, surface);
2007+}
2008+
2009+bool me::TilingWindowManagerPolicy::handle_keyboard_event(MirKeyboardEvent const* event)
2010+{
2011+ auto const action = mir_keyboard_event_action(event);
2012+ auto const scan_code = mir_keyboard_event_scan_code(event);
2013+ auto const modifiers = mir_keyboard_event_modifiers(event) & modifier_mask;
2014+
2015+ if (action == mir_keyboard_action_down && scan_code == KEY_F11)
2016+ {
2017+ switch (modifiers & modifier_mask)
2018+ {
2019+ case mir_input_event_modifier_alt:
2020+ toggle(mir_surface_state_maximized);
2021+ return true;
2022+
2023+ case mir_input_event_modifier_shift:
2024+ toggle(mir_surface_state_vertmaximized);
2025+ return true;
2026+
2027+ case mir_input_event_modifier_ctrl:
2028+ toggle(mir_surface_state_horizmaximized);
2029+ return true;
2030+
2031+ default:
2032+ break;
2033+ }
2034+ }
2035+ else if (action == mir_keyboard_action_down && scan_code == KEY_F4)
2036+ {
2037+ if (auto const session = tools->focused_session())
2038+ {
2039+ switch (modifiers & modifier_mask)
2040+ {
2041+ case mir_input_event_modifier_alt:
2042+ kill(session->process_id(), SIGTERM);
2043+ return true;
2044+
2045+ case mir_input_event_modifier_ctrl:
2046+ if (auto const surf = session->default_surface())
2047+ {
2048+ surf->request_client_surface_close();
2049+ return true;
2050+ }
2051+
2052+ default:
2053+ break;
2054+ }
2055+ }
2056+ }
2057+ else if (action == mir_keyboard_action_down &&
2058+ modifiers == mir_input_event_modifier_alt &&
2059+ scan_code == KEY_TAB)
2060+ {
2061+ tools->focus_next_session();
2062+ select_active_surface(tools->focused_session(), tools->focused_surface());
2063+
2064+ return true;
2065+ }
2066+ else if (action == mir_keyboard_action_down &&
2067+ modifiers == mir_input_event_modifier_alt &&
2068+ scan_code == KEY_GRAVE)
2069+ {
2070+ if (auto const prev = tools->focused_surface())
2071+ {
2072+ if (auto const app = tools->focused_session())
2073+ if (auto const surface = app->surface_after(prev))
2074+ {
2075+ select_active_surface(app, surface);
2076+ }
2077+ }
2078+
2079+ return true;
2080+ }
2081+
2082+ return false;
2083+}
2084+
2085+bool me::TilingWindowManagerPolicy::handle_touch_event(MirTouchEvent const* event)
2086+{
2087+ auto const count = mir_touch_event_point_count(event);
2088+
2089+ long total_x = 0;
2090+ long total_y = 0;
2091+
2092+ for (auto i = 0U; i != count; ++i)
2093+ {
2094+ total_x += mir_touch_event_axis_value(event, i, mir_touch_axis_x);
2095+ total_y += mir_touch_event_axis_value(event, i, mir_touch_axis_y);
2096+ }
2097+
2098+ Point const cursor{total_x/count, total_y/count};
2099+
2100+ bool is_drag = true;
2101+ for (auto i = 0U; i != count; ++i)
2102+ {
2103+ switch (mir_touch_event_action(event, i))
2104+ {
2105+ case mir_touch_action_up:
2106+ return false;
2107+
2108+ case mir_touch_action_down:
2109+ is_drag = false;
2110+
2111+ case mir_touch_action_change:
2112+ continue;
2113+ }
2114+ }
2115+
2116+ bool consumes_event = false;
2117+ if (is_drag)
2118+ {
2119+ switch (count)
2120+ {
2121+ case 2:
2122+ resize(cursor);
2123+ consumes_event = true;
2124+ break;
2125+
2126+ case 3:
2127+ drag(cursor);
2128+ consumes_event = true;
2129+ break;
2130+ }
2131+ }
2132+
2133+ old_cursor = cursor;
2134+ return consumes_event;
2135+}
2136+
2137+bool me::TilingWindowManagerPolicy::handle_pointer_event(MirPointerEvent const* event)
2138+{
2139+ auto const action = mir_pointer_event_action(event);
2140+ auto const modifiers = mir_pointer_event_modifiers(event) & modifier_mask;
2141+ Point const cursor{
2142+ mir_pointer_event_axis_value(event, mir_pointer_axis_x),
2143+ mir_pointer_event_axis_value(event, mir_pointer_axis_y)};
2144+
2145+ bool consumes_event = false;
2146+
2147+ if (action == mir_pointer_action_button_down)
2148+ {
2149+ click(cursor);
2150+ }
2151+ else if (action == mir_pointer_action_motion &&
2152+ modifiers == mir_input_event_modifier_alt)
2153+ {
2154+ if (mir_pointer_event_button_state(event, mir_pointer_button_primary))
2155+ {
2156+ drag(cursor);
2157+ consumes_event = true;
2158+ }
2159+ else if (mir_pointer_event_button_state(event, mir_pointer_button_tertiary))
2160+ {
2161+ resize(cursor);
2162+ consumes_event = true;
2163+ }
2164+ }
2165+
2166+ old_cursor = cursor;
2167+ return consumes_event;
2168+}
2169+
2170+void me::TilingWindowManagerPolicy::toggle(MirSurfaceState state)
2171+{
2172+ if (auto const session = tools->focused_session())
2173+ {
2174+ if (auto const surface = session->default_surface())
2175+ {
2176+ if (surface->state() == state)
2177+ state = mir_surface_state_restored;
2178+
2179+ auto const value = handle_set_state(surface, MirSurfaceState(state));
2180+ surface->configure(mir_surface_attrib_state, value);
2181+ }
2182+ }
2183+}
2184+
2185+std::shared_ptr<ms::Session> me::TilingWindowManagerPolicy::session_under(Point position)
2186+{
2187+ return tools->find_session([&](SessionInfo const& info) { return info.tile.contains(position);});
2188+}
2189+
2190+void me::TilingWindowManagerPolicy::update_tiles(
2191+ SessionInfoMap& session_info,
2192+ Rectangles const& displays)
2193+{
2194+ if (session_info.size() < 1 || displays.size() < 1) return;
2195+
2196+ auto const sessions = session_info.size();
2197+
2198+ auto const bounding_rect = displays.bounding_rectangle();
2199+
2200+ auto const total_width = bounding_rect.size.width.as_int();
2201+ auto const total_height = bounding_rect.size.height.as_int();
2202+
2203+ auto index = 0;
2204+
2205+ for (auto& info : session_info)
2206+ {
2207+ auto const x = (total_width*index)/sessions;
2208+ ++index;
2209+ auto const dx = (total_width*index)/sessions - x;
2210+
2211+ auto const old_tile = info.second.tile;
2212+ Rectangle const new_tile{{x, 0}, {dx, total_height}};
2213+
2214+ update_surfaces(info.first, old_tile, new_tile);
2215+
2216+ info.second.tile = new_tile;
2217+ }
2218+}
2219+
2220+void me::TilingWindowManagerPolicy::update_surfaces(std::weak_ptr<ms::Session> const& session, Rectangle const& old_tile, Rectangle const& new_tile)
2221+{
2222+ auto displacement = new_tile.top_left - old_tile.top_left;
2223+ auto& info = tools->info_for(session);
2224+
2225+ for (auto const& ps : info.surfaces)
2226+ {
2227+ if (auto const surface = ps.lock())
2228+ {
2229+ auto const old_pos = surface->top_left();
2230+ surface->move_to(old_pos + displacement);
2231+
2232+ fit_to_new_tile(*surface, old_tile, new_tile);
2233+ }
2234+ }
2235+}
2236+
2237+void me::TilingWindowManagerPolicy::clip_to_tile(ms::SurfaceCreationParameters& parameters, Rectangle const& tile)
2238+{
2239+ auto const displacement = parameters.top_left - tile.top_left;
2240+
2241+ auto width = std::min(tile.size.width.as_int()-displacement.dx.as_int(), parameters.size.width.as_int());
2242+ auto height = std::min(tile.size.height.as_int()-displacement.dy.as_int(), parameters.size.height.as_int());
2243+
2244+ parameters.size = Size{width, height};
2245+}
2246+
2247+void me::TilingWindowManagerPolicy::fit_to_new_tile(ms::Surface& surface, Rectangle const& old_tile, Rectangle const& new_tile)
2248+{
2249+ auto const displacement = surface.top_left() - new_tile.top_left;
2250+
2251+ // For now just scale if was filling width/height of tile
2252+ auto const old_size = surface.size();
2253+ auto const scaled_width = old_size.width == old_tile.size.width ? new_tile.size.width : old_size.width;
2254+ auto const scaled_height = old_size.height == old_tile.size.height ? new_tile.size.height : old_size.height;
2255+
2256+ auto width = std::min(new_tile.size.width.as_int()-displacement.dx.as_int(), scaled_width.as_int());
2257+ auto height = std::min(new_tile.size.height.as_int()-displacement.dy.as_int(), scaled_height.as_int());
2258+
2259+ surface.resize({width, height});
2260+}
2261+
2262+void me::TilingWindowManagerPolicy::drag(std::shared_ptr<ms::Surface> surface, Point to, Point from, Rectangle bounds)
2263+{
2264+ if (surface && surface->input_area_contains(from))
2265+ {
2266+ auto movement = to - from;
2267+
2268+ constrained_move(surface, movement, bounds);
2269+
2270+ for (auto const& child: tools->info_for(surface).children)
2271+ {
2272+ auto move = movement;
2273+ constrained_move(child.lock(), move, bounds);
2274+ }
2275+ }
2276+}
2277+
2278+void me::TilingWindowManagerPolicy::constrained_move(
2279+ std::shared_ptr<scene::Surface> const& surface,
2280+ Displacement& movement,
2281+ Rectangle const& bounds)
2282+{
2283+ auto const top_left = surface->top_left();
2284+ auto const surface_size = surface->size();
2285+ auto const bottom_right = top_left + as_displacement(surface_size);
2286+
2287+ if (movement.dx < DeltaX{0})
2288+ movement.dx = std::max(movement.dx, (bounds.top_left - top_left).dx);
2289+
2290+ if (movement.dy < DeltaY{0})
2291+ movement.dy = std::max(movement.dy, (bounds.top_left - top_left).dy);
2292+
2293+ if (movement.dx > DeltaX{0})
2294+ movement.dx = std::min(movement.dx, (bounds.bottom_right() - bottom_right).dx);
2295+
2296+ if (movement.dy > DeltaY{0})
2297+ movement.dy = std::min(movement.dy, (bounds.bottom_right() - bottom_right).dy);
2298+
2299+ auto new_pos = surface->top_left() + movement;
2300+
2301+ surface->move_to(new_pos);
2302+}
2303+
2304+void me::TilingWindowManagerPolicy::resize(std::shared_ptr<ms::Surface> surface, Point cursor, Point old_cursor, Rectangle bounds)
2305+{
2306+ if (surface && surface->input_area_contains(old_cursor))
2307+ {
2308+ auto const top_left = surface->top_left();
2309+
2310+ auto const old_displacement = old_cursor - top_left;
2311+ auto const new_displacement = cursor - top_left;
2312+
2313+ auto const scale_x = new_displacement.dx.as_float()/std::max(1.0f, old_displacement.dx.as_float());
2314+ auto const scale_y = new_displacement.dy.as_float()/std::max(1.0f, old_displacement.dy.as_float());
2315+
2316+ if (scale_x <= 0.0f || scale_y <= 0.0f) return;
2317+
2318+ auto const old_size = surface->size();
2319+ Size new_size{scale_x*old_size.width, scale_y*old_size.height};
2320+
2321+ auto const size_limits = as_size(bounds.bottom_right() - top_left);
2322+
2323+ if (new_size.width > size_limits.width)
2324+ new_size.width = size_limits.width;
2325+
2326+ if (new_size.height > size_limits.height)
2327+ new_size.height = size_limits.height;
2328+
2329+ surface->resize(new_size);
2330+ }
2331+}
2332+
2333+std::shared_ptr<ms::Surface> me::TilingWindowManagerPolicy::select_active_surface(std::shared_ptr<ms::Session> const& session, std::shared_ptr<scene::Surface> const& surface)
2334+{
2335+ if (!surface)
2336+ {
2337+ tools->set_focus_to({}, {});
2338+ return surface;
2339+ }
2340+
2341+ auto const& info_for = tools->info_for(surface);
2342+
2343+ if (info_for.can_be_active())
2344+ {
2345+ tools->set_focus_to(session, surface);
2346+ tools->raise_tree(surface);
2347+ return surface;
2348+ }
2349+ else
2350+ {
2351+ // Cannot have input focus - try the parent
2352+ if (auto const parent = info_for.parent.lock())
2353+ return select_active_surface(session, parent);
2354+
2355+ return {};
2356+ }
2357+}
2358
2359=== added file 'src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.h'
2360--- src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.h 1970-01-01 00:00:00 +0000
2361+++ src/platforms/mirserver/wm-wip/server_example_tiling_window_manager.h 2016-01-20 19:52:54 +0000
2362@@ -0,0 +1,124 @@
2363+/*
2364+ * Copyright © 2015 Canonical Ltd.
2365+ *
2366+ * This program is free software: you can redistribute it and/or modify it
2367+ * under the terms of the GNU General Public License version 3,
2368+ * as published by the Free Software Foundation.
2369+ *
2370+ * This program is distributed in the hope that it will be useful,
2371+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2372+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2373+ * GNU General Public License for more details.
2374+ *
2375+ * You should have received a copy of the GNU General Public License
2376+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2377+ *
2378+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
2379+ */
2380+
2381+#ifndef MIR_EXAMPLE_TILING_WINDOW_MANAGER_H_
2382+#define MIR_EXAMPLE_TILING_WINDOW_MANAGER_H_
2383+
2384+#include "server_example_basic_window_manager.h"
2385+
2386+///\example server_example_tiling_window_manager.h
2387+/// Demonstrate implementing a simple tiling algorithm
2388+
2389+namespace mir
2390+{
2391+namespace examples
2392+{
2393+// simple tiling algorithm:
2394+// o Switch apps: tap or click on the corresponding tile
2395+// o Move window: Alt-leftmousebutton drag (three finger drag)
2396+// o Resize window: Alt-middle_button drag (two finger drag)
2397+// o Maximize/restore current window (to tile size): Alt-F11
2398+// o Maximize/restore current window (to tile height): Shift-F11
2399+// o Maximize/restore current window (to tile width): Ctrl-F11
2400+// o client requests to maximize, vertically maximize & restore
2401+class TilingWindowManagerPolicy : public WindowManagementPolicy
2402+{
2403+public:
2404+ explicit TilingWindowManagerPolicy(WindowManagerTools* const tools);
2405+
2406+ void click(geometry::Point cursor);
2407+
2408+ void handle_session_info_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays);
2409+
2410+ void handle_displays_updated(SessionInfoMap& session_info, geometry::Rectangles const& displays);
2411+
2412+ void resize(geometry::Point cursor);
2413+
2414+ auto handle_place_new_surface(
2415+ std::shared_ptr<scene::Session> const& session,
2416+ scene::SurfaceCreationParameters const& request_parameters)
2417+ -> scene::SurfaceCreationParameters;
2418+
2419+ void handle_new_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface);
2420+
2421+ void handle_modify_surface(
2422+ std::shared_ptr<scene::Session> const& session,
2423+ std::shared_ptr<scene::Surface> const& surface,
2424+ shell::SurfaceSpecification const& modifications);
2425+
2426+ void handle_delete_surface(std::shared_ptr<scene::Session> const& session, std::weak_ptr<scene::Surface> const& surface);
2427+
2428+ int handle_set_state(std::shared_ptr<scene::Surface> const& surface, MirSurfaceState value);
2429+
2430+ void drag(geometry::Point cursor);
2431+
2432+ bool handle_keyboard_event(MirKeyboardEvent const* event);
2433+
2434+ bool handle_touch_event(MirTouchEvent const* event);
2435+
2436+ bool handle_pointer_event(MirPointerEvent const* event);
2437+
2438+ void handle_raise_surface(
2439+ std::shared_ptr<scene::Session> const& session,
2440+ std::shared_ptr<scene::Surface> const& surface);
2441+
2442+ void generate_decorations_for(
2443+ std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface,
2444+ SurfaceInfoMap& surface_info,
2445+ std::function<frontend::SurfaceId(std::shared_ptr<scene::Session> const&, scene::SurfaceCreationParameters const&)> const& build);
2446+
2447+private:
2448+ static const int modifier_mask =
2449+ mir_input_event_modifier_alt |
2450+ mir_input_event_modifier_shift |
2451+ mir_input_event_modifier_sym |
2452+ mir_input_event_modifier_ctrl |
2453+ mir_input_event_modifier_meta;
2454+
2455+ void toggle(MirSurfaceState state);
2456+
2457+ std::shared_ptr<scene::Session> session_under(geometry::Point position);
2458+
2459+ void update_tiles(
2460+ SessionInfoMap& session_info,
2461+ geometry::Rectangles const& displays);
2462+
2463+ void update_surfaces(std::weak_ptr<scene::Session> const& session, geometry::Rectangle const& old_tile, geometry::Rectangle const& new_tile);
2464+
2465+ static void clip_to_tile(scene::SurfaceCreationParameters& parameters, geometry::Rectangle const& tile);
2466+
2467+ static void fit_to_new_tile(scene::Surface& surface, geometry::Rectangle const& old_tile, geometry::Rectangle const& new_tile);
2468+
2469+ void drag(std::shared_ptr<scene::Surface> surface, geometry::Point to, geometry::Point from, geometry::Rectangle bounds);
2470+
2471+ static void resize(std::shared_ptr<scene::Surface> surface, geometry::Point cursor, geometry::Point old_cursor, geometry::Rectangle bounds);
2472+
2473+ static void constrained_move(std::shared_ptr<scene::Surface> const& surface, geometry::Displacement& movement, geometry::Rectangle const& bounds);
2474+
2475+ std::shared_ptr<scene::Surface> select_active_surface(std::shared_ptr<scene::Session> const& session, std::shared_ptr<scene::Surface> const& surface);
2476+
2477+ WindowManagerTools* const tools;
2478+
2479+ geometry::Point old_cursor{};
2480+};
2481+
2482+using TilingWindowManager = WindowManagerBuilder<TilingWindowManagerPolicy>;
2483+}
2484+}
2485+
2486+#endif /* MIR_EXAMPLE_TILING_WINDOW_MANAGER_H_ */
2487
2488=== added file 'src/platforms/mirserver/wm-wip/server_example_window_management.cpp'
2489--- src/platforms/mirserver/wm-wip/server_example_window_management.cpp 1970-01-01 00:00:00 +0000
2490+++ src/platforms/mirserver/wm-wip/server_example_window_management.cpp 2016-01-20 19:52:54 +0000
2491@@ -0,0 +1,155 @@
2492+/*
2493+ * Copyright © 2014 Canonical Ltd.
2494+ *
2495+ * This program is free software: you can redistribute it and/or modify it
2496+ * under the terms of the GNU General Public License version 3,
2497+ * as published by the Free Software Foundation.
2498+ *
2499+ * This program is distributed in the hope that it will be useful,
2500+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2501+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2502+ * GNU General Public License for more details.
2503+ *
2504+ * You should have received a copy of the GNU General Public License
2505+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2506+ *
2507+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
2508+ */
2509+
2510+#include "server_example_window_management.h"
2511+
2512+#include "server_example_tiling_window_manager.h"
2513+#include "server_example_canonical_window_manager.h"
2514+
2515+#include "mir/abnormal_exit.h"
2516+#include "mir/server.h"
2517+#include "mir/input/composite_event_filter.h"
2518+#include "mir/options/option.h"
2519+#include "mir/scene/session.h"
2520+#include "mir/scene/surface_creation_parameters.h"
2521+#include "mir/shell/display_layout.h"
2522+#include "mir/shell/system_compositor_window_manager.h"
2523+
2524+namespace me = mir::examples;
2525+namespace mf = mir::frontend;
2526+namespace mg = mir::graphics;
2527+namespace mi = mir::input;
2528+namespace ms = mir::scene;
2529+namespace msh = mir::shell;
2530+using namespace mir::geometry;
2531+
2532+///\example server_example_window_management.cpp
2533+/// Demonstrate introducing a window management strategy
2534+
2535+namespace
2536+{
2537+char const* const wm_option = "window-manager";
2538+char const* const wm_description = "window management strategy [{tiling|fullscreen|canonical|system-compositor}]";
2539+
2540+char const* const wm_tiling = "tiling";
2541+char const* const wm_fullscreen = "fullscreen";
2542+char const* const wm_canonical = "canonical";
2543+char const* const wm_system_compositor = "system-compositor";
2544+
2545+// Very simple - make every surface fullscreen
2546+class FullscreenWindowManagerPolicy : public me::WindowManagementPolicy
2547+{
2548+public:
2549+ FullscreenWindowManagerPolicy(me::WindowManagerTools* const /*tools*/, std::shared_ptr<msh::DisplayLayout> const& display_layout) :
2550+ display_layout{display_layout} {}
2551+
2552+ void handle_session_info_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) {}
2553+
2554+ void handle_displays_updated(SessionInfoMap& /*session_info*/, Rectangles const& /*displays*/) {}
2555+
2556+ auto handle_place_new_surface(
2557+ std::shared_ptr<ms::Session> const& /*session*/,
2558+ ms::SurfaceCreationParameters const& request_parameters)
2559+ -> ms::SurfaceCreationParameters
2560+ {
2561+ auto placed_parameters = request_parameters;
2562+
2563+ Rectangle rect{request_parameters.top_left, request_parameters.size};
2564+ display_layout->size_to_output(rect);
2565+ placed_parameters.size = rect.size;
2566+
2567+ return placed_parameters;
2568+ }
2569+ void handle_modify_surface(
2570+ std::shared_ptr<ms::Session> const& /*session*/,
2571+ std::shared_ptr<ms::Surface> const& /*surface*/,
2572+ msh::SurfaceSpecification const& /*modifications*/)
2573+ {
2574+ }
2575+
2576+ void handle_new_surface(std::shared_ptr<ms::Session> const& /*session*/, std::shared_ptr<ms::Surface> const& /*surface*/)
2577+ {
2578+ }
2579+
2580+ void handle_delete_surface(std::shared_ptr<ms::Session> const& session, std::weak_ptr<ms::Surface> const& surface)
2581+ { session->destroy_surface(surface); }
2582+
2583+ int handle_set_state(std::shared_ptr<ms::Surface> const& /*surface*/, MirSurfaceState value)
2584+ { return value; }
2585+
2586+ bool handle_keyboard_event(MirKeyboardEvent const* /*event*/) { return false; }
2587+
2588+ bool handle_touch_event(MirTouchEvent const* /*event*/) { return false; }
2589+
2590+ bool handle_pointer_event(MirPointerEvent const* /*event*/) { return false; }
2591+
2592+ void handle_raise_surface(
2593+ std::shared_ptr<ms::Session> const& /*session*/,
2594+ std::shared_ptr<ms::Surface> const& /*surface*/)
2595+ {
2596+ }
2597+
2598+ void generate_decorations_for(
2599+ std::shared_ptr<ms::Session> const&,
2600+ std::shared_ptr<ms::Surface> const&,
2601+ SurfaceInfoMap&,
2602+ std::function<mf::SurfaceId(std::shared_ptr<ms::Session> const&, ms::SurfaceCreationParameters const&)> const&)
2603+ {
2604+ }
2605+private:
2606+ std::shared_ptr<msh::DisplayLayout> const display_layout;
2607+};
2608+
2609+}
2610+
2611+using FullscreenWindowManager = me::WindowManagerBuilder<FullscreenWindowManagerPolicy>;
2612+using CanonicalWindowManager = me::WindowManagerBuilder<me::CanonicalWindowManagerPolicyCopy>;
2613+
2614+void me::add_window_manager_option_to(Server& server)
2615+{
2616+ server.add_configuration_option(wm_option, wm_description, wm_canonical);
2617+
2618+ server.override_the_window_manager_builder([&server](msh::FocusController* focus_controller)
2619+ -> std::shared_ptr<msh::WindowManager>
2620+ {
2621+ auto const options = server.get_options();
2622+ auto const selection = options->get<std::string>(wm_option);
2623+
2624+ if (selection == wm_tiling)
2625+ {
2626+ return std::make_shared<TilingWindowManager>(focus_controller);
2627+ }
2628+ else if (selection == wm_fullscreen)
2629+ {
2630+ return std::make_shared<FullscreenWindowManager>(focus_controller, server.the_shell_display_layout());
2631+ }
2632+ else if (selection == wm_canonical)
2633+ {
2634+ return std::make_shared<CanonicalWindowManager>(focus_controller, server.the_shell_display_layout());
2635+ }
2636+ else if (selection == wm_system_compositor)
2637+ {
2638+ return std::make_shared<msh::SystemCompositorWindowManager>(
2639+ focus_controller,
2640+ server.the_shell_display_layout(),
2641+ server.the_session_coordinator());
2642+ }
2643+
2644+ throw mir::AbnormalExit("Unknown window manager: " + selection);
2645+ });
2646+}
2647
2648=== added file 'src/platforms/mirserver/wm-wip/server_example_window_management.h'
2649--- src/platforms/mirserver/wm-wip/server_example_window_management.h 1970-01-01 00:00:00 +0000
2650+++ src/platforms/mirserver/wm-wip/server_example_window_management.h 2016-01-20 19:52:54 +0000
2651@@ -0,0 +1,33 @@
2652+/*
2653+ * Copyright © 2014 Canonical Ltd.
2654+ *
2655+ * This program is free software: you can redistribute it and/or modify it
2656+ * under the terms of the GNU General Public License version 3,
2657+ * as published by the Free Software Foundation.
2658+ *
2659+ * This program is distributed in the hope that it will be useful,
2660+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2661+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2662+ * GNU General Public License for more details.
2663+ *
2664+ * You should have received a copy of the GNU General Public License
2665+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2666+ *
2667+ * Authored By: Alan Griffiths <alan@octopull.co.uk>
2668+ */
2669+
2670+#ifndef MIR_EXAMPLES_WINDOW_MANAGEMENT_H_
2671+#define MIR_EXAMPLES_WINDOW_MANAGEMENT_H_
2672+
2673+namespace mir
2674+{
2675+class Server;
2676+
2677+namespace examples
2678+{
2679+void add_window_manager_option_to(Server& server);
2680+}
2681+} // namespace mir
2682+
2683+
2684+#endif // MIR_EXAMPLES_WINDOW_MANAGEMENT_H_
2685
2686=== added file 'src/platforms/mirserver/wm-wip/server_example_window_management_info.cpp'
2687--- src/platforms/mirserver/wm-wip/server_example_window_management_info.cpp 1970-01-01 00:00:00 +0000
2688+++ src/platforms/mirserver/wm-wip/server_example_window_management_info.cpp 2016-01-20 19:52:54 +0000
2689@@ -0,0 +1,406 @@
2690+/*
2691+ * Copyright © 2015 Canonical Ltd.
2692+ *
2693+ * This program is free software: you can redistribute it and/or modify it
2694+ * under the terms of the GNU General Public License version 3,
2695+ * as published by the Free Software Foundation.
2696+ *
2697+ * This program is distributed in the hope that it will be useful,
2698+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2699+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2700+ * GNU General Public License for more details.
2701+ *
2702+ * You should have received a copy of the GNU General Public License
2703+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2704+ *
2705+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
2706+ */
2707+
2708+#include "server_example_window_management_info.h"
2709+
2710+#include "mir/scene/surface.h"
2711+#include "mir/scene/surface_creation_parameters.h"
2712+
2713+#include "mir/graphics/buffer.h"
2714+
2715+#include <atomic>
2716+
2717+namespace me = mir::examples;
2718+namespace ms = mir::scene;
2719+namespace mg = mir::graphics;
2720+using namespace mir::geometry;
2721+
2722+me::SurfaceInfo::SurfaceInfo(
2723+ std::shared_ptr<scene::Session> const& session,
2724+ std::shared_ptr<scene::Surface> const& surface,
2725+ scene::SurfaceCreationParameters const& params) :
2726+ type{surface->type()},
2727+ state{surface->state()},
2728+ restore_rect{surface->top_left(), surface->size()},
2729+ session{session},
2730+ parent{params.parent},
2731+ min_width{params.min_width.is_set() ? params.min_width.value() : Width{}},
2732+ min_height{params.min_height.is_set() ? params.min_height.value() : Height{}},
2733+ max_width{params.max_width.is_set() ? params.max_width.value() : Width{std::numeric_limits<int>::max()}},
2734+ max_height{params.max_height.is_set() ? params.max_height.value() : Height{std::numeric_limits<int>::max()}},
2735+ width_inc{params.width_inc},
2736+ height_inc{params.height_inc},
2737+ min_aspect{params.min_aspect},
2738+ max_aspect{params.max_aspect}
2739+{
2740+ if (params.output_id != mir::graphics::DisplayConfigurationOutputId{0})
2741+ output_id = params.output_id;
2742+}
2743+
2744+bool me::SurfaceInfo::can_be_active() const
2745+{
2746+ switch (type)
2747+ {
2748+ case mir_surface_type_normal: /**< AKA "regular" */
2749+ case mir_surface_type_utility: /**< AKA "floating" */
2750+ case mir_surface_type_dialog:
2751+ case mir_surface_type_satellite: /**< AKA "toolbox"/"toolbar" */
2752+ case mir_surface_type_freestyle:
2753+ case mir_surface_type_menu:
2754+ case mir_surface_type_inputmethod: /**< AKA "OSK" or handwriting etc. */
2755+ return true;
2756+
2757+ case mir_surface_type_gloss:
2758+ case mir_surface_type_tip: /**< AKA "tooltip" */
2759+ default:
2760+ // Cannot have input focus
2761+ return false;
2762+ }
2763+}
2764+
2765+bool me::SurfaceInfo::must_have_parent() const
2766+{
2767+ switch (type)
2768+ {
2769+ case mir_surface_type_overlay:;
2770+ case mir_surface_type_inputmethod:
2771+ case mir_surface_type_satellite:
2772+ case mir_surface_type_tip:
2773+ return true;
2774+
2775+ default:
2776+ return false;
2777+ }
2778+}
2779+
2780+bool me::SurfaceInfo::can_morph_to(MirSurfaceType new_type) const
2781+{
2782+ switch (new_type)
2783+ {
2784+ case mir_surface_type_normal:
2785+ case mir_surface_type_utility:
2786+ case mir_surface_type_satellite:
2787+ switch (type)
2788+ {
2789+ case mir_surface_type_normal:
2790+ case mir_surface_type_utility:
2791+ case mir_surface_type_dialog:
2792+ case mir_surface_type_satellite:
2793+ return true;
2794+
2795+ default:
2796+ break;
2797+ }
2798+ break;
2799+
2800+ case mir_surface_type_dialog:
2801+ switch (type)
2802+ {
2803+ case mir_surface_type_normal:
2804+ case mir_surface_type_utility:
2805+ case mir_surface_type_dialog:
2806+ case mir_surface_type_popover:
2807+ case mir_surface_type_satellite:
2808+ return true;
2809+
2810+ default:
2811+ break;
2812+ }
2813+ break;
2814+
2815+ default:
2816+ break;
2817+ }
2818+
2819+ return false;
2820+}
2821+
2822+bool me::SurfaceInfo::must_not_have_parent() const
2823+{
2824+ switch (type)
2825+ {
2826+ case mir_surface_type_normal:
2827+ case mir_surface_type_utility:
2828+ return true;
2829+
2830+ default:
2831+ return false;
2832+ }
2833+}
2834+
2835+bool me::SurfaceInfo::is_visible() const
2836+{
2837+ switch (state)
2838+ {
2839+ case mir_surface_state_hidden:
2840+ case mir_surface_state_minimized:
2841+ return false;
2842+ default:
2843+ break;
2844+ }
2845+ return true;
2846+}
2847+
2848+struct mir::examples::SurfaceInfo::StreamPainter
2849+{
2850+ virtual void paint(int) = 0;
2851+ virtual ~StreamPainter() = default;
2852+ StreamPainter() = default;
2853+ StreamPainter(StreamPainter const&) = delete;
2854+ StreamPainter& operator=(StreamPainter const&) = delete;
2855+};
2856+
2857+struct mir::examples::SurfaceInfo::SwappingPainter
2858+ : mir::examples::SurfaceInfo::StreamPainter
2859+{
2860+ SwappingPainter(std::shared_ptr<frontend::BufferStream> const& buffer_stream) :
2861+ buffer_stream{buffer_stream}, buffer{nullptr}
2862+ {
2863+ swap_buffers();
2864+ }
2865+
2866+ void swap_buffers()
2867+ {
2868+ auto const callback = [this](mir::graphics::Buffer* new_buffer)
2869+ {
2870+ buffer.store(new_buffer);
2871+ };
2872+
2873+ buffer_stream->swap_buffers(buffer, callback);
2874+ }
2875+
2876+ void paint(int intensity) override
2877+ {
2878+ if (auto const buf = buffer.load())
2879+ {
2880+ auto const format = buffer_stream->pixel_format();
2881+ auto const sz = buf->size().height.as_int() *
2882+ buf->size().width.as_int() * MIR_BYTES_PER_PIXEL(format);
2883+ std::vector<unsigned char> pixels(sz, intensity);
2884+ buf->write(pixels.data(), sz);
2885+ swap_buffers();
2886+ }
2887+ }
2888+
2889+ std::shared_ptr<frontend::BufferStream> const buffer_stream;
2890+ std::atomic<graphics::Buffer*> buffer;
2891+};
2892+
2893+struct mir::examples::SurfaceInfo::AllocatingPainter
2894+ : mir::examples::SurfaceInfo::StreamPainter
2895+{
2896+ AllocatingPainter(std::shared_ptr<frontend::BufferStream> const& buffer_stream, Size size) :
2897+ buffer_stream(buffer_stream),
2898+ properties({
2899+ size,
2900+ buffer_stream->pixel_format(),
2901+ mg::BufferUsage::software
2902+ }),
2903+ front_buffer(buffer_stream->allocate_buffer(properties)),
2904+ back_buffer(buffer_stream->allocate_buffer(properties))
2905+ {
2906+ }
2907+
2908+ void paint(int intensity) override
2909+ {
2910+ buffer_stream->with_buffer(back_buffer,
2911+ [this, intensity](graphics::Buffer& buffer)
2912+ {
2913+ auto const format = buffer.pixel_format();
2914+ auto const sz = buffer.size().height.as_int() *
2915+ buffer.size().width.as_int() * MIR_BYTES_PER_PIXEL(format);
2916+ std::vector<unsigned char> pixels(sz, intensity);
2917+ buffer.write(pixels.data(), sz);
2918+ buffer_stream->swap_buffers(&buffer, [](mg::Buffer*){});
2919+ });
2920+ std::swap(front_buffer, back_buffer);
2921+ }
2922+
2923+ ~AllocatingPainter()
2924+ {
2925+ buffer_stream->remove_buffer(front_buffer);
2926+ buffer_stream->remove_buffer(back_buffer);
2927+ }
2928+
2929+ std::shared_ptr<frontend::BufferStream> const buffer_stream;
2930+ mg::BufferProperties properties;
2931+ mg::BufferID front_buffer;
2932+ mg::BufferID back_buffer;
2933+};
2934+
2935+void mir::examples::SurfaceInfo::init_titlebar(std::shared_ptr<scene::Surface> const& surface)
2936+{
2937+ auto stream = surface->primary_buffer_stream();
2938+ try
2939+ {
2940+ stream_painter = std::make_shared<AllocatingPainter>(stream, surface->size());
2941+ }
2942+ catch (...)
2943+ {
2944+ stream_painter = std::make_shared<SwappingPainter>(stream);
2945+ }
2946+}
2947+
2948+void mir::examples::SurfaceInfo::paint_titlebar(int intensity)
2949+{
2950+ stream_painter->paint(intensity);
2951+}
2952+
2953+void me::SurfaceInfo::constrain_resize(
2954+ std::shared_ptr<ms::Surface> const& surface,
2955+ Point& requested_pos,
2956+ Size& requested_size,
2957+ bool const left_resize,
2958+ bool const top_resize,
2959+ Rectangle const& /*bounds*/) const
2960+{
2961+ Point new_pos = requested_pos;
2962+ Size new_size = requested_size;
2963+
2964+ if (min_aspect.is_set())
2965+ {
2966+ auto const ar = min_aspect.value();
2967+
2968+ auto const error = new_size.height.as_int()*long(ar.width) - new_size.width.as_int()*long(ar.height);
2969+
2970+ if (error > 0)
2971+ {
2972+ // Add (denominator-1) to numerator to ensure rounding up
2973+ auto const width_correction = (error+(ar.height-1))/ar.height;
2974+ auto const height_correction = (error+(ar.width-1))/ar.width;
2975+
2976+ if (width_correction < height_correction)
2977+ {
2978+ new_size.width = new_size.width + DeltaX(width_correction);
2979+ }
2980+ else
2981+ {
2982+ new_size.height = new_size.height - DeltaY(height_correction);
2983+ }
2984+ }
2985+ }
2986+
2987+ if (max_aspect.is_set())
2988+ {
2989+ auto const ar = max_aspect.value();
2990+
2991+ auto const error = new_size.width.as_int()*long(ar.height) - new_size.height.as_int()*long(ar.width);
2992+
2993+ if (error > 0)
2994+ {
2995+ // Add (denominator-1) to numerator to ensure rounding up
2996+ auto const height_correction = (error+(ar.width-1))/ar.width;
2997+ auto const width_correction = (error+(ar.height-1))/ar.height;
2998+
2999+ if (width_correction < height_correction)
3000+ {
3001+ new_size.width = new_size.width - DeltaX(width_correction);
3002+ }
3003+ else
3004+ {
3005+ new_size.height = new_size.height + DeltaY(height_correction);
3006+ }
3007+ }
3008+ }
3009+
3010+ if (min_width > new_size.width)
3011+ new_size.width = min_width;
3012+
3013+ if (min_height > new_size.height)
3014+ new_size.height = min_height;
3015+
3016+ if (max_width < new_size.width)
3017+ new_size.width = max_width;
3018+
3019+ if (max_height < new_size.height)
3020+ new_size.height = max_height;
3021+
3022+ if (width_inc.is_set())
3023+ {
3024+ auto const width = new_size.width.as_int() - min_width.as_int();
3025+ auto inc = width_inc.value().as_int();
3026+ if (width % inc)
3027+ new_size.width = min_width + DeltaX{inc*(((2L*width + inc)/2)/inc)};
3028+ }
3029+
3030+ if (height_inc.is_set())
3031+ {
3032+ auto const height = new_size.height.as_int() - min_height.as_int();
3033+ auto inc = height_inc.value().as_int();
3034+ if (height % inc)
3035+ new_size.height = min_height + DeltaY{inc*(((2L*height + inc)/2)/inc)};
3036+ }
3037+
3038+ if (left_resize)
3039+ new_pos.x += new_size.width - requested_size.width;
3040+
3041+ if (top_resize)
3042+ new_pos.y += new_size.height - requested_size.height;
3043+
3044+ // placeholder - constrain onscreen
3045+
3046+ switch (state)
3047+ {
3048+ case mir_surface_state_restored:
3049+ break;
3050+
3051+ // "A vertically maximised surface is anchored to the top and bottom of
3052+ // the available workspace and can have any width."
3053+ case mir_surface_state_vertmaximized:
3054+ new_pos.y = surface->top_left().y;
3055+ new_size.height = surface->size().height;
3056+ break;
3057+
3058+ // "A horizontally maximised surface is anchored to the left and right of
3059+ // the available workspace and can have any height"
3060+ case mir_surface_state_horizmaximized:
3061+ new_pos.x = surface->top_left().x;
3062+ new_size.width = surface->size().width;
3063+ break;
3064+
3065+ // "A maximised surface is anchored to the top, bottom, left and right of the
3066+ // available workspace. For example, if the launcher is always-visible then
3067+ // the left-edge of the surface is anchored to the right-edge of the launcher."
3068+ case mir_surface_state_maximized:
3069+ default:
3070+ new_pos.x = surface->top_left().x;
3071+ new_pos.y = surface->top_left().y;
3072+ new_size.width = surface->size().width;
3073+ new_size.height = surface->size().height;
3074+ break;
3075+ }
3076+
3077+ requested_pos = new_pos;
3078+ requested_size = new_size;
3079+}
3080+
3081+bool me::SurfaceInfo::needs_titlebar(MirSurfaceType type)
3082+{
3083+ switch (type)
3084+ {
3085+ case mir_surface_type_freestyle:
3086+ case mir_surface_type_menu:
3087+ case mir_surface_type_inputmethod:
3088+ case mir_surface_type_gloss:
3089+ case mir_surface_type_tip:
3090+ // No decorations for these surface types
3091+ return false;
3092+ default:
3093+ return true;
3094+ }
3095+}
3096
3097=== added file 'src/platforms/mirserver/wm-wip/server_example_window_management_info.h'
3098--- src/platforms/mirserver/wm-wip/server_example_window_management_info.h 1970-01-01 00:00:00 +0000
3099+++ src/platforms/mirserver/wm-wip/server_example_window_management_info.h 2016-01-20 19:52:54 +0000
3100@@ -0,0 +1,103 @@
3101+/*
3102+ * Copyright © 2015 Canonical Ltd.
3103+ *
3104+ * This program is free software: you can redistribute it and/or modify it
3105+ * under the terms of the GNU General Public License version 3,
3106+ * as published by the Free Software Foundation.
3107+ *
3108+ * This program is distributed in the hope that it will be useful,
3109+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3110+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3111+ * GNU General Public License for more details.
3112+ *
3113+ * You should have received a copy of the GNU General Public License
3114+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3115+ *
3116+ * Authored by: Alan Griffiths <alan@octopull.co.uk>
3117+ */
3118+
3119+#ifndef MIR_SERVER_EXAMPLE_WINDOW_MANAGEMENT_INFO_H
3120+#define MIR_SERVER_EXAMPLE_WINDOW_MANAGEMENT_INFO_H
3121+
3122+#include "mir/geometry/rectangles.h"
3123+#include "mir/optional_value.h"
3124+#include "mir/shell/surface_specification.h"
3125+
3126+#include <vector>
3127+
3128+namespace mir
3129+{
3130+namespace scene { class Session; class Surface; class SurfaceCreationParameters; }
3131+namespace examples
3132+{
3133+struct SurfaceInfo
3134+{
3135+ SurfaceInfo(
3136+ std::shared_ptr <scene::Session> const& session,
3137+ std::shared_ptr <scene::Surface> const& surface,
3138+ scene::SurfaceCreationParameters const& params);
3139+
3140+ bool can_be_active() const;
3141+
3142+ bool can_morph_to(MirSurfaceType new_type) const;
3143+
3144+ bool must_have_parent() const;
3145+
3146+ bool must_not_have_parent() const;
3147+
3148+ bool is_visible() const;
3149+
3150+ static bool needs_titlebar(MirSurfaceType type);
3151+
3152+ void constrain_resize(
3153+ std::shared_ptr <scene::Surface> const& surface,
3154+ geometry::Point& requested_pos,
3155+ geometry::Size& requested_size,
3156+ const bool left_resize,
3157+ const bool top_resize,
3158+ geometry::Rectangle const& bounds) const;
3159+
3160+ MirSurfaceType type;
3161+ MirSurfaceState state;
3162+ geometry::Rectangle restore_rect;
3163+ std::weak_ptr <scene::Session> session;
3164+ std::weak_ptr <scene::Surface> parent;
3165+ std::vector <std::weak_ptr<scene::Surface>> children;
3166+ std::shared_ptr <scene::Surface> titlebar;
3167+ frontend::SurfaceId titlebar_id;
3168+ bool is_titlebar = false;
3169+ geometry::Width min_width;
3170+ geometry::Height min_height;
3171+ geometry::Width max_width;
3172+ geometry::Height max_height;
3173+ mir::optional_value<geometry::DeltaX> width_inc;
3174+ mir::optional_value<geometry::DeltaY> height_inc;
3175+ mir::optional_value<shell::SurfaceAspectRatio> min_aspect;
3176+ mir::optional_value<shell::SurfaceAspectRatio> max_aspect;
3177+ mir::optional_value<graphics::DisplayConfigurationOutputId> output_id;
3178+
3179+ void init_titlebar(std::shared_ptr <scene::Surface> const& surface);
3180+
3181+ void paint_titlebar(int intensity);
3182+
3183+private:
3184+
3185+ struct StreamPainter;
3186+ struct AllocatingPainter;
3187+ struct SwappingPainter;
3188+
3189+ std::shared_ptr <StreamPainter> stream_painter;
3190+};
3191+
3192+struct SessionInfo
3193+{
3194+ std::vector<std::weak_ptr<scene::Surface>> surfaces;
3195+
3196+ // This is only used by the TilingWindowManagerPolicy,
3197+ // perhaps we need a more extensible mechanism. (std::experimental::any?)
3198+ geometry::Rectangle tile;
3199+};
3200+}
3201+}
3202+
3203+#endif //MIR_SERVER_EXAMPLE_WINDOW_MANAGEMENT_INFO_H

Subscribers

People subscribed via source and target branches