Merge lp:~gerboland/qtmir/initialSurfaceGeometry into lp:qtmir

Proposed by Gerry Boland on 2014-08-21
Status: Work in progress
Proposed branch: lp:~gerboland/qtmir/initialSurfaceGeometry
Merge into: lp:qtmir
Prerequisite: lp:~dandrader/qtmir/lifecycle
Diff against target: 5503 lines (+1949/-1606)
45 files modified
CMakeLists.txt (+3/-1)
debian/changelog (+7/-0)
debian/control (+1/-1)
src/common/globals.h (+76/-0)
src/modules/Unity/Application/CMakeLists.txt (+2/-0)
src/modules/Unity/Application/application.cpp (+341/-69)
src/modules/Unity/Application/application.h (+61/-19)
src/modules/Unity/Application/application_manager.cpp (+118/-245)
src/modules/Unity/Application/application_manager.h (+22/-20)
src/modules/Unity/Application/applicationcontroller.h (+2/-1)
src/modules/Unity/Application/applicationscreenshotprovider.cpp (+1/-1)
src/modules/Unity/Application/mirsurfaceitem.cpp (+25/-25)
src/modules/Unity/Application/mirsurfaceitem.h (+21/-78)
src/modules/Unity/Application/mirsurfaceiteminterface.h (+89/-0)
src/modules/Unity/Application/mirsurfaceitemmodel.h (+2/-2)
src/modules/Unity/Application/mirsurfacemanager.cpp (+6/-6)
src/modules/Unity/Application/mirsurfacemanager.h (+4/-4)
src/modules/Unity/Application/plugin.cpp (+30/-32)
src/modules/Unity/Application/session.cpp (+123/-73)
src/modules/Unity/Application/session.h (+15/-8)
src/modules/Unity/Application/session_interface.h (+31/-23)
src/modules/Unity/Application/sessionmanager.cpp (+2/-2)
src/modules/Unity/Application/sessionmanager.h (+3/-1)
src/modules/Unity/Application/taskcontroller.cpp (+8/-13)
src/modules/Unity/Application/taskcontroller.h (+4/-6)
src/modules/Unity/Application/upstart/applicationcontroller.cpp (+10/-2)
src/platforms/mirserver/CMakeLists.txt (+2/-0)
src/platforms/mirserver/mirserver.cpp (+1/-2)
src/platforms/mirserver/mirshell.cpp (+28/-23)
src/platforms/mirserver/mirshell.h (+16/-12)
tests/modules/Application/CMakeLists.txt (+5/-0)
tests/modules/Application/application_test.cpp (+145/-71)
tests/modules/ApplicationManager/CMakeLists.txt (+5/-1)
tests/modules/ApplicationManager/application_manager_test.cpp (+410/-831)
tests/modules/MirSurfaceItem/CMakeLists.txt (+3/-1)
tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp (+1/-1)
tests/modules/SessionManager/CMakeLists.txt (+4/-0)
tests/modules/SessionManager/session_manager_test.cpp (+1/-1)
tests/modules/SessionManager/session_test.cpp (+57/-24)
tests/modules/TaskController/CMakeLists.txt (+1/-0)
tests/modules/common/fake_mirsurfaceitem.h (+95/-0)
tests/modules/common/mock_mirsurfaceitem.h (+50/-0)
tests/modules/common/mock_session.h (+44/-5)
tests/modules/common/qtmir_test.cpp (+61/-0)
tests/modules/common/qtmir_test.h (+13/-2)
To merge this branch: bzr merge lp:~gerboland/qtmir/initialSurfaceGeometry
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration 2014-08-21 Needs Fixing on 2015-01-19
Albert Astals Cid (community) Needs Fixing on 2015-01-19
Michał Sawicz 2014-08-21 Approve on 2014-08-21
Daniel d'Andrada 2014-08-21 Pending
Review via email: mp+231725@code.launchpad.net

This proposal supersedes a proposal from 2014-08-12.

Commit Message

Implement a callback mechanism to allow shell override the initial surface geometry requested by clients

Description of the Change

* Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~gerboland/unity-api/surfaceSizerCallback/+merge/231698
 * Did you perform an exploratory manual test run of your code change and any related functionality?
Y
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
 * If you changed the UI, has there been a design review?
N/A

To post a comment you must log in.
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

See inline.

review: Needs Fixing
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

Comments addressed, please check out rev 234

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) : Posted in a previous version of this proposal
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

In src/platforms/mirserver/mirplacementstrategy.h
+ void sessionAboutToCreateSurface(const mir::scene::Session &session, QSize &surfaceGeometry); // requires Qt::BlockingQueuedConnection!!

That line is way too long.

In tests/modules/ApplicationManager/application_manager_test.cpp

+ QJSValue callback = jsEngine->evaluate("(function(surface) { var out = new Object(); out.height = 333; return out; })");

Here as well.

Looks good otherwise.

review: Needs Fixing
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

Done

PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Daniel d'Andrada (dandrader) : Posted in a previous version of this proposal
review: Approve
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

Minor inline.

review: Needs Fixing
Gerry Boland (gerboland) : Posted in a previous version of this proposal
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Gerry Boland (gerboland) : Posted in a previous version of this proposal
Gerry Boland (gerboland) : Posted in a previous version of this proposal
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Michał Sawicz (saviq) wrote :

Looks good.

review: Approve
Alan Griffiths (alan-griffiths) wrote :

635 + std::shared_ptr<MirPlacementStrategy> m_placementStrategy; // remove when bug lp:1407687 fixed
...
601 + //auto sharedPtr = the_placement_strategy(); - re-instate when bug lp:1407687 fixed
602 + if (m_placementStrategy.unique()) return 0;
603 +
604 + return static_cast<MirPlacementStrategy*>(m_placementStrategy.get());

Better as:

std::weak_ptr<MirPlacementStrategy> m_placementStrategy;
....

return m_placementStrategy.lock().get());

PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Albert Astals Cid (aacid) wrote :

Code looks good, haven't time to test. If nobody beats me to it, i'll do on Monday

review: Approve (code)
Albert Astals Cid (aacid) wrote :

Doesn't merge correctly

Text conflict in debian/changelog
Text conflict in src/modules/Unity/Application/application_manager.cpp
Text conflict in src/modules/Unity/Application/application_manager.h
3 conflicts encountered.

review: Needs Fixing
262. By Gerry Boland on 2015-01-19

Merge trunk & fix conflicts

PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Albert Astals Cid (aacid) wrote :

Let's wait for https://code.launchpad.net/~gerboland/unity8/initialSurfaceGeometry/+merge/231726 to be finished so we can try it all together

263. By Gerry Boland on 2015-01-22

Move Surface Types & States into Globals enum class. Expand surfaceSizer parameter to include state

264. By Gerry Boland on 2015-02-09

Merge trunk

265. By Gerry Boland on 2015-03-30

Merge trunk, required many changes

266. By Gerry Boland on 2015-03-30

Remove accidental commits

267. By Gerry Boland on 2015-03-30

Remove local build tweak

268. By Gerry Boland on 2015-07-13

Merge trunk

269. By Gerry Boland on 2015-07-13

Merge detach-state-from-focus and resolve conflicts

Unmerged revisions

269. By Gerry Boland on 2015-07-13

Merge detach-state-from-focus and resolve conflicts

268. By Gerry Boland on 2015-07-13

Merge trunk

267. By Gerry Boland on 2015-03-30

Remove local build tweak

266. By Gerry Boland on 2015-03-30

Remove accidental commits

265. By Gerry Boland on 2015-03-30

Merge trunk, required many changes

264. By Gerry Boland on 2015-02-09

Merge trunk

263. By Gerry Boland on 2015-01-22

Move Surface Types & States into Globals enum class. Expand surfaceSizer parameter to include state

262. By Gerry Boland on 2015-01-19

Merge trunk & fix conflicts

261. By Gerry Boland on 2015-01-13

Merge trunk

260. By Gerry Boland on 2015-01-05

