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

Proposed by Gerry Boland
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 (community) continuous-integration Needs Fixing
Albert Astals Cid (community) Needs Fixing
Michał Sawicz Approve
Daniel d'Andrada 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.
Revision history for this message
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal

See inline.

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

Comments addressed, please check out rev 234

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) : Posted in a previous version of this proposal
Revision history for this message
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
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

Done

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

Minor inline.

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

Looks good.

review: Approve
Revision history for this message
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());

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

Merge trunk & fix conflicts

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

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

264. By Gerry Boland

Merge trunk

265. By Gerry Boland

Merge trunk, required many changes

266. By Gerry Boland

Remove accidental commits

267. By Gerry Boland

Remove local build tweak

268. By Gerry Boland

Merge trunk

269. By Gerry Boland

Merge detach-state-from-focus and resolve conflicts

Unmerged revisions

269. By Gerry Boland

Merge detach-state-from-focus and resolve conflicts

268. By Gerry Boland

Merge trunk

267. By Gerry Boland

Remove local build tweak

266. By Gerry Boland

Remove accidental commits

265. By Gerry Boland

Merge trunk, required many changes

264. By Gerry Boland

Merge trunk

263. By Gerry Boland

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

262. By Gerry Boland

Merge trunk & fix conflicts

261. By Gerry Boland

Merge trunk

260. By Gerry Boland

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