Workaround MirServer API missing the_placement_strategy method (bug lp:1407687)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-05-21 18:48:59 +0000
3+++ CMakeLists.txt 2015-07-13 21:57:32 +0000
4@@ -80,7 +80,9 @@
5 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
6 pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
7 pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7)
10+
11+include_directories(${APPLICATION_API_INCLUDE_DIRS})
12
13 add_definitions(-DMIR_REQUIRE_DEPRECATED_EVENT_OPT_IN=1)
14
15
16=== modified file 'debian/changelog'
17--- debian/changelog 2015-06-17 13:46:09 +0000
18+++ debian/changelog 2015-07-13 21:57:32 +0000
19@@ -1,3 +1,10 @@
20+qtmir (0.4.6) UNRELEASED; urgency=medium
21+
22+ * Implement a callback mechanism to allow shell override the initial
23+ surface geometry requested by clients
24+
25+ -- Gerry Boland <gerry.boland@canonical.com> Mon, 12 Jul 2015 14:47:07 +0100
26+
27 qtmir (0.4.5+15.10.20150617-0ubuntu1) wily; urgency=medium
28
29 [ Gerry Boland ]
30
31=== modified file 'debian/control'
32--- debian/control 2015-06-17 13:45:56 +0000
33+++ debian/control 2015-07-13 21:57:32 +0000
34@@ -28,7 +28,7 @@
35 libubuntu-app-launch2-dev,
36 libubuntu-application-api-dev (>= 2.1.0),
37 libudev-dev,
38- libunity-api-dev (>= 7.97),
39+ libunity-api-dev (>= 7.99),
40 liburl-dispatcher1-dev,
41 libxkbcommon-dev,
42 libxrender-dev,
43
44=== added file 'src/common/globals.h'
45--- src/common/globals.h 1970-01-01 00:00:00 +0000
46+++ src/common/globals.h 2015-07-13 21:57:32 +0000
47@@ -0,0 +1,76 @@
48+/*
49+ * Copyright (C) 2015 Canonical, Ltd.
50+ *
51+ * This program is free software: you can redistribute it and/or modify it under
52+ * the terms of the GNU Lesser General Public License version 3, as published by
53+ * the Free Software Foundation.
54+ *
55+ * This program is distributed in the hope that it will be useful, but WITHOUT
56+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
57+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
58+ * Lesser General Public License for more details.
59+ *
60+ * You should have received a copy of the GNU Lesser General Public License
61+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
62+ */
63+
64+#ifndef GLOBALS_H
65+#define GLOBALS_H
66+
67+#include <QObject>
68+#include <QSize>
69+#include <mir_toolkit/common.h>
70+
71+namespace qtmir {
72+
73+class Globals
74+{
75+ Q_GADGET
76+ Q_ENUMS(SurfaceState)
77+ Q_ENUMS(SurfaceType)
78+
79+public:
80+ enum SurfaceState {
81+ Unknown = mir_surface_state_unknown,
82+ Restored = mir_surface_state_restored,
83+ Minimized = mir_surface_state_minimized,
84+ Maximized = mir_surface_state_maximized,
85+ VertMaximized = mir_surface_state_vertmaximized,
86+ /* SemiMaximized = mir_surface_state_semimaximized, // see mircommon/mir_toolbox/common.h*/
87+ Fullscreen = mir_surface_state_fullscreen,
88+ };
89+
90+ enum SurfaceType {
91+ Normal = mir_surface_type_normal,
92+ Utility = mir_surface_type_utility,
93+ Dialog = mir_surface_type_dialog,
94+ Overlay = mir_surface_type_overlay,
95+ Freestyle = mir_surface_type_freestyle,
96+ Popover = mir_surface_type_popover,
97+ InputMethod = mir_surface_type_inputmethod,
98+ };
99+
100+ enum OrientationAngle {
101+ Angle0 = 0,
102+ Angle90 = 90,
103+ Angle180 = 180,
104+ Angle270 = 270
105+ };
106+
107+private:
108+ Globals() = default;
109+ ~Globals() = default;
110+};
111+
112+
113+struct SurfaceParameters
114+{
115+ QSize geometry;
116+ Globals::SurfaceState state;
117+};
118+
119+} // namespace qtmir
120+
121+Q_DECLARE_METATYPE(qtmir::Globals::OrientationAngle)
122+
123+#endif // GLOBALS_H
124
125=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
126--- src/modules/Unity/Application/CMakeLists.txt 2015-05-21 18:48:59 +0000
127+++ src/modules/Unity/Application/CMakeLists.txt 2015-07-13 21:57:32 +0000
128@@ -46,9 +46,11 @@
129 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
130 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
131 # Feed the automoc monster
132+ mirsurfaceiteminterface.h
133 session_interface.h
134 applicationcontroller.h
135 settings_interface.h
136+ ../../../common/globals.h
137 )
138
139 add_library(unityapplicationplugin SHARED
140
141=== modified file 'src/modules/Unity/Application/application.cpp'
142--- src/modules/Unity/Application/application.cpp 2015-04-10 14:54:44 +0000
143+++ src/modules/Unity/Application/application.cpp 2015-07-13 21:57:32 +0000
144@@ -1,5 +1,5 @@
145 /*
146- * Copyright (C) 2013-2014 Canonical, Ltd.
147+ * Copyright (C) 2013-2015 Canonical, Ltd.
148 *
149 * This program is free software: you can redistribute it and/or modify it under
150 * the terms of the GNU Lesser General Public License version 3, as published by
151@@ -37,25 +37,28 @@
152 namespace qtmir
153 {
154
155-Application::Application(const QSharedPointer<TaskController>& taskController,
156- const QSharedPointer<SharedWakelock>& sharedWakelock,
157+QStringList Application::lifecycleExceptions;
158+
159+Application::Application(const QSharedPointer<SharedWakelock>& sharedWakelock,
160 DesktopFileReader *desktopFileReader,
161- State state,
162 const QStringList &arguments,
163 ApplicationManager *parent)
164 : ApplicationInfoInterface(desktopFileReader->appId(), parent)
165- , m_taskController(taskController)
166 , m_sharedWakelock(sharedWakelock)
167 , m_desktopData(desktopFileReader)
168 , m_pid(0)
169 , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)
170- , m_state(state)
171+ , m_state(InternalState::Starting)
172 , m_focused(false)
173- , m_canBeResumed(true)
174 , m_arguments(arguments)
175 , m_session(nullptr)
176+ , m_requestedState(RequestedRunning)
177+ , m_processState(ProcessUnknown)
178 {
179- qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId() << "state=" << state;
180+ qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId();
181+
182+ // Because m_state is InternalState::Starting
183+ acquireWakelock();
184
185 // FIXME(greyback) need to save long appId internally until ubuntu-app-launch can hide it from us
186 m_longAppId = desktopFileReader->file().remove(QRegExp(".desktop$")).split('/').last();
187@@ -69,10 +72,33 @@
188 {
189 qCDebug(QTMIR_APPLICATIONS) << "Application::~Application";
190
191+ // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup
192+ if (m_processState == Application::ProcessUnknown
193+ || state() == Application::Starting
194+ || state() == Application::Running) {
195+ wipeQMLCache();
196+ }
197+
198 delete m_session;
199 delete m_desktopData;
200 }
201
202+
203+void Application::wipeQMLCache()
204+{
205+ QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/"));
206+ QDir dir(path);
207+ QStringList apps = dir.entryList();
208+ for (int i = 0; i < apps.size(); i++) {
209+ if (apps.at(i).contains(appId())) {
210+ qCDebug(QTMIR_APPLICATIONS) << "Application appId=" << apps.at(i) << " Wiping QML Cache";
211+ dir.cd(apps.at(i));
212+ dir.removeRecursively();
213+ break;
214+ }
215+ }
216+}
217+
218 bool Application::isValid() const
219 {
220 return m_desktopData->loaded();
221@@ -159,6 +185,30 @@
222 return color;
223 }
224
225+const char* Application::internalStateToStr(InternalState state)
226+{
227+ switch (state) {
228+ case InternalState::Starting:
229+ return "Starting";
230+ case InternalState::Running:
231+ return "Running";
232+ case InternalState::RunningInBackground:
233+ return "RunningInBackground";
234+ case InternalState::SuspendingWaitSession:
235+ return "SuspendingWaitSession";
236+ case InternalState::SuspendingWaitProcess:
237+ return "SuspendingWaitProcess";
238+ case InternalState::Suspended:
239+ return "Suspended";
240+ case InternalState::StoppedUnexpectedly:
241+ return "StoppedUnexpectedly";
242+ case InternalState::Stopped:
243+ return "Stopped";
244+ default:
245+ return "???";
246+ }
247+}
248+
249 bool Application::splashShowHeader() const
250 {
251 QString showHeader = m_desktopData->splashShowHeader();
252@@ -204,7 +254,104 @@
253
254 Application::State Application::state() const
255 {
256- return m_state;
257+ // The public state is a simplified version of the internal one as our consumers
258+ // don't have to know or care about all the nasty details.
259+ switch (m_state) {
260+ case InternalState::Starting:
261+ return Starting;
262+ case InternalState::Running:
263+ case InternalState::RunningInBackground:
264+ case InternalState::SuspendingWaitSession:
265+ case InternalState::SuspendingWaitProcess:
266+ return Running;
267+ case InternalState::Suspended:
268+ return Suspended;
269+ case InternalState::Stopped:
270+ default:
271+ return Stopped;
272+ }
273+}
274+
275+Application::RequestedState Application::requestedState() const
276+{
277+ return m_requestedState;
278+}
279+
280+void Application::setRequestedState(RequestedState value)
281+{
282+ if (m_requestedState == value) {
283+ // nothing to do
284+ return;
285+ }
286+
287+ qCDebug(QTMIR_APPLICATIONS) << "Application::setRequestedState - appId=" << appId()
288+ << "requestedState=" << applicationStateToStr(value);
289+ m_requestedState = value;
290+ Q_EMIT requestedStateChanged(m_requestedState);
291+
292+ applyRequestedState();
293+}
294+
295+void Application::applyRequestedState()
296+{
297+ if (m_requestedState == RequestedRunning) {
298+ applyRequestedRunning();
299+ } else {
300+ applyRequestedSuspended();
301+ }
302+}
303+
304+void Application::applyRequestedRunning()
305+{
306+ switch (m_state) {
307+ case InternalState::Starting:
308+ // should leave the app alone until it reaches Running state
309+ break;
310+ case InternalState::Running:
311+ // already where it's wanted to be
312+ break;
313+ case InternalState::RunningInBackground:
314+ case InternalState::SuspendingWaitSession:
315+ case InternalState::Suspended:
316+ resume();
317+ break;
318+ case InternalState::SuspendingWaitProcess:
319+ // should leave the app alone until it reaches Suspended state
320+ break;
321+ case InternalState::StoppedUnexpectedly:
322+ respawn();
323+ break;
324+ case InternalState::Stopped:
325+ // dead end.
326+ break;
327+ }
328+}
329+
330+void Application::applyRequestedSuspended()
331+{
332+ switch (m_state) {
333+ case InternalState::Starting:
334+ // should leave the app alone until it reaches Running state
335+ break;
336+ case InternalState::Running:
337+ if (m_processState == ProcessRunning) {
338+ suspend();
339+ } else {
340+ // we can't suspend it since we have no information on the app process
341+ Q_ASSERT(m_processState == ProcessUnknown);
342+ }
343+ break;
344+ case InternalState::RunningInBackground:
345+ case InternalState::SuspendingWaitSession:
346+ case InternalState::SuspendingWaitProcess:
347+ case InternalState::Suspended:
348+ // it's already going where we it's wanted
349+ break;
350+ case InternalState::StoppedUnexpectedly:
351+ case InternalState::Stopped:
352+ // the app doesn't have a process in the first place, so there's nothing to suspend
353+ break;
354+ }
355 }
356
357 bool Application::focused() const
358@@ -219,12 +366,7 @@
359
360 bool Application::canBeResumed() const
361 {
362- return m_canBeResumed;
363-}
364-
365-void Application::setCanBeResumed(const bool resume)
366-{
367- m_canBeResumed = resume;
368+ return m_processState != ProcessUnknown;
369 }
370
371 pid_t Application::pid() const
372@@ -242,7 +384,7 @@
373 m_arguments = arguments;
374 }
375
376-void Application::setSession(Session *newSession)
377+void Application::setSession(SessionInterface *newSession)
378 {
379 qCDebug(QTMIR_APPLICATIONS) << "Application::setSession - appId=" << appId() << "session=" << newSession;
380
381@@ -261,10 +403,25 @@
382 if (m_session) {
383 m_session->setParent(this);
384 m_session->setApplication(this);
385- m_session->setState(state());
386-
387- connect(m_session, &SessionInterface::suspended, this, &Application::onSessionSuspended);
388- connect(m_session, &SessionInterface::resumed, this, &Application::onSessionResumed);
389+
390+ switch (m_state) {
391+ case InternalState::Starting:
392+ case InternalState::Running:
393+ case InternalState::RunningInBackground:
394+ m_session->resume();
395+ break;
396+ case InternalState::SuspendingWaitSession:
397+ case InternalState::SuspendingWaitProcess:
398+ case InternalState::Suspended:
399+ m_session->suspend();
400+ break;
401+ case InternalState::Stopped:
402+ default:
403+ m_session->stop();
404+ break;
405+ }
406+
407+ connect(m_session, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged);
408 connect(m_session, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);
409
410 if (oldFullscreen != fullscreen())
411@@ -288,37 +445,53 @@
412 }
413 }
414
415-void Application::setState(Application::State state)
416+void Application::setInternalState(Application::InternalState state)
417 {
418- qCDebug(QTMIR_APPLICATIONS) << "Application::setState - appId=" << appId() << "state=" << applicationStateToStr(state);
419- if (m_state != state) {
420- if (session()) {
421- session()->setState((Session::State)state);
422- } else {
423- // If we have have no session, we may have to respawn it.
424- switch (state)
425- {
426- case Session::State::Running:
427- if (m_state == Session::State::Stopped) {
428- respawn();
429- state = Session::State::Starting;
430- }
431- break;
432- default:
433- break;
434- }
435- }
436- m_state = state;
437- Q_EMIT stateChanged(state);
438- }
439+ if (m_state == state) {
440+ return;
441+ }
442+
443+ qCDebug(QTMIR_APPLICATIONS) << "Application::setInternalState - appId=" << appId()
444+ << "state=" << internalStateToStr(state);
445+
446+ auto oldPublicState = this->state();
447+ m_state = state;
448+
449+ switch (m_state) {
450+ case InternalState::Starting:
451+ case InternalState::Running:
452+ acquireWakelock();
453+ break;
454+ case InternalState::RunningInBackground:
455+ releaseWakelock();
456+ break;
457+ case InternalState::Suspended:
458+ releaseWakelock();
459+ break;
460+ case InternalState::StoppedUnexpectedly:
461+ releaseWakelock();
462+ break;
463+ case InternalState::Stopped:
464+ Q_EMIT stopped();
465+ releaseWakelock();
466+ break;
467+ case InternalState::SuspendingWaitSession:
468+ case InternalState::SuspendingWaitProcess:
469+ // transitory states. leave as it is
470+ default:
471+ break;
472+ };
473+
474+ if (this->state() != oldPublicState) {
475+ Q_EMIT stateChanged(this->state());
476+ }
477+
478+ applyRequestedState();
479 }
480
481 void Application::setFocused(bool focused)
482 {
483 qCDebug(QTMIR_APPLICATIONS) << "Application::setFocused - appId=" << appId() << "focused=" << focused;
484- if (focused) {
485- holdWakelock(true);
486- }
487
488 if (m_focused != focused) {
489 m_focused = focused;
490@@ -326,25 +499,84 @@
491 }
492 }
493
494-void Application::onSessionSuspended()
495-{
496- qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionSuspended - appId=" << appId();
497- m_taskController->suspend(longAppId());
498- holdWakelock(false);
499-}
500-
501-void Application::onSessionResumed()
502-{
503- qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionResumed - appId=" << appId();
504- holdWakelock(true);
505- m_taskController->resume(longAppId());
506+void Application::setProcessState(ProcessState newProcessState)
507+{
508+ if (m_processState == newProcessState) {
509+ return;
510+ }
511+
512+ m_processState = newProcessState;
513+
514+ switch (m_processState) {
515+ case ProcessUnknown:
516+ // it would be a coding error
517+ Q_ASSERT(false);
518+ break;
519+ case ProcessRunning:
520+ if (m_state == InternalState::StoppedUnexpectedly) {
521+ setInternalState(InternalState::Starting);
522+ }
523+ break;
524+ case ProcessSuspended:
525+ Q_ASSERT(m_state == InternalState::SuspendingWaitProcess);
526+ setInternalState(InternalState::Suspended);
527+ break;
528+ case ProcessStopped:
529+ // we assume the session always stop before the process
530+ Q_ASSERT(!m_session || m_session->state() == Session::Stopped);
531+ if (m_state == InternalState::Starting) {
532+ setInternalState(InternalState::Stopped);
533+ } else {
534+ Q_ASSERT(m_state == InternalState::Stopped
535+ || m_state == InternalState::StoppedUnexpectedly);
536+ }
537+ break;
538+ }
539+
540+ applyRequestedState();
541+}
542+
543+void Application::suspend()
544+{
545+ Q_ASSERT(m_state == InternalState::Running);
546+ Q_ASSERT(m_session != nullptr);
547+
548+ if (!lifecycleExceptions.filter(appId().section('_',0,0)).empty()) {
549+ // Present in exceptions list.
550+ // There's no need to keep the wakelock as the process is never suspended
551+ // and thus has no cleanup to perform when (for example) the display is
552+ // blanked.
553+ setInternalState(InternalState::RunningInBackground);
554+ } else {
555+ setInternalState(InternalState::SuspendingWaitSession);
556+ m_session->suspend();
557+ }
558+}
559+
560+void Application::resume()
561+{
562+ if (m_state == InternalState::Suspended) {
563+ setInternalState(InternalState::Running);
564+ Q_EMIT resumeProcessRequested();
565+ if (m_processState == ProcessSuspended) {
566+ setProcessState(ProcessRunning); // should we wait for a resumed() signal?
567+ }
568+ m_session->resume();
569+ } else if (m_state == InternalState::SuspendingWaitSession) {
570+ setInternalState(InternalState::Running);
571+ m_session->resume();
572+ } else if (m_state == InternalState::RunningInBackground) {
573+ setInternalState(InternalState::Running);
574+ }
575 }
576
577 void Application::respawn()
578 {
579 qCDebug(QTMIR_APPLICATIONS) << "Application::respawn - appId=" << appId();
580- holdWakelock(true);
581- m_taskController->start(appId(), m_arguments);
582+
583+ setInternalState(InternalState::Starting);
584+
585+ Q_EMIT startProcessRequested();
586 }
587
588 QString Application::longAppId() const
589@@ -362,20 +594,60 @@
590 return m_rotatesWindowContents;
591 }
592
593-Session* Application::session() const
594+SessionInterface* Application::session() const
595 {
596 return m_session;
597 }
598
599-void Application::holdWakelock(bool enable) const
600-{
601- if (appId() == "unity8-dash")
602- return;
603-
604- if (enable) {
605- m_sharedWakelock->acquire(this);
606- } else {
607- m_sharedWakelock->release(this);
608+void Application::acquireWakelock() const
609+{
610+ if (appId() == "unity8-dash")
611+ return;
612+
613+ m_sharedWakelock->acquire(this);
614+}
615+
616+void Application::releaseWakelock() const
617+{
618+ if (appId() == "unity8-dash")
619+ return;
620+
621+ m_sharedWakelock->release(this);
622+}
623+
624+void Application::onSessionStateChanged(Session::State sessionState)
625+{
626+ switch (sessionState) {
627+ case Session::Starting:
628+ break;
629+ case Session::Running:
630+ if (m_state == InternalState::Starting) {
631+ setInternalState(InternalState::Running);
632+ }
633+ break;
634+ case Session::Suspending:
635+ break;
636+ case Session::Suspended:
637+ Q_ASSERT(m_state == InternalState::SuspendingWaitSession);
638+ setInternalState(InternalState::SuspendingWaitProcess);
639+ Q_EMIT suspendProcessRequested();
640+ break;
641+ case Session::Stopped:
642+ if (!canBeResumed()
643+ || m_state == InternalState::Starting
644+ || m_state == InternalState::Running) {
645+ /* 1. application is not managed by upstart
646+ * 2. application is managed by upstart, but has stopped before it managed
647+ * to create a surface, we can assume it crashed on startup, and thus
648+ * cannot be resumed
649+ * 3. application is managed by upstart and is in foreground (i.e. has
650+ * Running state), if Mir reports the application disconnects, it
651+ * either crashed or stopped itself.
652+ */
653+ setInternalState(InternalState::Stopped);
654+ } else {
655+ setInternalState(InternalState::StoppedUnexpectedly);
656+ }
657 }
658 }
659
660
661=== modified file 'src/modules/Unity/Application/application.h'
662--- src/modules/Unity/Application/application.h 2015-01-19 11:48:32 +0000
663+++ src/modules/Unity/Application/application.h 2015-07-13 21:57:32 +0000
664@@ -1,5 +1,5 @@
665 /*
666- * Copyright (C) 2013-2014 Canonical, Ltd.
667+ * Copyright (C) 2013-2015 Canonical, Ltd.
668 *
669 * This program is free software: you can redistribute it and/or modify it under
670 * the terms of the GNU Lesser General Public License version 3, as published by
671@@ -28,6 +28,8 @@
672 // Unity API
673 #include <unity/shell/application/ApplicationInfoInterface.h>
674
675+#include "session_interface.h"
676+
677 namespace mir {
678 namespace scene {
679 class Session;
680@@ -39,7 +41,6 @@
681
682 class ApplicationManager;
683 class DesktopFileReader;
684-class TaskController;
685 class Session;
686 class SharedWakelock;
687
688@@ -51,15 +52,32 @@
689 Q_PROPERTY(QString exec READ exec CONSTANT)
690 Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged)
691 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
692- Q_PROPERTY(Session* session READ session NOTIFY sessionChanged DESIGNABLE false)
693+ Q_PROPERTY(SessionInterface* session READ session NOTIFY sessionChanged DESIGNABLE false)
694
695 public:
696 Q_DECLARE_FLAGS(Stages, Stage)
697
698- Application(const QSharedPointer<TaskController>& taskController,
699- const QSharedPointer<SharedWakelock>& sharedWakelock,
700+ enum ProcessState {
701+ ProcessUnknown,
702+ ProcessRunning,
703+ ProcessSuspended,
704+ ProcessStopped
705+ };
706+
707+ enum class InternalState {
708+ Starting,
709+ Running,
710+ RunningInBackground,
711+ SuspendingWaitSession,
712+ SuspendingWaitProcess,
713+ Suspended,
714+ StoppedUnexpectedly,
715+ Stopped // It closed itself, crashed or it stopped and we can't respawn it
716+ // In any case, this is a dead end.
717+ };
718+
719+ Application(const QSharedPointer<SharedWakelock>& sharedWakelock,
720 DesktopFileReader *desktopFileReader,
721- State state,
722 const QStringList &arguments,
723 ApplicationManager *parent);
724 virtual ~Application();
725@@ -71,6 +89,8 @@
726 QUrl icon() const override;
727 Stage stage() const override;
728 State state() const override;
729+ RequestedState requestedState() const override;
730+ void setRequestedState(RequestedState) override;
731 bool focused() const override;
732 QString splashTitle() const override;
733 QUrl splashImage() const override;
734@@ -82,12 +102,16 @@
735 bool rotatesWindowContents() const override;
736
737 void setStage(Stage stage);
738- void setState(State state);
739-
740- Session* session() const;
741+
742+
743+ void setProcessState(ProcessState value);
744+
745+ QStringList arguments() const { return m_arguments; }
746+
747+ SessionInterface* session() const;
748+ void setSession(SessionInterface *session);
749
750 bool canBeResumed() const;
751- void setCanBeResumed(const bool);
752
753 bool isValid() const;
754 QString desktopFile() const;
755@@ -98,40 +122,58 @@
756
757 pid_t pid() const;
758
759+ // for tests
760+ InternalState internalState() const { return m_state; }
761+
762+ static QStringList lifecycleExceptions;
763+
764 Q_SIGNALS:
765 void fullscreenChanged(bool fullscreen);
766 void stageChanged(Stage stage);
767- void sessionChanged(Session *session);
768+ void sessionChanged(SessionInterface *session);
769+
770+ void startProcessRequested();
771+ void suspendProcessRequested();
772+ void resumeProcessRequested();
773+ void stopped();
774
775 private Q_SLOTS:
776- void onSessionSuspended();
777- void onSessionResumed();
778+ void onSessionStateChanged(SessionInterface::State sessionState);
779
780 void respawn();
781
782 private:
783+
784 QString longAppId() const;
785- void holdWakelock(bool enable) const;
786+ void acquireWakelock() const;
787+ void releaseWakelock() const;
788 void setPid(pid_t pid);
789 void setArguments(const QStringList arguments);
790 void setFocused(bool focus);
791- void setSession(Session *session);
792+ void setInternalState(InternalState state);
793+ void wipeQMLCache();
794+ void suspend();
795+ void resume();
796 QColor colorFromString(const QString &colorString, const char *colorName) const;
797+ static const char* internalStateToStr(InternalState state);
798+ void applyRequestedState();
799+ void applyRequestedRunning();
800+ void applyRequestedSuspended();
801
802- QSharedPointer<TaskController> m_taskController;
803 QSharedPointer<SharedWakelock> m_sharedWakelock;
804 DesktopFileReader* m_desktopData;
805 QString m_longAppId;
806 qint64 m_pid;
807 Stage m_stage;
808 Stages m_supportedStages;
809- State m_state;
810+ InternalState m_state;
811 bool m_focused;
812- bool m_canBeResumed;
813 QStringList m_arguments;
814 Qt::ScreenOrientations m_supportedOrientations;
815 bool m_rotatesWindowContents;
816- Session *m_session;
817+ SessionInterface *m_session;
818+ RequestedState m_requestedState;
819+ ProcessState m_processState;
820
821 friend class ApplicationManager;
822 friend class SessionManager;
823
824=== modified file 'src/modules/Unity/Application/application_manager.cpp'
825--- src/modules/Unity/Application/application_manager.cpp 2015-05-21 18:38:27 +0000
826+++ src/modules/Unity/Application/application_manager.cpp 2015-07-13 21:57:32 +0000
827@@ -1,5 +1,5 @@
828 /*
829- * Copyright (C) 2013,2014 Canonical, Ltd.
830+ * Copyright (C) 2013-2015 Canonical, Ltd.
831 *
832 * This program is free software: you can redistribute it and/or modify it under
833 * the terms of the GNU Lesser General Public License version 3, as published by
834@@ -29,6 +29,7 @@
835
836 // mirserver
837 #include "mirserver.h"
838+#include "mirshell.h"
839 #include "nativeinterface.h"
840 #include "sessionlistener.h"
841 #include "sessionauthorizer.h"
842@@ -47,6 +48,8 @@
843 #include <QDebug>
844 #include <QByteArray>
845 #include <QDir>
846+#include <QJSEngine>
847+#include <QJSValue>
848
849 // std
850 #include <csignal>
851@@ -90,23 +93,31 @@
852 manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
853 }
854
855+void connectToMirShell(ApplicationManager *manager, MirShell *shell)
856+{
857+ QObject::connect(shell, &MirShell::sessionAboutToCreateSurface,
858+ manager, &ApplicationManager::onSessionAboutToCreateSurface, Qt::BlockingQueuedConnection);
859+}
860+
861 void connectToTaskController(ApplicationManager *manager, TaskController *controller)
862 {
863 QObject::connect(controller, &TaskController::processStarting,
864 manager, &ApplicationManager::onProcessStarting);
865 QObject::connect(controller, &TaskController::processStopped,
866 manager, &ApplicationManager::onProcessStopped);
867+ QObject::connect(controller, &TaskController::processSuspended,
868+ manager, &ApplicationManager::onProcessSuspended);
869 QObject::connect(controller, &TaskController::processFailed,
870 manager, &ApplicationManager::onProcessFailed);
871- QObject::connect(controller, &TaskController::requestFocus,
872+ QObject::connect(controller, &TaskController::focusRequested,
873 manager, &ApplicationManager::onFocusRequested);
874- QObject::connect(controller, &TaskController::requestResume,
875+ QObject::connect(controller, &TaskController::resumeRequested,
876 manager, &ApplicationManager::onResumeRequested);
877 }
878
879 } // namespace
880
881-ApplicationManager* ApplicationManager::Factory::Factory::create()
882+ApplicationManager* ApplicationManager::Factory::Factory::create(QJSEngine *jsEngine)
883 {
884 NativeInterface *nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());
885
886@@ -139,11 +150,13 @@
887 sharedWakelock,
888 fileReaderFactory,
889 procInfo,
890- settings
891+ settings,
892+ jsEngine
893 );
894
895 connectToSessionListener(appManager, sessionListener);
896 connectToSessionAuthorizer(appManager, sessionAuthorizer);
897+ connectToMirShell(appManager, mirServer.lock()->shell());
898 connectToTaskController(appManager, taskController.data());
899
900 // Emit signal to notify Upstart that Mir is ready to receive client connections
901@@ -159,12 +172,12 @@
902 }
903
904
905-ApplicationManager* ApplicationManager::singleton()
906+ApplicationManager* ApplicationManager::singleton(QJSEngine *jsEngine)
907 {
908 static ApplicationManager* instance;
909 if (!instance) {
910 Factory appFactory;
911- instance = appFactory.create();
912+ instance = appFactory.create(jsEngine);
913 }
914 return instance;
915 }
916@@ -176,20 +189,19 @@
917 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
918 const QSharedPointer<ProcInfo>& procInfo,
919 const QSharedPointer<SettingsInterface>& settings,
920+ QJSEngine *jsEngine,
921 QObject *parent)
922 : ApplicationManagerInterface(parent)
923 , m_mirServer(mirServer)
924 , m_focusedApplication(nullptr)
925- , m_mainStageApplication(nullptr)
926- , m_sideStageApplication(nullptr)
927 , m_dbusWindowStack(new DBusWindowStack(this))
928 , m_taskController(taskController)
929 , m_desktopFileReaderFactory(desktopFileReaderFactory)
930 , m_procInfo(procInfo)
931 , m_sharedWakelock(sharedWakelock)
932 , m_settings(settings)
933- , m_suspended(false)
934- , m_forceDashActive(false)
935+ , m_surfaceAboutToBeCreatedCallback(QJSValue::UndefinedValue)
936+ , m_jsEngine(jsEngine)
937 {
938 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;
939 setObjectName("qtmir::ApplicationManager");
940@@ -198,7 +210,7 @@
941 m_roleNames.insert(RoleFullscreen, "fullscreen");
942
943 if (settings.data()) {
944- m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
945+ Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
946 connect(m_settings.data(), &Settings::changed, this, &ApplicationManager::onSettingsChanged);
947 }
948 }
949@@ -206,6 +218,7 @@
950 ApplicationManager::~ApplicationManager()
951 {
952 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::~ApplicationManager";
953+ m_surfaceAboutToBeCreatedCallback = QJSValue::UndefinedValue; //unset immediately to avoid racing on shutdown
954 }
955
956 int ApplicationManager::rowCount(const QModelIndex &parent) const
957@@ -289,96 +302,23 @@
958 }
959 }
960
961-bool ApplicationManager::suspended() const
962+QJSValue ApplicationManager::surfaceAboutToBeCreatedCallback() const
963 {
964- return m_suspended;
965+ return m_surfaceAboutToBeCreatedCallback;
966 }
967
968-void ApplicationManager::setSuspended(bool suspended)
969+void ApplicationManager::setSurfaceAboutToBeCreatedCallback(const QJSValue &callback)
970 {
971- if (suspended == m_suspended) {
972+ if (m_surfaceAboutToBeCreatedCallback.equals(callback))
973 return;
974- }
975- m_suspended = suspended;
976- Q_EMIT suspendedChanged();
977
978- if (m_suspended) {
979- suspendApplication(m_mainStageApplication);
980- suspendApplication(m_sideStageApplication);
981- if (m_focusedApplication) {
982- m_focusedApplication->setFocused(false);
983- m_dbusWindowStack->FocusedWindowChanged(0, QString(), 0);
984- }
985+ if (callback.isCallable()) {
986+ m_surfaceAboutToBeCreatedCallback = callback;
987 } else {
988- resumeApplication(m_mainStageApplication);
989- resumeApplication(m_sideStageApplication);
990- if (m_focusedApplication) {
991- m_focusedApplication->setFocused(true);
992- m_dbusWindowStack->FocusedWindowChanged(0, m_focusedApplication->appId(), m_focusedApplication->stage());
993- }
994- }
995-}
996-
997-bool ApplicationManager::forceDashActive() const
998-{
999- return m_forceDashActive;
1000-}
1001-
1002-void ApplicationManager::setForceDashActive(bool forceDashActive)
1003-{
1004- if (m_forceDashActive == forceDashActive) {
1005- return;
1006- }
1007-
1008- m_forceDashActive = forceDashActive;
1009- Q_EMIT forceDashActiveChanged();
1010-
1011- Application *dashApp = findApplication("unity8-dash");
1012- if (!dashApp) {
1013- qCWarning(QTMIR_APPLICATIONS) << "Dash doesn't seem to be running... Ignoring.";
1014- return;
1015- }
1016-
1017- if (m_forceDashActive && dashApp->state() != Application::Running) {
1018- resumeApplication(dashApp);
1019- } else if (!m_forceDashActive && dashApp->state() == Application::Running
1020- && m_mainStageApplication != dashApp
1021- && m_sideStageApplication != dashApp) {
1022- suspendApplication(dashApp);
1023- }
1024-}
1025-
1026-bool ApplicationManager::suspendApplication(Application *application)
1027-{
1028- if (application == nullptr)
1029- return false;
1030- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::suspendApplication - appId=" << application->appId();
1031-
1032- // Present in exceptions list, explicitly release wakelock and return. There's no need to keep the wakelock
1033- // as the process is never suspended and thus has no cleanup to perform when (for example) the display is blanked
1034- if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty()) {
1035- m_sharedWakelock->release(application);
1036- return false;
1037- }
1038-
1039- if (m_forceDashActive && application->appId() == "unity8-dash") {
1040- return false;
1041- }
1042-
1043- if (application->state() == Application::Running)
1044- application->setState(Application::Suspended);
1045-
1046- return true;
1047-}
1048-
1049-void ApplicationManager::resumeApplication(Application *application)
1050-{
1051- if (application == nullptr)
1052- return;
1053- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::resumeApplication - appId=" << application->appId();
1054-
1055- if (application->state() == Application::Suspended || application->state() == Application::Stopped)
1056- application->setState(Application::Running);
1057+ qWarning() << "ApplicationManager::setSurfaceAboutToBeCreatedCallback - attempted to register a non-function!";
1058+ m_surfaceAboutToBeCreatedCallback = QJSValue::UndefinedValue;
1059+ }
1060+ Q_EMIT surfaceAboutToBeCreatedCallbackChanged();
1061 }
1062
1063 bool ApplicationManager::focusApplication(const QString &inputAppId)
1064@@ -392,27 +332,8 @@
1065 return false;
1066 }
1067
1068- resumeApplication(application);
1069-
1070- // set state of previously focused app to suspended
1071 if (m_focusedApplication) {
1072 m_focusedApplication->setFocused(false);
1073- Application *lastApplication = applicationForStage(application->stage());
1074- if (lastApplication != application) {
1075- suspendApplication(lastApplication);
1076- }
1077- }
1078-
1079- if (application->stage() == Application::MainStage) {
1080- m_mainStageApplication = application;
1081- } else {
1082- m_sideStageApplication = application;
1083- }
1084-
1085- if (!m_suspended) {
1086- resumeApplication(application); // in case unfocusCurrentApplication() was last called
1087- } else {
1088- suspendApplication(application); // Make sure we also have this one suspended if everything is suspended
1089 }
1090
1091 m_focusedApplication = application;
1092@@ -422,9 +343,6 @@
1093 Q_EMIT focusedApplicationIdChanged();
1094 m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage());
1095
1096- // FIXME(dandrader): lying here. The operation is async. So we will only know whether
1097- // the focusing was successful once the server replies. Maybe the API in unity-api should
1098- // reflect that?
1099 return true;
1100 }
1101
1102@@ -432,9 +350,6 @@
1103 {
1104 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::unfocusCurrentApplication";
1105
1106- suspendApplication(m_sideStageApplication);
1107- suspendApplication(m_mainStageApplication);
1108-
1109 m_focusedApplication = nullptr;
1110 Q_EMIT focusedApplicationIdChanged();
1111 }
1112@@ -484,10 +399,8 @@
1113 application->setArguments(arguments);
1114 } else {
1115 application = new Application(
1116- m_taskController,
1117 m_sharedWakelock,
1118 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
1119- Application::Starting,
1120 arguments,
1121 this);
1122
1123@@ -514,10 +427,8 @@
1124 Application *application = findApplication(appId);
1125 if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
1126 application = new Application(
1127- m_taskController,
1128 m_sharedWakelock,
1129 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
1130- Application::Starting,
1131 QStringList(),
1132 this);
1133
1134@@ -530,17 +441,17 @@
1135 Q_EMIT focusRequested(appId);
1136 }
1137 else {
1138- // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
1139- // application and focus it immediately (as user expects app to still be running).
1140 if (application->state() == Application::Stopped) {
1141+ // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
1142+ // application and focus it immediately (as user expects app to still be running).
1143 qCDebug(QTMIR_APPLICATIONS) << "Stopped application appId=" << appId << "is being resumed externally";
1144- application->setState(Application::Starting);
1145 Q_EMIT focusRequested(appId);
1146 } else {
1147 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting application already found with appId"
1148 << appId;
1149 }
1150 }
1151+ application->setProcessState(Application::ProcessRunning);
1152 }
1153
1154 /**
1155@@ -559,14 +470,7 @@
1156 return false;
1157 }
1158
1159- if (application == m_focusedApplication) {
1160- // unfocus, and let shell decide what next to focus
1161- m_focusedApplication = nullptr;
1162- Q_EMIT focusedApplicationIdChanged();
1163- }
1164-
1165 remove(application);
1166- m_dbusWindowStack->WindowDestroyed(0, appId);
1167
1168 bool result = m_taskController->stop(application->longAppId());
1169
1170@@ -583,10 +487,7 @@
1171
1172 void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup)
1173 {
1174- /* Applications fail if they fail to launch, crash or are killed. If failed to start, must
1175- * immediately remove from list of applications. If crash or kill, instead we set flag on the
1176- * Application to indicate it can be resumed.
1177- */
1178+ // Applications fail if they fail to launch, crash or are killed.
1179
1180 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessFailed - appId=" << appId << "duringStartup=" << duringStartup;
1181
1182@@ -598,20 +499,8 @@
1183 }
1184
1185 Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
1186- if (application->state() == Application::Starting) {
1187- if (application == m_focusedApplication) {
1188- m_focusedApplication = nullptr;
1189- Q_EMIT focusedApplicationIdChanged();
1190- }
1191- remove(application);
1192- m_dbusWindowStack->WindowDestroyed(0, application->appId());
1193- delete application;
1194- } else {
1195- // We need to set flags on the Application to say the app can be resumed, and thus should not be removed
1196- // from the list by onProcessStopped.
1197- application->setCanBeResumed(true);
1198- application->setPid(0);
1199- }
1200+ application->setProcessState(Application::ProcessStopped);
1201+ application->setPid(0);
1202 }
1203
1204 void ApplicationManager::onProcessStopped(const QString &appId)
1205@@ -626,29 +515,15 @@
1206 return;
1207 }
1208
1209- // if shell did not stop the application, but ubuntu-app-launch says it died, we assume the process has been
1210- // killed, so it can be respawned later. Only exception is if that application is focused or running
1211- // as then it most likely crashed. Update this logic when ubuntu-app-launch gives some failure info.
1212- bool removeApplication = true;
1213-
1214- if (application == m_focusedApplication) {
1215- // Very bad case where focused application dies. Remove from list. Should give error message
1216- m_focusedApplication = nullptr;
1217- Q_EMIT focusedApplicationIdChanged();
1218- }
1219-
1220- // The following scenario is the only time that we do NOT remove the application from the app list:
1221- if ((application->state() == Application::Suspended || application->state() == Application::Stopped)
1222- && application->pid() == 0 // i.e. onProcessFailed was called, which resets the PID of this application
1223- && application->canBeResumed()) {
1224- removeApplication = false;
1225- }
1226-
1227- if (removeApplication) {
1228- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - removing appId=" << appId;
1229- remove(application);
1230- m_dbusWindowStack->WindowDestroyed(0, application->appId());
1231- delete application;
1232+ application->setProcessState(Application::ProcessStopped);
1233+ application->setPid(0);
1234+}
1235+
1236+void ApplicationManager::onProcessSuspended(const QString &appId)
1237+{
1238+ Application *application = findApplication(appId);
1239+ if (application) {
1240+ application->setProcessState(Application::ProcessSuspended);
1241 }
1242 }
1243
1244@@ -670,10 +545,10 @@
1245 return;
1246 }
1247
1248- // If app Stopped, trust that ubuntu-app-launch respawns it itself, and AppManager will
1249- // be notified of that through the onProcessStartReportReceived slot. Else resume.
1250+ // We interpret this as a focus request for a suspended app.
1251+ // Shell will have this app resumed if it complies with the focus request
1252 if (application->state() == Application::Suspended) {
1253- application->setState(Application::Running);
1254+ Q_EMIT focusRequested(appId);
1255 }
1256 }
1257
1258@@ -693,7 +568,7 @@
1259 void ApplicationManager::onSettingsChanged(const QString &key)
1260 {
1261 if (key == "lifecycleExemptAppids") {
1262- m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
1263+ Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
1264 }
1265 }
1266
1267@@ -732,7 +607,6 @@
1268
1269 if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) {
1270 authorized = true;
1271- m_hiddenPIDs << pid;
1272 return;
1273 }
1274
1275@@ -792,15 +666,12 @@
1276
1277 QStringList arguments(info->asStringList());
1278 application = new Application(
1279- m_taskController,
1280 m_sharedWakelock,
1281 desktopData,
1282- Application::Starting,
1283 arguments,
1284 this);
1285 application->setPid(pid);
1286 application->setStage(stage);
1287- application->setCanBeResumed(false);
1288 add(application);
1289 authorized = true;
1290 }
1291@@ -812,66 +683,56 @@
1292
1293 void ApplicationManager::onSessionStopping(std::shared_ptr<ms::Session> const& session)
1294 {
1295- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping - sessionName=" << session->name().c_str();
1296-
1297- // in case application closed not by hand of shell, check again here:
1298 Application* application = findApplicationWithSession(session);
1299 if (application) {
1300- /* Can remove the application from the running apps list immediately in these curcumstances:
1301- * 1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do
1302- * it here)
1303- * 2. application is managed by upstart, but has stopped before it managed to create a surface, we can assume
1304- * it crashed on startup, and thus cannot be resumed - so remove it.
1305- * 3. application is managed by upstart and is in foreground (i.e. has Running state), if Mir reports the
1306- * application disconnects, it either crashed or stopped itself. Either case, remove it.
1307- */
1308- if (!application->canBeResumed()
1309- || application->state() == Application::Starting
1310- || application->state() == Application::Running) {
1311- m_dbusWindowStack->WindowDestroyed(0, application->appId());
1312- remove(application);
1313-
1314- // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup
1315- QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/"));
1316- QDir dir(path);
1317- QStringList apps = dir.entryList();
1318- for (int i = 0; i < apps.size(); i++) {
1319- if (apps.at(i).contains(application->appId())) {
1320- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping appId=" << apps.at(i) << " Wiping QML Cache";
1321- dir.cd(apps.at(i));
1322- dir.removeRecursively();
1323- break;
1324- }
1325- }
1326-
1327- delete application;
1328-
1329- if (application == m_focusedApplication) {
1330- m_focusedApplication = nullptr;
1331- Q_EMIT focusedApplicationIdChanged();
1332- }
1333+ m_dbusWindowStack->WindowDestroyed(0, application->appId());
1334+ }
1335+}
1336+
1337+void ApplicationManager::onSessionAboutToCreateSurface(const std::shared_ptr<mir::scene::Session> &session,
1338+ qtmir::SurfaceParameters &params)
1339+{
1340+ qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionAboutToCreateSurface - sessionName="
1341+ << session->name().c_str() << "geometry=" << params.geometry
1342+ << "state" << params.state;
1343+
1344+ if (m_surfaceAboutToBeCreatedCallback.isCallable()) {
1345+ QJSValue argument = m_jsEngine->newObject();
1346+ argument.setProperty("width", params.geometry.width());
1347+ argument.setProperty("height", params.geometry.height());
1348+ argument.setProperty("state", params.state);
1349+
1350+ Application* application = findApplicationWithSession(session.get());
1351+ if (application)
1352+ argument.setProperty("appId", application->appId());
1353+
1354+ QJSValue output = m_surfaceAboutToBeCreatedCallback.call(QJSValueList() << argument);
1355+ if (output.isObject()) {
1356+ QJSValue width = output.property("width");
1357+ QJSValue height = output.property("height");
1358+ QJSValue state = output.property("state");
1359+ if (width.isNumber())
1360+ params.geometry.setWidth(width.toInt());
1361+ if (height.isNumber())
1362+ params.geometry.setHeight(height.toInt());
1363+ if (state.isNumber())
1364+ params.state = static_cast<Globals::SurfaceState>(state.toInt());
1365 } else {
1366- // otherwise, we do not have enough information to make any changes to the model, so await events from
1367- // upstart to go further, but set the app state
1368- application->setState(Application::Stopped);
1369+ qWarning() << "ApplicationManager::onSessionAboutToCreateSurface - unrecognised object returned from JS callback!!"
1370+ << "Surface size has *not* been overridden by shell!";
1371 }
1372 }
1373- m_hiddenPIDs.removeOne(session->process_id());
1374 }
1375
1376-void ApplicationManager::onSessionCreatedSurface(ms::Session const* session,
1377- std::shared_ptr<ms::Surface> const& surface)
1378+void ApplicationManager::onSessionCreatedSurface(const ms::Session *session,
1379+ const std::shared_ptr<ms::Surface> &surface)
1380 {
1381 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionCreatedSurface - sessionName=" << session->name().c_str();
1382 Q_UNUSED(surface);
1383
1384 Application* application = findApplicationWithSession(session);
1385- if (application && application->state() == Application::Starting) {
1386+ if (application) {
1387 m_dbusWindowStack->WindowCreated(0, application->appId());
1388- application->setState(Application::Running);
1389- if ((application != m_mainStageApplication && application != m_sideStageApplication) || m_suspended) {
1390- suspendApplication(application);
1391- }
1392 }
1393 }
1394
1395@@ -900,16 +761,6 @@
1396 return nullptr;
1397 }
1398
1399-Application* ApplicationManager::applicationForStage(Application::Stage stage)
1400-{
1401- qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::focusedApplicationForStage" << stage;
1402-
1403- if (stage == Application::MainStage)
1404- return m_mainStageApplication;
1405- else
1406- return m_sideStageApplication;
1407-}
1408-
1409 void ApplicationManager::add(Application* application)
1410 {
1411 Q_ASSERT(application != nullptr);
1412@@ -920,6 +771,28 @@
1413 connect(application, &Application::stateChanged, this, [this](Application::State) { onAppDataChanged(RoleState); });
1414 connect(application, &Application::stageChanged, this, [this](Application::Stage) { onAppDataChanged(RoleStage); });
1415
1416+ QString appId = application->appId();
1417+ QString longAppId = application->longAppId();
1418+ QStringList arguments = application->arguments();
1419+
1420+ // The connection is queued as a workaround an issue in the PhoneStage animation that
1421+ // happens when you tap on a killed app in the spread to bring it to foreground, causing
1422+ // a Application::respawn() to take place.
1423+ // In any case, it seems like in general QML works better when don't do too many things
1424+ // in the same event loop iteration.
1425+ connect(application, &Application::startProcessRequested,
1426+ this, [=]() { m_taskController->start(appId, arguments); },
1427+ Qt::QueuedConnection);
1428+
1429+ connect(application, &Application::suspendProcessRequested, this, [=]() { m_taskController->suspend(longAppId); } );
1430+ connect(application, &Application::resumeProcessRequested, this, [=]() { m_taskController->resume(longAppId); } );
1431+
1432+ connect(application, &Application::stopped, this, [=]() {
1433+ remove(application);
1434+ application->deleteLater();
1435+ });
1436+
1437+
1438 beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count());
1439 m_applications.append(application);
1440 endInsertRows();
1441@@ -935,11 +808,6 @@
1442 Q_ASSERT(application != nullptr);
1443 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId();
1444
1445- if (application == m_sideStageApplication)
1446- m_sideStageApplication = nullptr;
1447- if (application == m_mainStageApplication)
1448- m_mainStageApplication = nullptr;
1449-
1450 application->disconnect(this);
1451
1452 int i = m_applications.indexOf(application);
1453@@ -953,6 +821,11 @@
1454 Q_EMIT emptyChanged();
1455 }
1456 }
1457+
1458+ if (application == m_focusedApplication) {
1459+ m_focusedApplication = nullptr;
1460+ Q_EMIT focusedApplicationIdChanged();
1461+ }
1462 }
1463
1464 void ApplicationManager::move(int from, int to) {
1465
1466=== modified file 'src/modules/Unity/Application/application_manager.h'
1467--- src/modules/Unity/Application/application_manager.h 2015-03-24 23:38:33 +0000
1468+++ src/modules/Unity/Application/application_manager.h 2015-07-13 21:57:32 +0000
1469@@ -1,5 +1,5 @@
1470 /*
1471- * Copyright (C) 2013 Canonical, Ltd.
1472+ * Copyright (C) 2013-2015 Canonical, Ltd.
1473 *
1474 * This program is free software: you can redistribute it and/or modify it under
1475 * the terms of the GNU Lesser General Public License version 3, as published by
1476@@ -21,6 +21,7 @@
1477 #include <memory>
1478
1479 // Qt
1480+#include <QJSEngine>
1481 #include <QObject>
1482 #include <QStringList>
1483
1484@@ -28,6 +29,7 @@
1485 #include <unity/shell/application/ApplicationManagerInterface.h>
1486
1487 // local
1488+#include "globals.h"
1489 #include "application.h"
1490 #include "desktopfilereader.h"
1491
1492@@ -40,6 +42,7 @@
1493 }
1494
1495 class MirServer;
1496+class QJSValue;
1497
1498 namespace qtmir {
1499
1500@@ -63,7 +66,7 @@
1501 class Factory
1502 {
1503 public:
1504- ApplicationManager* create();
1505+ ApplicationManager* create(QJSEngine *jsEngine);
1506 };
1507
1508 // FIXME: these roles should be added to unity-api and removed from here
1509@@ -79,7 +82,7 @@
1510 };
1511 Q_DECLARE_FLAGS(ExecFlags, Flag)
1512
1513- static ApplicationManager* singleton();
1514+ static ApplicationManager* singleton(QJSEngine *jsEngine);
1515
1516 explicit ApplicationManager(
1517 const QSharedPointer<MirServer> &mirServer,
1518@@ -88,15 +91,16 @@
1519 const QSharedPointer<DesktopFileReader::Factory> &desktopFileReaderFactory,
1520 const QSharedPointer<ProcInfo> &processInfo,
1521 const QSharedPointer<SettingsInterface> &settings,
1522+ QJSEngine *jsEngine,
1523 QObject *parent = 0);
1524 virtual ~ApplicationManager();
1525
1526 // ApplicationManagerInterface
1527 QString focusedApplicationId() const override;
1528- bool suspended() const override;
1529- void setSuspended(bool suspended) override;
1530- bool forceDashActive() const override;
1531- void setForceDashActive(bool forceDashActive) override;
1532+
1533+ QJSValue surfaceAboutToBeCreatedCallback() const override;
1534+ void setSurfaceAboutToBeCreatedCallback(const QJSValue &callback) override;
1535+
1536 Q_INVOKABLE qtmir::Application* get(int index) const override;
1537 Q_INVOKABLE qtmir::Application* findApplication(const QString &appId) const override;
1538 Q_INVOKABLE bool requestFocusApplication(const QString &appId) override;
1539@@ -110,7 +114,7 @@
1540 QVariant data(const QModelIndex & index, int role) const override;
1541
1542 Q_INVOKABLE qtmir::Application *startApplication(const QString &appId, ExecFlags flags,
1543- const QStringList &arguments = QStringList());
1544+ const QStringList &arguments = QStringList());
1545 Q_INVOKABLE void move(int from, int to);
1546
1547 bool isEmpty() const { return rowCount() == 0; }
1548@@ -121,20 +125,24 @@
1549 public Q_SLOTS:
1550 void authorizeSession(const quint64 pid, bool &authorized);
1551
1552- void onSessionStarting(std::shared_ptr<mir::scene::Session> const& session);
1553- void onSessionStopping(std::shared_ptr<mir::scene::Session> const& session);
1554+ void onSessionStarting(const std::shared_ptr<mir::scene::Session> &session);
1555+ void onSessionStopping(const std::shared_ptr<mir::scene::Session> &session);
1556
1557- void onSessionCreatedSurface(mir::scene::Session const*, std::shared_ptr<mir::scene::Surface> const&);
1558+ void onSessionAboutToCreateSurface(const std::shared_ptr<mir::scene::Session> &session,
1559+ SurfaceParameters &surfaceParameters);
1560+ void onSessionCreatedSurface(const mir::scene::Session *session,
1561+ const std::shared_ptr<mir::scene::Surface> &surface);
1562
1563 void onProcessStarting(const QString& appId);
1564 void onProcessStopped(const QString& appId);
1565+ void onProcessSuspended(const QString& appId);
1566 void onProcessFailed(const QString& appId, const bool duringStartup);
1567 void onFocusRequested(const QString& appId);
1568 void onResumeRequested(const QString& appId);
1569
1570 Q_SIGNALS:
1571- void focusRequested(const QString &appId);
1572 void emptyChanged();
1573+ void surfaceAboutToBeCreatedCallbackChanged();
1574
1575 private Q_SLOTS:
1576 void onAppDataChanged(const int role);
1577@@ -146,9 +154,7 @@
1578 void remove(Application* application);
1579 Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);
1580 Application* findApplicationWithSession(const mir::scene::Session *session);
1581- Application* applicationForStage(Application::Stage stage);
1582 QModelIndex findIndex(Application* application);
1583- bool suspendApplication(Application *application);
1584 void resumeApplication(Application *application);
1585 QString toString() const;
1586
1587@@ -158,19 +164,15 @@
1588
1589 QList<Application*> m_applications;
1590 Application* m_focusedApplication;
1591- Application* m_mainStageApplication;
1592- Application* m_sideStageApplication;
1593- QStringList m_lifecycleExceptions;
1594 DBusWindowStack* m_dbusWindowStack;
1595 QSharedPointer<TaskController> m_taskController;
1596 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;
1597 QSharedPointer<ProcInfo> m_procInfo;
1598 QSharedPointer<SharedWakelock> m_sharedWakelock;
1599 QSharedPointer<SettingsInterface> m_settings;
1600+ QJSValue m_surfaceAboutToBeCreatedCallback;
1601+ QJSEngine *m_jsEngine;
1602 static ApplicationManager* the_application_manager;
1603- QList<pid_t> m_hiddenPIDs;
1604- bool m_suspended;
1605- bool m_forceDashActive;
1606
1607 friend class Application;
1608 friend class DBusWindowStack;
1609
1610=== modified file 'src/modules/Unity/Application/applicationcontroller.h'
1611--- src/modules/Unity/Application/applicationcontroller.h 2014-09-18 22:03:02 +0000
1612+++ src/modules/Unity/Application/applicationcontroller.h 2015-07-13 21:57:32 +0000
1613@@ -57,8 +57,9 @@
1614 void applicationAboutToBeStarted(const QString &appId);
1615 void applicationStarted(const QString &appId);
1616 void applicationStopped(const QString &appId);
1617+ void applicationPaused(const QString &appId);
1618 void applicationFocusRequest(const QString &appId);
1619- void applicationResumeRequest(const QString &appId);
1620+ void applicationResumeRequested(const QString &appId);
1621
1622 void applicationError(const QString &appId, ApplicationController::Error error);
1623
1624
1625=== modified file 'src/modules/Unity/Application/applicationscreenshotprovider.cpp'
1626--- src/modules/Unity/Application/applicationscreenshotprovider.cpp 2014-09-18 09:38:41 +0000
1627+++ src/modules/Unity/Application/applicationscreenshotprovider.cpp 2015-07-13 21:57:32 +0000
1628@@ -55,7 +55,7 @@
1629
1630 // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state
1631 // return the screenshot that was saved to disk.
1632- Session* session = app->session();
1633+ SessionInterface* session = app->session();
1634 if (!session || !session->session() || !session->session()->default_surface()) {
1635 qWarning() << "ApplicationScreenshotProvider - app session not found - asking for screenshot too early";
1636 return QImage();
1637
1638=== modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
1639--- src/modules/Unity/Application/mirsurfaceitem.cpp 2015-05-21 18:38:27 +0000
1640+++ src/modules/Unity/Application/mirsurfaceitem.cpp 2015-07-13 21:57:32 +0000
1641@@ -190,13 +190,13 @@
1642 MirShell *shell,
1643 std::shared_ptr<SurfaceObserver> observer,
1644 QQuickItem *parent)
1645- : QQuickItem(parent)
1646+ : MirSurfaceItemInterface(parent)
1647 , m_surface(surface)
1648 , m_session(session)
1649 , m_shell(shell)
1650 , m_firstFrameDrawn(false)
1651 , m_live(true)
1652- , m_orientationAngle(Angle0)
1653+ , m_orientationAngle(Globals::Angle0)
1654 , m_textureProvider(nullptr)
1655 , m_lastTouchEvent(nullptr)
1656 {
1657@@ -296,22 +296,22 @@
1658 return m_session.data();
1659 }
1660
1661-MirSurfaceItem::Type MirSurfaceItem::type() const
1662-{
1663- return static_cast<MirSurfaceItem::Type>(m_surface->type());
1664-}
1665-
1666-MirSurfaceItem::State MirSurfaceItem::state() const
1667-{
1668- return static_cast<MirSurfaceItem::State>(m_surface->state());
1669-}
1670-
1671-MirSurfaceItem::OrientationAngle MirSurfaceItem::orientationAngle() const
1672+Globals::SurfaceType MirSurfaceItem::type() const
1673+{
1674+ return static_cast<Globals::SurfaceType>(m_surface->type());
1675+}
1676+
1677+Globals::SurfaceState MirSurfaceItem::state() const
1678+{
1679+ return static_cast<Globals::SurfaceState>(m_surface->state());
1680+}
1681+
1682+Globals::OrientationAngle MirSurfaceItem::orientationAngle() const
1683 {
1684 return m_orientationAngle;
1685 }
1686
1687-void MirSurfaceItem::setOrientationAngle(MirSurfaceItem::OrientationAngle angle)
1688+void MirSurfaceItem::setOrientationAngle(Globals::OrientationAngle angle)
1689 {
1690 qCDebug(QTMIR_SURFACES, "MirSurfaceItem::setOrientationAngle(%d)", angle);
1691
1692@@ -321,16 +321,16 @@
1693 MirOrientation mirOrientation;
1694
1695 switch (angle) {
1696- case Angle0:
1697+ case Globals::Angle0:
1698 mirOrientation = mir_orientation_normal;
1699 break;
1700- case Angle90:
1701+ case Globals::Angle90:
1702 mirOrientation = mir_orientation_right;
1703 break;
1704- case Angle180:
1705+ case Globals::Angle180:
1706 mirOrientation = mir_orientation_inverted;
1707 break;
1708- case Angle270:
1709+ case Globals::Angle270:
1710 mirOrientation = mir_orientation_left;
1711 break;
1712 default:
1713@@ -375,7 +375,7 @@
1714 {
1715 if (!m_firstFrameDrawn) {
1716 m_firstFrameDrawn = true;
1717- Q_EMIT firstFrameDrawn(this);
1718+ Q_EMIT firstFrameDrawn();
1719 }
1720
1721 scheduleTextureUpdate();
1722@@ -456,7 +456,7 @@
1723
1724 void MirSurfaceItem::mousePressEvent(QMouseEvent *event)
1725 {
1726- if (type() == InputMethod) {
1727+ if (type() == Globals::SurfaceType::InputMethod) {
1728 // FIXME: Hack to get the VKB use case working while we don't have the proper solution in place.
1729 if (isMouseInsideUbuntuKeyboard(event)) {
1730 auto ev = makeMirEvent(event, mir_pointer_action_button_down);
1731@@ -562,7 +562,7 @@
1732
1733 touchEvent.updateTouchPointStatesAndType();
1734
1735- auto ev = makeMirEvent(touchEvent.modifiers, touchEvent.touchPoints,
1736+ auto ev = makeMirEvent(touchEvent.modifiers, touchEvent.touchPoints,
1737 touchEvent.touchPointStates, touchEvent.timestamp);
1738 m_surface->consume(*ev);
1739
1740@@ -615,7 +615,7 @@
1741 Qt::TouchPointStates touchPointStates)
1742 {
1743 bool accepted = true;
1744- if (type() == InputMethod && eventType == QEvent::TouchBegin) {
1745+ if (type() == Globals::SurfaceType::InputMethod && eventType == QEvent::TouchBegin) {
1746 // FIXME: Hack to get the VKB use case working while we don't have the proper solution in place.
1747 if (hasTouchInsideUbuntuKeyboard(touchPoints)) {
1748 validateAndDeliverTouchEvent(eventType, timestamp, mods, touchPoints, touchPointStates);
1749@@ -659,21 +659,21 @@
1750 && pos.y() <= (ubuntuKeyboardInfo->y() + ubuntuKeyboardInfo->height());
1751 }
1752
1753-void MirSurfaceItem::setType(const Type &type)
1754+void MirSurfaceItem::setType(const Globals::SurfaceType &type)
1755 {
1756 if (this->type() != type) {
1757 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_type, static_cast<int>(type));
1758 }
1759 }
1760
1761-void MirSurfaceItem::setState(const State &state)
1762+void MirSurfaceItem::setState(const Globals::SurfaceState &state)
1763 {
1764 if (this->state() != state) {
1765 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_state, static_cast<int>(state));
1766 }
1767 }
1768
1769-void MirSurfaceItem::setLive(const bool live)
1770+void MirSurfaceItem::setLive(bool live)
1771 {
1772 if (m_live != live) {
1773 m_live = live;
1774
1775=== modified file 'src/modules/Unity/Application/mirsurfaceitem.h'
1776--- src/modules/Unity/Application/mirsurfaceitem.h 2015-05-13 17:18:45 +0000
1777+++ src/modules/Unity/Application/mirsurfaceitem.h 2015-07-13 21:57:32 +0000
1778@@ -22,15 +22,13 @@
1779 // Qt
1780 #include <QMutex>
1781 #include <QPointer>
1782-#include <QSet>
1783-#include <QQuickItem>
1784 #include <QTimer>
1785 #include <QQmlListProperty>
1786
1787 // mir
1788 #include <mir/scene/surface.h>
1789-#include <mir_toolkit/common.h>
1790
1791+#include "mirsurfaceiteminterface.h"
1792 #include "session_interface.h"
1793
1794 class SurfaceObserver;
1795@@ -41,24 +39,10 @@
1796 class MirSurfaceManager;
1797 class QSGMirSurfaceNode;
1798 class QMirSurfaceTextureProvider;
1799-class Application;
1800
1801-class MirSurfaceItem : public QQuickItem
1802+class MirSurfaceItem : public MirSurfaceItemInterface
1803 {
1804 Q_OBJECT
1805- Q_ENUMS(Type)
1806- Q_ENUMS(State)
1807- Q_ENUMS(OrientationAngle)
1808-
1809- Q_PROPERTY(Type type READ type NOTIFY typeChanged)
1810- Q_PROPERTY(State state READ state NOTIFY stateChanged)
1811- Q_PROPERTY(QString name READ name NOTIFY nameChanged)
1812- Q_PROPERTY(bool live READ live NOTIFY liveChanged)
1813-
1814- // How many degrees, clockwise, the UI in the surface has to rotate to match with the
1815- // shell UI orientation
1816- Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
1817- NOTIFY orientationAngleChanged DESIGNABLE false)
1818
1819 public:
1820 explicit MirSurfaceItem(std::shared_ptr<mir::scene::Surface> surface,
1821@@ -66,57 +50,29 @@
1822 MirShell *shell,
1823 std::shared_ptr<SurfaceObserver> observer,
1824 QQuickItem *parent = 0);
1825- ~MirSurfaceItem();
1826-
1827- enum Type {
1828- Normal = mir_surface_type_normal,
1829- Utility = mir_surface_type_utility,
1830- Dialog = mir_surface_type_dialog,
1831- Overlay = mir_surface_type_overlay,
1832- Freestyle = mir_surface_type_freestyle,
1833- Popover = mir_surface_type_popover,
1834- InputMethod = mir_surface_type_inputmethod,
1835- };
1836-
1837- enum State {
1838- Unknown = mir_surface_state_unknown,
1839- Restored = mir_surface_state_restored,
1840- Minimized = mir_surface_state_minimized,
1841- Maximized = mir_surface_state_maximized,
1842- VertMaximized = mir_surface_state_vertmaximized,
1843- /* SemiMaximized = mir_surface_state_semimaximized, // see mircommon/mir_toolbox/common.h*/
1844- Fullscreen = mir_surface_state_fullscreen,
1845- };
1846-
1847- enum OrientationAngle {
1848- Angle0 = 0,
1849- Angle90 = 90,
1850- Angle180 = 180,
1851- Angle270 = 270
1852- };
1853+ virtual ~MirSurfaceItem();
1854
1855 //getters
1856- Type type() const;
1857- State state() const;
1858- QString name() const;
1859- bool live() const;
1860- SessionInterface *session() const;
1861+ Globals::SurfaceType type() const override;
1862+ Globals::SurfaceState state() const override;
1863+ QString name() const override;
1864+ bool live() const override;
1865+ SessionInterface *session() const override;
1866+ Globals::OrientationAngle orientationAngle() const override;
1867
1868- Q_INVOKABLE void release();
1869+ Q_INVOKABLE void release() override;
1870
1871 // Item surface/texture management
1872 bool isTextureProvider() const { return true; }
1873 QSGTextureProvider *textureProvider() const;
1874
1875- void stopFrameDropper();
1876- void startFrameDropper();
1877-
1878- bool isFirstFrameDrawn() const { return m_firstFrameDrawn; }
1879-
1880- OrientationAngle orientationAngle() const;
1881- void setOrientationAngle(OrientationAngle angle);
1882-
1883- void setSession(SessionInterface *app);
1884+ void stopFrameDropper() override;
1885+ void startFrameDropper() override;
1886+
1887+ bool isFirstFrameDrawn() const override { return m_firstFrameDrawn; }
1888+
1889+ void setOrientationAngle(Globals::OrientationAngle angle) override;
1890+ void setSession(SessionInterface *app) override;
1891
1892 // to allow easy touch event injection from tests
1893 bool processTouchEvent(int eventType,
1894@@ -125,14 +81,6 @@
1895 const QList<QTouchEvent::TouchPoint> &touchPoints,
1896 Qt::TouchPointStates touchPointStates);
1897
1898-Q_SIGNALS:
1899- void typeChanged();
1900- void stateChanged();
1901- void nameChanged();
1902- void orientationAngleChanged(OrientationAngle angle);
1903- void liveChanged(bool live);
1904- void firstFrameDrawn(MirSurfaceItem *item);
1905-
1906 protected Q_SLOTS:
1907 void onSessionStateChanged(SessionInterface::State state);
1908
1909@@ -167,9 +115,9 @@
1910 bool updateTexture();
1911 void ensureProvider();
1912
1913- void setType(const Type&);
1914- void setState(const State&);
1915- void setLive(const bool);
1916+ void setType(const Globals::SurfaceType&);
1917+ void setState(const Globals::SurfaceState&);
1918+ void setLive(bool) override;
1919
1920 // called by MirSurfaceManager
1921 void setSurfaceValid(const bool);
1922@@ -197,7 +145,7 @@
1923 bool m_live;
1924
1925 //FIXME - have to save the state as Mir has no getter for it (bug:1357429)
1926- OrientationAngle m_orientationAngle;
1927+ Globals::OrientationAngle m_orientationAngle;
1928
1929 QMirSurfaceTextureProvider *m_textureProvider;
1930
1931@@ -226,13 +174,8 @@
1932 QList<QTouchEvent::TouchPoint> touchPoints;
1933 Qt::TouchPointStates touchPointStates;
1934 } *m_lastTouchEvent;
1935-
1936- friend class MirSurfaceManager;
1937 };
1938
1939 } // namespace qtmir
1940
1941-Q_DECLARE_METATYPE(qtmir::MirSurfaceItem*)
1942-Q_DECLARE_METATYPE(qtmir::MirSurfaceItem::OrientationAngle)
1943-
1944 #endif // MIRSURFACEITEM_H
1945
1946=== added file 'src/modules/Unity/Application/mirsurfaceiteminterface.h'
1947--- src/modules/Unity/Application/mirsurfaceiteminterface.h 1970-01-01 00:00:00 +0000
1948+++ src/modules/Unity/Application/mirsurfaceiteminterface.h 2015-07-13 21:57:32 +0000
1949@@ -0,0 +1,89 @@
1950+/*
1951+ * Copyright (C) 2015 Canonical, Ltd.
1952+ *
1953+ * This program is free software: you can redistribute it and/or modify it under
1954+ * the terms of the GNU Lesser General Public License version 3, as published by
1955+ * the Free Software Foundation.
1956+ *
1957+ * This program is distributed in the hope that it will be useful, but WITHOUT
1958+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1959+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1960+ * Lesser General Public License for more details.
1961+ *
1962+ * You should have received a copy of the GNU Lesser General Public License
1963+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1964+ */
1965+
1966+#ifndef MIRSURFACEITEMINTERFACE_H
1967+#define MIRSURFACEITEMINTERFACE_H
1968+
1969+// Qt
1970+#include <QQuickItem>
1971+
1972+// mir
1973+#include <mir_toolkit/common.h>
1974+
1975+#include "session_interface.h"
1976+#include "globals.h"
1977+
1978+namespace qtmir {
1979+
1980+class MirSurfaceItemInterface : public QQuickItem
1981+{
1982+ Q_OBJECT
1983+ Q_ENUMS(Type)
1984+ Q_ENUMS(State)
1985+ Q_ENUMS(OrientationAngle)
1986+
1987+ Q_PROPERTY(qtmir::Globals::SurfaceType type READ type NOTIFY typeChanged)
1988+ Q_PROPERTY(qtmir::Globals::SurfaceState state READ state NOTIFY stateChanged)
1989+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
1990+ Q_PROPERTY(bool live READ live NOTIFY liveChanged)
1991+
1992+ // How many degrees, clockwise, the UI in the surface has to rotate to match with the
1993+ // shell UI orientation
1994+ Q_PROPERTY(qtmir::Globals::OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
1995+ NOTIFY orientationAngleChanged DESIGNABLE false)
1996+
1997+public:
1998+ MirSurfaceItemInterface(QQuickItem *parent) : QQuickItem(parent) {}
1999+ virtual ~MirSurfaceItemInterface() {}
2000+
2001+ //getters
2002+ virtual Globals::SurfaceType type() const = 0;
2003+ virtual Globals::SurfaceState state() const = 0;
2004+ virtual QString name() const = 0;
2005+ virtual bool live() const = 0;
2006+ virtual SessionInterface *session() const = 0;
2007+ virtual Globals::OrientationAngle orientationAngle() const = 0;
2008+
2009+ virtual Q_INVOKABLE void release() = 0;
2010+
2011+ virtual void stopFrameDropper() = 0;
2012+ virtual void startFrameDropper() = 0;
2013+
2014+ virtual bool isFirstFrameDrawn() const = 0;
2015+
2016+ virtual void setOrientationAngle(Globals::OrientationAngle angle) = 0;
2017+ virtual void setSession(SessionInterface *app) = 0;
2018+
2019+Q_SIGNALS:
2020+ void typeChanged();
2021+ void stateChanged();
2022+ void nameChanged();
2023+ void orientationAngleChanged(Globals::OrientationAngle angle);
2024+ void liveChanged(bool live);
2025+ void firstFrameDrawn();
2026+
2027+private:
2028+ virtual void setLive(bool) = 0;
2029+
2030+ friend class MirSurfaceManager;
2031+};
2032+
2033+} // namespace qtmir
2034+
2035+Q_DECLARE_METATYPE(qtmir::MirSurfaceItemInterface*)
2036+
2037+#endif // MIRSURFACEITEMINTERFACE_H
2038+
2039
2040=== modified file 'src/modules/Unity/Application/mirsurfaceitemmodel.h'
2041--- src/modules/Unity/Application/mirsurfaceitemmodel.h 2014-08-29 11:15:51 +0000
2042+++ src/modules/Unity/Application/mirsurfaceitemmodel.h 2015-07-13 21:57:32 +0000
2043@@ -22,8 +22,8 @@
2044
2045 namespace qtmir {
2046
2047-class MirSurfaceItem;
2048-typedef ObjectListModel<MirSurfaceItem> MirSurfaceItemModel;
2049+class MirSurfaceItemInterface;
2050+typedef ObjectListModel<MirSurfaceItemInterface> MirSurfaceItemModel;
2051
2052 } // namespace qtmir
2053
2054
2055=== modified file 'src/modules/Unity/Application/mirsurfacemanager.cpp'
2056--- src/modules/Unity/Application/mirsurfacemanager.cpp 2015-03-11 10:10:49 +0000
2057+++ src/modules/Unity/Application/mirsurfacemanager.cpp 2015-07-13 21:57:32 +0000
2058@@ -51,7 +51,7 @@
2059 manager, &MirSurfaceManager::onSessionDestroyingSurface);
2060 }
2061
2062-MirSurfaceManager* MirSurfaceManager::singleton()
2063+MirSurfaceManager* MirSurfaceManager::singleton(QJSEngine *jsEngine)
2064 {
2065 if (!the_surface_manager) {
2066
2067@@ -66,7 +66,7 @@
2068 SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));
2069 MirShell *shell = static_cast<MirShell*>(nativeInterface->nativeResourceForIntegration("Shell"));
2070
2071- the_surface_manager = new MirSurfaceManager(nativeInterface->m_mirServer, shell, SessionManager::singleton());
2072+ the_surface_manager = new MirSurfaceManager(nativeInterface->m_mirServer, shell, SessionManager::singleton(jsEngine));
2073
2074 connectToSessionListener(the_surface_manager, sessionListener);
2075 }
2076@@ -112,11 +112,11 @@
2077 session->setSurface(qmlSurface);
2078
2079 // Only notify QML of surface creation once it has drawn its first frame.
2080- connect(qmlSurface, &MirSurfaceItem::firstFrameDrawn, this, [&](MirSurfaceItem *item) {
2081+ connect(qmlSurface, &MirSurfaceItemInterface::firstFrameDrawn, this, [=]() {
2082 tracepoint(qtmir, firstFrameDrawn);
2083- Q_EMIT surfaceCreated(item);
2084+ Q_EMIT surfaceCreated(qmlSurface);
2085
2086- insert(0, item);
2087+ insert(0, qmlSurface);
2088 });
2089
2090 // clean up after MirSurfaceItem is destroyed
2091@@ -139,7 +139,7 @@
2092 qCDebug(QTMIR_SURFACES) << "MirSurfaceManager::onSessionDestroyingSurface - session=" << session
2093 << "surface=" << surface.get() << "surface.name=" << surface->name().c_str();
2094
2095- MirSurfaceItem* item = nullptr;
2096+ MirSurfaceItemInterface* item = nullptr;
2097 {
2098 QMutexLocker lock(&m_mutex);
2099 auto it = m_mirSurfaceToItemHash.find(surface.get());
2100
2101=== modified file 'src/modules/Unity/Application/mirsurfacemanager.h'
2102--- src/modules/Unity/Application/mirsurfacemanager.h 2015-03-11 10:10:49 +0000
2103+++ src/modules/Unity/Application/mirsurfacemanager.h 2015-07-13 21:57:32 +0000
2104@@ -61,11 +61,11 @@
2105 );
2106 ~MirSurfaceManager();
2107
2108- static MirSurfaceManager* singleton();
2109+ static MirSurfaceManager* singleton(QJSEngine *jsEngine);
2110
2111 Q_SIGNALS:
2112- void surfaceCreated(MirSurfaceItem* surface);
2113- void surfaceDestroyed(MirSurfaceItem* surface);
2114+ void surfaceCreated(MirSurfaceItemInterface* surface);
2115+ void surfaceDestroyed(MirSurfaceItemInterface* surface);
2116 // void surfaceResized(MirSurface*);
2117 // void fullscreenSurfaceChanged();
2118
2119@@ -74,7 +74,7 @@
2120 void onSessionDestroyingSurface(const mir::scene::Session *, const std::shared_ptr<mir::scene::Surface> &);
2121
2122 protected:
2123- QHash<const mir::scene::Surface *, MirSurfaceItem *> m_mirSurfaceToItemHash;
2124+ QHash<const mir::scene::Surface *, MirSurfaceItemInterface *> m_mirSurfaceToItemHash;
2125 QMutex m_mutex;
2126
2127 private:
2128
2129=== modified file 'src/modules/Unity/Application/plugin.cpp'
2130--- src/modules/Unity/Application/plugin.cpp 2015-02-05 10:28:31 +0000
2131+++ src/modules/Unity/Application/plugin.cpp 2015-07-13 21:57:32 +0000
2132@@ -28,41 +28,36 @@
2133
2134 // qtmir
2135 #include "logging.h"
2136-
2137-using namespace qtmir;
2138-
2139-static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
2140- Q_UNUSED(engine);
2141- Q_UNUSED(scriptEngine);
2142- qCDebug(QTMIR_APPLICATIONS) << "applicationManagerSingleton - engine=" << engine << "scriptEngine=" << scriptEngine;
2143-
2144- return qtmir::ApplicationManager::singleton();
2145-}
2146-
2147-static QObject* surfaceManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
2148- Q_UNUSED(engine);
2149- Q_UNUSED(scriptEngine);
2150- qCDebug(QTMIR_APPLICATIONS) << "surfaceManagerSingleton - engine=" << engine << "scriptEngine=" << scriptEngine;
2151-
2152- return qtmir::MirSurfaceManager::singleton();
2153-}
2154-
2155-static QObject* sessionManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
2156- Q_UNUSED(engine);
2157- Q_UNUSED(scriptEngine);
2158- return qtmir::SessionManager::singleton();
2159-}
2160+#include "globals.h"
2161
2162 namespace {
2163-QObject* ubuntuKeyboardInfoSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) {
2164- if (!UbuntuKeyboardInfo::instance()) {
2165- new UbuntuKeyboardInfo;
2166+
2167+QObject* applicationManagerSingleton(QQmlEngine* /*engine*/, QJSEngine* jsEngine)
2168+{
2169+ return qtmir::ApplicationManager::singleton(jsEngine);
2170+}
2171+
2172+QObject* surfaceManagerSingleton(QQmlEngine* /*engine*/, QJSEngine* jsEngine)
2173+{
2174+ return qtmir::MirSurfaceManager::singleton(jsEngine);
2175+}
2176+
2177+QObject* sessionManagerSingleton(QQmlEngine* /*engine*/, QJSEngine* jsEngine)
2178+{
2179+ return qtmir::SessionManager::singleton(jsEngine);
2180+}
2181+
2182+QObject* ubuntuKeyboardInfoSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/)
2183+{
2184+ if (!qtmir::UbuntuKeyboardInfo::instance()) {
2185+ new qtmir::UbuntuKeyboardInfo;
2186 }
2187- return UbuntuKeyboardInfo::instance();
2188+ return qtmir::UbuntuKeyboardInfo::instance();
2189 }
2190 } // anonymous namespace
2191
2192-class UnityApplicationPlugin : public QQmlExtensionPlugin {
2193+class UnityApplicationPlugin : public QQmlExtensionPlugin
2194+{
2195 Q_OBJECT
2196 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
2197
2198@@ -71,9 +66,12 @@
2199 qCDebug(QTMIR_APPLICATIONS) << "UnityApplicationPlugin::registerTypes - this=" << this << "uri=" << uri;
2200 Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Application"));
2201
2202+ qmlRegisterUncreatableType<qtmir::Globals>(
2203+ uri, 0, 1, "QtMir", "QtMir provides enum values, it can't be instantiated");
2204+
2205 qRegisterMetaType<qtmir::ApplicationManager*>("ApplicationManager*"); //need for queueing signals
2206 qRegisterMetaType<qtmir::Application*>("Application*");
2207- qRegisterMetaType<qtmir::MirSurfaceItem*>("MirSurfaceItem*");
2208+ qRegisterMetaType<qtmir::MirSurfaceItemInterface*>("MirSurfaceItemInterface*");
2209 qRegisterMetaType<qtmir::MirSurfaceItemModel*>("MirSurfaceItemModel*");
2210 qRegisterMetaType<qtmir::Session*>("Session*");
2211 qRegisterMetaType<qtmir::SessionInterface*>("SessionInterface*");
2212@@ -92,7 +90,7 @@
2213 uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);
2214 qmlRegisterSingletonType<qtmir::SessionManager>(
2215 uri, 0, 1, "SessionManager", sessionManagerSingleton);
2216- qmlRegisterUncreatableType<qtmir::MirSurfaceItem>(
2217+ qmlRegisterUncreatableType<qtmir::MirSurfaceItemInterface>(
2218 uri, 0, 1, "MirSurfaceItem", "MirSurfaceItem can't be instantiated from QML");
2219 qmlRegisterUncreatableType<qtmir::Session>(
2220 uri, 0, 1, "Session", "Session can't be instantiated from QML");
2221@@ -105,7 +103,7 @@
2222 QQmlExtensionPlugin::initializeEngine(engine, uri);
2223
2224 qtmir::ApplicationManager* appManager
2225- = static_cast<qtmir::ApplicationManager*>(applicationManagerSingleton(engine, nullptr));
2226+ = static_cast<qtmir::ApplicationManager*>(applicationManagerSingleton(engine, engine));
2227 engine->addImageProvider(QLatin1String("application"), new qtmir::ApplicationScreenshotProvider(appManager));
2228 }
2229 };
2230
2231=== modified file 'src/modules/Unity/Application/session.cpp'
2232--- src/modules/Unity/Application/session.cpp 2015-01-15 15:19:26 +0000
2233+++ src/modules/Unity/Application/session.cpp 2015-07-13 21:57:32 +0000
2234@@ -1,5 +1,5 @@
2235 /*
2236- * Copyright (C) 2014 Canonical, Ltd.
2237+ * Copyright (C) 2014,2015 Canonical, Ltd.
2238 *
2239 * This program is free software; you can redistribute it and/or modify
2240 * it under the terms of the GNU General Public License as published by
2241@@ -60,14 +60,7 @@
2242 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
2243
2244 m_suspendTimer->setSingleShot(true);
2245- connect(m_suspendTimer, &QTimer::timeout, this, [this]() {
2246- if (m_surface) {
2247- m_surface->stopFrameDropper();
2248- } else {
2249- qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!";
2250- }
2251- Q_EMIT suspended();
2252- });
2253+ connect(m_suspendTimer, &QTimer::timeout, this, &Session::doSuspend);
2254 }
2255
2256 Session::~Session()
2257@@ -89,6 +82,17 @@
2258 delete m_children; m_children = nullptr;
2259 }
2260
2261+void Session::doSuspend()
2262+{
2263+ Q_ASSERT(m_state == Session::Suspending);
2264+ if (m_surface) {
2265+ m_surface->stopFrameDropper();
2266+ } else {
2267+ qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!";
2268+ }
2269+ setState(Suspended);
2270+}
2271+
2272 void Session::release()
2273 {
2274 qCDebug(QTMIR_SESSIONS) << "Session::release " << name();
2275@@ -120,7 +124,7 @@
2276 return m_application;
2277 }
2278
2279-MirSurfaceItem* Session::surface() const
2280+MirSurfaceItemInterface* Session::surface() const
2281 {
2282 // Only notify QML of surface creation once it has drawn its first frame.
2283 if (m_surface && m_surface->isFirstFrameDrawn()) {
2284@@ -140,6 +144,13 @@
2285 return m_state;
2286 }
2287
2288+void Session::setState(State state) {
2289+ if (state != m_state) {
2290+ m_state = state;
2291+ Q_EMIT stateChanged(m_state);
2292+ }
2293+}
2294+
2295 bool Session::fullscreen() const
2296 {
2297 return m_fullscreen;
2298@@ -159,7 +170,7 @@
2299 Q_EMIT applicationChanged(application);
2300 }
2301
2302-void Session::setSurface(MirSurfaceItem *newSurface)
2303+void Session::setSurface(MirSurfaceItemInterface *newSurface)
2304 {
2305 qCDebug(QTMIR_SESSIONS) << "Session::setSurface - session=" << name() << "surface=" << newSurface;
2306
2307@@ -173,34 +184,46 @@
2308 m_surface->setParent(nullptr);
2309 }
2310
2311- MirSurfaceItem *previousSurface = surface();
2312+ MirSurfaceItemInterface *previousSurface = surface();
2313 m_surface = newSurface;
2314
2315 if (newSurface) {
2316 m_surface->setParent(this);
2317 m_surface->setSession(this);
2318
2319+ connect(newSurface, &MirSurfaceItemInterface::stateChanged,
2320+ this, &Session::updateFullscreenProperty);
2321+
2322 // Only notify QML of surface creation once it has drawn its first frame.
2323- if (!surface()) {
2324- connect(newSurface, &MirSurfaceItem::firstFrameDrawn,
2325- this, [this] { Q_EMIT surfaceChanged(m_surface); });
2326+ if (m_surface->isFirstFrameDrawn()) {
2327+ setState(Running);
2328+ } else {
2329+ connect(newSurface, &MirSurfaceItemInterface::firstFrameDrawn,
2330+ this, &Session::onFirstSurfaceFrameDrawn);
2331 }
2332-
2333- connect(newSurface, &MirSurfaceItem::stateChanged,
2334- this, &Session::updateFullscreenProperty);
2335 }
2336
2337 if (previousSurface != surface()) {
2338+ qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this
2339+ << " surface=" << m_surface;
2340 Q_EMIT surfaceChanged(m_surface);
2341 }
2342
2343 updateFullscreenProperty();
2344 }
2345
2346+void Session::onFirstSurfaceFrameDrawn()
2347+{
2348+ qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this
2349+ << " surface=" << m_surface;
2350+ Q_EMIT surfaceChanged(m_surface);
2351+ setState(Running);
2352+}
2353+
2354 void Session::updateFullscreenProperty()
2355 {
2356 if (m_surface) {
2357- setFullscreen(m_surface->state() == MirSurfaceItem::Fullscreen);
2358+ setFullscreen(m_surface->state() == Globals::SurfaceState::Fullscreen);
2359 } else {
2360 // Keep the current value of the fullscreen property until we get a new
2361 // surface
2362@@ -216,59 +239,71 @@
2363 }
2364 }
2365
2366-void Session::setState(State state)
2367-{
2368- qCDebug(QTMIR_SESSIONS) << "Session::setState - session=" << this << "state=" << applicationStateToStr(state);
2369- if (m_state != state) {
2370- switch (state)
2371- {
2372- case Session::State::Suspended:
2373- if (m_state == Session::State::Running) {
2374- session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
2375- m_suspendTimer->start(1500);
2376- }
2377- break;
2378- case Session::State::Running:
2379- if (m_suspendTimer->isActive())
2380- m_suspendTimer->stop();
2381-
2382- if (m_state == Session::State::Suspended) {
2383- if (m_surface)
2384- m_surface->startFrameDropper();
2385- Q_EMIT resumed();
2386- session()->set_lifecycle_state(mir_lifecycle_state_resumed);
2387- }
2388- break;
2389- case Session::State::Stopped:
2390- stopPromptSessions();
2391- if (m_suspendTimer->isActive())
2392- m_suspendTimer->stop();
2393- if (m_surface)
2394- m_surface->stopFrameDropper();
2395- break;
2396- default:
2397- break;
2398- }
2399-
2400- m_state = state;
2401- Q_EMIT stateChanged(state);
2402-
2403- foreachPromptSession([this, state](const std::shared_ptr<ms::PromptSession>& promptSession) {
2404- switch (state) {
2405- case Session::State::Suspended:
2406- m_promptSessionManager->suspend_prompt_session(promptSession);
2407- break;
2408- case Session::State::Running:
2409- m_promptSessionManager->resume_prompt_session(promptSession);
2410- break;
2411- default:
2412- break;
2413- }
2414- });
2415-
2416- foreachChildSession([state](SessionInterface* session) {
2417- session->setState(state);
2418- });
2419+void Session::suspend()
2420+{
2421+ qCDebug(QTMIR_SESSIONS) << "Session::suspend - session=" << this << "state=" << applicationStateToStr(m_state);
2422+ if (m_state == Running) {
2423+ session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
2424+ m_suspendTimer->start(1500);
2425+
2426+ foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) {
2427+ m_promptSessionManager->suspend_prompt_session(promptSession);
2428+ });
2429+
2430+ foreachChildSession([](SessionInterface* session) {
2431+ session->suspend();
2432+ });
2433+
2434+ setState(Suspending);
2435+ }
2436+}
2437+
2438+void Session::resume()
2439+{
2440+ qCDebug(QTMIR_SESSIONS) << "Session::resume - session=" << this << "state=" << applicationStateToStr(m_state);
2441+
2442+ if (m_state == Suspending || m_state == Suspended) {
2443+ doResume();
2444+ }
2445+}
2446+
2447+void Session::doResume()
2448+{
2449+ if (m_state == Suspending) {
2450+ Q_ASSERT(m_suspendTimer->isActive());
2451+ m_suspendTimer->stop();
2452+ } else if (m_state == Suspended) {
2453+ Q_ASSERT(m_surface);
2454+ m_surface->startFrameDropper();
2455+ }
2456+
2457+ session()->set_lifecycle_state(mir_lifecycle_state_resumed);
2458+
2459+ foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) {
2460+ m_promptSessionManager->resume_prompt_session(promptSession);
2461+ });
2462+
2463+ foreachChildSession([](SessionInterface* session) {
2464+ session->resume();
2465+ });
2466+
2467+ setState(Running);
2468+}
2469+
2470+void Session::stop()
2471+{
2472+ if (m_state != Stopped) {
2473+ stopPromptSessions();
2474+ if (m_suspendTimer->isActive())
2475+ m_suspendTimer->stop();
2476+ if (m_surface)
2477+ m_surface->stopFrameDropper();
2478+
2479+ foreachChildSession([](SessionInterface* session) {
2480+ session->stop();
2481+ });
2482+
2483+ setState(Stopped);
2484 }
2485 }
2486
2487@@ -277,6 +312,9 @@
2488 if (m_live != live) {
2489 m_live = live;
2490 Q_EMIT liveChanged(m_live);
2491+ if (!live) {
2492+ setState(Stopped);
2493+ }
2494 }
2495 }
2496
2497@@ -302,7 +340,19 @@
2498 static_cast<Session*>(session)->setParentSession(this);
2499 m_children->insert(index, session);
2500
2501- session->setState(state());
2502+ switch (m_state) {
2503+ case Starting:
2504+ case Running:
2505+ session->resume();
2506+ break;
2507+ case Suspending:
2508+ case Suspended:
2509+ session->suspend();
2510+ break;
2511+ case Stopped:
2512+ session->stop();
2513+ break;
2514+ }
2515 }
2516
2517 void Session::removeChildSession(SessionInterface* session)
2518
2519=== modified file 'src/modules/Unity/Application/session.h'
2520--- src/modules/Unity/Application/session.h 2014-09-11 16:18:40 +0000
2521+++ src/modules/Unity/Application/session.h 2015-07-13 21:57:32 +0000
2522@@ -1,5 +1,5 @@
2523 /*
2524- * Copyright (C) 2014 Canonical, Ltd.
2525+ * Copyright (C) 2014-2015 Canonical, Ltd.
2526 *
2527 * This program is free software; you can redistribute it and/or modify
2528 * it under the terms of the GNU General Public License as published by
2529@@ -52,15 +52,18 @@
2530 //getters
2531 QString name() const override;
2532 unity::shell::application::ApplicationInfoInterface* application() const override;
2533- MirSurfaceItem* surface() const override;
2534+ MirSurfaceItemInterface* surface() const override;
2535 SessionInterface* parentSession() const override;
2536 State state() const override;
2537 bool fullscreen() const override;
2538 bool live() const override;
2539
2540 void setApplication(unity::shell::application::ApplicationInfoInterface* item) override;
2541- void setSurface(MirSurfaceItem* surface) override;
2542- void setState(State state) override;
2543+ void setSurface(MirSurfaceItemInterface* surface) override;
2544+
2545+ void suspend() override;
2546+ void resume() override;
2547+ void stop() override;
2548
2549 void addChildSession(SessionInterface* session) override;
2550 void insertChildSession(uint index, SessionInterface* session) override;
2551@@ -74,23 +77,29 @@
2552
2553 SessionModel* childSessions() const override;
2554
2555-protected:
2556 void setFullscreen(bool fullscreen) override;
2557 void setLive(const bool) override;
2558 void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override;
2559 void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override;
2560
2561+public Q_SLOTS:
2562+ // it's public to ease testing
2563+ void doSuspend();
2564+
2565 private Q_SLOTS:
2566 void updateFullscreenProperty();
2567+ void onFirstSurfaceFrameDrawn();
2568
2569 private:
2570 void setParentSession(Session* session);
2571+ void setState(State state);
2572+ void doResume();
2573
2574 void stopPromptSessions();
2575
2576 std::shared_ptr<mir::scene::Session> m_session;
2577 Application* m_application;
2578- MirSurfaceItem* m_surface;
2579+ MirSurfaceItemInterface* m_surface;
2580 SessionInterface* m_parentSession;
2581 SessionModel* m_children;
2582 bool m_fullscreen;
2583@@ -103,6 +112,4 @@
2584
2585 } // namespace qtmir
2586
2587-Q_DECLARE_METATYPE(qtmir::Session*)
2588-
2589 #endif // SESSION_H
2590
2591=== modified file 'src/modules/Unity/Application/session_interface.h'
2592--- src/modules/Unity/Application/session_interface.h 2014-09-22 18:06:58 +0000
2593+++ src/modules/Unity/Application/session_interface.h 2015-07-13 21:57:32 +0000
2594@@ -1,5 +1,5 @@
2595 /*
2596- * Copyright (C) 2014 Canonical, Ltd.
2597+ * Copyright (C) 2014,2015 Canonical, Ltd.
2598 *
2599 * This program is free software; you can redistribute it and/or modify
2600 * it under the terms of the GNU General Public License as published by
2601@@ -35,11 +35,11 @@
2602
2603 namespace qtmir {
2604
2605-class MirSurfaceItem;
2606+class MirSurfaceItemInterface;
2607
2608 class SessionInterface : public QObject {
2609 Q_OBJECT
2610- Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)
2611+ Q_PROPERTY(MirSurfaceItemInterface* surface READ surface NOTIFY surfaceChanged)
2612 Q_PROPERTY(unity::shell::application::ApplicationInfoInterface* application READ application NOTIFY applicationChanged DESIGNABLE false)
2613 Q_PROPERTY(SessionInterface* parentSession READ parentSession NOTIFY parentSessionChanged DESIGNABLE false)
2614 Q_PROPERTY(SessionModel* childSessions READ childSessions DESIGNABLE false CONSTANT)
2615@@ -49,58 +49,66 @@
2616 SessionInterface(QObject *parent = 0) : QObject(parent) {}
2617 virtual ~SessionInterface() {}
2618
2619- // Session State
2620- typedef unity::shell::application::ApplicationInfoInterface::State State;
2621+ enum State {
2622+ Starting,
2623+ Running,
2624+ Suspending,
2625+ Suspended,
2626+ Stopped
2627+ };
2628
2629 Q_INVOKABLE virtual void release() = 0;
2630
2631 //getters
2632 virtual QString name() const = 0;
2633 virtual unity::shell::application::ApplicationInfoInterface* application() const = 0;
2634- virtual MirSurfaceItem* surface() const = 0;
2635+ virtual MirSurfaceItemInterface* surface() const = 0;
2636 virtual SessionInterface* parentSession() const = 0;
2637+ virtual SessionModel* childSessions() const = 0;
2638 virtual State state() const = 0;
2639 virtual bool fullscreen() const = 0;
2640 virtual bool live() const = 0;
2641
2642+ virtual std::shared_ptr<mir::scene::Session> session() const = 0;
2643+
2644+ // For MirSurfaceItem and MirSurfaceManager use
2645+
2646+ virtual void setSurface(MirSurfaceItemInterface* surface) = 0;
2647+
2648+ // For Application use
2649+
2650 virtual void setApplication(unity::shell::application::ApplicationInfoInterface* item) = 0;
2651- virtual void setSurface(MirSurfaceItem* surface) = 0;
2652- virtual void setState(State state) = 0;
2653+ virtual void suspend() = 0;
2654+ virtual void resume() = 0;
2655+ virtual void stop() = 0;
2656+
2657+ // For SessionManager use
2658
2659 virtual void addChildSession(SessionInterface* session) = 0;
2660 virtual void insertChildSession(uint index, SessionInterface* session) = 0;
2661 virtual void removeChildSession(SessionInterface* session) = 0;
2662 virtual void foreachChildSession(std::function<void(SessionInterface* session)> f) const = 0;
2663
2664- virtual std::shared_ptr<mir::scene::Session> session() const = 0;
2665-
2666 virtual std::shared_ptr<mir::scene::PromptSession> activePromptSession() const = 0;
2667 virtual void foreachPromptSession(std::function<void(const std::shared_ptr<mir::scene::PromptSession>&)> f) const = 0;
2668
2669- virtual SessionModel* childSessions() const = 0;
2670+ virtual void setFullscreen(bool fullscreen) = 0;
2671+ virtual void setLive(const bool) = 0;
2672+ virtual void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
2673+ virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
2674
2675 Q_SIGNALS:
2676- void surfaceChanged(MirSurfaceItem*);
2677+ void surfaceChanged(MirSurfaceItemInterface*);
2678 void parentSessionChanged(SessionInterface*);
2679 void applicationChanged(unity::shell::application::ApplicationInfoInterface* application);
2680 void aboutToBeDestroyed();
2681 void stateChanged(State state);
2682 void fullscreenChanged(bool fullscreen);
2683 void liveChanged(bool live);
2684-
2685- void suspended();
2686- void resumed();
2687-
2688-protected:
2689- virtual void setFullscreen(bool fullscreen) = 0;
2690- virtual void setLive(const bool) = 0;
2691- virtual void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
2692- virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
2693-
2694- friend class SessionManager;
2695 };
2696
2697 } // namespace qtmir
2698
2699+Q_DECLARE_METATYPE(qtmir::SessionInterface*)
2700
2701 #endif // SESSION_INTERFACE_H
2702
2703=== modified file 'src/modules/Unity/Application/sessionmanager.cpp'
2704--- src/modules/Unity/Application/sessionmanager.cpp 2015-01-28 14:25:36 +0000
2705+++ src/modules/Unity/Application/sessionmanager.cpp 2015-07-13 21:57:32 +0000
2706@@ -63,7 +63,7 @@
2707 manager, &SessionManager::onPromptProviderRemoved);
2708 }
2709
2710-SessionManager* SessionManager::singleton()
2711+SessionManager* SessionManager::singleton(QJSEngine *jsEngine)
2712 {
2713 if (!the_session_manager) {
2714
2715@@ -78,7 +78,7 @@
2716 SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));
2717 PromptSessionListener *promptSessionListener = static_cast<PromptSessionListener*>(nativeInterface->nativeResourceForIntegration("PromptSessionListener"));
2718
2719- the_session_manager = new SessionManager(nativeInterface->m_mirServer, ApplicationManager::singleton());
2720+ the_session_manager = new SessionManager(nativeInterface->m_mirServer, ApplicationManager::singleton(jsEngine));
2721
2722 connectToSessionListener(the_session_manager, sessionListener);
2723 connectToPromptSessionListener(the_session_manager, promptSessionListener);
2724
2725=== modified file 'src/modules/Unity/Application/sessionmanager.h'
2726--- src/modules/Unity/Application/sessionmanager.h 2014-12-01 11:05:01 +0000
2727+++ src/modules/Unity/Application/sessionmanager.h 2015-07-13 21:57:32 +0000
2728@@ -38,6 +38,8 @@
2729 }
2730 }
2731
2732+class QJSEngine;
2733+
2734 class MirServer;
2735
2736 namespace qtmir {
2737@@ -57,7 +59,7 @@
2738 );
2739 ~SessionManager();
2740
2741- static SessionManager* singleton();
2742+ static SessionManager* singleton(QJSEngine *jsEngine);
2743
2744 SessionInterface *findSession(const mir::scene::Session* session) const;
2745
2746
2747=== modified file 'src/modules/Unity/Application/taskcontroller.cpp'
2748--- src/modules/Unity/Application/taskcontroller.cpp 2014-10-20 18:48:53 +0000
2749+++ src/modules/Unity/Application/taskcontroller.cpp 2015-07-13 21:57:32 +0000
2750@@ -50,14 +50,19 @@
2751 &TaskController::processStopped);
2752
2753 connect(m_appController.data(),
2754+ &ApplicationController::applicationPaused,
2755+ this,
2756+ &TaskController::processSuspended);
2757+
2758+ connect(m_appController.data(),
2759 &ApplicationController::applicationFocusRequest,
2760 this,
2761- &TaskController::onApplicationFocusRequest);
2762+ &TaskController::focusRequested);
2763
2764 connect(m_appController.data(),
2765- &ApplicationController::applicationResumeRequest,
2766+ &ApplicationController::applicationResumeRequested,
2767 this,
2768- &TaskController::onApplicationResumeRequest);
2769+ &TaskController::resumeRequested);
2770
2771 connect(m_appController.data(),
2772 &ApplicationController::applicationError,
2773@@ -108,16 +113,6 @@
2774 return m_appController->resumeApplicationWithAppId(appId);
2775 }
2776
2777-void TaskController::onApplicationFocusRequest(const QString& id)
2778-{
2779- Q_EMIT requestFocus(id);
2780-}
2781-
2782-void TaskController::onApplicationResumeRequest(const QString& id)
2783-{
2784- Q_EMIT requestResume(id);
2785-}
2786-
2787 void TaskController::onApplicationError(const QString& id, ApplicationController::Error error)
2788 {
2789 Q_EMIT processFailed(id, (error == ApplicationController::Error::APPLICATION_FAILED_TO_START) );
2790
2791=== modified file 'src/modules/Unity/Application/taskcontroller.h'
2792--- src/modules/Unity/Application/taskcontroller.h 2014-10-07 03:21:30 +0000
2793+++ src/modules/Unity/Application/taskcontroller.h 2015-07-13 21:57:32 +0000
2794@@ -1,5 +1,5 @@
2795 /*
2796- * Copyright (C) 2013-2014 Canonical, Ltd.
2797+ * Copyright (C) 2013-2015 Canonical, Ltd.
2798 *
2799 * This program is free software: you can redistribute it and/or modify it under
2800 * the terms of the GNU Lesser General Public License version 3, as published by
2801@@ -48,14 +48,12 @@
2802 Q_SIGNALS:
2803 void processStarting(const QString &appId);
2804 void processStopped(const QString &appId);
2805+ void processSuspended(const QString &appId);
2806 void processFailed(const QString &appId, const bool duringStartup);
2807- void requestFocus(const QString &appId);
2808- void requestResume(const QString &appId);
2809+ void focusRequested(const QString &appId);
2810+ void resumeRequested(const QString &appId);
2811
2812 private Q_SLOTS:
2813- void onApplicationFocusRequest(const QString &id);
2814- void onApplicationResumeRequest(const QString &id);
2815-
2816 void onApplicationError(const QString &id, ApplicationController::Error error);
2817
2818 private:
2819
2820=== modified file 'src/modules/Unity/Application/upstart/applicationcontroller.cpp'
2821--- src/modules/Unity/Application/upstart/applicationcontroller.cpp 2014-11-13 15:47:30 +0000
2822+++ src/modules/Unity/Application/upstart/applicationcontroller.cpp 2015-07-13 21:57:32 +0000
2823@@ -1,5 +1,5 @@
2824 /*
2825- * Copyright (C) 2014 Canonical, Ltd.
2826+ * Copyright (C) 2014,2015 Canonical, Ltd.
2827 *
2828 * This program is free software: you can redistribute it and/or modify it under
2829 * the terms of the GNU Lesser General Public License version 3, as published by
2830@@ -40,6 +40,7 @@
2831 UbuntuAppLaunchAppObserver stopCallback = nullptr;
2832 UbuntuAppLaunchAppObserver focusCallback = nullptr;
2833 UbuntuAppLaunchAppObserver resumeCallback = nullptr;
2834+ UbuntuAppLaunchAppPausedResumedObserver pausedCallback = nullptr;
2835 UbuntuAppLaunchAppFailedObserver failureCallback = nullptr;
2836 };
2837
2838@@ -125,7 +126,12 @@
2839
2840 impl->resumeCallback = [](const gchar * appId, gpointer userData) {
2841 auto thiz = static_cast<ApplicationController*>(userData);
2842- Q_EMIT(thiz->applicationResumeRequest(toShortAppIdIfPossible(appId)));
2843+ Q_EMIT(thiz->applicationResumeRequested(toShortAppIdIfPossible(appId)));
2844+ };
2845+
2846+ impl->pausedCallback = [](const gchar * appId, GPid *, gpointer userData) {
2847+ auto thiz = static_cast<ApplicationController*>(userData);
2848+ Q_EMIT(thiz->applicationPaused(toShortAppIdIfPossible(appId)));
2849 };
2850
2851 impl->failureCallback = [](const gchar * appId, UbuntuAppLaunchAppFailed failureType, gpointer userData) {
2852@@ -145,6 +151,7 @@
2853 ubuntu_app_launch_observer_add_app_stop(impl->stopCallback, this);
2854 ubuntu_app_launch_observer_add_app_focus(impl->focusCallback, this);
2855 ubuntu_app_launch_observer_add_app_resume(impl->resumeCallback, this);
2856+ ubuntu_app_launch_observer_add_app_paused(impl->pausedCallback, this);
2857 ubuntu_app_launch_observer_add_app_failed(impl->failureCallback, this);
2858 }
2859
2860@@ -155,6 +162,7 @@
2861 ubuntu_app_launch_observer_delete_app_stop(impl->stopCallback, this);
2862 ubuntu_app_launch_observer_delete_app_focus(impl->focusCallback, this);
2863 ubuntu_app_launch_observer_delete_app_resume(impl->resumeCallback, this);
2864+ ubuntu_app_launch_observer_delete_app_paused(impl->pausedCallback, this);
2865 ubuntu_app_launch_observer_delete_app_failed(impl->failureCallback, this);
2866 }
2867
2868
2869=== modified file 'src/platforms/mirserver/CMakeLists.txt'
2870--- src/platforms/mirserver/CMakeLists.txt 2015-05-19 15:10:48 +0000
2871+++ src/platforms/mirserver/CMakeLists.txt 2015-07-13 21:57:32 +0000
2872@@ -31,6 +31,8 @@
2873 ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS}
2874 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
2875 ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS}
2876+
2877+ ${APPLICATION_API_INCLUDE_DIRS}
2878 )
2879
2880 # We have to remove -pedantic for tracepoints.c
2881
2882=== modified file 'src/platforms/mirserver/mirserver.cpp'
2883--- src/platforms/mirserver/mirserver.cpp 2015-02-09 16:28:40 +0000
2884+++ src/platforms/mirserver/mirserver.cpp 2015-07-13 21:57:32 +0000
2885@@ -92,8 +92,7 @@
2886 the_input_targeter(),
2887 the_surface_coordinator(),
2888 the_session_coordinator(),
2889- the_prompt_session_manager(),
2890- the_shell_display_layout());
2891+ the_prompt_session_manager());
2892
2893 m_shell = shell;
2894 return shell;
2895
2896=== modified file 'src/platforms/mirserver/mirshell.cpp'
2897--- src/platforms/mirserver/mirshell.cpp 2015-05-13 09:40:03 +0000
2898+++ src/platforms/mirserver/mirshell.cpp 2015-07-13 21:57:32 +0000
2899@@ -71,37 +71,42 @@
2900 const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter,
2901 const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator,
2902 const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator,
2903- const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager,
2904- const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout) :
2905+ const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager) :
2906 AbstractShell(inputTargeter, surfaceCoordinator, sessionCoordinator, promptSessionManager,
2907- [](mir::shell::FocusController*) { return std::make_shared<NullWindowManager>(); }),
2908- m_displayLayout{displayLayout}
2909+ [](mir::shell::FocusController*) { return std::make_shared<NullWindowManager>(); })
2910 {
2911 qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::MirShell";
2912 }
2913
2914-mir::frontend::SurfaceId MirShell::create_surface(const std::shared_ptr<ms::Session> &session, const ms::SurfaceCreationParameters &requestParameters)
2915+mir::frontend::SurfaceId MirShell::create_surface(const std::shared_ptr<ms::Session> &session,
2916+ const ms::SurfaceCreationParameters &requestParameters)
2917 {
2918+ using namespace mir::geometry;
2919+ using namespace qtmir;
2920 tracepoint(qtmirserver, surfacePlacementStart);
2921
2922- // TODO: Callback unity8 so that it can make a decision on that.
2923- // unity8 must bear in mind that the called function will be on a Mir thread though.
2924- // The QPA shouldn't be deciding for itself on such things.
2925-
2926- ms::SurfaceCreationParameters placedParameters = requestParameters;
2927-
2928- // Just make it fullscreen for now
2929- mir::geometry::Rectangle rect{requestParameters.top_left, requestParameters.size};
2930- m_displayLayout->size_to_output(rect);
2931- placedParameters.size = rect.size;
2932-
2933- qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::create_surface(): size requested ("
2934- << requestParameters.size.width.as_int() << "," << requestParameters.size.height.as_int() << ") and placed ("
2935- << placedParameters.size.width.as_int() << "," << placedParameters.size.height.as_int() << ")";
2936-
2937- tracepoint(qtmirserver, surfacePlacementEnd);
2938-
2939- return AbstractShell::create_surface(session, placedParameters);
2940+ SurfaceParameters params;
2941+ params.geometry.setWidth(requestParameters.size.width.as_int());
2942+ params.geometry.setHeight(requestParameters.size.height.as_int());
2943+ if (requestParameters.state.is_set()) {
2944+ params.state = static_cast<Globals::SurfaceState>(requestParameters.state.value());
2945+ } else {
2946+ params.state = Globals::SurfaceState::Unknown;
2947+ }
2948+
2949+ Q_EMIT sessionAboutToCreateSurface(session, params); // can be connected to via Qt::BlockingQueuedConnection
2950+ // to alter surface initial geometry and state
2951+
2952+ ms::SurfaceCreationParameters placedParameters = requestParameters;
2953+ placedParameters.size = Size{ Width{params.geometry.width()}, Height{params.geometry.height()} };
2954+
2955+ qCDebug(QTMIR_MIR_MESSAGES) << "MirPlacementStrategy: requested ("
2956+ << requestParameters.size.width.as_int() << "," << requestParameters.size.height.as_int() << ") and returned ("
2957+ << placedParameters.size.width.as_int() << "," << placedParameters.size.height.as_int() << ")";
2958+
2959+ tracepoint(qtmirserver, surfacePlacementEnd);
2960+
2961+ return AbstractShell::create_surface(session, placedParameters);
2962 }
2963
2964 void NullWindowManager::add_session(std::shared_ptr<ms::Session> const& /*session*/)
2965
2966=== modified file 'src/platforms/mirserver/mirshell.h'
2967--- src/platforms/mirserver/mirshell.h 2015-03-11 10:10:49 +0000
2968+++ src/platforms/mirserver/mirshell.h 2015-07-13 21:57:32 +0000
2969@@ -17,14 +17,15 @@
2970 #ifndef QPAMIRSERVER_SHELL_H
2971 #define QPAMIRSERVER_SHELL_H
2972
2973+// Mir
2974 #include <mir/shell/abstract_shell.h>
2975+
2976+// Qt
2977 #include <QObject>
2978
2979-namespace mir {
2980- namespace shell {
2981- class DisplayLayout;
2982- }
2983-}
2984+// local
2985+#include "globals.h"
2986+
2987
2988 class MirShell : public QObject, public mir::shell::AbstractShell
2989 {
2990@@ -35,13 +36,16 @@
2991 const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter,
2992 const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator,
2993 const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator,
2994- const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager,
2995- const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout);
2996-
2997- virtual mir::frontend::SurfaceId create_surface(const std::shared_ptr<mir::scene::Session>& session, const mir::scene::SurfaceCreationParameters &params);
2998-
2999-private:
3000- std::shared_ptr<mir::shell::DisplayLayout> const m_displayLayout;
3001+ const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager);
3002+
3003+ virtual mir::frontend::SurfaceId create_surface(const std::shared_ptr<mir::scene::Session> &session,
3004+ const mir::scene::SurfaceCreationParameters &params);
3005+
3006+Q_SIGNALS:
3007+ void sessionAboutToCreateSurface(const std::shared_ptr<mir::scene::Session> &session,
3008+ qtmir::SurfaceParameters &params); // requires Qt::BlockingQueuedConnection!!
3009+
3010+ void surfaceAttributeChanged(mir::scene::Surface const*, const MirSurfaceAttrib, const int);
3011 };
3012
3013 #endif /* QPAMIRSERVER_SHELL_H */
3014
3015=== modified file 'tests/modules/Application/CMakeLists.txt'
3016--- tests/modules/Application/CMakeLists.txt 2015-05-21 18:48:59 +0000
3017+++ tests/modules/Application/CMakeLists.txt 2015-07-13 21:57:32 +0000
3018@@ -1,9 +1,12 @@
3019 set(
3020 APPLICATION_TEST_SOURCES
3021 application_test.cpp
3022+ ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp
3023 )
3024
3025 include_directories(
3026+ ${APPLICATION_API_INCLUDE_DIRS}
3027+ ${CMAKE_SOURCE_DIR}/src/common
3028 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
3029 ${CMAKE_SOURCE_DIR}/src/modules
3030 ${CMAKE_SOURCE_DIR}/tests/modules/common
3031@@ -18,6 +21,8 @@
3032 unityapplicationplugin
3033 qpa-mirserver
3034
3035+ Qt5::Test
3036+
3037 ${GTEST_BOTH_LIBRARIES}
3038 ${GMOCK_LIBRARIES}
3039 )
3040
3041=== modified file 'tests/modules/Application/application_test.cpp'
3042--- tests/modules/Application/application_test.cpp 2015-03-04 22:13:06 +0000
3043+++ tests/modules/Application/application_test.cpp 2015-07-13 21:57:32 +0000
3044@@ -20,6 +20,10 @@
3045
3046 #include "qtmir_test.h"
3047
3048+#include <mock_session.h>
3049+
3050+#include <QScopedPointer>
3051+#include <QSignalSpy>
3052
3053 using namespace qtmir;
3054
3055@@ -30,93 +34,163 @@
3056 {}
3057 };
3058
3059-TEST_F(ApplicationTests, checkFocusAcquiresWakeLock)
3060-{
3061- using namespace ::testing;
3062-
3063- EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);
3064-
3065- startApplication(123, "app");
3066- applicationManager.focusApplication("app");
3067-}
3068-
3069-TEST_F(ApplicationTests, checkSuspendReleasesWakeLock)
3070-{
3071- using namespace ::testing;
3072-
3073- auto app = startApplication(123, "app");
3074- auto session = app->session();
3075-
3076- applicationManager.focusApplication("app");
3077-
3078- Q_EMIT session->suspended();
3079+TEST_F(ApplicationTests, acquiresWakelockWhenRunningAndReleasesWhenSuspended)
3080+{
3081+ using namespace ::testing;
3082+ QString appId("foo-app");
3083+
3084+ auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
3085+ ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
3086+
3087+ QScopedPointer<Application> application(new Application(
3088+ QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
3089+ desktopFileReader, QStringList(), nullptr));
3090+
3091+ application->setProcessState(Application::ProcessRunning);
3092+
3093+ NiceMock<MockSession> *session = new NiceMock<MockSession>;
3094+
3095+ EXPECT_CALL(*session, setApplication(_));
3096+ EXPECT_CALL(*session, fullscreen()).WillRepeatedly(Return(false));
3097+
3098+ application->setSession(session);
3099+
3100+ ASSERT_EQ(Application::InternalState::Starting, application->internalState());
3101+
3102+ session->setState(SessionInterface::Running);
3103+
3104+ EXPECT_TRUE(sharedWakelock.enabled());
3105+
3106+ ASSERT_EQ(Application::InternalState::Running, application->internalState());
3107+
3108+ application->setRequestedState(Application::RequestedSuspended);
3109+
3110+ ASSERT_EQ(SessionInterface::Suspending, session->state());
3111+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
3112+
3113+ session->setState(SessionInterface::Suspended);
3114+
3115+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
3116+
3117+ application->setProcessState(Application::ProcessSuspended);
3118+
3119+ ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
3120+
3121 EXPECT_FALSE(sharedWakelock.enabled());
3122 }
3123
3124 TEST_F(ApplicationTests, checkResumeAcquiresWakeLock)
3125 {
3126 using namespace ::testing;
3127-
3128- EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);
3129-
3130- auto app = startApplication(123, "app");
3131- auto session = app->session();
3132-
3133- Q_EMIT session->resumed();
3134+ QString appId("foo-app");
3135+
3136+ auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
3137+ ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
3138+
3139+ QScopedPointer<Application> application(new Application(
3140+ QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
3141+ desktopFileReader, QStringList(), nullptr));
3142+ NiceMock<MockSession> *session = new NiceMock<MockSession>;
3143+
3144+ // Get it running and then suspend it
3145+ application->setProcessState(Application::ProcessRunning);
3146+ application->setSession(session);
3147+ session->setState(SessionInterface::Running);
3148+ application->setRequestedState(Application::RequestedSuspended);
3149+ session->setState(SessionInterface::Suspended);
3150+ application->setProcessState(Application::ProcessSuspended);
3151+ ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
3152+
3153+ EXPECT_FALSE(sharedWakelock.enabled());
3154+
3155+ application->setRequestedState(Application::RequestedRunning);
3156+
3157+ ASSERT_EQ(Application::InternalState::Running, application->internalState());
3158+
3159+ EXPECT_TRUE(sharedWakelock.enabled());
3160 }
3161
3162 TEST_F(ApplicationTests, checkRespawnAcquiresWakeLock)
3163 {
3164 using namespace ::testing;
3165-
3166- EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);
3167- const QString appId = "app";
3168-
3169- auto app = startApplication(123, "app");
3170-
3171- // as respawn fires startApplicationWithAppIdAndArgs again, keep gmock quiet about another call
3172- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
3173- .Times(1)
3174- .WillRepeatedly(Return(true));
3175-
3176- // respawn by setting app state as Stopped, delete the Session associated, then set to Running state
3177- app->setState(Session::State::Stopped);
3178- delete app->session();
3179- app->setState(Session::State::Running);
3180-}
3181-
3182-TEST_F(ApplicationTests, checkDashFocusDoesNotAcquireWakeLock)
3183-{
3184- using namespace ::testing;
3185-
3186- EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);
3187-
3188- startApplication(123, "unity8-dash");
3189- applicationManager.focusApplication("unity8-dash");
3190-}
3191-
3192-TEST_F(ApplicationTests, checkDashSuspendDoesNotImpactWakeLock)
3193-{
3194- using namespace ::testing;
3195-
3196- auto app = startApplication(123, "unity8-dash");
3197- auto session = app->session();
3198-
3199- applicationManager.focusApplication("unity8-dash");
3200-
3201- Q_EMIT session->suspended();
3202+ QString appId("foo-app");
3203+
3204+ auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
3205+ ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
3206+
3207+ QScopedPointer<Application> application(new Application(
3208+ QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
3209+ desktopFileReader, QStringList(), nullptr));
3210+ NiceMock<MockSession> *session = new NiceMock<MockSession>;
3211+
3212+ // Get it running, suspend it, and finally stop it
3213+ application->setProcessState(Application::ProcessRunning);
3214+ application->setSession(session);
3215+ session->setState(SessionInterface::Running);
3216+ application->setRequestedState(Application::RequestedSuspended);
3217+ session->setState(SessionInterface::Suspended);
3218+ application->setProcessState(Application::ProcessSuspended);
3219+ ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
3220+ session->setState(SessionInterface::Stopped);
3221+ application->setProcessState(Application::ProcessStopped);
3222+ ASSERT_EQ(Application::InternalState::StoppedUnexpectedly, application->internalState());
3223+
3224 EXPECT_FALSE(sharedWakelock.enabled());
3225+
3226+ QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested()));
3227+ application->setRequestedState(Application::RequestedRunning);
3228+ ASSERT_EQ(1, spyStartProcess.count());
3229+ application->setProcessState(Application::ProcessRunning);
3230+
3231+ ASSERT_EQ(Application::InternalState::Starting, application->internalState());
3232+
3233+ EXPECT_TRUE(sharedWakelock.enabled());
3234 }
3235
3236-TEST_F(ApplicationTests, checkDashResumeDoesNotAcquireWakeLock)
3237+TEST_F(ApplicationTests, checkDashDoesNotImpactWakeLock)
3238 {
3239 using namespace ::testing;
3240+ QString appId("unity8-dash");
3241
3242 EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);
3243-
3244- auto app = startApplication(123, "unity8-dash");
3245- auto session = app->session();
3246-
3247- Q_EMIT session->resumed();
3248+ EXPECT_CALL(sharedWakelock, release(_)).Times(0);
3249+
3250+ auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
3251+ ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
3252+
3253+ QScopedPointer<Application> application(new Application(
3254+ QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
3255+ desktopFileReader, QStringList(), nullptr));
3256+
3257+ application->setProcessState(Application::ProcessRunning);
3258+
3259+ NiceMock<MockSession> *session = new NiceMock<MockSession>;
3260+
3261+ EXPECT_CALL(*session, setApplication(_));
3262+ EXPECT_CALL(*session, fullscreen()).WillRepeatedly(Return(false));
3263+
3264+ application->setSession(session);
3265+
3266+ ASSERT_EQ(Application::InternalState::Starting, application->internalState());
3267+
3268+ session->setState(SessionInterface::Running);
3269+
3270+ ASSERT_EQ(Application::InternalState::Running, application->internalState());
3271+
3272+ application->setRequestedState(Application::RequestedSuspended);
3273+
3274+ ASSERT_EQ(SessionInterface::Suspending, session->state());
3275+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
3276+
3277+ session->setState(SessionInterface::Suspended);
3278+
3279+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
3280+
3281+ application->setProcessState(Application::ProcessSuspended);
3282+
3283+ ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
3284+
3285+ application->setRequestedState(Application::RequestedRunning);
3286+
3287+ ASSERT_EQ(Application::InternalState::Running, application->internalState());
3288 }
3289-
3290
3291=== modified file 'tests/modules/ApplicationManager/CMakeLists.txt'
3292--- tests/modules/ApplicationManager/CMakeLists.txt 2015-05-21 18:48:59 +0000
3293+++ tests/modules/ApplicationManager/CMakeLists.txt 2015-07-13 21:57:32 +0000
3294@@ -2,9 +2,13 @@
3295 APPLICATION_MANAGER_TEST_SOURCES
3296 application_manager_test.cpp
3297 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
3298+ ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp
3299+ ../common/fake_mirsurfaceitem.h
3300 )
3301
3302 include_directories(
3303+ ${APPLICATION_API_INCLUDE_DIRS}
3304+ ${CMAKE_SOURCE_DIR}/src/common
3305 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
3306 ${CMAKE_SOURCE_DIR}/src/modules
3307 ${CMAKE_SOURCE_DIR}/tests/modules/common
3308@@ -15,7 +19,7 @@
3309
3310 target_link_libraries(
3311 applicationmanager_test
3312-
3313+
3314 qpa-mirserver
3315 unityapplicationplugin
3316
3317
3318=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
3319--- tests/modules/ApplicationManager/application_manager_test.cpp 2015-05-13 17:18:45 +0000
3320+++ tests/modules/ApplicationManager/application_manager_test.cpp 2015-07-13 21:57:32 +0000
3321@@ -1,5 +1,5 @@
3322 /*
3323- * Copyright (C) 2013 Canonical, Ltd.
3324+ * Copyright (C) 2013-2015 Canonical, Ltd.
3325 *
3326 * This program is free software: you can redistribute it and/or modify it under
3327 * the terms of the GNU Lesser General Public License version 3, as published by
3328@@ -23,8 +23,9 @@
3329
3330 #include <Unity/Application/applicationscreenshotprovider.h>
3331
3332- #include "mock_surface.h"
3333- #include "qtmir_test.h"
3334+#include "fake_mirsurfaceitem.h"
3335+#include "mock_surface.h"
3336+#include "qtmir_test.h"
3337
3338 using namespace qtmir;
3339 using mir::scene::MockSession;
3340@@ -45,100 +46,41 @@
3341 applicationManager.onSessionStopping(session);
3342 sessionManager.onSessionStopping(session);
3343 }
3344+ inline void onSessionCreatedSurface(const mir::scene::Session *mirSession,
3345+ MirSurfaceItemInterface *qmlSurface) {
3346+
3347+ SessionInterface* qmlSession = sessionManager.findSession(mirSession);
3348+ if (qmlSession) {
3349+ qmlSession->setSurface(qmlSurface);
3350+ }
3351+
3352+ // I assume that applicationManager ignores the mirSurface parameter, so sending
3353+ // a null shared pointer must suffice
3354+ std::shared_ptr<mir::scene::Surface> mirSurface(nullptr);
3355+ applicationManager.onSessionCreatedSurface(mirSession, mirSurface);
3356+ }
3357+
3358+ inline void suspend(Application *application) {
3359+ application->setRequestedState(Application::RequestedSuspended);
3360+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
3361+ static_cast<qtmir::Session*>(application->session())->doSuspend();
3362+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
3363+ applicationManager.onProcessSuspended(application->appId());
3364+ ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
3365+ }
3366 };
3367
3368-TEST_F(ApplicationManagerTests, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment)
3369-{
3370- using namespace ::testing;
3371-
3372- const QString appId("com.canonical.does.not.exist");
3373-
3374- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
3375- EXPECT_CALL(appController, findDesktopFileForAppId(appId)).Times(1);
3376-
3377- EXPECT_CALL(desktopFileReaderFactory, createInstance(_, _)).Times(1);
3378-
3379- auto application = applicationManager.startApplication(
3380- appId,
3381- ApplicationManager::NoFlag,
3382- QStringList());
3383-
3384- // FIXME - this is doesn't really excerise the actualt behaviour since suspend/resume should be
3385- // controlled by state changes. Requires using suspend timer.
3386- QMetaObject::invokeMethod(application, "onSessionSuspended");
3387- QMetaObject::invokeMethod(application, "onSessionResumed");
3388-}
3389-
3390-TEST_F(ApplicationManagerTests, SuspendingAndResumingDashResultsInOomScoreAdjustment)
3391-{
3392- using namespace ::testing;
3393-
3394- quint64 procId = 5921;
3395- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3396- QByteArray cmdLine( "/usr/bin/app1 --desktop_file_hint=unity8-dash");
3397-
3398- EXPECT_CALL(procInfo,command_line(procId))
3399- .Times(1)
3400- .WillOnce(Return(cmdLine));
3401-
3402- ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
3403-
3404- bool authed = true;
3405-
3406- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("Oo", procId);
3407- applicationManager.authorizeSession(procId, authed);
3408- onSessionStarting(session);
3409-
3410- auto application = applicationManager.findApplication("unity8-dash");
3411- applicationManager.onSessionCreatedSurface(session.get(), aSurface);
3412-
3413- // FIXME - this is doesn't really excerise the actualt behaviour since suspend/resume should be
3414- // controlled by state changes. Requires using suspend timer.
3415- QMetaObject::invokeMethod(application, "onSessionSuspended");
3416- QMetaObject::invokeMethod(application, "onSessionResumed");
3417-}
3418-
3419-// Currently disabled as we need to make sure that we have a corresponding mir session, too.
3420-TEST_F(ApplicationManagerTests, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment)
3421-{
3422- using namespace ::testing;
3423-
3424- const QString appId("com.canonical.does.not.exist");
3425-
3426- QSet<QString> appIds;
3427-
3428- for (unsigned int i = 0; i < 50; i++)
3429- {
3430- QString appIdFormat("%1.does.not.exist");
3431- auto appId = appIdFormat.arg(i);
3432-
3433- auto application = applicationManager.startApplication(
3434- appId,
3435- ApplicationManager::NoFlag,
3436- QStringList());
3437-
3438- std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(appIdFormat.toStdString(), i);
3439- onSessionStarting( mirSession );
3440-
3441- EXPECT_NE(nullptr, application);
3442- }
3443-
3444- for (auto appId : appIds)
3445- {
3446- applicationManager.focusApplication(appId);
3447- }
3448-}
3449-
3450 TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_authorize_and_gets_mixed_up_with_first_one)
3451 {
3452 using namespace ::testing;
3453- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3454 quint64 firstProcId = 5921;
3455 quint64 secondProcId = 5922;
3456 const char dialer_app_id[] = "dialer-app";
3457 QByteArray cmdLine( "/usr/bin/dialer-app --desktop_file_hint=dialer-app");
3458 QByteArray secondcmdLine( "/usr/bin/dialer-app");
3459
3460+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
3461+
3462 EXPECT_CALL(procInfo,command_line(firstProcId))
3463 .Times(1)
3464 .WillOnce(Return(cmdLine));
3465@@ -150,18 +92,20 @@
3466
3467 std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(dialer_app_id, firstProcId);
3468 applicationManager.authorizeSession(firstProcId, authed);
3469- EXPECT_EQ(true, authed);
3470+ ASSERT_EQ(true, authed);
3471 onSessionStarting(mirSession);
3472- applicationManager.onSessionCreatedSurface(mirSession.get(),aSurface);
3473- Application * app = applicationManager.findApplication(dialer_app_id);
3474- EXPECT_NE(nullptr,app);
3475+ onSessionCreatedSurface(mirSession.get(), surface);
3476+ surface->drawFirstFrame();
3477+ Application * application = applicationManager.findApplication(dialer_app_id);
3478+ ASSERT_NE(nullptr,application);
3479+ ASSERT_EQ(Application::InternalState::Running, application->internalState());
3480
3481 // now a second session without desktop file is launched:
3482 applicationManager.authorizeSession(secondProcId, authed);
3483 applicationManager.onProcessStarting(dialer_app_id);
3484
3485 EXPECT_FALSE(authed);
3486- EXPECT_EQ(app,applicationManager.findApplication(dialer_app_id));
3487+ EXPECT_EQ(application, applicationManager.findApplication(dialer_app_id));
3488 }
3489
3490 TEST_F(ApplicationManagerTests,application_dies_while_starting)
3491@@ -182,6 +126,7 @@
3492 onSessionStarting(mirSession);
3493 Application * beforeFailure = applicationManager.findApplication(app_id);
3494 applicationManager.onProcessStarting(app_id);
3495+ onSessionStopping(mirSession);
3496 applicationManager.onProcessFailed(app_id, true);
3497 Application * afterFailure = applicationManager.findApplication(app_id);
3498
3499@@ -190,34 +135,6 @@
3500 EXPECT_EQ(nullptr, afterFailure);
3501 }
3502
3503-TEST_F(ApplicationManagerTests,application_start_failure_after_starting)
3504-{
3505- using namespace ::testing;
3506- quint64 procId = 5921;
3507- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3508- const char app_id[] = "my-app";
3509- QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app");
3510-
3511- EXPECT_CALL(procInfo,command_line(procId))
3512- .Times(1)
3513- .WillOnce(Return(cmdLine));
3514-
3515- bool authed = true;
3516-
3517- std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(app_id, procId);
3518- applicationManager.authorizeSession(procId, authed);
3519- onSessionStarting(mirSession);
3520- Application * beforeFailure = applicationManager.findApplication(app_id);
3521- applicationManager.onSessionCreatedSurface(mirSession.get(), aSurface);
3522- applicationManager.onProcessStarting(app_id);
3523- applicationManager.onProcessFailed(app_id, false);
3524- Application * afterFailure = applicationManager.findApplication(app_id);
3525-
3526- EXPECT_EQ(true, authed);
3527- EXPECT_NE(nullptr, beforeFailure);
3528- EXPECT_EQ(beforeFailure, afterFailure);
3529-}
3530-
3531 TEST_F(ApplicationManagerTests,startApplicationSupportsShortAppId)
3532 {
3533 using namespace ::testing;
3534@@ -400,7 +317,7 @@
3535 quint64 a_procId = 5921;
3536 const char an_app_id[] = "some_app";
3537 QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");
3538- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3539+ FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
3540
3541 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
3542
3543@@ -413,8 +330,8 @@
3544 applicationManager.authorizeSession(a_procId, authed);
3545
3546 onSessionStarting(first_session);
3547- applicationManager.focusApplication(an_app_id);
3548- applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
3549+ onSessionCreatedSurface(first_session.get(), aSurface);
3550+ aSurface->drawFirstFrame();
3551 onSessionStarting(second_session);
3552
3553 Application * the_app = applicationManager.findApplication(an_app_id);
3554@@ -430,211 +347,62 @@
3555 quint64 a_procId = 5921;
3556 const char an_app_id[] = "some_app";
3557 QByteArray a_cmd("/usr/bin/app1 --desktop_file_hint=some_app");
3558- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3559+ FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
3560
3561 ON_CALL(procInfo, command_line(_)).WillByDefault(Return(a_cmd));
3562 ON_CALL(appController, appIdHasProcessId(_,_)).WillByDefault(Return(false));
3563-
3564+
3565 bool authed = true;
3566
3567 std::shared_ptr<mir::scene::Session> a_session = std::make_shared<MockSession>("Oo", a_procId);
3568-
3569+
3570 applicationManager.authorizeSession(a_procId, authed);
3571 onSessionStarting(a_session);
3572- applicationManager.onSessionCreatedSurface(a_session.get(), aSurface);
3573-
3574- Application * the_app = applicationManager.findApplication(an_app_id);
3575- applicationManager.focusApplication(an_app_id);
3576-
3577- EXPECT_EQ(Application::Running, the_app->state());
3578- EXPECT_EQ(true, the_app->focused());
3579-
3580- applicationManager.focusApplication(an_app_id);
3581- EXPECT_EQ(true, the_app->focused());
3582-}
3583-
3584-TEST_F(ApplicationManagerTests,suspended_suspends_focused_app_and_marks_it_unfocused_in_the_model)
3585-{
3586- using namespace ::testing;
3587- quint64 a_procId = 5921;
3588- const char an_app_id[] = "some_app";
3589- QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");
3590- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3591-
3592- ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
3593-
3594- ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
3595-
3596- bool authed = true;
3597-
3598- std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);
3599- std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId);
3600- applicationManager.authorizeSession(a_procId, authed);
3601-
3602- onSessionStarting(first_session);
3603- applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
3604- onSessionStarting(second_session);
3605-
3606- Application * the_app = applicationManager.findApplication(an_app_id);
3607- applicationManager.focusApplication(an_app_id);
3608-
3609- EXPECT_EQ(Application::Running, the_app->state());
3610-
3611- applicationManager.setSuspended(true);
3612-
3613- EXPECT_EQ(Application::Suspended, the_app->state());
3614- EXPECT_FALSE(the_app->focused());
3615-
3616- applicationManager.setSuspended(false);
3617-
3618- EXPECT_EQ(Application::Running, the_app->state());
3619- EXPECT_EQ(true, the_app->focused());
3620-}
3621-
3622-TEST_F(ApplicationManagerTests,suspended_suspends_starting_app_when_it_gets_ready)
3623-{
3624- using namespace ::testing;
3625- quint64 a_procId = 5921;
3626- const char an_app_id[] = "some_app";
3627- QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");
3628- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3629-
3630- ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
3631-
3632- ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
3633-
3634- bool authed = true;
3635-
3636- std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);
3637- applicationManager.authorizeSession(a_procId, authed);
3638-
3639- onSessionStarting(first_session);
3640-
3641- Application * the_app = applicationManager.findApplication(an_app_id);
3642- applicationManager.focusApplication(an_app_id);
3643- EXPECT_EQ(Application::Starting, the_app->state());
3644-
3645- applicationManager.setSuspended(true);
3646-
3647- // Not suspending yet, as it's still starting
3648- EXPECT_EQ(Application::Starting, the_app->state());
3649-
3650- // This signals the app is ready now
3651- applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
3652-
3653- // And given that the AppManager is suspended now, this should go to suspended too
3654- EXPECT_EQ(Application::Suspended, the_app->state());
3655- EXPECT_FALSE(the_app->focused());
3656-
3657- applicationManager.setSuspended(false);
3658-
3659- EXPECT_EQ(Application::Running, the_app->state());
3660- EXPECT_EQ(true, the_app->focused());
3661-}
3662-
3663-TEST_F(ApplicationManagerTests,focus_change_suspends_starting_app_when_it_gets_ready)
3664-{
3665- using namespace ::testing;
3666- quint64 first_procId = 5921;
3667- quint64 second_procId = 5922;
3668- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3669- QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=app1");
3670- QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2");
3671-
3672- EXPECT_CALL(procInfo,command_line(first_procId))
3673- .Times(1)
3674- .WillOnce(Return(first_cmdLine));
3675-
3676- ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
3677-
3678- EXPECT_CALL(procInfo,command_line(second_procId))
3679- .Times(1)
3680- .WillOnce(Return(second_cmdLine));
3681-
3682- bool authed = true;
3683-
3684- std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", first_procId);
3685- std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", second_procId);
3686- applicationManager.authorizeSession(first_procId, authed);
3687- applicationManager.authorizeSession(second_procId, authed);
3688- onSessionStarting(first_session);
3689-
3690- Application * app1 = applicationManager.findApplication("app1");
3691- applicationManager.focusApplication("app1");
3692+ onSessionCreatedSurface(a_session.get(), aSurface);
3693+ aSurface->drawFirstFrame();
3694+
3695+ Application * the_app = applicationManager.findApplication(an_app_id);
3696+ applicationManager.focusApplication(an_app_id);
3697+
3698+ EXPECT_EQ(Application::Running, the_app->state());
3699+ EXPECT_EQ(true, the_app->focused());
3700+
3701+ applicationManager.focusApplication(an_app_id);
3702+ EXPECT_EQ(true, the_app->focused());
3703+}
3704+
3705+TEST_F(ApplicationManagerTests,starting_app_is_suspended_when_it_gets_ready_if_requested)
3706+{
3707+ using namespace ::testing;
3708+ quint64 procId = 5921;
3709+ FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
3710+ QByteArray cmdLine( "/usr/bin/app --desktop_file_hint=app");
3711+
3712+ EXPECT_CALL(procInfo,command_line(procId))
3713+ .Times(1)
3714+ .WillOnce(Return(cmdLine));
3715+
3716+ ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
3717+
3718+ bool authed = true;
3719+
3720+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("Oo", procId);
3721+ applicationManager.authorizeSession(procId, authed);
3722+ onSessionStarting(session);
3723+
3724+ Application * app = applicationManager.findApplication("app");
3725+ app->setRequestedState(Application::RequestedSuspended);
3726
3727 // First app starting...
3728- EXPECT_EQ(Application::Starting, app1->state());
3729-
3730- onSessionStarting(second_session);
3731- Application * app2 = applicationManager.findApplication("app2");
3732- applicationManager.focusApplication("app2");
3733-
3734- // Second app starting...
3735- EXPECT_EQ(Application::Starting, app2->state());
3736-
3737- // Make sure first one is still in starting state
3738- EXPECT_EQ(Application::Starting, app1->state());
3739-
3740- // Signal app1 is ready now
3741- applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
3742-
3743- // Make sure AppMan suspended it now that its ready
3744- EXPECT_EQ(Application::Suspended, app1->state());
3745-}
3746-
3747-TEST_F(ApplicationManagerTests,forceDashActive_activates_dash_while_not_focused)
3748-{
3749- using namespace ::testing;
3750- quint64 first_procId = 5921;
3751- quint64 second_procId = 5922;
3752- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
3753- QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=unity8-dash");
3754- QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2");
3755-
3756- EXPECT_CALL(procInfo,command_line(first_procId))
3757- .Times(1)
3758- .WillOnce(Return(first_cmdLine));
3759-
3760- ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
3761-
3762- EXPECT_CALL(procInfo,command_line(second_procId))
3763- .Times(1)
3764- .WillOnce(Return(second_cmdLine));
3765-
3766- bool authed = true;
3767-
3768- std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", first_procId);
3769- std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", second_procId);
3770- applicationManager.authorizeSession(first_procId, authed);
3771- applicationManager.authorizeSession(second_procId, authed);
3772- onSessionStarting(first_session);
3773-
3774- Application * dashApp = applicationManager.findApplication("unity8-dash");
3775- applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
3776- applicationManager.focusApplication("unity8-dash");
3777-
3778- // Dash app should be ready now...
3779- EXPECT_EQ(Application::Running, dashApp->state());
3780-
3781- // Launch second app
3782- onSessionStarting(second_session);
3783- applicationManager.onSessionCreatedSurface(second_session.get(), aSurface);
3784- applicationManager.focusApplication("app2");
3785- EXPECT_EQ(applicationManager.focusedApplicationId(), "app2");
3786-
3787- // Make sure the dash is suspended
3788- EXPECT_EQ(dashApp->state(), Application::Suspended);
3789-
3790- // Now set the dashactive flag
3791- applicationManager.setForceDashActive(true);
3792-
3793- // And make sure the dash is woken up but not focused
3794- EXPECT_EQ(applicationManager.focusedApplicationId(), "app2");
3795- EXPECT_EQ(dashApp->state(), Application::Running);
3796-
3797- // Unset the dashactive flag
3798- applicationManager.setForceDashActive(false);
3799- EXPECT_EQ(dashApp->state(), Application::Suspended);
3800+ EXPECT_EQ(Application::Starting, app->state());
3801+
3802+ // Signal app is ready now
3803+ applicationManager.onProcessStarting("app");
3804+ onSessionCreatedSurface(session.get(), aSurface);
3805+ aSurface->drawFirstFrame();
3806+
3807+ // now that its ready, suspend process should have begun
3808+ EXPECT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());
3809 }
3810
3811 TEST_F(ApplicationManagerTests,requestFocusApplication)
3812@@ -713,20 +481,20 @@
3813 Application *theApp = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
3814
3815 // check application data
3816- EXPECT_EQ(theApp->state(), Application::Starting);
3817- EXPECT_EQ(theApp->appId(), appId);
3818- EXPECT_EQ(theApp->name(), name);
3819- EXPECT_EQ(theApp->canBeResumed(), true);
3820+ EXPECT_EQ(Application::Starting, theApp->state());
3821+ EXPECT_EQ(appId, theApp->appId());
3822+ EXPECT_EQ(name, theApp->name());
3823+ EXPECT_FALSE(theApp->canBeResumed());
3824
3825 // check signals were emitted
3826- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
3827- EXPECT_EQ(applicationManager.count(), 1);
3828- EXPECT_EQ(addedSpy.count(), 1);
3829- EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId);
3830+ EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
3831+ EXPECT_EQ(1, applicationManager.count());
3832+ EXPECT_EQ(1, addedSpy.count());
3833+ EXPECT_EQ(appId, addedSpy.takeFirst().at(0).toString());
3834
3835 // check application in list of apps
3836 Application *theAppAgain = applicationManager.findApplication(appId);
3837- EXPECT_EQ(theAppAgain, theApp);
3838+ EXPECT_EQ(theApp, theAppAgain);
3839 }
3840
3841 /*
3842@@ -757,18 +525,18 @@
3843 Application *theApp = applicationManager.findApplication(appId);
3844
3845 // check application data
3846- EXPECT_EQ(theApp->state(), Application::Starting);
3847- EXPECT_EQ(theApp->appId(), appId);
3848- EXPECT_EQ(theApp->name(), name);
3849- EXPECT_EQ(theApp->canBeResumed(), true);
3850+ EXPECT_EQ(Application::Starting, theApp->state());
3851+ EXPECT_EQ(appId, theApp->appId());
3852+ EXPECT_EQ(name, theApp->name());
3853+ EXPECT_EQ(true, theApp->canBeResumed());
3854
3855 // check signals were emitted
3856- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
3857- EXPECT_EQ(applicationManager.count(), 1);
3858- EXPECT_EQ(addedSpy.count(), 1);
3859- EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId);
3860- EXPECT_EQ(focusSpy.count(), 1);
3861- EXPECT_EQ(focusSpy.takeFirst().at(0).toString(), appId);
3862+ EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
3863+ EXPECT_EQ(1, applicationManager.count());
3864+ EXPECT_EQ(1, addedSpy.count());
3865+ EXPECT_EQ(appId, addedSpy.takeFirst().at(0).toString());
3866+ EXPECT_EQ(1, focusSpy.count());
3867+ EXPECT_EQ(appId, focusSpy.takeFirst().at(0).toString());
3868 }
3869
3870 /*
3871@@ -1067,7 +835,6 @@
3872
3873 applicationManager.startApplication(appId, ApplicationManager::NoFlag);
3874 applicationManager.onProcessStarting(appId);
3875- applicationManager.focusApplication(appId);
3876
3877 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
3878
3879@@ -1075,9 +842,10 @@
3880 applicationManager.authorizeSession(procId, authed);
3881 onSessionStarting(session);
3882
3883- std::shared_ptr<mir::scene::Surface> surface(nullptr);
3884+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
3885
3886- applicationManager.onSessionCreatedSurface(session.get(), surface);
3887+ onSessionCreatedSurface(session.get(), surface);
3888+ surface->drawFirstFrame();
3889
3890 // Check application state is correctly set
3891 Application *theApp = applicationManager.findApplication(appId);
3892@@ -1124,7 +892,7 @@
3893 }
3894
3895 /*
3896- * Test that the foreground application is stopped correctly (is in Running state, has surface)
3897+ * Test that a running application is stopped correctly (is in Running state, has surface)
3898 */
3899 TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly)
3900 {
3901@@ -1150,100 +918,61 @@
3902 applicationManager.authorizeSession(procId, authed);
3903 onSessionStarting(session);
3904
3905- std::shared_ptr<mir::scene::Surface> surface(nullptr);
3906- applicationManager.onSessionCreatedSurface(session.get(), surface);
3907- applicationManager.focusApplication(appId);
3908- EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
3909-
3910- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
3911- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
3912-
3913- // Stop app
3914- applicationManager.stopApplication(appId);
3915-
3916- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
3917- EXPECT_EQ(applicationManager.count(), 0);
3918- EXPECT_EQ(removedSpy.count(), 1);
3919- EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
3920-}
3921-
3922-/*
3923- * Test that the background application is stopped correctly
3924- */
3925-TEST_F(ApplicationManagerTests,shellStopsBackgroundAppCorrectly)
3926-{
3927- using namespace ::testing;
3928- const QString appId("testAppId");
3929- quint64 procId = 5551;
3930-
3931- // Set up Mocks & signal watcher
3932- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
3933- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
3934- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
3935-
3936- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
3937-
3938- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
3939- .Times(1)
3940- .WillOnce(Return(true));
3941-
3942- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
3943- applicationManager.onProcessStarting(appId);
3944- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
3945- bool authed = true;
3946- applicationManager.authorizeSession(procId, authed);
3947- onSessionStarting(session);
3948-
3949- std::shared_ptr<mir::scene::Surface> surface(nullptr);
3950- applicationManager.onSessionCreatedSurface(session.get(), surface);
3951- applicationManager.unfocusCurrentApplication();
3952-
3953- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
3954-
3955- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
3956- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
3957-
3958- // Stop app
3959- applicationManager.stopApplication(appId);
3960-
3961- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
3962- EXPECT_EQ(applicationManager.count(), 0);
3963- EXPECT_EQ(removedSpy.count(), 1);
3964- EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
3965-}
3966-
3967-/*
3968- * Test that if an application is stopped by upstart, before it has created a surface, AppMan cleans up after it ok
3969- */
3970-TEST_F(ApplicationManagerTests,upstartNotificationOfStartingAppBeingStopped)
3971-{
3972- using namespace ::testing;
3973- const QString appId("testAppId");
3974- quint64 procId = 5551;
3975-
3976- // Set up Mocks & signal watcher
3977- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
3978- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
3979- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
3980-
3981- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
3982-
3983- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
3984- .Times(1)
3985- .WillOnce(Return(true));
3986-
3987- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
3988- applicationManager.onProcessStarting(appId);
3989- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
3990- bool authed = true;
3991- applicationManager.authorizeSession(procId, authed);
3992- onSessionStarting(session);
3993-
3994- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
3995- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
3996-
3997- // Upstart notifies of stopping app
3998- applicationManager.onProcessStopped(appId);
3999+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4000+ onSessionCreatedSurface(session.get(), surface);
4001+ surface->drawFirstFrame();
4002+
4003+ QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4004+ QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4005+
4006+ // Stop app
4007+ applicationManager.stopApplication(appId);
4008+
4009+ EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
4010+ EXPECT_EQ(applicationManager.count(), 0);
4011+ EXPECT_EQ(removedSpy.count(), 1);
4012+ EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
4013+}
4014+
4015+/*
4016+ * Test that a suspended application is stopped correctly
4017+ */
4018+TEST_F(ApplicationManagerTests,shellStopsSuspendedAppCorrectly)
4019+{
4020+ using namespace ::testing;
4021+ const QString appId("testAppId");
4022+ quint64 procId = 5551;
4023+
4024+ // Set up Mocks & signal watcher
4025+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
4026+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4027+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
4028+
4029+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
4030+
4031+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
4032+ .Times(1)
4033+ .WillOnce(Return(true));
4034+
4035+ Application *application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4036+ applicationManager.onProcessStarting(appId);
4037+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4038+ bool authed = true;
4039+ applicationManager.authorizeSession(procId, authed);
4040+ onSessionStarting(session);
4041+ applicationManager.onProcessStarting(appId);
4042+
4043+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4044+ onSessionCreatedSurface(session.get(), surface);
4045+ surface->drawFirstFrame();
4046+
4047+ suspend(application);
4048+
4049+ QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4050+ QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4051+
4052+ // Stop app
4053+ applicationManager.stopApplication(appId);
4054
4055 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
4056 EXPECT_EQ(applicationManager.count(), 0);
4057@@ -1278,28 +1007,28 @@
4058 applicationManager.authorizeSession(procId, authed);
4059 onSessionStarting(session);
4060
4061- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4062- applicationManager.onSessionCreatedSurface(session.get(), surface);
4063- applicationManager.focusApplication(appId);
4064- EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
4065+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4066+ onSessionCreatedSurface(session.get(), surface);
4067+ surface->drawFirstFrame();
4068
4069 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4070 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4071
4072+ onSessionStopping(session);
4073 // Upstart notifies of stopping app
4074 applicationManager.onProcessStopped(appId);
4075
4076- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
4077- EXPECT_EQ(applicationManager.count(), 0);
4078- EXPECT_EQ(removedSpy.count(), 1);
4079- EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
4080+ EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
4081+ EXPECT_EQ(0, applicationManager.count());
4082+ EXPECT_EQ(1, removedSpy.count());
4083+ EXPECT_EQ(appId, removedSpy.takeFirst().at(0).toString());
4084 }
4085
4086 /*
4087- * Test that if the foreground Running application is reported to unexpectedly stop by upstart, AppMan
4088- * cleans up after it ok (as was not in background, had not lifecycle saved its state, so cannot be resumed)
4089+ * Test that if a running application is reported to have stopped unexpectedly by upstart, AppMan
4090+ * cleans up after it ok (as was not suspended, had not lifecycle saved its state, so cannot be resumed)
4091 */
4092-TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfForegroundApp)
4093+TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfRunningApp)
4094 {
4095 using namespace ::testing;
4096 const QString appId("testAppId");
4097@@ -1323,14 +1052,15 @@
4098 applicationManager.authorizeSession(procId, authed);
4099 onSessionStarting(session);
4100
4101- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4102- applicationManager.onSessionCreatedSurface(session.get(), surface);
4103- applicationManager.focusApplication(appId);
4104- EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
4105+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4106+ onSessionCreatedSurface(session.get(), surface);
4107+ surface->drawFirstFrame();
4108
4109 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4110 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4111
4112+ onSessionStopping(session);
4113+
4114 // Upstart notifies of crashing / OOM killed app
4115 applicationManager.onProcessFailed(appId, false);
4116
4117@@ -1344,53 +1074,6 @@
4118 }
4119
4120 /*
4121- * Test that if a background application is stopped by upstart, AppMan removes it from the app list
4122- * as the event is a result of direct user interaction
4123- */
4124-TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingBackgroundApp)
4125-{
4126- using namespace ::testing;
4127- const QString appId("testAppId");
4128- quint64 procId = 5551;
4129-
4130- // Set up Mocks & signal watcher
4131- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
4132- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4133- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
4134-
4135- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
4136-
4137- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
4138- .Times(1)
4139- .WillOnce(Return(true));
4140-
4141- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4142- applicationManager.onProcessStarting(appId);
4143- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4144- bool authed = true;
4145- applicationManager.authorizeSession(procId, authed);
4146- onSessionStarting(session);
4147-
4148- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4149- applicationManager.onSessionCreatedSurface(session.get(), surface);
4150- applicationManager.unfocusCurrentApplication();
4151- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
4152-
4153- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4154- QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));
4155- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4156-
4157- // Upstart notifies of stopping app
4158- applicationManager.onProcessStopped(appId);
4159-
4160- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
4161- EXPECT_EQ(applicationManager.count(), 0);
4162- EXPECT_EQ(focusSpy.count(), 0);
4163- EXPECT_EQ(removedSpy.count(), 1);
4164- EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
4165-}
4166-
4167-/*
4168 * Test that if a background application is reported to unexpectedly stop by upstart, AppMan does not remove
4169 * it from the app lists but instead considers it Stopped, ready to be resumed. This is due to the fact the
4170 * app should have saved its state, so can be resumed. This situation can occur due to the OOM killer, or
4171@@ -1413,18 +1096,18 @@
4172 .Times(1)
4173 .WillOnce(Return(true));
4174
4175- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4176+ Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4177 applicationManager.onProcessStarting(appId);
4178 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4179 bool authed = true;
4180 applicationManager.authorizeSession(procId, authed);
4181 onSessionStarting(session);
4182
4183- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4184- applicationManager.onSessionCreatedSurface(session.get(), surface);
4185- applicationManager.focusApplication(appId);
4186- applicationManager.unfocusCurrentApplication();
4187- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
4188+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4189+ onSessionCreatedSurface(session.get(), surface);
4190+ surface->drawFirstFrame();
4191+
4192+ suspend(app);
4193
4194 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4195 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));
4196@@ -1436,14 +1119,14 @@
4197 // Upstart notifies of crashing / OOM-killed app
4198 applicationManager.onProcessFailed(appId, false);
4199
4200- EXPECT_EQ(focusSpy.count(), 0);
4201+ EXPECT_EQ(0, focusSpy.count());
4202
4203 // Upstart finally notifies the app stopped
4204 applicationManager.onProcessStopped(appId);
4205
4206- EXPECT_EQ(countSpy.count(), 0);
4207- EXPECT_EQ(applicationManager.count(), 1);
4208- EXPECT_EQ(removedSpy.count(), 0);
4209+ EXPECT_EQ(0, countSpy.count());
4210+ EXPECT_EQ(1, applicationManager.count());
4211+ EXPECT_EQ(0, removedSpy.count());
4212 }
4213
4214 /*
4215@@ -1471,18 +1154,18 @@
4216 .Times(1)
4217 .WillOnce(Return(true));
4218
4219- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4220+ Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4221 applicationManager.onProcessStarting(appId);
4222 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4223 bool authed = true;
4224 applicationManager.authorizeSession(procId, authed);
4225 onSessionStarting(session);
4226
4227- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4228- applicationManager.onSessionCreatedSurface(session.get(), surface);
4229- applicationManager.focusApplication(appId);
4230- applicationManager.unfocusCurrentApplication();
4231- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
4232+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4233+ onSessionCreatedSurface(session.get(), surface);
4234+ surface->drawFirstFrame();
4235+
4236+ suspend(app);
4237
4238 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4239 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));
4240@@ -1571,10 +1254,9 @@
4241 onSessionStarting(session);
4242
4243 // Associate a surface so AppMan considers app Running, check focused
4244- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4245- applicationManager.onSessionCreatedSurface(session.get(), surface);
4246- applicationManager.focusApplication(appId);
4247- EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
4248+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4249+ onSessionCreatedSurface(session.get(), surface);
4250+ surface->drawFirstFrame();
4251
4252 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4253 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4254@@ -1589,10 +1271,10 @@
4255 }
4256
4257 /*
4258- * Test that if a foreground application (one launched via desktop_file_hint) is reported to be stopping by
4259+ * Test that if an application (one launched via desktop_file_hint) is reported to be stopping by
4260 * Mir, AppMan removes it from the model immediately
4261 */
4262-TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundAppLaunchedWithDesktopFileHint)
4263+TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingAppLaunchedWithDesktopFileHint)
4264 {
4265 using namespace ::testing;
4266 const QString appId("testAppId");
4267@@ -1621,10 +1303,9 @@
4268 onSessionStarting(session);
4269
4270 // Associate a surface so AppMan considers app Running, check focused
4271- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4272- applicationManager.onSessionCreatedSurface(session.get(), surface);
4273- applicationManager.focusApplication(appId);
4274- EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
4275+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4276+ onSessionCreatedSurface(session.get(), surface);
4277+ surface->drawFirstFrame();
4278
4279 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4280 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4281@@ -1661,86 +1342,43 @@
4282 .Times(1)
4283 .WillOnce(Return(true));
4284
4285- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4286+ Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4287 applicationManager.onProcessStarting(appId);
4288 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4289 bool authed = true;
4290 applicationManager.authorizeSession(procId, authed);
4291 onSessionStarting(session);
4292-
4293- // Associate a surface so AppMan considers app Running, check in background
4294- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4295- applicationManager.onSessionCreatedSurface(session.get(), surface);
4296- applicationManager.focusApplication(appId);
4297- applicationManager.unfocusCurrentApplication();
4298- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
4299-
4300- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4301- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4302-
4303- // Mir notifies of stopping app
4304- onSessionStopping(session);
4305-
4306- EXPECT_EQ(countSpy.count(), 0);
4307- EXPECT_EQ(applicationManager.count(), 1);
4308- EXPECT_EQ(removedSpy.count(), 0);
4309-
4310- Application * app = applicationManager.findApplication(appId);
4311- EXPECT_NE(nullptr,app);
4312- EXPECT_EQ(app->state(), Application::Stopped);
4313-}
4314-
4315-/*
4316- * Test that if a background application (one launched via desktop_file_hint) is reported to be stopping by
4317- * Mir, AppMan removes it from the model immediately
4318- */
4319-TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundAppLaunchedWithDesktopFileHint)
4320-{
4321- using namespace ::testing;
4322- const QString appId("testAppId");
4323- const QString name("Test App");
4324- quint64 procId = 5551;
4325- QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint=");
4326- cmdLine = cmdLine.append(appId);
4327-
4328- // Set up Mocks & signal watcher
4329- EXPECT_CALL(procInfo,command_line(procId))
4330- .Times(1)
4331- .WillOnce(Return(cmdLine));
4332-
4333- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
4334- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4335- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
4336- ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name));
4337-
4338- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
4339-
4340- // Mir requests authentication for an application that was started
4341- bool authed = true;
4342- applicationManager.authorizeSession(procId, authed);
4343- EXPECT_EQ(authed, true);
4344- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4345- onSessionStarting(session);
4346-
4347- // Associate a surface so AppMan considers app Running, check in background
4348- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4349- applicationManager.onSessionCreatedSurface(session.get(), surface);
4350- applicationManager.focusApplication(appId);
4351- applicationManager.unfocusCurrentApplication();
4352- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
4353-
4354- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4355- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4356-
4357- // Mir notifies of stopping app
4358- onSessionStopping(session);
4359-
4360- EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
4361- EXPECT_EQ(applicationManager.count(), 0);
4362- EXPECT_EQ(removedSpy.count(), 1);
4363-
4364- Application * app = applicationManager.findApplication(appId);
4365- EXPECT_EQ(nullptr,app);
4366+ EXPECT_EQ(Application::Starting, app->state());
4367+
4368+ app->setRequestedState(Application::RequestedSuspended);
4369+
4370+ // should not suspend an app that`s still starting up
4371+ ASSERT_EQ(Application::InternalState::Starting, app->internalState());
4372+
4373+ // Associate a surface so AppMan considers app Running
4374+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4375+ onSessionCreatedSurface(session.get(), surface);
4376+ surface->drawFirstFrame();
4377+
4378+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());
4379+
4380+ static_cast<qtmir::Session*>(app->session())->doSuspend();
4381+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, app->internalState());
4382+
4383+ applicationManager.onProcessSuspended(app->appId());
4384+ ASSERT_EQ(Application::InternalState::Suspended, app->internalState());
4385+
4386+ QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4387+ QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4388+
4389+ // Mir notifies of stopping app
4390+ onSessionStopping(session);
4391+
4392+ EXPECT_EQ(0, countSpy.count());
4393+ EXPECT_EQ(1, applicationManager.count());
4394+ EXPECT_EQ(0, removedSpy.count());
4395+
4396+ EXPECT_EQ(Application::Stopped, app->state());
4397 }
4398
4399 /*
4400@@ -1822,45 +1460,6 @@
4401 }
4402
4403 /*
4404- * Test that if an application is stopped by upstart, the Mir stopping event is ignored
4405- */
4406-TEST_F(ApplicationManagerTests,appStoppedByUpstart_mirSessionStoppingEventIgnored)
4407-{
4408- using namespace ::testing;
4409- const QString appId("testAppId");
4410- quint64 procId = 5551;
4411-
4412- // Set up Mocks & signal watcher
4413- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
4414- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4415- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
4416-
4417- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
4418-
4419- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
4420- .Times(1)
4421- .WillOnce(Return(true));
4422-
4423- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4424- applicationManager.onProcessStarting(appId);
4425- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4426- bool authed = true;
4427- applicationManager.authorizeSession(procId, authed);
4428- onSessionStarting(session);
4429-
4430- applicationManager.onProcessStopped(appId);
4431-
4432- QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4433- QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4434-
4435- // Mir notifies of stopping app
4436- onSessionStopping(session);
4437-
4438- EXPECT_EQ(countSpy.count(), 0);
4439- EXPECT_EQ(removedSpy.count(), 0);
4440-}
4441-
4442-/*
4443 * Webapps have multiple sessions, but only one is linked to the application (other is considered a hidden session).
4444 * If webapp in foreground stops unexpectedly, remove it and it alone from app list
4445 */
4446@@ -1903,8 +1502,9 @@
4447 applicationManager.authorizeSession(procId2, authed);
4448 onSessionStarting(session2);
4449 EXPECT_EQ(authed, true);
4450- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4451- applicationManager.onSessionCreatedSurface(session2.get(), surface);
4452+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4453+ onSessionCreatedSurface(session2.get(), surface);
4454+ surface->drawFirstFrame();
4455
4456 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4457 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4458@@ -1950,7 +1550,7 @@
4459 .Times(1)
4460 .WillOnce(Return(true));
4461
4462- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4463+ Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4464 applicationManager.onProcessStarting(appId);
4465 std::shared_ptr<mir::scene::Session> session1 = std::make_shared<MockSession>("", procId1);
4466 std::shared_ptr<mir::scene::Session> session2 = std::make_shared<MockSession>("", procId2);
4467@@ -1958,19 +1558,20 @@
4468 bool authed = false;
4469 applicationManager.authorizeSession(procId1, authed);
4470 onSessionStarting(session1);
4471- EXPECT_EQ(authed, true);
4472+ EXPECT_EQ(true, authed);
4473 applicationManager.authorizeSession(procId2, authed);
4474 onSessionStarting(session2);
4475- EXPECT_EQ(authed, true);
4476+ EXPECT_EQ(true, authed);
4477
4478- // both sessions create surfaces, then unfocus everything.
4479- std::shared_ptr<mir::scene::Surface> surface1(nullptr);
4480- applicationManager.onSessionCreatedSurface(session1.get(), surface1);
4481- std::shared_ptr<mir::scene::Surface> surface2(nullptr);
4482- applicationManager.onSessionCreatedSurface(session2.get(), surface2);
4483- applicationManager.focusApplication(appId);
4484- applicationManager.unfocusCurrentApplication();
4485- EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
4486+ // both sessions create surfaces, then get them all suspended
4487+ FakeMirSurfaceItem *surface1 = new FakeMirSurfaceItem;
4488+ onSessionCreatedSurface(session1.get(), surface1);
4489+ surface1->drawFirstFrame();
4490+ FakeMirSurfaceItem *surface2 = new FakeMirSurfaceItem;
4491+ onSessionCreatedSurface(session2.get(), surface2);
4492+ surface2->drawFirstFrame();
4493+ suspend(app);
4494+ EXPECT_EQ(Application::Suspended, app->state());
4495
4496 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
4497 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
4498@@ -1979,8 +1580,104 @@
4499 onSessionStopping(session2);
4500 onSessionStopping(session1);
4501
4502- EXPECT_EQ(countSpy.count(), 0);
4503- EXPECT_EQ(removedSpy.count(), 0);
4504+ EXPECT_EQ(0, countSpy.count());
4505+ EXPECT_EQ(0, removedSpy.count());
4506+}
4507+
4508+/*
4509+ * Test requested surface size unchanged if no surfaceSizer registered
4510+ */
4511+TEST_F(ApplicationManagerTests, registerSurfaceSizeUnchangedWhenNoSizerCallbackRegistered)
4512+{
4513+ using namespace ::testing;
4514+ quint64 procId = 5551;
4515+ SurfaceParameters params;
4516+ params.geometry = {400, 250}; // can be overridden
4517+
4518+ const auto session = std::make_shared<MockSession>("", procId);
4519+ applicationManager.onSessionAboutToCreateSurface(session, params);
4520+
4521+ EXPECT_EQ(params.geometry, QSize(400, 250));
4522+}
4523+
4524+/*
4525+ * Test registerSurfaceSizerCallback functionality
4526+ */
4527+TEST_F(ApplicationManagerTests, registerSurfaceSizerFunctional)
4528+{
4529+ using namespace ::testing;
4530+ quint64 procId = 5551;
4531+ SurfaceParameters params;
4532+ params.geometry = {400, 250}; // can be overridden
4533+
4534+ QJSValue callback = jsEngine->evaluate("(function(surface) { surface.width = 111; return surface; })");
4535+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4536+
4537+ const auto session = std::make_shared<MockSession>("", procId);
4538+ applicationManager.onSessionAboutToCreateSurface(session, params);
4539+
4540+ EXPECT_EQ(params.geometry, QSize(111, 250));
4541+}
4542+
4543+/*
4544+ * Test deregisterSurfaceSizerCallback correctly removes the callback
4545+ */
4546+TEST_F(ApplicationManagerTests, deregisterSurfaceSizerWorks)
4547+{
4548+ using namespace ::testing;
4549+ quint64 procId = 5551;
4550+ SurfaceParameters params;
4551+ params.geometry = {400, 250}; // can be overridden
4552+
4553+ QJSValue callback = jsEngine->evaluate("(function(surface) { surface.width = 111; return surface; })");
4554+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4555+
4556+ const auto session = std::make_shared<MockSession>("", procId);
4557+
4558+ applicationManager.setSurfaceAboutToBeCreatedCallback(false);
4559+ applicationManager.onSessionAboutToCreateSurface(session, params);
4560+
4561+ EXPECT_EQ(params.geometry, QSize(400, 250)); // should be unchanged
4562+}
4563+
4564+/*
4565+ * Test registerSurfaceSizerCallback rejects an uncallable QJSValue (i.e. something that's not a function was passed)
4566+ */
4567+TEST_F(ApplicationManagerTests, registerSurfaceSizerRejectsNonCallableJSValues)
4568+{
4569+ using namespace ::testing;
4570+
4571+ QJSValue callback = jsEngine->evaluate("21");
4572+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4573+
4574+ callback = jsEngine->evaluate("true");
4575+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4576+
4577+ callback = jsEngine->evaluate("{}");
4578+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4579+
4580+ callback = jsEngine->evaluate("new Array()");
4581+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4582+}
4583+
4584+/*
4585+ * Test in case that registered SurfaceSizerCallback does not return a type with a 'width' (or 'height') property,
4586+ * we return the requested surface width/height unchanged
4587+ */
4588+TEST_F(ApplicationManagerTests, registeredSurfaceSizerDealswithBadReturns)
4589+{
4590+ using namespace ::testing;
4591+ quint64 procId = 5551;
4592+ SurfaceParameters params;
4593+ params.geometry = {400, 250}; // can be overridden
4594+
4595+ QJSValue callback = jsEngine->evaluate("(function(s) { var out = new Object(); out.height = 333; return out; })");
4596+ applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
4597+
4598+ const auto session = std::make_shared<MockSession>("", procId);
4599+ applicationManager.onSessionAboutToCreateSurface(session, params);
4600+
4601+ EXPECT_EQ(params.geometry, QSize(400, 333));
4602 }
4603
4604 /*
4605@@ -2007,34 +1704,33 @@
4606 .Times(1)
4607 .WillOnce(Return(true));
4608
4609- applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4610+ Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4611 applicationManager.onProcessStarting(appId);
4612 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4613 bool authed = true;
4614 applicationManager.authorizeSession(procId, authed);
4615 onSessionStarting(session);
4616
4617- // App creates surface, focuses it, puts it in background, then is OOM killed.
4618- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4619- applicationManager.onSessionCreatedSurface(session.get(), surface);
4620- applicationManager.focusApplication(appId);
4621- applicationManager.unfocusCurrentApplication();
4622+ // App creates surface, puts it in background, then is OOM killed.
4623+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4624+ onSessionCreatedSurface(session.get(), surface);
4625+ surface->drawFirstFrame();
4626+ suspend(app);
4627
4628 onSessionStopping(session);
4629 applicationManager.onProcessFailed(appId, false);
4630 applicationManager.onProcessStopped(appId);
4631
4632- Application *app = applicationManager.findApplication(appId);
4633- EXPECT_EQ(app->state(), Application::Stopped);
4634+ EXPECT_EQ(Application::Stopped, app->state());
4635
4636 QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &)));
4637
4638 // Upstart re-launches app
4639 applicationManager.onProcessStarting(appId);
4640
4641- EXPECT_EQ(app->state(), Application::Starting);
4642- EXPECT_EQ(focusRequestSpy.count(), 1);
4643- EXPECT_EQ(applicationManager.count(), 1);
4644+ EXPECT_EQ(Application::Starting, app->state());
4645+ EXPECT_EQ(1, focusRequestSpy.count());
4646+ EXPECT_EQ(1, applicationManager.count());
4647 }
4648
4649 /*
4650@@ -2138,142 +1834,61 @@
4651 }
4652 }
4653
4654-/*
4655- 1 - launch and focus a main stage app
4656- * main stage app is running and focused
4657- 2 - launch and focus a side stage app
4658- * main stage app is running but is not focused
4659- * side stage app is running and has focus
4660- 3 - focus the main stage app
4661- * main stage app is running and has focus
4662- * side stage app is running but is not focused
4663-
4664- This is a regression test for the bug where on step 3, the main stage app was momentarily
4665- suspended and then resumed again.
4666- */
4667-TEST_F(ApplicationManagerTests, focusMainStageAfterSideStage)
4668-{
4669- using namespace testing;
4670-
4671- QString webbrowserAppId("webbrowser-app");
4672- quint64 webbrowserPID = 123;
4673- std::shared_ptr<mir::scene::Surface> webbrowserSurface(nullptr);
4674-
4675- QString dialerAppId("dialer-app");
4676- quint64 dialerPID = 456;
4677- std::shared_ptr<mir::scene::Surface> dialerSurface(nullptr);
4678-
4679- /*** Start webbrowser-app (main stage) ***/
4680-
4681- ON_CALL(appController,appIdHasProcessId(webbrowserPID, webbrowserAppId)).WillByDefault(Return(true));
4682-
4683- {
4684- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(webbrowserAppId, QFileInfo());
4685- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4686- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(webbrowserAppId));
4687- ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("MainStage"));
4688-
4689- ON_CALL(desktopFileReaderFactory, createInstance(webbrowserAppId, _))
4690- .WillByDefault(Return(mockDesktopFileReader));
4691- }
4692-
4693- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(webbrowserAppId, _))
4694- .Times(1)
4695- .WillOnce(Return(true));
4696-
4697- /*auto application =*/ applicationManager.startApplication(webbrowserAppId, ApplicationManager::NoFlag);
4698- applicationManager.onProcessStarting(webbrowserAppId);
4699-
4700- {
4701- bool authed = false;
4702- applicationManager.authorizeSession(webbrowserPID, authed);
4703- EXPECT_EQ(authed, true);
4704- }
4705-
4706- auto webbrowserSession = std::make_shared<mir::scene::MockSession>(webbrowserAppId.toStdString(), webbrowserPID);
4707- sessionManager.onSessionStarting(webbrowserSession);
4708- applicationManager.focusApplication(webbrowserAppId);
4709- applicationManager.onSessionCreatedSurface(webbrowserSession.get(), webbrowserSurface);
4710-
4711- /*** Start dialer-app (side stage) ***/
4712-
4713- ON_CALL(appController, appIdHasProcessId(dialerPID, dialerAppId)).WillByDefault(Return(true));
4714-
4715- {
4716- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(dialerAppId, QFileInfo());
4717- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4718- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(dialerAppId));
4719- ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("SideStage"));
4720-
4721- ON_CALL(desktopFileReaderFactory, createInstance(dialerAppId, _))
4722- .WillByDefault(Return(mockDesktopFileReader));
4723- }
4724-
4725- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(dialerAppId, _))
4726- .Times(1)
4727- .WillOnce(Return(true));
4728-
4729- /*auto application =*/ applicationManager.startApplication(dialerAppId, ApplicationManager::NoFlag);
4730- applicationManager.onProcessStarting(dialerAppId);
4731-
4732- {
4733- bool authed = false;
4734- applicationManager.authorizeSession(dialerPID, authed);
4735- EXPECT_EQ(authed, true);
4736- }
4737-
4738- auto dialerSession = std::make_shared<mir::scene::MockSession>(dialerAppId.toStdString(), dialerPID);
4739- sessionManager.onSessionStarting(dialerSession);
4740- applicationManager.focusApplication(dialerAppId);
4741- applicationManager.onSessionCreatedSurface(dialerSession.get(), dialerSurface);
4742-
4743- /*** Focus webbrowser ***/
4744-
4745- // Nothing should happen as it's already the running main stage app
4746- EXPECT_CALL(*webbrowserSession.get(), set_lifecycle_state(_))
4747- .Times(0);
4748- applicationManager.focusApplication(webbrowserAppId);
4749-}
4750-
4751 TEST_F(ApplicationManagerTests,lifecycle_exempt_appId_is_not_suspended)
4752 {
4753 using namespace ::testing;
4754 quint64 a_procId = 5921;
4755- const char an_app_id[] = "some_app";
4756- QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");
4757- std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
4758+ const QString appId("some_app");
4759+ QByteArray a_cmd("/usr/bin/app1");
4760
4761 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
4762
4763- ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
4764-
4765- bool authed = true;
4766+ ON_CALL(appController,appIdHasProcessId(a_procId, appId)).WillByDefault(Return(true));
4767+
4768+ // Set up Mocks & signal watcher
4769+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
4770+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4771+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
4772+
4773+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
4774+
4775+
4776+ Application *the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4777+ applicationManager.onProcessStarting(appId);
4778
4779 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);
4780 std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId);
4781- applicationManager.authorizeSession(a_procId, authed);
4782+ {
4783+ bool authed = false;
4784+ applicationManager.authorizeSession(a_procId, authed);
4785+ ASSERT_EQ(authed, true);
4786+ }
4787
4788 onSessionStarting(first_session);
4789- applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
4790+ FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
4791+ onSessionCreatedSurface(first_session.get(), aSurface);
4792+ aSurface->drawFirstFrame();
4793 onSessionStarting(second_session);
4794
4795- Application * the_app = applicationManager.findApplication(an_app_id);
4796- applicationManager.focusApplication(an_app_id);
4797-
4798 // Add to other apps to the list (Not "some_app")
4799 QVariantList lifecycleExemptAppIds;
4800 lifecycleExemptAppIds << "one_app" << "another_app";
4801 ON_CALL(settings,get(_)).WillByDefault(Return(lifecycleExemptAppIds));
4802 settings.changed("lifecycleExemptAppids");
4803
4804- EXPECT_EQ(Application::Running, the_app->state());
4805-
4806- applicationManager.setSuspended(true);
4807-
4808- // And expect "some_app" to get suspended
4809- EXPECT_EQ(Application::Suspended, the_app->state());
4810-
4811- applicationManager.setSuspended(false);
4812+ ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
4813+
4814+ EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(mir_lifecycle_state_will_suspend));
4815+ the_app->setRequestedState(Application::RequestedSuspended);
4816+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());
4817+
4818+ static_cast<qtmir::Session*>(the_app->session())->doSuspend();
4819+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());
4820+ applicationManager.onProcessSuspended(the_app->appId());
4821+ ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());
4822+
4823+ EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(mir_lifecycle_state_resumed));
4824+ the_app->setRequestedState(Application::RequestedRunning);
4825
4826 EXPECT_EQ(Application::Running, the_app->state());
4827
4828@@ -2284,14 +1899,16 @@
4829
4830 EXPECT_EQ(Application::Running, the_app->state());
4831
4832- applicationManager.setSuspended(true);
4833+ EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(_)).Times(0);
4834+ the_app->setRequestedState(Application::RequestedSuspended);
4835
4836 // And expect it to be running still
4837- EXPECT_EQ(Application::Running, the_app->state());
4838-
4839- applicationManager.setSuspended(false);
4840-
4841- EXPECT_EQ(Application::Running, the_app->state());
4842+ ASSERT_EQ(Application::InternalState::RunningInBackground, the_app->internalState());
4843+
4844+ the_app->setRequestedState(Application::RequestedRunning);
4845+
4846+ EXPECT_EQ(Application::Running, the_app->state());
4847+ ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
4848 }
4849
4850 /*
4851@@ -2323,51 +1940,13 @@
4852 onSessionStarting(session);
4853
4854 // App creates surface, focuses it so state is running
4855- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4856- applicationManager.onSessionCreatedSurface(session.get(), surface);
4857- applicationManager.focusApplication(appId);
4858-
4859- applicationManager.unfocusCurrentApplication();
4860-
4861- EXPECT_FALSE(sharedWakelock.enabled());
4862- EXPECT_EQ(application->state(), Application::Running);
4863-}
4864-
4865-/*
4866- * Test lifecycle exempt applications have their wakelocks released on suspend
4867- */
4868-TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnUnSuspend)
4869-{
4870- using namespace ::testing;
4871-
4872- const QString appId("com.ubuntu.music"); // member of lifecycle exemption list
4873- const quint64 procId = 12345;
4874-
4875- // Set up Mocks & signal watcher
4876- auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
4877- ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
4878- ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
4879-
4880- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
4881-
4882- EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
4883- .Times(1)
4884- .WillOnce(Return(true));
4885-
4886- auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
4887- applicationManager.onProcessStarting(appId);
4888- std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
4889- bool authed = true;
4890- applicationManager.authorizeSession(procId, authed);
4891- onSessionStarting(session);
4892-
4893- // App creates surface, focuses it so state is running
4894- std::shared_ptr<mir::scene::Surface> surface(nullptr);
4895- applicationManager.onSessionCreatedSurface(session.get(), surface);
4896- applicationManager.focusApplication(appId);
4897-
4898- applicationManager.setSuspended(true);
4899-
4900- EXPECT_FALSE(sharedWakelock.enabled());
4901- EXPECT_EQ(application->state(), Application::Running);
4902+ FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
4903+ onSessionCreatedSurface(session.get(), surface);
4904+ surface->drawFirstFrame();
4905+
4906+ application->setRequestedState(Application::RequestedSuspended);
4907+
4908+ EXPECT_FALSE(sharedWakelock.enabled());
4909+ ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState());
4910+ EXPECT_EQ(Application::Running, application->state());
4911 }
4912
4913=== modified file 'tests/modules/MirSurfaceItem/CMakeLists.txt'
4914--- tests/modules/MirSurfaceItem/CMakeLists.txt 2014-12-03 08:56:35 +0000
4915+++ tests/modules/MirSurfaceItem/CMakeLists.txt 2015-07-13 21:57:32 +0000
4916@@ -5,7 +5,9 @@
4917 )
4918
4919 include_directories(
4920- ${CMAKE_SOURCE_DIR}/src/modules/Unity/Application
4921+ ${CMAKE_SOURCE_DIR}/src/common
4922+# ${CMAKE_SOURCE_DIR}/src/modules/Unity/Application
4923+ ${CMAKE_SOURCE_DIR}/src/modules
4924 ${CMAKE_SOURCE_DIR}/tests/modules/common
4925 ${MIRSERVER_INCLUDE_DIRS}
4926 )
4927
4928=== modified file 'tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp'
4929--- tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp 2015-05-01 13:37:03 +0000
4930+++ tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp 2015-07-13 21:57:32 +0000
4931@@ -23,7 +23,7 @@
4932 #include <QTest>
4933
4934 // the test subject
4935-#include <mirsurfaceitem.h>
4936+#include <Unity/Application/mirsurfaceitem.h>
4937
4938 // mocks
4939 #include <mock_surface.h>
4940
4941=== modified file 'tests/modules/SessionManager/CMakeLists.txt'
4942--- tests/modules/SessionManager/CMakeLists.txt 2015-05-21 18:48:59 +0000
4943+++ tests/modules/SessionManager/CMakeLists.txt 2015-07-13 21:57:32 +0000
4944@@ -3,9 +3,13 @@
4945 session_manager_test.cpp
4946 session_test.cpp
4947 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
4948+ ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp
4949+ ${CMAKE_SOURCE_DIR}/tests/modules/common/fake_mirsurfaceitem.h
4950 )
4951
4952 include_directories(
4953+ ${APPLICATION_API_INCLUDE_DIRS}
4954+ ${CMAKE_SOURCE_DIR}/src/common
4955 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
4956 ${CMAKE_SOURCE_DIR}/src/modules
4957 ${CMAKE_SOURCE_DIR}/tests/modules/common
4958
4959=== modified file 'tests/modules/SessionManager/session_manager_test.cpp'
4960--- tests/modules/SessionManager/session_manager_test.cpp 2014-12-01 11:05:01 +0000
4961+++ tests/modules/SessionManager/session_manager_test.cpp 2015-07-13 21:57:32 +0000
4962@@ -21,7 +21,7 @@
4963
4964 #include <Unity/Application/session.h>
4965
4966- #include "qtmir_test.h"
4967+#include "qtmir_test.h"
4968
4969 using namespace qtmir;
4970 using mir::scene::MockSession;
4971
4972=== modified file 'tests/modules/SessionManager/session_test.cpp'
4973--- tests/modules/SessionManager/session_test.cpp 2015-01-09 15:13:29 +0000
4974+++ tests/modules/SessionManager/session_test.cpp 2015-07-13 21:57:32 +0000
4975@@ -1,5 +1,5 @@
4976 /*
4977- * Copyright (C) 2014 Canonical, Ltd.
4978+ * Copyright (C) 2014,2015 Canonical, Ltd.
4979 *
4980 * This program is free software: you can redistribute it and/or modify it under
4981 * the terms of the GNU Lesser General Public License version 3, as published by
4982@@ -15,16 +15,17 @@
4983 *
4984 */
4985
4986+#include <qtmir_test.h>
4987+#include <fake_mirsurfaceitem.h>
4988+
4989 #include <Unity/Application/application.h>
4990 #include <Unity/Application/mirsurfaceitem.h>
4991
4992-#include "qtmir_test.h"
4993 #include "stub_scene_surface.h"
4994
4995 using namespace qtmir;
4996 using mir::scene::MockSession;
4997
4998-
4999 namespace ms = mir::scene;
5000 namespace mtd = mir::test::doubles;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches