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
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-05-21 18:48:59 +0000
+++ CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -80,7 +80,9 @@
80pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)80pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
81pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)81pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
82pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)82pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
83pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=6)83pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=7)
84
85include_directories(${APPLICATION_API_INCLUDE_DIRS})
8486
85add_definitions(-DMIR_REQUIRE_DEPRECATED_EVENT_OPT_IN=1)87add_definitions(-DMIR_REQUIRE_DEPRECATED_EVENT_OPT_IN=1)
8688
8789
=== modified file 'debian/changelog'
--- debian/changelog 2015-06-17 13:46:09 +0000
+++ debian/changelog 2015-07-13 21:57:32 +0000
@@ -1,3 +1,10 @@
1qtmir (0.4.6) UNRELEASED; urgency=medium
2
3 * Implement a callback mechanism to allow shell override the initial
4 surface geometry requested by clients
5
6 -- Gerry Boland <gerry.boland@canonical.com> Mon, 12 Jul 2015 14:47:07 +0100
7
1qtmir (0.4.5+15.10.20150617-0ubuntu1) wily; urgency=medium8qtmir (0.4.5+15.10.20150617-0ubuntu1) wily; urgency=medium
29
3 [ Gerry Boland ]10 [ Gerry Boland ]
411
=== modified file 'debian/control'
--- debian/control 2015-06-17 13:45:56 +0000
+++ debian/control 2015-07-13 21:57:32 +0000
@@ -28,7 +28,7 @@
28 libubuntu-app-launch2-dev,28 libubuntu-app-launch2-dev,
29 libubuntu-application-api-dev (>= 2.1.0),29 libubuntu-application-api-dev (>= 2.1.0),
30 libudev-dev,30 libudev-dev,
31 libunity-api-dev (>= 7.97),31 libunity-api-dev (>= 7.99),
32 liburl-dispatcher1-dev,32 liburl-dispatcher1-dev,
33 libxkbcommon-dev,33 libxkbcommon-dev,
34 libxrender-dev,34 libxrender-dev,
3535
=== added file 'src/common/globals.h'
--- src/common/globals.h 1970-01-01 00:00:00 +0000
+++ src/common/globals.h 2015-07-13 21:57:32 +0000
@@ -0,0 +1,76 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef GLOBALS_H
18#define GLOBALS_H
19
20#include <QObject>
21#include <QSize>
22#include <mir_toolkit/common.h>
23
24namespace qtmir {
25
26class Globals
27{
28 Q_GADGET
29 Q_ENUMS(SurfaceState)
30 Q_ENUMS(SurfaceType)
31
32public:
33 enum SurfaceState {
34 Unknown = mir_surface_state_unknown,
35 Restored = mir_surface_state_restored,
36 Minimized = mir_surface_state_minimized,
37 Maximized = mir_surface_state_maximized,
38 VertMaximized = mir_surface_state_vertmaximized,
39 /* SemiMaximized = mir_surface_state_semimaximized, // see mircommon/mir_toolbox/common.h*/
40 Fullscreen = mir_surface_state_fullscreen,
41 };
42
43 enum SurfaceType {
44 Normal = mir_surface_type_normal,
45 Utility = mir_surface_type_utility,
46 Dialog = mir_surface_type_dialog,
47 Overlay = mir_surface_type_overlay,
48 Freestyle = mir_surface_type_freestyle,
49 Popover = mir_surface_type_popover,
50 InputMethod = mir_surface_type_inputmethod,
51 };
52
53 enum OrientationAngle {
54 Angle0 = 0,
55 Angle90 = 90,
56 Angle180 = 180,
57 Angle270 = 270
58 };
59
60private:
61 Globals() = default;
62 ~Globals() = default;
63};
64
65
66struct SurfaceParameters
67{
68 QSize geometry;
69 Globals::SurfaceState state;
70};
71
72} // namespace qtmir
73
74Q_DECLARE_METATYPE(qtmir::Globals::OrientationAngle)
75
76#endif // GLOBALS_H
077
=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
--- src/modules/Unity/Application/CMakeLists.txt 2015-05-21 18:48:59 +0000
+++ src/modules/Unity/Application/CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -46,9 +46,11 @@
46 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h46 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
47 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h47 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
48# Feed the automoc monster48# Feed the automoc monster
49 mirsurfaceiteminterface.h
49 session_interface.h50 session_interface.h
50 applicationcontroller.h51 applicationcontroller.h
51 settings_interface.h52 settings_interface.h
53 ../../../common/globals.h
52 )54 )
5355
54add_library(unityapplicationplugin SHARED56add_library(unityapplicationplugin SHARED
5557
=== modified file 'src/modules/Unity/Application/application.cpp'
--- src/modules/Unity/Application/application.cpp 2015-04-10 14:54:44 +0000
+++ src/modules/Unity/Application/application.cpp 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -37,25 +37,28 @@
37namespace qtmir37namespace qtmir
38{38{
3939
40Application::Application(const QSharedPointer<TaskController>& taskController,40QStringList Application::lifecycleExceptions;
41 const QSharedPointer<SharedWakelock>& sharedWakelock,41
42Application::Application(const QSharedPointer<SharedWakelock>& sharedWakelock,
42 DesktopFileReader *desktopFileReader,43 DesktopFileReader *desktopFileReader,
43 State state,
44 const QStringList &arguments,44 const QStringList &arguments,
45 ApplicationManager *parent)45 ApplicationManager *parent)
46 : ApplicationInfoInterface(desktopFileReader->appId(), parent)46 : ApplicationInfoInterface(desktopFileReader->appId(), parent)
47 , m_taskController(taskController)
48 , m_sharedWakelock(sharedWakelock)47 , m_sharedWakelock(sharedWakelock)
49 , m_desktopData(desktopFileReader)48 , m_desktopData(desktopFileReader)
50 , m_pid(0)49 , m_pid(0)
51 , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)50 , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)
52 , m_state(state)51 , m_state(InternalState::Starting)
53 , m_focused(false)52 , m_focused(false)
54 , m_canBeResumed(true)
55 , m_arguments(arguments)53 , m_arguments(arguments)
56 , m_session(nullptr)54 , m_session(nullptr)
55 , m_requestedState(RequestedRunning)
56 , m_processState(ProcessUnknown)
57{57{
58 qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId() << "state=" << state;58 qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId();
59
60 // Because m_state is InternalState::Starting
61 acquireWakelock();
5962
60 // FIXME(greyback) need to save long appId internally until ubuntu-app-launch can hide it from us63 // FIXME(greyback) need to save long appId internally until ubuntu-app-launch can hide it from us
61 m_longAppId = desktopFileReader->file().remove(QRegExp(".desktop$")).split('/').last();64 m_longAppId = desktopFileReader->file().remove(QRegExp(".desktop$")).split('/').last();
@@ -69,10 +72,33 @@
69{72{
70 qCDebug(QTMIR_APPLICATIONS) << "Application::~Application";73 qCDebug(QTMIR_APPLICATIONS) << "Application::~Application";
7174
75 // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup
76 if (m_processState == Application::ProcessUnknown
77 || state() == Application::Starting
78 || state() == Application::Running) {
79 wipeQMLCache();
80 }
81
72 delete m_session;82 delete m_session;
73 delete m_desktopData;83 delete m_desktopData;
74}84}
7585
86
87void Application::wipeQMLCache()
88{
89 QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/"));
90 QDir dir(path);
91 QStringList apps = dir.entryList();
92 for (int i = 0; i < apps.size(); i++) {
93 if (apps.at(i).contains(appId())) {
94 qCDebug(QTMIR_APPLICATIONS) << "Application appId=" << apps.at(i) << " Wiping QML Cache";
95 dir.cd(apps.at(i));
96 dir.removeRecursively();
97 break;
98 }
99 }
100}
101
76bool Application::isValid() const102bool Application::isValid() const
77{103{
78 return m_desktopData->loaded();104 return m_desktopData->loaded();
@@ -159,6 +185,30 @@
159 return color;185 return color;
160}186}
161187
188const char* Application::internalStateToStr(InternalState state)
189{
190 switch (state) {
191 case InternalState::Starting:
192 return "Starting";
193 case InternalState::Running:
194 return "Running";
195 case InternalState::RunningInBackground:
196 return "RunningInBackground";
197 case InternalState::SuspendingWaitSession:
198 return "SuspendingWaitSession";
199 case InternalState::SuspendingWaitProcess:
200 return "SuspendingWaitProcess";
201 case InternalState::Suspended:
202 return "Suspended";
203 case InternalState::StoppedUnexpectedly:
204 return "StoppedUnexpectedly";
205 case InternalState::Stopped:
206 return "Stopped";
207 default:
208 return "???";
209 }
210}
211
162bool Application::splashShowHeader() const212bool Application::splashShowHeader() const
163{213{
164 QString showHeader = m_desktopData->splashShowHeader();214 QString showHeader = m_desktopData->splashShowHeader();
@@ -204,7 +254,104 @@
204254
205Application::State Application::state() const255Application::State Application::state() const
206{256{
207 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
275Application::RequestedState Application::requestedState() const
276{
277 return m_requestedState;
278}
279
280void 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
295void Application::applyRequestedState()
296{
297 if (m_requestedState == RequestedRunning) {
298 applyRequestedRunning();
299 } else {
300 applyRequestedSuspended();
301 }
302}
303
304void 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
330void 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 }
208}355}
209356
210bool Application::focused() const357bool Application::focused() const
@@ -219,12 +366,7 @@
219366
220bool Application::canBeResumed() const367bool Application::canBeResumed() const
221{368{
222 return m_canBeResumed;369 return m_processState != ProcessUnknown;
223}
224
225void Application::setCanBeResumed(const bool resume)
226{
227 m_canBeResumed = resume;
228}370}
229371
230pid_t Application::pid() const372pid_t Application::pid() const
@@ -242,7 +384,7 @@
242 m_arguments = arguments;384 m_arguments = arguments;
243}385}
244386
245void Application::setSession(Session *newSession)387void Application::setSession(SessionInterface *newSession)
246{388{
247 qCDebug(QTMIR_APPLICATIONS) << "Application::setSession - appId=" << appId() << "session=" << newSession;389 qCDebug(QTMIR_APPLICATIONS) << "Application::setSession - appId=" << appId() << "session=" << newSession;
248390
@@ -261,10 +403,25 @@
261 if (m_session) {403 if (m_session) {
262 m_session->setParent(this);404 m_session->setParent(this);
263 m_session->setApplication(this);405 m_session->setApplication(this);
264 m_session->setState(state());406
265407 switch (m_state) {
266 connect(m_session, &SessionInterface::suspended, this, &Application::onSessionSuspended);408 case InternalState::Starting:
267 connect(m_session, &SessionInterface::resumed, this, &Application::onSessionResumed);409 case InternalState::Running:
410 case InternalState::RunningInBackground:
411 m_session->resume();
412 break;
413 case InternalState::SuspendingWaitSession:
414 case InternalState::SuspendingWaitProcess:
415 case InternalState::Suspended:
416 m_session->suspend();
417 break;
418 case InternalState::Stopped:
419 default:
420 m_session->stop();
421 break;
422 }
423
424 connect(m_session, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged);
268 connect(m_session, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);425 connect(m_session, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);
269426
270 if (oldFullscreen != fullscreen())427 if (oldFullscreen != fullscreen())
@@ -288,37 +445,53 @@
288 }445 }
289}446}
290447
291void Application::setState(Application::State state)448void Application::setInternalState(Application::InternalState state)
292{449{
293 qCDebug(QTMIR_APPLICATIONS) << "Application::setState - appId=" << appId() << "state=" << applicationStateToStr(state);450 if (m_state == state) {
294 if (m_state != state) {451 return;
295 if (session()) {452 }
296 session()->setState((Session::State)state);453
297 } else {454 qCDebug(QTMIR_APPLICATIONS) << "Application::setInternalState - appId=" << appId()
298 // If we have have no session, we may have to respawn it.455 << "state=" << internalStateToStr(state);
299 switch (state)456
300 {457 auto oldPublicState = this->state();
301 case Session::State::Running:458 m_state = state;
302 if (m_state == Session::State::Stopped) {459
303 respawn();460 switch (m_state) {
304 state = Session::State::Starting;461 case InternalState::Starting:
305 }462 case InternalState::Running:
306 break;463 acquireWakelock();
307 default:464 break;
308 break;465 case InternalState::RunningInBackground:
309 }466 releaseWakelock();
310 }467 break;
311 m_state = state;468 case InternalState::Suspended:
312 Q_EMIT stateChanged(state);469 releaseWakelock();
313 }470 break;
471 case InternalState::StoppedUnexpectedly:
472 releaseWakelock();
473 break;
474 case InternalState::Stopped:
475 Q_EMIT stopped();
476 releaseWakelock();
477 break;
478 case InternalState::SuspendingWaitSession:
479 case InternalState::SuspendingWaitProcess:
480 // transitory states. leave as it is
481 default:
482 break;
483 };
484
485 if (this->state() != oldPublicState) {
486 Q_EMIT stateChanged(this->state());
487 }
488
489 applyRequestedState();
314}490}
315491
316void Application::setFocused(bool focused)492void Application::setFocused(bool focused)
317{493{
318 qCDebug(QTMIR_APPLICATIONS) << "Application::setFocused - appId=" << appId() << "focused=" << focused;494 qCDebug(QTMIR_APPLICATIONS) << "Application::setFocused - appId=" << appId() << "focused=" << focused;
319 if (focused) {
320 holdWakelock(true);
321 }
322495
323 if (m_focused != focused) {496 if (m_focused != focused) {
324 m_focused = focused;497 m_focused = focused;
@@ -326,25 +499,84 @@
326 }499 }
327}500}
328501
329void Application::onSessionSuspended()502void Application::setProcessState(ProcessState newProcessState)
330{503{
331 qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionSuspended - appId=" << appId();504 if (m_processState == newProcessState) {
332 m_taskController->suspend(longAppId());505 return;
333 holdWakelock(false);506 }
334}507
335508 m_processState = newProcessState;
336void Application::onSessionResumed()509
337{510 switch (m_processState) {
338 qCDebug(QTMIR_APPLICATIONS) << "Application::onSessionResumed - appId=" << appId();511 case ProcessUnknown:
339 holdWakelock(true);512 // it would be a coding error
340 m_taskController->resume(longAppId());513 Q_ASSERT(false);
514 break;
515 case ProcessRunning:
516 if (m_state == InternalState::StoppedUnexpectedly) {
517 setInternalState(InternalState::Starting);
518 }
519 break;
520 case ProcessSuspended:
521 Q_ASSERT(m_state == InternalState::SuspendingWaitProcess);
522 setInternalState(InternalState::Suspended);
523 break;
524 case ProcessStopped:
525 // we assume the session always stop before the process
526 Q_ASSERT(!m_session || m_session->state() == Session::Stopped);
527 if (m_state == InternalState::Starting) {
528 setInternalState(InternalState::Stopped);
529 } else {
530 Q_ASSERT(m_state == InternalState::Stopped
531 || m_state == InternalState::StoppedUnexpectedly);
532 }
533 break;
534 }
535
536 applyRequestedState();
537}
538
539void Application::suspend()
540{
541 Q_ASSERT(m_state == InternalState::Running);
542 Q_ASSERT(m_session != nullptr);
543
544 if (!lifecycleExceptions.filter(appId().section('_',0,0)).empty()) {
545 // Present in exceptions list.
546 // There's no need to keep the wakelock as the process is never suspended
547 // and thus has no cleanup to perform when (for example) the display is
548 // blanked.
549 setInternalState(InternalState::RunningInBackground);
550 } else {
551 setInternalState(InternalState::SuspendingWaitSession);
552 m_session->suspend();
553 }
554}
555
556void Application::resume()
557{
558 if (m_state == InternalState::Suspended) {
559 setInternalState(InternalState::Running);
560 Q_EMIT resumeProcessRequested();
561 if (m_processState == ProcessSuspended) {
562 setProcessState(ProcessRunning); // should we wait for a resumed() signal?
563 }
564 m_session->resume();
565 } else if (m_state == InternalState::SuspendingWaitSession) {
566 setInternalState(InternalState::Running);
567 m_session->resume();
568 } else if (m_state == InternalState::RunningInBackground) {
569 setInternalState(InternalState::Running);
570 }
341}571}
342572
343void Application::respawn()573void Application::respawn()
344{574{
345 qCDebug(QTMIR_APPLICATIONS) << "Application::respawn - appId=" << appId();575 qCDebug(QTMIR_APPLICATIONS) << "Application::respawn - appId=" << appId();
346 holdWakelock(true);576
347 m_taskController->start(appId(), m_arguments);577 setInternalState(InternalState::Starting);
578
579 Q_EMIT startProcessRequested();
348}580}
349581
350QString Application::longAppId() const582QString Application::longAppId() const
@@ -362,20 +594,60 @@
362 return m_rotatesWindowContents;594 return m_rotatesWindowContents;
363}595}
364596
365Session* Application::session() const597SessionInterface* Application::session() const
366{598{
367 return m_session;599 return m_session;
368}600}
369601
370void Application::holdWakelock(bool enable) const602void Application::acquireWakelock() const
371{603{
372 if (appId() == "unity8-dash")604 if (appId() == "unity8-dash")
373 return;605 return;
374606
375 if (enable) {607 m_sharedWakelock->acquire(this);
376 m_sharedWakelock->acquire(this);608}
377 } else {609
378 m_sharedWakelock->release(this);610void Application::releaseWakelock() const
611{
612 if (appId() == "unity8-dash")
613 return;
614
615 m_sharedWakelock->release(this);
616}
617
618void Application::onSessionStateChanged(Session::State sessionState)
619{
620 switch (sessionState) {
621 case Session::Starting:
622 break;
623 case Session::Running:
624 if (m_state == InternalState::Starting) {
625 setInternalState(InternalState::Running);
626 }
627 break;
628 case Session::Suspending:
629 break;
630 case Session::Suspended:
631 Q_ASSERT(m_state == InternalState::SuspendingWaitSession);
632 setInternalState(InternalState::SuspendingWaitProcess);
633 Q_EMIT suspendProcessRequested();
634 break;
635 case Session::Stopped:
636 if (!canBeResumed()
637 || m_state == InternalState::Starting
638 || m_state == InternalState::Running) {
639 /* 1. application is not managed by upstart
640 * 2. application is managed by upstart, but has stopped before it managed
641 * to create a surface, we can assume it crashed on startup, and thus
642 * cannot be resumed
643 * 3. application is managed by upstart and is in foreground (i.e. has
644 * Running state), if Mir reports the application disconnects, it
645 * either crashed or stopped itself.
646 */
647 setInternalState(InternalState::Stopped);
648 } else {
649 setInternalState(InternalState::StoppedUnexpectedly);
650 }
379 }651 }
380}652}
381653
382654
=== modified file 'src/modules/Unity/Application/application.h'
--- src/modules/Unity/Application/application.h 2015-01-19 11:48:32 +0000
+++ src/modules/Unity/Application/application.h 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -28,6 +28,8 @@
28// Unity API28// Unity API
29#include <unity/shell/application/ApplicationInfoInterface.h>29#include <unity/shell/application/ApplicationInfoInterface.h>
3030
31#include "session_interface.h"
32
31namespace mir {33namespace mir {
32 namespace scene {34 namespace scene {
33 class Session;35 class Session;
@@ -39,7 +41,6 @@
3941
40class ApplicationManager;42class ApplicationManager;
41class DesktopFileReader;43class DesktopFileReader;
42class TaskController;
43class Session;44class Session;
44class SharedWakelock;45class SharedWakelock;
4546
@@ -51,15 +52,32 @@
51 Q_PROPERTY(QString exec READ exec CONSTANT)52 Q_PROPERTY(QString exec READ exec CONSTANT)
52 Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged)53 Q_PROPERTY(bool fullscreen READ fullscreen NOTIFY fullscreenChanged)
53 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)54 Q_PROPERTY(Stage stage READ stage WRITE setStage NOTIFY stageChanged)
54 Q_PROPERTY(Session* session READ session NOTIFY sessionChanged DESIGNABLE false)55 Q_PROPERTY(SessionInterface* session READ session NOTIFY sessionChanged DESIGNABLE false)
5556
56public:57public:
57 Q_DECLARE_FLAGS(Stages, Stage)58 Q_DECLARE_FLAGS(Stages, Stage)
5859
59 Application(const QSharedPointer<TaskController>& taskController,60 enum ProcessState {
60 const QSharedPointer<SharedWakelock>& sharedWakelock,61 ProcessUnknown,
62 ProcessRunning,
63 ProcessSuspended,
64 ProcessStopped
65 };
66
67 enum class InternalState {
68 Starting,
69 Running,
70 RunningInBackground,
71 SuspendingWaitSession,
72 SuspendingWaitProcess,
73 Suspended,
74 StoppedUnexpectedly,
75 Stopped // It closed itself, crashed or it stopped and we can't respawn it
76 // In any case, this is a dead end.
77 };
78
79 Application(const QSharedPointer<SharedWakelock>& sharedWakelock,
61 DesktopFileReader *desktopFileReader,80 DesktopFileReader *desktopFileReader,
62 State state,
63 const QStringList &arguments,81 const QStringList &arguments,
64 ApplicationManager *parent);82 ApplicationManager *parent);
65 virtual ~Application();83 virtual ~Application();
@@ -71,6 +89,8 @@
71 QUrl icon() const override;89 QUrl icon() const override;
72 Stage stage() const override;90 Stage stage() const override;
73 State state() const override;91 State state() const override;
92 RequestedState requestedState() const override;
93 void setRequestedState(RequestedState) override;
74 bool focused() const override;94 bool focused() const override;
75 QString splashTitle() const override;95 QString splashTitle() const override;
76 QUrl splashImage() const override;96 QUrl splashImage() const override;
@@ -82,12 +102,16 @@
82 bool rotatesWindowContents() const override;102 bool rotatesWindowContents() const override;
83103
84 void setStage(Stage stage);104 void setStage(Stage stage);
85 void setState(State state);105
86106
87 Session* session() const;107 void setProcessState(ProcessState value);
108
109 QStringList arguments() const { return m_arguments; }
110
111 SessionInterface* session() const;
112 void setSession(SessionInterface *session);
88113
89 bool canBeResumed() const;114 bool canBeResumed() const;
90 void setCanBeResumed(const bool);
91115
92 bool isValid() const;116 bool isValid() const;
93 QString desktopFile() const;117 QString desktopFile() const;
@@ -98,40 +122,58 @@
98122
99 pid_t pid() const;123 pid_t pid() const;
100124
125 // for tests
126 InternalState internalState() const { return m_state; }
127
128 static QStringList lifecycleExceptions;
129
101Q_SIGNALS:130Q_SIGNALS:
102 void fullscreenChanged(bool fullscreen);131 void fullscreenChanged(bool fullscreen);
103 void stageChanged(Stage stage);132 void stageChanged(Stage stage);
104 void sessionChanged(Session *session);133 void sessionChanged(SessionInterface *session);
134
135 void startProcessRequested();
136 void suspendProcessRequested();
137 void resumeProcessRequested();
138 void stopped();
105139
106private Q_SLOTS:140private Q_SLOTS:
107 void onSessionSuspended();141 void onSessionStateChanged(SessionInterface::State sessionState);
108 void onSessionResumed();
109142
110 void respawn();143 void respawn();
111144
112private:145private:
146
113 QString longAppId() const;147 QString longAppId() const;
114 void holdWakelock(bool enable) const;148 void acquireWakelock() const;
149 void releaseWakelock() const;
115 void setPid(pid_t pid);150 void setPid(pid_t pid);
116 void setArguments(const QStringList arguments);151 void setArguments(const QStringList arguments);
117 void setFocused(bool focus);152 void setFocused(bool focus);
118 void setSession(Session *session);153 void setInternalState(InternalState state);
154 void wipeQMLCache();
155 void suspend();
156 void resume();
119 QColor colorFromString(const QString &colorString, const char *colorName) const;157 QColor colorFromString(const QString &colorString, const char *colorName) const;
158 static const char* internalStateToStr(InternalState state);
159 void applyRequestedState();
160 void applyRequestedRunning();
161 void applyRequestedSuspended();
120162
121 QSharedPointer<TaskController> m_taskController;
122 QSharedPointer<SharedWakelock> m_sharedWakelock;163 QSharedPointer<SharedWakelock> m_sharedWakelock;
123 DesktopFileReader* m_desktopData;164 DesktopFileReader* m_desktopData;
124 QString m_longAppId;165 QString m_longAppId;
125 qint64 m_pid;166 qint64 m_pid;
126 Stage m_stage;167 Stage m_stage;
127 Stages m_supportedStages;168 Stages m_supportedStages;
128 State m_state;169 InternalState m_state;
129 bool m_focused;170 bool m_focused;
130 bool m_canBeResumed;
131 QStringList m_arguments;171 QStringList m_arguments;
132 Qt::ScreenOrientations m_supportedOrientations;172 Qt::ScreenOrientations m_supportedOrientations;
133 bool m_rotatesWindowContents;173 bool m_rotatesWindowContents;
134 Session *m_session;174 SessionInterface *m_session;
175 RequestedState m_requestedState;
176 ProcessState m_processState;
135177
136 friend class ApplicationManager;178 friend class ApplicationManager;
137 friend class SessionManager;179 friend class SessionManager;
138180
=== modified file 'src/modules/Unity/Application/application_manager.cpp'
--- src/modules/Unity/Application/application_manager.cpp 2015-05-21 18:38:27 +0000
+++ src/modules/Unity/Application/application_manager.cpp 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013,2014 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -29,6 +29,7 @@
2929
30// mirserver30// mirserver
31#include "mirserver.h"31#include "mirserver.h"
32#include "mirshell.h"
32#include "nativeinterface.h"33#include "nativeinterface.h"
33#include "sessionlistener.h"34#include "sessionlistener.h"
34#include "sessionauthorizer.h"35#include "sessionauthorizer.h"
@@ -47,6 +48,8 @@
47#include <QDebug>48#include <QDebug>
48#include <QByteArray>49#include <QByteArray>
49#include <QDir>50#include <QDir>
51#include <QJSEngine>
52#include <QJSValue>
5053
51// std54// std
52#include <csignal>55#include <csignal>
@@ -90,23 +93,31 @@
90 manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);93 manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
91}94}
9295
96void connectToMirShell(ApplicationManager *manager, MirShell *shell)
97{
98 QObject::connect(shell, &MirShell::sessionAboutToCreateSurface,
99 manager, &ApplicationManager::onSessionAboutToCreateSurface, Qt::BlockingQueuedConnection);
100}
101
93void connectToTaskController(ApplicationManager *manager, TaskController *controller)102void connectToTaskController(ApplicationManager *manager, TaskController *controller)
94{103{
95 QObject::connect(controller, &TaskController::processStarting,104 QObject::connect(controller, &TaskController::processStarting,
96 manager, &ApplicationManager::onProcessStarting);105 manager, &ApplicationManager::onProcessStarting);
97 QObject::connect(controller, &TaskController::processStopped,106 QObject::connect(controller, &TaskController::processStopped,
98 manager, &ApplicationManager::onProcessStopped);107 manager, &ApplicationManager::onProcessStopped);
108 QObject::connect(controller, &TaskController::processSuspended,
109 manager, &ApplicationManager::onProcessSuspended);
99 QObject::connect(controller, &TaskController::processFailed,110 QObject::connect(controller, &TaskController::processFailed,
100 manager, &ApplicationManager::onProcessFailed);111 manager, &ApplicationManager::onProcessFailed);
101 QObject::connect(controller, &TaskController::requestFocus,112 QObject::connect(controller, &TaskController::focusRequested,
102 manager, &ApplicationManager::onFocusRequested);113 manager, &ApplicationManager::onFocusRequested);
103 QObject::connect(controller, &TaskController::requestResume,114 QObject::connect(controller, &TaskController::resumeRequested,
104 manager, &ApplicationManager::onResumeRequested);115 manager, &ApplicationManager::onResumeRequested);
105}116}
106117
107} // namespace118} // namespace
108119
109ApplicationManager* ApplicationManager::Factory::Factory::create()120ApplicationManager* ApplicationManager::Factory::Factory::create(QJSEngine *jsEngine)
110{121{
111 NativeInterface *nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());122 NativeInterface *nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());
112123
@@ -139,11 +150,13 @@
139 sharedWakelock,150 sharedWakelock,
140 fileReaderFactory,151 fileReaderFactory,
141 procInfo,152 procInfo,
142 settings153 settings,
154 jsEngine
143 );155 );
144156
145 connectToSessionListener(appManager, sessionListener);157 connectToSessionListener(appManager, sessionListener);
146 connectToSessionAuthorizer(appManager, sessionAuthorizer);158 connectToSessionAuthorizer(appManager, sessionAuthorizer);
159 connectToMirShell(appManager, mirServer.lock()->shell());
147 connectToTaskController(appManager, taskController.data());160 connectToTaskController(appManager, taskController.data());
148161
149 // Emit signal to notify Upstart that Mir is ready to receive client connections162 // Emit signal to notify Upstart that Mir is ready to receive client connections
@@ -159,12 +172,12 @@
159}172}
160173
161174
162ApplicationManager* ApplicationManager::singleton()175ApplicationManager* ApplicationManager::singleton(QJSEngine *jsEngine)
163{176{
164 static ApplicationManager* instance;177 static ApplicationManager* instance;
165 if (!instance) {178 if (!instance) {
166 Factory appFactory;179 Factory appFactory;
167 instance = appFactory.create();180 instance = appFactory.create(jsEngine);
168 }181 }
169 return instance;182 return instance;
170}183}
@@ -176,20 +189,19 @@
176 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,189 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
177 const QSharedPointer<ProcInfo>& procInfo,190 const QSharedPointer<ProcInfo>& procInfo,
178 const QSharedPointer<SettingsInterface>& settings,191 const QSharedPointer<SettingsInterface>& settings,
192 QJSEngine *jsEngine,
179 QObject *parent)193 QObject *parent)
180 : ApplicationManagerInterface(parent)194 : ApplicationManagerInterface(parent)
181 , m_mirServer(mirServer)195 , m_mirServer(mirServer)
182 , m_focusedApplication(nullptr)196 , m_focusedApplication(nullptr)
183 , m_mainStageApplication(nullptr)
184 , m_sideStageApplication(nullptr)
185 , m_dbusWindowStack(new DBusWindowStack(this))197 , m_dbusWindowStack(new DBusWindowStack(this))
186 , m_taskController(taskController)198 , m_taskController(taskController)
187 , m_desktopFileReaderFactory(desktopFileReaderFactory)199 , m_desktopFileReaderFactory(desktopFileReaderFactory)
188 , m_procInfo(procInfo)200 , m_procInfo(procInfo)
189 , m_sharedWakelock(sharedWakelock)201 , m_sharedWakelock(sharedWakelock)
190 , m_settings(settings)202 , m_settings(settings)
191 , m_suspended(false)203 , m_surfaceAboutToBeCreatedCallback(QJSValue::UndefinedValue)
192 , m_forceDashActive(false)204 , m_jsEngine(jsEngine)
193{205{
194 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;206 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;
195 setObjectName("qtmir::ApplicationManager");207 setObjectName("qtmir::ApplicationManager");
@@ -198,7 +210,7 @@
198 m_roleNames.insert(RoleFullscreen, "fullscreen");210 m_roleNames.insert(RoleFullscreen, "fullscreen");
199211
200 if (settings.data()) {212 if (settings.data()) {
201 m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();213 Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
202 connect(m_settings.data(), &Settings::changed, this, &ApplicationManager::onSettingsChanged);214 connect(m_settings.data(), &Settings::changed, this, &ApplicationManager::onSettingsChanged);
203 }215 }
204}216}
@@ -206,6 +218,7 @@
206ApplicationManager::~ApplicationManager()218ApplicationManager::~ApplicationManager()
207{219{
208 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::~ApplicationManager";220 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::~ApplicationManager";
221 m_surfaceAboutToBeCreatedCallback = QJSValue::UndefinedValue; //unset immediately to avoid racing on shutdown
209}222}
210223
211int ApplicationManager::rowCount(const QModelIndex &parent) const224int ApplicationManager::rowCount(const QModelIndex &parent) const
@@ -289,96 +302,23 @@
289 }302 }
290}303}
291304
292bool ApplicationManager::suspended() const305QJSValue ApplicationManager::surfaceAboutToBeCreatedCallback() const
293{306{
294 return m_suspended;307 return m_surfaceAboutToBeCreatedCallback;
295}308}
296309
297void ApplicationManager::setSuspended(bool suspended)310void ApplicationManager::setSurfaceAboutToBeCreatedCallback(const QJSValue &callback)
298{311{
299 if (suspended == m_suspended) {312 if (m_surfaceAboutToBeCreatedCallback.equals(callback))
300 return;313 return;
301 }
302 m_suspended = suspended;
303 Q_EMIT suspendedChanged();
304314
305 if (m_suspended) {315 if (callback.isCallable()) {
306 suspendApplication(m_mainStageApplication);316 m_surfaceAboutToBeCreatedCallback = callback;
307 suspendApplication(m_sideStageApplication);
308 if (m_focusedApplication) {
309 m_focusedApplication->setFocused(false);
310 m_dbusWindowStack->FocusedWindowChanged(0, QString(), 0);
311 }
312 } else {317 } else {
313 resumeApplication(m_mainStageApplication);318 qWarning() << "ApplicationManager::setSurfaceAboutToBeCreatedCallback - attempted to register a non-function!";
314 resumeApplication(m_sideStageApplication);319 m_surfaceAboutToBeCreatedCallback = QJSValue::UndefinedValue;
315 if (m_focusedApplication) {320 }
316 m_focusedApplication->setFocused(true);321 Q_EMIT surfaceAboutToBeCreatedCallbackChanged();
317 m_dbusWindowStack->FocusedWindowChanged(0, m_focusedApplication->appId(), m_focusedApplication->stage());
318 }
319 }
320}
321
322bool ApplicationManager::forceDashActive() const
323{
324 return m_forceDashActive;
325}
326
327void ApplicationManager::setForceDashActive(bool forceDashActive)
328{
329 if (m_forceDashActive == forceDashActive) {
330 return;
331 }
332
333 m_forceDashActive = forceDashActive;
334 Q_EMIT forceDashActiveChanged();
335
336 Application *dashApp = findApplication("unity8-dash");
337 if (!dashApp) {
338 qCWarning(QTMIR_APPLICATIONS) << "Dash doesn't seem to be running... Ignoring.";
339 return;
340 }
341
342 if (m_forceDashActive && dashApp->state() != Application::Running) {
343 resumeApplication(dashApp);
344 } else if (!m_forceDashActive && dashApp->state() == Application::Running
345 && m_mainStageApplication != dashApp
346 && m_sideStageApplication != dashApp) {
347 suspendApplication(dashApp);
348 }
349}
350
351bool ApplicationManager::suspendApplication(Application *application)
352{
353 if (application == nullptr)
354 return false;
355 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::suspendApplication - appId=" << application->appId();
356
357 // Present in exceptions list, explicitly release wakelock and return. There's no need to keep the wakelock
358 // as the process is never suspended and thus has no cleanup to perform when (for example) the display is blanked
359 if (!m_lifecycleExceptions.filter(application->appId().section('_',0,0)).empty()) {
360 m_sharedWakelock->release(application);
361 return false;
362 }
363
364 if (m_forceDashActive && application->appId() == "unity8-dash") {
365 return false;
366 }
367
368 if (application->state() == Application::Running)
369 application->setState(Application::Suspended);
370
371 return true;
372}
373
374void ApplicationManager::resumeApplication(Application *application)
375{
376 if (application == nullptr)
377 return;
378 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::resumeApplication - appId=" << application->appId();
379
380 if (application->state() == Application::Suspended || application->state() == Application::Stopped)
381 application->setState(Application::Running);
382}322}
383323
384bool ApplicationManager::focusApplication(const QString &inputAppId)324bool ApplicationManager::focusApplication(const QString &inputAppId)
@@ -392,27 +332,8 @@
392 return false;332 return false;
393 }333 }
394334
395 resumeApplication(application);
396
397 // set state of previously focused app to suspended
398 if (m_focusedApplication) {335 if (m_focusedApplication) {
399 m_focusedApplication->setFocused(false);336 m_focusedApplication->setFocused(false);
400 Application *lastApplication = applicationForStage(application->stage());
401 if (lastApplication != application) {
402 suspendApplication(lastApplication);
403 }
404 }
405
406 if (application->stage() == Application::MainStage) {
407 m_mainStageApplication = application;
408 } else {
409 m_sideStageApplication = application;
410 }
411
412 if (!m_suspended) {
413 resumeApplication(application); // in case unfocusCurrentApplication() was last called
414 } else {
415 suspendApplication(application); // Make sure we also have this one suspended if everything is suspended
416 }337 }
417338
418 m_focusedApplication = application;339 m_focusedApplication = application;
@@ -422,9 +343,6 @@
422 Q_EMIT focusedApplicationIdChanged();343 Q_EMIT focusedApplicationIdChanged();
423 m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage());344 m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage());
424345
425 // FIXME(dandrader): lying here. The operation is async. So we will only know whether
426 // the focusing was successful once the server replies. Maybe the API in unity-api should
427 // reflect that?
428 return true;346 return true;
429}347}
430348
@@ -432,9 +350,6 @@
432{350{
433 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::unfocusCurrentApplication";351 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::unfocusCurrentApplication";
434352
435 suspendApplication(m_sideStageApplication);
436 suspendApplication(m_mainStageApplication);
437
438 m_focusedApplication = nullptr;353 m_focusedApplication = nullptr;
439 Q_EMIT focusedApplicationIdChanged();354 Q_EMIT focusedApplicationIdChanged();
440}355}
@@ -484,10 +399,8 @@
484 application->setArguments(arguments);399 application->setArguments(arguments);
485 } else {400 } else {
486 application = new Application(401 application = new Application(
487 m_taskController,
488 m_sharedWakelock,402 m_sharedWakelock,
489 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),403 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
490 Application::Starting,
491 arguments,404 arguments,
492 this);405 this);
493406
@@ -514,10 +427,8 @@
514 Application *application = findApplication(appId);427 Application *application = findApplication(appId);
515 if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list428 if (!application) { // then shell did not start this application, so ubuntu-app-launch must have - add to list
516 application = new Application(429 application = new Application(
517 m_taskController,
518 m_sharedWakelock,430 m_sharedWakelock,
519 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),431 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
520 Application::Starting,
521 QStringList(),432 QStringList(),
522 this);433 this);
523434
@@ -530,17 +441,17 @@
530 Q_EMIT focusRequested(appId);441 Q_EMIT focusRequested(appId);
531 }442 }
532 else {443 else {
533 // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
534 // application and focus it immediately (as user expects app to still be running).
535 if (application->state() == Application::Stopped) {444 if (application->state() == Application::Stopped) {
445 // url-dispatcher can relaunch apps which have been OOM-killed - AppMan must accept the newly spawned
446 // application and focus it immediately (as user expects app to still be running).
536 qCDebug(QTMIR_APPLICATIONS) << "Stopped application appId=" << appId << "is being resumed externally";447 qCDebug(QTMIR_APPLICATIONS) << "Stopped application appId=" << appId << "is being resumed externally";
537 application->setState(Application::Starting);
538 Q_EMIT focusRequested(appId);448 Q_EMIT focusRequested(appId);
539 } else {449 } else {
540 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting application already found with appId"450 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStarting application already found with appId"
541 << appId;451 << appId;
542 }452 }
543 }453 }
454 application->setProcessState(Application::ProcessRunning);
544}455}
545456
546/**457/**
@@ -559,14 +470,7 @@
559 return false;470 return false;
560 }471 }
561472
562 if (application == m_focusedApplication) {
563 // unfocus, and let shell decide what next to focus
564 m_focusedApplication = nullptr;
565 Q_EMIT focusedApplicationIdChanged();
566 }
567
568 remove(application);473 remove(application);
569 m_dbusWindowStack->WindowDestroyed(0, appId);
570474
571 bool result = m_taskController->stop(application->longAppId());475 bool result = m_taskController->stop(application->longAppId());
572476
@@ -583,10 +487,7 @@
583487
584void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup)488void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup)
585{489{
586 /* Applications fail if they fail to launch, crash or are killed. If failed to start, must490 // Applications fail if they fail to launch, crash or are killed.
587 * immediately remove from list of applications. If crash or kill, instead we set flag on the
588 * Application to indicate it can be resumed.
589 */
590491
591 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessFailed - appId=" << appId << "duringStartup=" << duringStartup;492 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessFailed - appId=" << appId << "duringStartup=" << duringStartup;
592493
@@ -598,20 +499,8 @@
598 }499 }
599500
600 Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??501 Q_UNUSED(duringStartup); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
601 if (application->state() == Application::Starting) {502 application->setProcessState(Application::ProcessStopped);
602 if (application == m_focusedApplication) {503 application->setPid(0);
603 m_focusedApplication = nullptr;
604 Q_EMIT focusedApplicationIdChanged();
605 }
606 remove(application);
607 m_dbusWindowStack->WindowDestroyed(0, application->appId());
608 delete application;
609 } else {
610 // We need to set flags on the Application to say the app can be resumed, and thus should not be removed
611 // from the list by onProcessStopped.
612 application->setCanBeResumed(true);
613 application->setPid(0);
614 }
615}504}
616505
617void ApplicationManager::onProcessStopped(const QString &appId)506void ApplicationManager::onProcessStopped(const QString &appId)
@@ -626,29 +515,15 @@
626 return;515 return;
627 }516 }
628517
629 // if shell did not stop the application, but ubuntu-app-launch says it died, we assume the process has been518 application->setProcessState(Application::ProcessStopped);
630 // killed, so it can be respawned later. Only exception is if that application is focused or running519 application->setPid(0);
631 // as then it most likely crashed. Update this logic when ubuntu-app-launch gives some failure info.520}
632 bool removeApplication = true;521
633522void ApplicationManager::onProcessSuspended(const QString &appId)
634 if (application == m_focusedApplication) {523{
635 // Very bad case where focused application dies. Remove from list. Should give error message524 Application *application = findApplication(appId);
636 m_focusedApplication = nullptr;525 if (application) {
637 Q_EMIT focusedApplicationIdChanged();526 application->setProcessState(Application::ProcessSuspended);
638 }
639
640 // The following scenario is the only time that we do NOT remove the application from the app list:
641 if ((application->state() == Application::Suspended || application->state() == Application::Stopped)
642 && application->pid() == 0 // i.e. onProcessFailed was called, which resets the PID of this application
643 && application->canBeResumed()) {
644 removeApplication = false;
645 }
646
647 if (removeApplication) {
648 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - removing appId=" << appId;
649 remove(application);
650 m_dbusWindowStack->WindowDestroyed(0, application->appId());
651 delete application;
652 }527 }
653}528}
654529
@@ -670,10 +545,10 @@
670 return;545 return;
671 }546 }
672547
673 // If app Stopped, trust that ubuntu-app-launch respawns it itself, and AppManager will548 // We interpret this as a focus request for a suspended app.
674 // be notified of that through the onProcessStartReportReceived slot. Else resume.549 // Shell will have this app resumed if it complies with the focus request
675 if (application->state() == Application::Suspended) {550 if (application->state() == Application::Suspended) {
676 application->setState(Application::Running);551 Q_EMIT focusRequested(appId);
677 }552 }
678}553}
679554
@@ -693,7 +568,7 @@
693void ApplicationManager::onSettingsChanged(const QString &key)568void ApplicationManager::onSettingsChanged(const QString &key)
694{569{
695 if (key == "lifecycleExemptAppids") {570 if (key == "lifecycleExemptAppids") {
696 m_lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();571 Application::lifecycleExceptions = m_settings->get("lifecycleExemptAppids").toStringList();
697 }572 }
698}573}
699574
@@ -732,7 +607,6 @@
732607
733 if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) {608 if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) {
734 authorized = true;609 authorized = true;
735 m_hiddenPIDs << pid;
736 return;610 return;
737 }611 }
738612
@@ -792,15 +666,12 @@
792666
793 QStringList arguments(info->asStringList());667 QStringList arguments(info->asStringList());
794 application = new Application(668 application = new Application(
795 m_taskController,
796 m_sharedWakelock,669 m_sharedWakelock,
797 desktopData,670 desktopData,
798 Application::Starting,
799 arguments,671 arguments,
800 this);672 this);
801 application->setPid(pid);673 application->setPid(pid);
802 application->setStage(stage);674 application->setStage(stage);
803 application->setCanBeResumed(false);
804 add(application);675 add(application);
805 authorized = true;676 authorized = true;
806}677}
@@ -812,66 +683,56 @@
812683
813void ApplicationManager::onSessionStopping(std::shared_ptr<ms::Session> const& session)684void ApplicationManager::onSessionStopping(std::shared_ptr<ms::Session> const& session)
814{685{
815 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping - sessionName=" << session->name().c_str();
816
817 // in case application closed not by hand of shell, check again here:
818 Application* application = findApplicationWithSession(session);686 Application* application = findApplicationWithSession(session);
819 if (application) {687 if (application) {
820 /* Can remove the application from the running apps list immediately in these curcumstances:688 m_dbusWindowStack->WindowDestroyed(0, application->appId());
821 * 1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do689 }
822 * it here)690}
823 * 2. application is managed by upstart, but has stopped before it managed to create a surface, we can assume691
824 * it crashed on startup, and thus cannot be resumed - so remove it.692void ApplicationManager::onSessionAboutToCreateSurface(const std::shared_ptr<mir::scene::Session> &session,
825 * 3. application is managed by upstart and is in foreground (i.e. has Running state), if Mir reports the693 qtmir::SurfaceParameters &params)
826 * application disconnects, it either crashed or stopped itself. Either case, remove it.694{
827 */695 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionAboutToCreateSurface - sessionName="
828 if (!application->canBeResumed()696 << session->name().c_str() << "geometry=" << params.geometry
829 || application->state() == Application::Starting697 << "state" << params.state;
830 || application->state() == Application::Running) {698
831 m_dbusWindowStack->WindowDestroyed(0, application->appId());699 if (m_surfaceAboutToBeCreatedCallback.isCallable()) {
832 remove(application);700 QJSValue argument = m_jsEngine->newObject();
833 701 argument.setProperty("width", params.geometry.width());
834 // (ricmm) -- To be on the safe side, better wipe the application QML compile cache if it crashes on startup702 argument.setProperty("height", params.geometry.height());
835 QString path(QDir::homePath() + QStringLiteral("/.cache/QML/Apps/"));703 argument.setProperty("state", params.state);
836 QDir dir(path);704
837 QStringList apps = dir.entryList();705 Application* application = findApplicationWithSession(session.get());
838 for (int i = 0; i < apps.size(); i++) {706 if (application)
839 if (apps.at(i).contains(application->appId())) {707 argument.setProperty("appId", application->appId());
840 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionStopping appId=" << apps.at(i) << " Wiping QML Cache";708
841 dir.cd(apps.at(i));709 QJSValue output = m_surfaceAboutToBeCreatedCallback.call(QJSValueList() << argument);
842 dir.removeRecursively();710 if (output.isObject()) {
843 break;711 QJSValue width = output.property("width");
844 }712 QJSValue height = output.property("height");
845 }713 QJSValue state = output.property("state");
846714 if (width.isNumber())
847 delete application;715 params.geometry.setWidth(width.toInt());
848716 if (height.isNumber())
849 if (application == m_focusedApplication) {717 params.geometry.setHeight(height.toInt());
850 m_focusedApplication = nullptr;718 if (state.isNumber())
851 Q_EMIT focusedApplicationIdChanged();719 params.state = static_cast<Globals::SurfaceState>(state.toInt());
852 }
853 } else {720 } else {
854 // otherwise, we do not have enough information to make any changes to the model, so await events from721 qWarning() << "ApplicationManager::onSessionAboutToCreateSurface - unrecognised object returned from JS callback!!"
855 // upstart to go further, but set the app state722 << "Surface size has *not* been overridden by shell!";
856 application->setState(Application::Stopped);
857 }723 }
858 }724 }
859 m_hiddenPIDs.removeOne(session->process_id());
860}725}
861726
862void ApplicationManager::onSessionCreatedSurface(ms::Session const* session,727void ApplicationManager::onSessionCreatedSurface(const ms::Session *session,
863 std::shared_ptr<ms::Surface> const& surface)728 const std::shared_ptr<ms::Surface> &surface)
864{729{
865 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionCreatedSurface - sessionName=" << session->name().c_str();730 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onSessionCreatedSurface - sessionName=" << session->name().c_str();
866 Q_UNUSED(surface);731 Q_UNUSED(surface);
867732
868 Application* application = findApplicationWithSession(session);733 Application* application = findApplicationWithSession(session);
869 if (application && application->state() == Application::Starting) {734 if (application) {
870 m_dbusWindowStack->WindowCreated(0, application->appId());735 m_dbusWindowStack->WindowCreated(0, application->appId());
871 application->setState(Application::Running);
872 if ((application != m_mainStageApplication && application != m_sideStageApplication) || m_suspended) {
873 suspendApplication(application);
874 }
875 }736 }
876}737}
877738
@@ -900,16 +761,6 @@
900 return nullptr;761 return nullptr;
901}762}
902763
903Application* ApplicationManager::applicationForStage(Application::Stage stage)
904{
905 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::focusedApplicationForStage" << stage;
906
907 if (stage == Application::MainStage)
908 return m_mainStageApplication;
909 else
910 return m_sideStageApplication;
911}
912
913void ApplicationManager::add(Application* application)764void ApplicationManager::add(Application* application)
914{765{
915 Q_ASSERT(application != nullptr);766 Q_ASSERT(application != nullptr);
@@ -920,6 +771,28 @@
920 connect(application, &Application::stateChanged, this, [this](Application::State) { onAppDataChanged(RoleState); });771 connect(application, &Application::stateChanged, this, [this](Application::State) { onAppDataChanged(RoleState); });
921 connect(application, &Application::stageChanged, this, [this](Application::Stage) { onAppDataChanged(RoleStage); });772 connect(application, &Application::stageChanged, this, [this](Application::Stage) { onAppDataChanged(RoleStage); });
922773
774 QString appId = application->appId();
775 QString longAppId = application->longAppId();
776 QStringList arguments = application->arguments();
777
778 // The connection is queued as a workaround an issue in the PhoneStage animation that
779 // happens when you tap on a killed app in the spread to bring it to foreground, causing
780 // a Application::respawn() to take place.
781 // In any case, it seems like in general QML works better when don't do too many things
782 // in the same event loop iteration.
783 connect(application, &Application::startProcessRequested,
784 this, [=]() { m_taskController->start(appId, arguments); },
785 Qt::QueuedConnection);
786
787 connect(application, &Application::suspendProcessRequested, this, [=]() { m_taskController->suspend(longAppId); } );
788 connect(application, &Application::resumeProcessRequested, this, [=]() { m_taskController->resume(longAppId); } );
789
790 connect(application, &Application::stopped, this, [=]() {
791 remove(application);
792 application->deleteLater();
793 });
794
795
923 beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count());796 beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count());
924 m_applications.append(application);797 m_applications.append(application);
925 endInsertRows();798 endInsertRows();
@@ -935,11 +808,6 @@
935 Q_ASSERT(application != nullptr);808 Q_ASSERT(application != nullptr);
936 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId();809 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId();
937810
938 if (application == m_sideStageApplication)
939 m_sideStageApplication = nullptr;
940 if (application == m_mainStageApplication)
941 m_mainStageApplication = nullptr;
942
943 application->disconnect(this);811 application->disconnect(this);
944812
945 int i = m_applications.indexOf(application);813 int i = m_applications.indexOf(application);
@@ -953,6 +821,11 @@
953 Q_EMIT emptyChanged();821 Q_EMIT emptyChanged();
954 }822 }
955 }823 }
824
825 if (application == m_focusedApplication) {
826 m_focusedApplication = nullptr;
827 Q_EMIT focusedApplicationIdChanged();
828 }
956}829}
957830
958void ApplicationManager::move(int from, int to) {831void ApplicationManager::move(int from, int to) {
959832
=== modified file 'src/modules/Unity/Application/application_manager.h'
--- src/modules/Unity/Application/application_manager.h 2015-03-24 23:38:33 +0000
+++ src/modules/Unity/Application/application_manager.h 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -21,6 +21,7 @@
21#include <memory>21#include <memory>
2222
23// Qt23// Qt
24#include <QJSEngine>
24#include <QObject>25#include <QObject>
25#include <QStringList>26#include <QStringList>
2627
@@ -28,6 +29,7 @@
28#include <unity/shell/application/ApplicationManagerInterface.h>29#include <unity/shell/application/ApplicationManagerInterface.h>
2930
30// local31// local
32#include "globals.h"
31#include "application.h"33#include "application.h"
32#include "desktopfilereader.h"34#include "desktopfilereader.h"
3335
@@ -40,6 +42,7 @@
40}42}
4143
42class MirServer;44class MirServer;
45class QJSValue;
4346
44namespace qtmir {47namespace qtmir {
4548
@@ -63,7 +66,7 @@
63 class Factory66 class Factory
64 {67 {
65 public:68 public:
66 ApplicationManager* create();69 ApplicationManager* create(QJSEngine *jsEngine);
67 };70 };
6871
69 // FIXME: these roles should be added to unity-api and removed from here72 // FIXME: these roles should be added to unity-api and removed from here
@@ -79,7 +82,7 @@
79 };82 };
80 Q_DECLARE_FLAGS(ExecFlags, Flag)83 Q_DECLARE_FLAGS(ExecFlags, Flag)
8184
82 static ApplicationManager* singleton();85 static ApplicationManager* singleton(QJSEngine *jsEngine);
8386
84 explicit ApplicationManager(87 explicit ApplicationManager(
85 const QSharedPointer<MirServer> &mirServer,88 const QSharedPointer<MirServer> &mirServer,
@@ -88,15 +91,16 @@
88 const QSharedPointer<DesktopFileReader::Factory> &desktopFileReaderFactory,91 const QSharedPointer<DesktopFileReader::Factory> &desktopFileReaderFactory,
89 const QSharedPointer<ProcInfo> &processInfo,92 const QSharedPointer<ProcInfo> &processInfo,
90 const QSharedPointer<SettingsInterface> &settings,93 const QSharedPointer<SettingsInterface> &settings,
94 QJSEngine *jsEngine,
91 QObject *parent = 0);95 QObject *parent = 0);
92 virtual ~ApplicationManager();96 virtual ~ApplicationManager();
9397
94 // ApplicationManagerInterface98 // ApplicationManagerInterface
95 QString focusedApplicationId() const override;99 QString focusedApplicationId() const override;
96 bool suspended() const override;100
97 void setSuspended(bool suspended) override;101 QJSValue surfaceAboutToBeCreatedCallback() const override;
98 bool forceDashActive() const override;102 void setSurfaceAboutToBeCreatedCallback(const QJSValue &callback) override;
99 void setForceDashActive(bool forceDashActive) override;103
100 Q_INVOKABLE qtmir::Application* get(int index) const override;104 Q_INVOKABLE qtmir::Application* get(int index) const override;
101 Q_INVOKABLE qtmir::Application* findApplication(const QString &appId) const override;105 Q_INVOKABLE qtmir::Application* findApplication(const QString &appId) const override;
102 Q_INVOKABLE bool requestFocusApplication(const QString &appId) override;106 Q_INVOKABLE bool requestFocusApplication(const QString &appId) override;
@@ -110,7 +114,7 @@
110 QVariant data(const QModelIndex & index, int role) const override;114 QVariant data(const QModelIndex & index, int role) const override;
111115
112 Q_INVOKABLE qtmir::Application *startApplication(const QString &appId, ExecFlags flags,116 Q_INVOKABLE qtmir::Application *startApplication(const QString &appId, ExecFlags flags,
113 const QStringList &arguments = QStringList());117 const QStringList &arguments = QStringList());
114 Q_INVOKABLE void move(int from, int to);118 Q_INVOKABLE void move(int from, int to);
115119
116 bool isEmpty() const { return rowCount() == 0; }120 bool isEmpty() const { return rowCount() == 0; }
@@ -121,20 +125,24 @@
121public Q_SLOTS:125public Q_SLOTS:
122 void authorizeSession(const quint64 pid, bool &authorized);126 void authorizeSession(const quint64 pid, bool &authorized);
123127
124 void onSessionStarting(std::shared_ptr<mir::scene::Session> const& session);128 void onSessionStarting(const std::shared_ptr<mir::scene::Session> &session);
125 void onSessionStopping(std::shared_ptr<mir::scene::Session> const& session);129 void onSessionStopping(const std::shared_ptr<mir::scene::Session> &session);
126130
127 void onSessionCreatedSurface(mir::scene::Session const*, std::shared_ptr<mir::scene::Surface> const&);131 void onSessionAboutToCreateSurface(const std::shared_ptr<mir::scene::Session> &session,
132 SurfaceParameters &surfaceParameters);
133 void onSessionCreatedSurface(const mir::scene::Session *session,
134 const std::shared_ptr<mir::scene::Surface> &surface);
128135
129 void onProcessStarting(const QString& appId);136 void onProcessStarting(const QString& appId);
130 void onProcessStopped(const QString& appId);137 void onProcessStopped(const QString& appId);
138 void onProcessSuspended(const QString& appId);
131 void onProcessFailed(const QString& appId, const bool duringStartup);139 void onProcessFailed(const QString& appId, const bool duringStartup);
132 void onFocusRequested(const QString& appId);140 void onFocusRequested(const QString& appId);
133 void onResumeRequested(const QString& appId);141 void onResumeRequested(const QString& appId);
134142
135Q_SIGNALS:143Q_SIGNALS:
136 void focusRequested(const QString &appId);
137 void emptyChanged();144 void emptyChanged();
145 void surfaceAboutToBeCreatedCallbackChanged();
138146
139private Q_SLOTS:147private Q_SLOTS:
140 void onAppDataChanged(const int role);148 void onAppDataChanged(const int role);
@@ -146,9 +154,7 @@
146 void remove(Application* application);154 void remove(Application* application);
147 Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);155 Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);
148 Application* findApplicationWithSession(const mir::scene::Session *session);156 Application* findApplicationWithSession(const mir::scene::Session *session);
149 Application* applicationForStage(Application::Stage stage);
150 QModelIndex findIndex(Application* application);157 QModelIndex findIndex(Application* application);
151 bool suspendApplication(Application *application);
152 void resumeApplication(Application *application);158 void resumeApplication(Application *application);
153 QString toString() const;159 QString toString() const;
154160
@@ -158,19 +164,15 @@
158164
159 QList<Application*> m_applications;165 QList<Application*> m_applications;
160 Application* m_focusedApplication;166 Application* m_focusedApplication;
161 Application* m_mainStageApplication;
162 Application* m_sideStageApplication;
163 QStringList m_lifecycleExceptions;
164 DBusWindowStack* m_dbusWindowStack;167 DBusWindowStack* m_dbusWindowStack;
165 QSharedPointer<TaskController> m_taskController;168 QSharedPointer<TaskController> m_taskController;
166 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;169 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;
167 QSharedPointer<ProcInfo> m_procInfo;170 QSharedPointer<ProcInfo> m_procInfo;
168 QSharedPointer<SharedWakelock> m_sharedWakelock;171 QSharedPointer<SharedWakelock> m_sharedWakelock;
169 QSharedPointer<SettingsInterface> m_settings;172 QSharedPointer<SettingsInterface> m_settings;
173 QJSValue m_surfaceAboutToBeCreatedCallback;
174 QJSEngine *m_jsEngine;
170 static ApplicationManager* the_application_manager;175 static ApplicationManager* the_application_manager;
171 QList<pid_t> m_hiddenPIDs;
172 bool m_suspended;
173 bool m_forceDashActive;
174176
175 friend class Application;177 friend class Application;
176 friend class DBusWindowStack;178 friend class DBusWindowStack;
177179
=== modified file 'src/modules/Unity/Application/applicationcontroller.h'
--- src/modules/Unity/Application/applicationcontroller.h 2014-09-18 22:03:02 +0000
+++ src/modules/Unity/Application/applicationcontroller.h 2015-07-13 21:57:32 +0000
@@ -57,8 +57,9 @@
57 void applicationAboutToBeStarted(const QString &appId);57 void applicationAboutToBeStarted(const QString &appId);
58 void applicationStarted(const QString &appId);58 void applicationStarted(const QString &appId);
59 void applicationStopped(const QString &appId);59 void applicationStopped(const QString &appId);
60 void applicationPaused(const QString &appId);
60 void applicationFocusRequest(const QString &appId);61 void applicationFocusRequest(const QString &appId);
61 void applicationResumeRequest(const QString &appId);62 void applicationResumeRequested(const QString &appId);
6263
63 void applicationError(const QString &appId, ApplicationController::Error error);64 void applicationError(const QString &appId, ApplicationController::Error error);
6465
6566
=== modified file 'src/modules/Unity/Application/applicationscreenshotprovider.cpp'
--- src/modules/Unity/Application/applicationscreenshotprovider.cpp 2014-09-18 09:38:41 +0000
+++ src/modules/Unity/Application/applicationscreenshotprovider.cpp 2015-07-13 21:57:32 +0000
@@ -55,7 +55,7 @@
5555
56 // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state56 // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state
57 // return the screenshot that was saved to disk.57 // return the screenshot that was saved to disk.
58 Session* session = app->session();58 SessionInterface* session = app->session();
59 if (!session || !session->session() || !session->session()->default_surface()) {59 if (!session || !session->session() || !session->session()->default_surface()) {
60 qWarning() << "ApplicationScreenshotProvider - app session not found - asking for screenshot too early";60 qWarning() << "ApplicationScreenshotProvider - app session not found - asking for screenshot too early";
61 return QImage();61 return QImage();
6262
=== modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
--- src/modules/Unity/Application/mirsurfaceitem.cpp 2015-05-21 18:38:27 +0000
+++ src/modules/Unity/Application/mirsurfaceitem.cpp 2015-07-13 21:57:32 +0000
@@ -190,13 +190,13 @@
190 MirShell *shell,190 MirShell *shell,
191 std::shared_ptr<SurfaceObserver> observer,191 std::shared_ptr<SurfaceObserver> observer,
192 QQuickItem *parent)192 QQuickItem *parent)
193 : QQuickItem(parent)193 : MirSurfaceItemInterface(parent)
194 , m_surface(surface)194 , m_surface(surface)
195 , m_session(session)195 , m_session(session)
196 , m_shell(shell)196 , m_shell(shell)
197 , m_firstFrameDrawn(false)197 , m_firstFrameDrawn(false)
198 , m_live(true)198 , m_live(true)
199 , m_orientationAngle(Angle0)199 , m_orientationAngle(Globals::Angle0)
200 , m_textureProvider(nullptr)200 , m_textureProvider(nullptr)
201 , m_lastTouchEvent(nullptr)201 , m_lastTouchEvent(nullptr)
202{202{
@@ -296,22 +296,22 @@
296 return m_session.data();296 return m_session.data();
297}297}
298298
299MirSurfaceItem::Type MirSurfaceItem::type() const299Globals::SurfaceType MirSurfaceItem::type() const
300{300{
301 return static_cast<MirSurfaceItem::Type>(m_surface->type());301 return static_cast<Globals::SurfaceType>(m_surface->type());
302}302}
303303
304MirSurfaceItem::State MirSurfaceItem::state() const304Globals::SurfaceState MirSurfaceItem::state() const
305{305{
306 return static_cast<MirSurfaceItem::State>(m_surface->state());306 return static_cast<Globals::SurfaceState>(m_surface->state());
307}307}
308308
309MirSurfaceItem::OrientationAngle MirSurfaceItem::orientationAngle() const309Globals::OrientationAngle MirSurfaceItem::orientationAngle() const
310{310{
311 return m_orientationAngle;311 return m_orientationAngle;
312}312}
313313
314void MirSurfaceItem::setOrientationAngle(MirSurfaceItem::OrientationAngle angle)314void MirSurfaceItem::setOrientationAngle(Globals::OrientationAngle angle)
315{315{
316 qCDebug(QTMIR_SURFACES, "MirSurfaceItem::setOrientationAngle(%d)", angle);316 qCDebug(QTMIR_SURFACES, "MirSurfaceItem::setOrientationAngle(%d)", angle);
317317
@@ -321,16 +321,16 @@
321 MirOrientation mirOrientation;321 MirOrientation mirOrientation;
322322
323 switch (angle) {323 switch (angle) {
324 case Angle0:324 case Globals::Angle0:
325 mirOrientation = mir_orientation_normal;325 mirOrientation = mir_orientation_normal;
326 break;326 break;
327 case Angle90:327 case Globals::Angle90:
328 mirOrientation = mir_orientation_right;328 mirOrientation = mir_orientation_right;
329 break;329 break;
330 case Angle180:330 case Globals::Angle180:
331 mirOrientation = mir_orientation_inverted;331 mirOrientation = mir_orientation_inverted;
332 break;332 break;
333 case Angle270:333 case Globals::Angle270:
334 mirOrientation = mir_orientation_left;334 mirOrientation = mir_orientation_left;
335 break;335 break;
336 default:336 default:
@@ -375,7 +375,7 @@
375{375{
376 if (!m_firstFrameDrawn) {376 if (!m_firstFrameDrawn) {
377 m_firstFrameDrawn = true;377 m_firstFrameDrawn = true;
378 Q_EMIT firstFrameDrawn(this);378 Q_EMIT firstFrameDrawn();
379 }379 }
380380
381 scheduleTextureUpdate();381 scheduleTextureUpdate();
@@ -456,7 +456,7 @@
456456
457void MirSurfaceItem::mousePressEvent(QMouseEvent *event)457void MirSurfaceItem::mousePressEvent(QMouseEvent *event)
458{458{
459 if (type() == InputMethod) {459 if (type() == Globals::SurfaceType::InputMethod) {
460 // FIXME: Hack to get the VKB use case working while we don't have the proper solution in place.460 // FIXME: Hack to get the VKB use case working while we don't have the proper solution in place.
461 if (isMouseInsideUbuntuKeyboard(event)) {461 if (isMouseInsideUbuntuKeyboard(event)) {
462 auto ev = makeMirEvent(event, mir_pointer_action_button_down);462 auto ev = makeMirEvent(event, mir_pointer_action_button_down);
@@ -562,7 +562,7 @@
562562
563 touchEvent.updateTouchPointStatesAndType();563 touchEvent.updateTouchPointStatesAndType();
564564
565 auto ev = makeMirEvent(touchEvent.modifiers, touchEvent.touchPoints, 565 auto ev = makeMirEvent(touchEvent.modifiers, touchEvent.touchPoints,
566 touchEvent.touchPointStates, touchEvent.timestamp);566 touchEvent.touchPointStates, touchEvent.timestamp);
567 m_surface->consume(*ev);567 m_surface->consume(*ev);
568568
@@ -615,7 +615,7 @@
615 Qt::TouchPointStates touchPointStates)615 Qt::TouchPointStates touchPointStates)
616{616{
617 bool accepted = true;617 bool accepted = true;
618 if (type() == InputMethod && eventType == QEvent::TouchBegin) {618 if (type() == Globals::SurfaceType::InputMethod && eventType == QEvent::TouchBegin) {
619 // FIXME: Hack to get the VKB use case working while we don't have the proper solution in place.619 // FIXME: Hack to get the VKB use case working while we don't have the proper solution in place.
620 if (hasTouchInsideUbuntuKeyboard(touchPoints)) {620 if (hasTouchInsideUbuntuKeyboard(touchPoints)) {
621 validateAndDeliverTouchEvent(eventType, timestamp, mods, touchPoints, touchPointStates);621 validateAndDeliverTouchEvent(eventType, timestamp, mods, touchPoints, touchPointStates);
@@ -659,21 +659,21 @@
659 && pos.y() <= (ubuntuKeyboardInfo->y() + ubuntuKeyboardInfo->height());659 && pos.y() <= (ubuntuKeyboardInfo->y() + ubuntuKeyboardInfo->height());
660}660}
661661
662void MirSurfaceItem::setType(const Type &type)662void MirSurfaceItem::setType(const Globals::SurfaceType &type)
663{663{
664 if (this->type() != type) {664 if (this->type() != type) {
665 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_type, static_cast<int>(type));665 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_type, static_cast<int>(type));
666 }666 }
667}667}
668668
669void MirSurfaceItem::setState(const State &state)669void MirSurfaceItem::setState(const Globals::SurfaceState &state)
670{670{
671 if (this->state() != state) {671 if (this->state() != state) {
672 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_state, static_cast<int>(state));672 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_state, static_cast<int>(state));
673 }673 }
674}674}
675675
676void MirSurfaceItem::setLive(const bool live)676void MirSurfaceItem::setLive(bool live)
677{677{
678 if (m_live != live) {678 if (m_live != live) {
679 m_live = live;679 m_live = live;
680680
=== modified file 'src/modules/Unity/Application/mirsurfaceitem.h'
--- src/modules/Unity/Application/mirsurfaceitem.h 2015-05-13 17:18:45 +0000
+++ src/modules/Unity/Application/mirsurfaceitem.h 2015-07-13 21:57:32 +0000
@@ -22,15 +22,13 @@
22// Qt22// Qt
23#include <QMutex>23#include <QMutex>
24#include <QPointer>24#include <QPointer>
25#include <QSet>
26#include <QQuickItem>
27#include <QTimer>25#include <QTimer>
28#include <QQmlListProperty>26#include <QQmlListProperty>
2927
30// mir28// mir
31#include <mir/scene/surface.h>29#include <mir/scene/surface.h>
32#include <mir_toolkit/common.h>
3330
31#include "mirsurfaceiteminterface.h"
34#include "session_interface.h"32#include "session_interface.h"
3533
36class SurfaceObserver;34class SurfaceObserver;
@@ -41,24 +39,10 @@
41class MirSurfaceManager;39class MirSurfaceManager;
42class QSGMirSurfaceNode;40class QSGMirSurfaceNode;
43class QMirSurfaceTextureProvider;41class QMirSurfaceTextureProvider;
44class Application;
4542
46class MirSurfaceItem : public QQuickItem43class MirSurfaceItem : public MirSurfaceItemInterface
47{44{
48 Q_OBJECT45 Q_OBJECT
49 Q_ENUMS(Type)
50 Q_ENUMS(State)
51 Q_ENUMS(OrientationAngle)
52
53 Q_PROPERTY(Type type READ type NOTIFY typeChanged)
54 Q_PROPERTY(State state READ state NOTIFY stateChanged)
55 Q_PROPERTY(QString name READ name NOTIFY nameChanged)
56 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
57
58 // How many degrees, clockwise, the UI in the surface has to rotate to match with the
59 // shell UI orientation
60 Q_PROPERTY(OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
61 NOTIFY orientationAngleChanged DESIGNABLE false)
6246
63public:47public:
64 explicit MirSurfaceItem(std::shared_ptr<mir::scene::Surface> surface,48 explicit MirSurfaceItem(std::shared_ptr<mir::scene::Surface> surface,
@@ -66,57 +50,29 @@
66 MirShell *shell,50 MirShell *shell,
67 std::shared_ptr<SurfaceObserver> observer,51 std::shared_ptr<SurfaceObserver> observer,
68 QQuickItem *parent = 0);52 QQuickItem *parent = 0);
69 ~MirSurfaceItem();53 virtual ~MirSurfaceItem();
70
71 enum Type {
72 Normal = mir_surface_type_normal,
73 Utility = mir_surface_type_utility,
74 Dialog = mir_surface_type_dialog,
75 Overlay = mir_surface_type_overlay,
76 Freestyle = mir_surface_type_freestyle,
77 Popover = mir_surface_type_popover,
78 InputMethod = mir_surface_type_inputmethod,
79 };
80
81 enum State {
82 Unknown = mir_surface_state_unknown,
83 Restored = mir_surface_state_restored,
84 Minimized = mir_surface_state_minimized,
85 Maximized = mir_surface_state_maximized,
86 VertMaximized = mir_surface_state_vertmaximized,
87 /* SemiMaximized = mir_surface_state_semimaximized, // see mircommon/mir_toolbox/common.h*/
88 Fullscreen = mir_surface_state_fullscreen,
89 };
90
91 enum OrientationAngle {
92 Angle0 = 0,
93 Angle90 = 90,
94 Angle180 = 180,
95 Angle270 = 270
96 };
9754
98 //getters55 //getters
99 Type type() const;56 Globals::SurfaceType type() const override;
100 State state() const;57 Globals::SurfaceState state() const override;
101 QString name() const;58 QString name() const override;
102 bool live() const;59 bool live() const override;
103 SessionInterface *session() const;60 SessionInterface *session() const override;
61 Globals::OrientationAngle orientationAngle() const override;
10462
105 Q_INVOKABLE void release();63 Q_INVOKABLE void release() override;
10664
107 // Item surface/texture management65 // Item surface/texture management
108 bool isTextureProvider() const { return true; }66 bool isTextureProvider() const { return true; }
109 QSGTextureProvider *textureProvider() const;67 QSGTextureProvider *textureProvider() const;
11068
111 void stopFrameDropper();69 void stopFrameDropper() override;
112 void startFrameDropper();70 void startFrameDropper() override;
11371
114 bool isFirstFrameDrawn() const { return m_firstFrameDrawn; }72 bool isFirstFrameDrawn() const override { return m_firstFrameDrawn; }
11573
116 OrientationAngle orientationAngle() const;74 void setOrientationAngle(Globals::OrientationAngle angle) override;
117 void setOrientationAngle(OrientationAngle angle);75 void setSession(SessionInterface *app) override;
118
119 void setSession(SessionInterface *app);
12076
121 // to allow easy touch event injection from tests77 // to allow easy touch event injection from tests
122 bool processTouchEvent(int eventType,78 bool processTouchEvent(int eventType,
@@ -125,14 +81,6 @@
125 const QList<QTouchEvent::TouchPoint> &touchPoints,81 const QList<QTouchEvent::TouchPoint> &touchPoints,
126 Qt::TouchPointStates touchPointStates);82 Qt::TouchPointStates touchPointStates);
12783
128Q_SIGNALS:
129 void typeChanged();
130 void stateChanged();
131 void nameChanged();
132 void orientationAngleChanged(OrientationAngle angle);
133 void liveChanged(bool live);
134 void firstFrameDrawn(MirSurfaceItem *item);
135
136protected Q_SLOTS:84protected Q_SLOTS:
137 void onSessionStateChanged(SessionInterface::State state);85 void onSessionStateChanged(SessionInterface::State state);
13886
@@ -167,9 +115,9 @@
167 bool updateTexture();115 bool updateTexture();
168 void ensureProvider();116 void ensureProvider();
169117
170 void setType(const Type&);118 void setType(const Globals::SurfaceType&);
171 void setState(const State&);119 void setState(const Globals::SurfaceState&);
172 void setLive(const bool);120 void setLive(bool) override;
173121
174 // called by MirSurfaceManager122 // called by MirSurfaceManager
175 void setSurfaceValid(const bool);123 void setSurfaceValid(const bool);
@@ -197,7 +145,7 @@
197 bool m_live;145 bool m_live;
198146
199 //FIXME - have to save the state as Mir has no getter for it (bug:1357429)147 //FIXME - have to save the state as Mir has no getter for it (bug:1357429)
200 OrientationAngle m_orientationAngle;148 Globals::OrientationAngle m_orientationAngle;
201149
202 QMirSurfaceTextureProvider *m_textureProvider;150 QMirSurfaceTextureProvider *m_textureProvider;
203151
@@ -226,13 +174,8 @@
226 QList<QTouchEvent::TouchPoint> touchPoints;174 QList<QTouchEvent::TouchPoint> touchPoints;
227 Qt::TouchPointStates touchPointStates;175 Qt::TouchPointStates touchPointStates;
228 } *m_lastTouchEvent;176 } *m_lastTouchEvent;
229
230 friend class MirSurfaceManager;
231};177};
232178
233} // namespace qtmir179} // namespace qtmir
234180
235Q_DECLARE_METATYPE(qtmir::MirSurfaceItem*)
236Q_DECLARE_METATYPE(qtmir::MirSurfaceItem::OrientationAngle)
237
238#endif // MIRSURFACEITEM_H181#endif // MIRSURFACEITEM_H
239182
=== added file 'src/modules/Unity/Application/mirsurfaceiteminterface.h'
--- src/modules/Unity/Application/mirsurfaceiteminterface.h 1970-01-01 00:00:00 +0000
+++ src/modules/Unity/Application/mirsurfaceiteminterface.h 2015-07-13 21:57:32 +0000
@@ -0,0 +1,89 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef MIRSURFACEITEMINTERFACE_H
18#define MIRSURFACEITEMINTERFACE_H
19
20// Qt
21#include <QQuickItem>
22
23// mir
24#include <mir_toolkit/common.h>
25
26#include "session_interface.h"
27#include "globals.h"
28
29namespace qtmir {
30
31class MirSurfaceItemInterface : public QQuickItem
32{
33 Q_OBJECT
34 Q_ENUMS(Type)
35 Q_ENUMS(State)
36 Q_ENUMS(OrientationAngle)
37
38 Q_PROPERTY(qtmir::Globals::SurfaceType type READ type NOTIFY typeChanged)
39 Q_PROPERTY(qtmir::Globals::SurfaceState state READ state NOTIFY stateChanged)
40 Q_PROPERTY(QString name READ name NOTIFY nameChanged)
41 Q_PROPERTY(bool live READ live NOTIFY liveChanged)
42
43 // How many degrees, clockwise, the UI in the surface has to rotate to match with the
44 // shell UI orientation
45 Q_PROPERTY(qtmir::Globals::OrientationAngle orientationAngle READ orientationAngle WRITE setOrientationAngle
46 NOTIFY orientationAngleChanged DESIGNABLE false)
47
48public:
49 MirSurfaceItemInterface(QQuickItem *parent) : QQuickItem(parent) {}
50 virtual ~MirSurfaceItemInterface() {}
51
52 //getters
53 virtual Globals::SurfaceType type() const = 0;
54 virtual Globals::SurfaceState state() const = 0;
55 virtual QString name() const = 0;
56 virtual bool live() const = 0;
57 virtual SessionInterface *session() const = 0;
58 virtual Globals::OrientationAngle orientationAngle() const = 0;
59
60 virtual Q_INVOKABLE void release() = 0;
61
62 virtual void stopFrameDropper() = 0;
63 virtual void startFrameDropper() = 0;
64
65 virtual bool isFirstFrameDrawn() const = 0;
66
67 virtual void setOrientationAngle(Globals::OrientationAngle angle) = 0;
68 virtual void setSession(SessionInterface *app) = 0;
69
70Q_SIGNALS:
71 void typeChanged();
72 void stateChanged();
73 void nameChanged();
74 void orientationAngleChanged(Globals::OrientationAngle angle);
75 void liveChanged(bool live);
76 void firstFrameDrawn();
77
78private:
79 virtual void setLive(bool) = 0;
80
81 friend class MirSurfaceManager;
82};
83
84} // namespace qtmir
85
86Q_DECLARE_METATYPE(qtmir::MirSurfaceItemInterface*)
87
88#endif // MIRSURFACEITEMINTERFACE_H
89
090
=== modified file 'src/modules/Unity/Application/mirsurfaceitemmodel.h'
--- src/modules/Unity/Application/mirsurfaceitemmodel.h 2014-08-29 11:15:51 +0000
+++ src/modules/Unity/Application/mirsurfaceitemmodel.h 2015-07-13 21:57:32 +0000
@@ -22,8 +22,8 @@
2222
23namespace qtmir {23namespace qtmir {
2424
25class MirSurfaceItem;25class MirSurfaceItemInterface;
26typedef ObjectListModel<MirSurfaceItem> MirSurfaceItemModel;26typedef ObjectListModel<MirSurfaceItemInterface> MirSurfaceItemModel;
2727
28} // namespace qtmir28} // namespace qtmir
2929
3030
=== modified file 'src/modules/Unity/Application/mirsurfacemanager.cpp'
--- src/modules/Unity/Application/mirsurfacemanager.cpp 2015-03-11 10:10:49 +0000
+++ src/modules/Unity/Application/mirsurfacemanager.cpp 2015-07-13 21:57:32 +0000
@@ -51,7 +51,7 @@
51 manager, &MirSurfaceManager::onSessionDestroyingSurface);51 manager, &MirSurfaceManager::onSessionDestroyingSurface);
52}52}
5353
54MirSurfaceManager* MirSurfaceManager::singleton()54MirSurfaceManager* MirSurfaceManager::singleton(QJSEngine *jsEngine)
55{55{
56 if (!the_surface_manager) {56 if (!the_surface_manager) {
5757
@@ -66,7 +66,7 @@
66 SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));66 SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));
67 MirShell *shell = static_cast<MirShell*>(nativeInterface->nativeResourceForIntegration("Shell"));67 MirShell *shell = static_cast<MirShell*>(nativeInterface->nativeResourceForIntegration("Shell"));
6868
69 the_surface_manager = new MirSurfaceManager(nativeInterface->m_mirServer, shell, SessionManager::singleton());69 the_surface_manager = new MirSurfaceManager(nativeInterface->m_mirServer, shell, SessionManager::singleton(jsEngine));
7070
71 connectToSessionListener(the_surface_manager, sessionListener);71 connectToSessionListener(the_surface_manager, sessionListener);
72 }72 }
@@ -112,11 +112,11 @@
112 session->setSurface(qmlSurface);112 session->setSurface(qmlSurface);
113113
114 // Only notify QML of surface creation once it has drawn its first frame.114 // Only notify QML of surface creation once it has drawn its first frame.
115 connect(qmlSurface, &MirSurfaceItem::firstFrameDrawn, this, [&](MirSurfaceItem *item) {115 connect(qmlSurface, &MirSurfaceItemInterface::firstFrameDrawn, this, [=]() {
116 tracepoint(qtmir, firstFrameDrawn);116 tracepoint(qtmir, firstFrameDrawn);
117 Q_EMIT surfaceCreated(item);117 Q_EMIT surfaceCreated(qmlSurface);
118118
119 insert(0, item);119 insert(0, qmlSurface);
120 });120 });
121121
122 // clean up after MirSurfaceItem is destroyed122 // clean up after MirSurfaceItem is destroyed
@@ -139,7 +139,7 @@
139 qCDebug(QTMIR_SURFACES) << "MirSurfaceManager::onSessionDestroyingSurface - session=" << session139 qCDebug(QTMIR_SURFACES) << "MirSurfaceManager::onSessionDestroyingSurface - session=" << session
140 << "surface=" << surface.get() << "surface.name=" << surface->name().c_str();140 << "surface=" << surface.get() << "surface.name=" << surface->name().c_str();
141141
142 MirSurfaceItem* item = nullptr;142 MirSurfaceItemInterface* item = nullptr;
143 {143 {
144 QMutexLocker lock(&m_mutex);144 QMutexLocker lock(&m_mutex);
145 auto it = m_mirSurfaceToItemHash.find(surface.get());145 auto it = m_mirSurfaceToItemHash.find(surface.get());
146146
=== modified file 'src/modules/Unity/Application/mirsurfacemanager.h'
--- src/modules/Unity/Application/mirsurfacemanager.h 2015-03-11 10:10:49 +0000
+++ src/modules/Unity/Application/mirsurfacemanager.h 2015-07-13 21:57:32 +0000
@@ -61,11 +61,11 @@
61 );61 );
62 ~MirSurfaceManager();62 ~MirSurfaceManager();
6363
64 static MirSurfaceManager* singleton();64 static MirSurfaceManager* singleton(QJSEngine *jsEngine);
6565
66Q_SIGNALS:66Q_SIGNALS:
67 void surfaceCreated(MirSurfaceItem* surface);67 void surfaceCreated(MirSurfaceItemInterface* surface);
68 void surfaceDestroyed(MirSurfaceItem* surface);68 void surfaceDestroyed(MirSurfaceItemInterface* surface);
69// void surfaceResized(MirSurface*);69// void surfaceResized(MirSurface*);
70// void fullscreenSurfaceChanged();70// void fullscreenSurfaceChanged();
7171
@@ -74,7 +74,7 @@
74 void onSessionDestroyingSurface(const mir::scene::Session *, const std::shared_ptr<mir::scene::Surface> &);74 void onSessionDestroyingSurface(const mir::scene::Session *, const std::shared_ptr<mir::scene::Surface> &);
7575
76protected:76protected:
77 QHash<const mir::scene::Surface *, MirSurfaceItem *> m_mirSurfaceToItemHash;77 QHash<const mir::scene::Surface *, MirSurfaceItemInterface *> m_mirSurfaceToItemHash;
78 QMutex m_mutex;78 QMutex m_mutex;
7979
80private:80private:
8181
=== modified file 'src/modules/Unity/Application/plugin.cpp'
--- src/modules/Unity/Application/plugin.cpp 2015-02-05 10:28:31 +0000
+++ src/modules/Unity/Application/plugin.cpp 2015-07-13 21:57:32 +0000
@@ -28,41 +28,36 @@
2828
29// qtmir29// qtmir
30#include "logging.h"30#include "logging.h"
3131#include "globals.h"
32using namespace qtmir;
33
34static QObject* applicationManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
35 Q_UNUSED(engine);
36 Q_UNUSED(scriptEngine);
37 qCDebug(QTMIR_APPLICATIONS) << "applicationManagerSingleton - engine=" << engine << "scriptEngine=" << scriptEngine;
38
39 return qtmir::ApplicationManager::singleton();
40}
41
42static QObject* surfaceManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
43 Q_UNUSED(engine);
44 Q_UNUSED(scriptEngine);
45 qCDebug(QTMIR_APPLICATIONS) << "surfaceManagerSingleton - engine=" << engine << "scriptEngine=" << scriptEngine;
46
47 return qtmir::MirSurfaceManager::singleton();
48}
49
50static QObject* sessionManagerSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
51 Q_UNUSED(engine);
52 Q_UNUSED(scriptEngine);
53 return qtmir::SessionManager::singleton();
54}
5532
56namespace {33namespace {
57QObject* ubuntuKeyboardInfoSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) {34
58 if (!UbuntuKeyboardInfo::instance()) {35QObject* applicationManagerSingleton(QQmlEngine* /*engine*/, QJSEngine* jsEngine)
59 new UbuntuKeyboardInfo;36{
37 return qtmir::ApplicationManager::singleton(jsEngine);
38}
39
40QObject* surfaceManagerSingleton(QQmlEngine* /*engine*/, QJSEngine* jsEngine)
41{
42 return qtmir::MirSurfaceManager::singleton(jsEngine);
43}
44
45QObject* sessionManagerSingleton(QQmlEngine* /*engine*/, QJSEngine* jsEngine)
46{
47 return qtmir::SessionManager::singleton(jsEngine);
48}
49
50QObject* ubuntuKeyboardInfoSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/)
51{
52 if (!qtmir::UbuntuKeyboardInfo::instance()) {
53 new qtmir::UbuntuKeyboardInfo;
60 }54 }
61 return UbuntuKeyboardInfo::instance();55 return qtmir::UbuntuKeyboardInfo::instance();
62}56}
63} // anonymous namespace57} // anonymous namespace
6458
65class UnityApplicationPlugin : public QQmlExtensionPlugin {59class UnityApplicationPlugin : public QQmlExtensionPlugin
60{
66 Q_OBJECT61 Q_OBJECT
67 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")62 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
6863
@@ -71,9 +66,12 @@
71 qCDebug(QTMIR_APPLICATIONS) << "UnityApplicationPlugin::registerTypes - this=" << this << "uri=" << uri;66 qCDebug(QTMIR_APPLICATIONS) << "UnityApplicationPlugin::registerTypes - this=" << this << "uri=" << uri;
72 Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Application"));67 Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Application"));
7368
69 qmlRegisterUncreatableType<qtmir::Globals>(
70 uri, 0, 1, "QtMir", "QtMir provides enum values, it can't be instantiated");
71
74 qRegisterMetaType<qtmir::ApplicationManager*>("ApplicationManager*"); //need for queueing signals72 qRegisterMetaType<qtmir::ApplicationManager*>("ApplicationManager*"); //need for queueing signals
75 qRegisterMetaType<qtmir::Application*>("Application*");73 qRegisterMetaType<qtmir::Application*>("Application*");
76 qRegisterMetaType<qtmir::MirSurfaceItem*>("MirSurfaceItem*");74 qRegisterMetaType<qtmir::MirSurfaceItemInterface*>("MirSurfaceItemInterface*");
77 qRegisterMetaType<qtmir::MirSurfaceItemModel*>("MirSurfaceItemModel*");75 qRegisterMetaType<qtmir::MirSurfaceItemModel*>("MirSurfaceItemModel*");
78 qRegisterMetaType<qtmir::Session*>("Session*");76 qRegisterMetaType<qtmir::Session*>("Session*");
79 qRegisterMetaType<qtmir::SessionInterface*>("SessionInterface*");77 qRegisterMetaType<qtmir::SessionInterface*>("SessionInterface*");
@@ -92,7 +90,7 @@
92 uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);90 uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);
93 qmlRegisterSingletonType<qtmir::SessionManager>(91 qmlRegisterSingletonType<qtmir::SessionManager>(
94 uri, 0, 1, "SessionManager", sessionManagerSingleton);92 uri, 0, 1, "SessionManager", sessionManagerSingleton);
95 qmlRegisterUncreatableType<qtmir::MirSurfaceItem>(93 qmlRegisterUncreatableType<qtmir::MirSurfaceItemInterface>(
96 uri, 0, 1, "MirSurfaceItem", "MirSurfaceItem can't be instantiated from QML");94 uri, 0, 1, "MirSurfaceItem", "MirSurfaceItem can't be instantiated from QML");
97 qmlRegisterUncreatableType<qtmir::Session>(95 qmlRegisterUncreatableType<qtmir::Session>(
98 uri, 0, 1, "Session", "Session can't be instantiated from QML");96 uri, 0, 1, "Session", "Session can't be instantiated from QML");
@@ -105,7 +103,7 @@
105 QQmlExtensionPlugin::initializeEngine(engine, uri);103 QQmlExtensionPlugin::initializeEngine(engine, uri);
106104
107 qtmir::ApplicationManager* appManager105 qtmir::ApplicationManager* appManager
108 = static_cast<qtmir::ApplicationManager*>(applicationManagerSingleton(engine, nullptr));106 = static_cast<qtmir::ApplicationManager*>(applicationManagerSingleton(engine, engine));
109 engine->addImageProvider(QLatin1String("application"), new qtmir::ApplicationScreenshotProvider(appManager));107 engine->addImageProvider(QLatin1String("application"), new qtmir::ApplicationScreenshotProvider(appManager));
110 }108 }
111};109};
112110
=== modified file 'src/modules/Unity/Application/session.cpp'
--- src/modules/Unity/Application/session.cpp 2015-01-15 15:19:26 +0000
+++ src/modules/Unity/Application/session.cpp 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014,2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -60,14 +60,7 @@
60 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);60 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
6161
62 m_suspendTimer->setSingleShot(true);62 m_suspendTimer->setSingleShot(true);
63 connect(m_suspendTimer, &QTimer::timeout, this, [this]() {63 connect(m_suspendTimer, &QTimer::timeout, this, &Session::doSuspend);
64 if (m_surface) {
65 m_surface->stopFrameDropper();
66 } else {
67 qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!";
68 }
69 Q_EMIT suspended();
70 });
71}64}
7265
73Session::~Session()66Session::~Session()
@@ -89,6 +82,17 @@
89 delete m_children; m_children = nullptr;82 delete m_children; m_children = nullptr;
90}83}
9184
85void Session::doSuspend()
86{
87 Q_ASSERT(m_state == Session::Suspending);
88 if (m_surface) {
89 m_surface->stopFrameDropper();
90 } else {
91 qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!";
92 }
93 setState(Suspended);
94}
95
92void Session::release()96void Session::release()
93{97{
94 qCDebug(QTMIR_SESSIONS) << "Session::release " << name();98 qCDebug(QTMIR_SESSIONS) << "Session::release " << name();
@@ -120,7 +124,7 @@
120 return m_application;124 return m_application;
121}125}
122126
123MirSurfaceItem* Session::surface() const127MirSurfaceItemInterface* Session::surface() const
124{128{
125 // Only notify QML of surface creation once it has drawn its first frame.129 // Only notify QML of surface creation once it has drawn its first frame.
126 if (m_surface && m_surface->isFirstFrameDrawn()) {130 if (m_surface && m_surface->isFirstFrameDrawn()) {
@@ -140,6 +144,13 @@
140 return m_state;144 return m_state;
141}145}
142146
147void Session::setState(State state) {
148 if (state != m_state) {
149 m_state = state;
150 Q_EMIT stateChanged(m_state);
151 }
152}
153
143bool Session::fullscreen() const154bool Session::fullscreen() const
144{155{
145 return m_fullscreen;156 return m_fullscreen;
@@ -159,7 +170,7 @@
159 Q_EMIT applicationChanged(application);170 Q_EMIT applicationChanged(application);
160}171}
161172
162void Session::setSurface(MirSurfaceItem *newSurface)173void Session::setSurface(MirSurfaceItemInterface *newSurface)
163{174{
164 qCDebug(QTMIR_SESSIONS) << "Session::setSurface - session=" << name() << "surface=" << newSurface;175 qCDebug(QTMIR_SESSIONS) << "Session::setSurface - session=" << name() << "surface=" << newSurface;
165176
@@ -173,34 +184,46 @@
173 m_surface->setParent(nullptr);184 m_surface->setParent(nullptr);
174 }185 }
175186
176 MirSurfaceItem *previousSurface = surface();187 MirSurfaceItemInterface *previousSurface = surface();
177 m_surface = newSurface;188 m_surface = newSurface;
178189
179 if (newSurface) {190 if (newSurface) {
180 m_surface->setParent(this);191 m_surface->setParent(this);
181 m_surface->setSession(this);192 m_surface->setSession(this);
182193
194 connect(newSurface, &MirSurfaceItemInterface::stateChanged,
195 this, &Session::updateFullscreenProperty);
196
183 // Only notify QML of surface creation once it has drawn its first frame.197 // Only notify QML of surface creation once it has drawn its first frame.
184 if (!surface()) {198 if (m_surface->isFirstFrameDrawn()) {
185 connect(newSurface, &MirSurfaceItem::firstFrameDrawn,199 setState(Running);
186 this, [this] { Q_EMIT surfaceChanged(m_surface); });200 } else {
201 connect(newSurface, &MirSurfaceItemInterface::firstFrameDrawn,
202 this, &Session::onFirstSurfaceFrameDrawn);
187 }203 }
188
189 connect(newSurface, &MirSurfaceItem::stateChanged,
190 this, &Session::updateFullscreenProperty);
191 }204 }
192205
193 if (previousSurface != surface()) {206 if (previousSurface != surface()) {
207 qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this
208 << " surface=" << m_surface;
194 Q_EMIT surfaceChanged(m_surface);209 Q_EMIT surfaceChanged(m_surface);
195 }210 }
196211
197 updateFullscreenProperty();212 updateFullscreenProperty();
198}213}
199214
215void Session::onFirstSurfaceFrameDrawn()
216{
217 qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this
218 << " surface=" << m_surface;
219 Q_EMIT surfaceChanged(m_surface);
220 setState(Running);
221}
222
200void Session::updateFullscreenProperty()223void Session::updateFullscreenProperty()
201{224{
202 if (m_surface) {225 if (m_surface) {
203 setFullscreen(m_surface->state() == MirSurfaceItem::Fullscreen);226 setFullscreen(m_surface->state() == Globals::SurfaceState::Fullscreen);
204 } else {227 } else {
205 // Keep the current value of the fullscreen property until we get a new228 // Keep the current value of the fullscreen property until we get a new
206 // surface229 // surface
@@ -216,59 +239,71 @@
216 }239 }
217}240}
218241
219void Session::setState(State state)242void Session::suspend()
220{243{
221 qCDebug(QTMIR_SESSIONS) << "Session::setState - session=" << this << "state=" << applicationStateToStr(state);244 qCDebug(QTMIR_SESSIONS) << "Session::suspend - session=" << this << "state=" << applicationStateToStr(m_state);
222 if (m_state != state) {245 if (m_state == Running) {
223 switch (state)246 session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
224 {247 m_suspendTimer->start(1500);
225 case Session::State::Suspended:248
226 if (m_state == Session::State::Running) {249 foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) {
227 session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);250 m_promptSessionManager->suspend_prompt_session(promptSession);
228 m_suspendTimer->start(1500);251 });
229 }252
230 break;253 foreachChildSession([](SessionInterface* session) {
231 case Session::State::Running:254 session->suspend();
232 if (m_suspendTimer->isActive())255 });
233 m_suspendTimer->stop();256
234257 setState(Suspending);
235 if (m_state == Session::State::Suspended) {258 }
236 if (m_surface)259}
237 m_surface->startFrameDropper();260
238 Q_EMIT resumed();261void Session::resume()
239 session()->set_lifecycle_state(mir_lifecycle_state_resumed);262{
240 }263 qCDebug(QTMIR_SESSIONS) << "Session::resume - session=" << this << "state=" << applicationStateToStr(m_state);
241 break;264
242 case Session::State::Stopped:265 if (m_state == Suspending || m_state == Suspended) {
243 stopPromptSessions();266 doResume();
244 if (m_suspendTimer->isActive())267 }
245 m_suspendTimer->stop();268}
246 if (m_surface)269
247 m_surface->stopFrameDropper();270void Session::doResume()
248 break;271{
249 default:272 if (m_state == Suspending) {
250 break;273 Q_ASSERT(m_suspendTimer->isActive());
251 }274 m_suspendTimer->stop();
252275 } else if (m_state == Suspended) {
253 m_state = state;276 Q_ASSERT(m_surface);
254 Q_EMIT stateChanged(state);277 m_surface->startFrameDropper();
255278 }
256 foreachPromptSession([this, state](const std::shared_ptr<ms::PromptSession>& promptSession) {279
257 switch (state) {280 session()->set_lifecycle_state(mir_lifecycle_state_resumed);
258 case Session::State::Suspended:281
259 m_promptSessionManager->suspend_prompt_session(promptSession);282 foreachPromptSession([this](const std::shared_ptr<ms::PromptSession>& promptSession) {
260 break;283 m_promptSessionManager->resume_prompt_session(promptSession);
261 case Session::State::Running:284 });
262 m_promptSessionManager->resume_prompt_session(promptSession);285
263 break;286 foreachChildSession([](SessionInterface* session) {
264 default:287 session->resume();
265 break;288 });
266 }289
267 });290 setState(Running);
268291}
269 foreachChildSession([state](SessionInterface* session) {292
270 session->setState(state);293void Session::stop()
271 });294{
295 if (m_state != Stopped) {
296 stopPromptSessions();
297 if (m_suspendTimer->isActive())
298 m_suspendTimer->stop();
299 if (m_surface)
300 m_surface->stopFrameDropper();
301
302 foreachChildSession([](SessionInterface* session) {
303 session->stop();
304 });
305
306 setState(Stopped);
272 }307 }
273}308}
274309
@@ -277,6 +312,9 @@
277 if (m_live != live) {312 if (m_live != live) {
278 m_live = live;313 m_live = live;
279 Q_EMIT liveChanged(m_live);314 Q_EMIT liveChanged(m_live);
315 if (!live) {
316 setState(Stopped);
317 }
280 }318 }
281}319}
282320
@@ -302,7 +340,19 @@
302 static_cast<Session*>(session)->setParentSession(this);340 static_cast<Session*>(session)->setParentSession(this);
303 m_children->insert(index, session);341 m_children->insert(index, session);
304342
305 session->setState(state());343 switch (m_state) {
344 case Starting:
345 case Running:
346 session->resume();
347 break;
348 case Suspending:
349 case Suspended:
350 session->suspend();
351 break;
352 case Stopped:
353 session->stop();
354 break;
355 }
306}356}
307357
308void Session::removeChildSession(SessionInterface* session)358void Session::removeChildSession(SessionInterface* session)
309359
=== modified file 'src/modules/Unity/Application/session.h'
--- src/modules/Unity/Application/session.h 2014-09-11 16:18:40 +0000
+++ src/modules/Unity/Application/session.h 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -52,15 +52,18 @@
52 //getters52 //getters
53 QString name() const override;53 QString name() const override;
54 unity::shell::application::ApplicationInfoInterface* application() const override;54 unity::shell::application::ApplicationInfoInterface* application() const override;
55 MirSurfaceItem* surface() const override;55 MirSurfaceItemInterface* surface() const override;
56 SessionInterface* parentSession() const override;56 SessionInterface* parentSession() const override;
57 State state() const override;57 State state() const override;
58 bool fullscreen() const override;58 bool fullscreen() const override;
59 bool live() const override;59 bool live() const override;
6060
61 void setApplication(unity::shell::application::ApplicationInfoInterface* item) override;61 void setApplication(unity::shell::application::ApplicationInfoInterface* item) override;
62 void setSurface(MirSurfaceItem* surface) override;62 void setSurface(MirSurfaceItemInterface* surface) override;
63 void setState(State state) override;63
64 void suspend() override;
65 void resume() override;
66 void stop() override;
6467
65 void addChildSession(SessionInterface* session) override;68 void addChildSession(SessionInterface* session) override;
66 void insertChildSession(uint index, SessionInterface* session) override;69 void insertChildSession(uint index, SessionInterface* session) override;
@@ -74,23 +77,29 @@
7477
75 SessionModel* childSessions() const override;78 SessionModel* childSessions() const override;
7679
77protected:
78 void setFullscreen(bool fullscreen) override;80 void setFullscreen(bool fullscreen) override;
79 void setLive(const bool) override;81 void setLive(const bool) override;
80 void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override;82 void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override;
81 void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override;83 void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) override;
8284
85public Q_SLOTS:
86 // it's public to ease testing
87 void doSuspend();
88
83private Q_SLOTS:89private Q_SLOTS:
84 void updateFullscreenProperty();90 void updateFullscreenProperty();
91 void onFirstSurfaceFrameDrawn();
8592
86private:93private:
87 void setParentSession(Session* session);94 void setParentSession(Session* session);
95 void setState(State state);
96 void doResume();
8897
89 void stopPromptSessions();98 void stopPromptSessions();
9099
91 std::shared_ptr<mir::scene::Session> m_session;100 std::shared_ptr<mir::scene::Session> m_session;
92 Application* m_application;101 Application* m_application;
93 MirSurfaceItem* m_surface;102 MirSurfaceItemInterface* m_surface;
94 SessionInterface* m_parentSession;103 SessionInterface* m_parentSession;
95 SessionModel* m_children;104 SessionModel* m_children;
96 bool m_fullscreen;105 bool m_fullscreen;
@@ -103,6 +112,4 @@
103112
104} // namespace qtmir113} // namespace qtmir
105114
106Q_DECLARE_METATYPE(qtmir::Session*)
107
108#endif // SESSION_H115#endif // SESSION_H
109116
=== modified file 'src/modules/Unity/Application/session_interface.h'
--- src/modules/Unity/Application/session_interface.h 2014-09-22 18:06:58 +0000
+++ src/modules/Unity/Application/session_interface.h 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014,2015 Canonical, Ltd.
3 *3 *
4 * This program is free software; you can redistribute it and/or modify4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by5 * it under the terms of the GNU General Public License as published by
@@ -35,11 +35,11 @@
3535
36namespace qtmir {36namespace qtmir {
3737
38class MirSurfaceItem;38class MirSurfaceItemInterface;
3939
40class SessionInterface : public QObject {40class SessionInterface : public QObject {
41 Q_OBJECT41 Q_OBJECT
42 Q_PROPERTY(MirSurfaceItem* surface READ surface NOTIFY surfaceChanged)42 Q_PROPERTY(MirSurfaceItemInterface* surface READ surface NOTIFY surfaceChanged)
43 Q_PROPERTY(unity::shell::application::ApplicationInfoInterface* application READ application NOTIFY applicationChanged DESIGNABLE false)43 Q_PROPERTY(unity::shell::application::ApplicationInfoInterface* application READ application NOTIFY applicationChanged DESIGNABLE false)
44 Q_PROPERTY(SessionInterface* parentSession READ parentSession NOTIFY parentSessionChanged DESIGNABLE false)44 Q_PROPERTY(SessionInterface* parentSession READ parentSession NOTIFY parentSessionChanged DESIGNABLE false)
45 Q_PROPERTY(SessionModel* childSessions READ childSessions DESIGNABLE false CONSTANT)45 Q_PROPERTY(SessionModel* childSessions READ childSessions DESIGNABLE false CONSTANT)
@@ -49,58 +49,66 @@
49 SessionInterface(QObject *parent = 0) : QObject(parent) {}49 SessionInterface(QObject *parent = 0) : QObject(parent) {}
50 virtual ~SessionInterface() {}50 virtual ~SessionInterface() {}
5151
52 // Session State52 enum State {
53 typedef unity::shell::application::ApplicationInfoInterface::State State;53 Starting,
54 Running,
55 Suspending,
56 Suspended,
57 Stopped
58 };
5459
55 Q_INVOKABLE virtual void release() = 0;60 Q_INVOKABLE virtual void release() = 0;
5661
57 //getters62 //getters
58 virtual QString name() const = 0;63 virtual QString name() const = 0;
59 virtual unity::shell::application::ApplicationInfoInterface* application() const = 0;64 virtual unity::shell::application::ApplicationInfoInterface* application() const = 0;
60 virtual MirSurfaceItem* surface() const = 0;65 virtual MirSurfaceItemInterface* surface() const = 0;
61 virtual SessionInterface* parentSession() const = 0;66 virtual SessionInterface* parentSession() const = 0;
67 virtual SessionModel* childSessions() const = 0;
62 virtual State state() const = 0;68 virtual State state() const = 0;
63 virtual bool fullscreen() const = 0;69 virtual bool fullscreen() const = 0;
64 virtual bool live() const = 0;70 virtual bool live() const = 0;
6571
72 virtual std::shared_ptr<mir::scene::Session> session() const = 0;
73
74 // For MirSurfaceItem and MirSurfaceManager use
75
76 virtual void setSurface(MirSurfaceItemInterface* surface) = 0;
77
78 // For Application use
79
66 virtual void setApplication(unity::shell::application::ApplicationInfoInterface* item) = 0;80 virtual void setApplication(unity::shell::application::ApplicationInfoInterface* item) = 0;
67 virtual void setSurface(MirSurfaceItem* surface) = 0;81 virtual void suspend() = 0;
68 virtual void setState(State state) = 0;82 virtual void resume() = 0;
83 virtual void stop() = 0;
84
85 // For SessionManager use
6986
70 virtual void addChildSession(SessionInterface* session) = 0;87 virtual void addChildSession(SessionInterface* session) = 0;
71 virtual void insertChildSession(uint index, SessionInterface* session) = 0;88 virtual void insertChildSession(uint index, SessionInterface* session) = 0;
72 virtual void removeChildSession(SessionInterface* session) = 0;89 virtual void removeChildSession(SessionInterface* session) = 0;
73 virtual void foreachChildSession(std::function<void(SessionInterface* session)> f) const = 0;90 virtual void foreachChildSession(std::function<void(SessionInterface* session)> f) const = 0;
7491
75 virtual std::shared_ptr<mir::scene::Session> session() const = 0;
76
77 virtual std::shared_ptr<mir::scene::PromptSession> activePromptSession() const = 0;92 virtual std::shared_ptr<mir::scene::PromptSession> activePromptSession() const = 0;
78 virtual void foreachPromptSession(std::function<void(const std::shared_ptr<mir::scene::PromptSession>&)> f) const = 0;93 virtual void foreachPromptSession(std::function<void(const std::shared_ptr<mir::scene::PromptSession>&)> f) const = 0;
7994
80 virtual SessionModel* childSessions() const = 0;95 virtual void setFullscreen(bool fullscreen) = 0;
96 virtual void setLive(const bool) = 0;
97 virtual void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
98 virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
8199
82Q_SIGNALS:100Q_SIGNALS:
83 void surfaceChanged(MirSurfaceItem*);101 void surfaceChanged(MirSurfaceItemInterface*);
84 void parentSessionChanged(SessionInterface*);102 void parentSessionChanged(SessionInterface*);
85 void applicationChanged(unity::shell::application::ApplicationInfoInterface* application);103 void applicationChanged(unity::shell::application::ApplicationInfoInterface* application);
86 void aboutToBeDestroyed();104 void aboutToBeDestroyed();
87 void stateChanged(State state);105 void stateChanged(State state);
88 void fullscreenChanged(bool fullscreen);106 void fullscreenChanged(bool fullscreen);
89 void liveChanged(bool live);107 void liveChanged(bool live);
90
91 void suspended();
92 void resumed();
93
94protected:
95 virtual void setFullscreen(bool fullscreen) = 0;
96 virtual void setLive(const bool) = 0;
97 virtual void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
98 virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
99
100 friend class SessionManager;
101};108};
102109
103} // namespace qtmir110} // namespace qtmir
104111
112Q_DECLARE_METATYPE(qtmir::SessionInterface*)
105113
106#endif // SESSION_INTERFACE_H114#endif // SESSION_INTERFACE_H
107115
=== modified file 'src/modules/Unity/Application/sessionmanager.cpp'
--- src/modules/Unity/Application/sessionmanager.cpp 2015-01-28 14:25:36 +0000
+++ src/modules/Unity/Application/sessionmanager.cpp 2015-07-13 21:57:32 +0000
@@ -63,7 +63,7 @@
63 manager, &SessionManager::onPromptProviderRemoved);63 manager, &SessionManager::onPromptProviderRemoved);
64}64}
6565
66SessionManager* SessionManager::singleton()66SessionManager* SessionManager::singleton(QJSEngine *jsEngine)
67{67{
68 if (!the_session_manager) {68 if (!the_session_manager) {
6969
@@ -78,7 +78,7 @@
78 SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));78 SessionListener *sessionListener = static_cast<SessionListener*>(nativeInterface->nativeResourceForIntegration("SessionListener"));
79 PromptSessionListener *promptSessionListener = static_cast<PromptSessionListener*>(nativeInterface->nativeResourceForIntegration("PromptSessionListener"));79 PromptSessionListener *promptSessionListener = static_cast<PromptSessionListener*>(nativeInterface->nativeResourceForIntegration("PromptSessionListener"));
8080
81 the_session_manager = new SessionManager(nativeInterface->m_mirServer, ApplicationManager::singleton());81 the_session_manager = new SessionManager(nativeInterface->m_mirServer, ApplicationManager::singleton(jsEngine));
8282
83 connectToSessionListener(the_session_manager, sessionListener);83 connectToSessionListener(the_session_manager, sessionListener);
84 connectToPromptSessionListener(the_session_manager, promptSessionListener);84 connectToPromptSessionListener(the_session_manager, promptSessionListener);
8585
=== modified file 'src/modules/Unity/Application/sessionmanager.h'
--- src/modules/Unity/Application/sessionmanager.h 2014-12-01 11:05:01 +0000
+++ src/modules/Unity/Application/sessionmanager.h 2015-07-13 21:57:32 +0000
@@ -38,6 +38,8 @@
38 }38 }
39}39}
4040
41class QJSEngine;
42
41class MirServer;43class MirServer;
4244
43namespace qtmir {45namespace qtmir {
@@ -57,7 +59,7 @@
57 );59 );
58 ~SessionManager();60 ~SessionManager();
5961
60 static SessionManager* singleton();62 static SessionManager* singleton(QJSEngine *jsEngine);
6163
62 SessionInterface *findSession(const mir::scene::Session* session) const;64 SessionInterface *findSession(const mir::scene::Session* session) const;
6365
6466
=== modified file 'src/modules/Unity/Application/taskcontroller.cpp'
--- src/modules/Unity/Application/taskcontroller.cpp 2014-10-20 18:48:53 +0000
+++ src/modules/Unity/Application/taskcontroller.cpp 2015-07-13 21:57:32 +0000
@@ -50,14 +50,19 @@
50 &TaskController::processStopped);50 &TaskController::processStopped);
5151
52 connect(m_appController.data(),52 connect(m_appController.data(),
53 &ApplicationController::applicationPaused,
54 this,
55 &TaskController::processSuspended);
56
57 connect(m_appController.data(),
53 &ApplicationController::applicationFocusRequest,58 &ApplicationController::applicationFocusRequest,
54 this,59 this,
55 &TaskController::onApplicationFocusRequest);60 &TaskController::focusRequested);
5661
57 connect(m_appController.data(),62 connect(m_appController.data(),
58 &ApplicationController::applicationResumeRequest,63 &ApplicationController::applicationResumeRequested,
59 this,64 this,
60 &TaskController::onApplicationResumeRequest);65 &TaskController::resumeRequested);
6166
62 connect(m_appController.data(),67 connect(m_appController.data(),
63 &ApplicationController::applicationError,68 &ApplicationController::applicationError,
@@ -108,16 +113,6 @@
108 return m_appController->resumeApplicationWithAppId(appId);113 return m_appController->resumeApplicationWithAppId(appId);
109}114}
110115
111void TaskController::onApplicationFocusRequest(const QString& id)
112{
113 Q_EMIT requestFocus(id);
114}
115
116void TaskController::onApplicationResumeRequest(const QString& id)
117{
118 Q_EMIT requestResume(id);
119}
120
121void TaskController::onApplicationError(const QString& id, ApplicationController::Error error)116void TaskController::onApplicationError(const QString& id, ApplicationController::Error error)
122{117{
123 Q_EMIT processFailed(id, (error == ApplicationController::Error::APPLICATION_FAILED_TO_START) );118 Q_EMIT processFailed(id, (error == ApplicationController::Error::APPLICATION_FAILED_TO_START) );
124119
=== modified file 'src/modules/Unity/Application/taskcontroller.h'
--- src/modules/Unity/Application/taskcontroller.h 2014-10-07 03:21:30 +0000
+++ src/modules/Unity/Application/taskcontroller.h 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2014 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -48,14 +48,12 @@
48Q_SIGNALS:48Q_SIGNALS:
49 void processStarting(const QString &appId);49 void processStarting(const QString &appId);
50 void processStopped(const QString &appId);50 void processStopped(const QString &appId);
51 void processSuspended(const QString &appId);
51 void processFailed(const QString &appId, const bool duringStartup);52 void processFailed(const QString &appId, const bool duringStartup);
52 void requestFocus(const QString &appId);53 void focusRequested(const QString &appId);
53 void requestResume(const QString &appId);54 void resumeRequested(const QString &appId);
5455
55private Q_SLOTS:56private Q_SLOTS:
56 void onApplicationFocusRequest(const QString &id);
57 void onApplicationResumeRequest(const QString &id);
58
59 void onApplicationError(const QString &id, ApplicationController::Error error);57 void onApplicationError(const QString &id, ApplicationController::Error error);
6058
61private:59private:
6260
=== modified file 'src/modules/Unity/Application/upstart/applicationcontroller.cpp'
--- src/modules/Unity/Application/upstart/applicationcontroller.cpp 2014-11-13 15:47:30 +0000
+++ src/modules/Unity/Application/upstart/applicationcontroller.cpp 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014,2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -40,6 +40,7 @@
40 UbuntuAppLaunchAppObserver stopCallback = nullptr;40 UbuntuAppLaunchAppObserver stopCallback = nullptr;
41 UbuntuAppLaunchAppObserver focusCallback = nullptr;41 UbuntuAppLaunchAppObserver focusCallback = nullptr;
42 UbuntuAppLaunchAppObserver resumeCallback = nullptr;42 UbuntuAppLaunchAppObserver resumeCallback = nullptr;
43 UbuntuAppLaunchAppPausedResumedObserver pausedCallback = nullptr;
43 UbuntuAppLaunchAppFailedObserver failureCallback = nullptr;44 UbuntuAppLaunchAppFailedObserver failureCallback = nullptr;
44};45};
4546
@@ -125,7 +126,12 @@
125126
126 impl->resumeCallback = [](const gchar * appId, gpointer userData) {127 impl->resumeCallback = [](const gchar * appId, gpointer userData) {
127 auto thiz = static_cast<ApplicationController*>(userData);128 auto thiz = static_cast<ApplicationController*>(userData);
128 Q_EMIT(thiz->applicationResumeRequest(toShortAppIdIfPossible(appId)));129 Q_EMIT(thiz->applicationResumeRequested(toShortAppIdIfPossible(appId)));
130 };
131
132 impl->pausedCallback = [](const gchar * appId, GPid *, gpointer userData) {
133 auto thiz = static_cast<ApplicationController*>(userData);
134 Q_EMIT(thiz->applicationPaused(toShortAppIdIfPossible(appId)));
129 };135 };
130136
131 impl->failureCallback = [](const gchar * appId, UbuntuAppLaunchAppFailed failureType, gpointer userData) {137 impl->failureCallback = [](const gchar * appId, UbuntuAppLaunchAppFailed failureType, gpointer userData) {
@@ -145,6 +151,7 @@
145 ubuntu_app_launch_observer_add_app_stop(impl->stopCallback, this);151 ubuntu_app_launch_observer_add_app_stop(impl->stopCallback, this);
146 ubuntu_app_launch_observer_add_app_focus(impl->focusCallback, this);152 ubuntu_app_launch_observer_add_app_focus(impl->focusCallback, this);
147 ubuntu_app_launch_observer_add_app_resume(impl->resumeCallback, this);153 ubuntu_app_launch_observer_add_app_resume(impl->resumeCallback, this);
154 ubuntu_app_launch_observer_add_app_paused(impl->pausedCallback, this);
148 ubuntu_app_launch_observer_add_app_failed(impl->failureCallback, this);155 ubuntu_app_launch_observer_add_app_failed(impl->failureCallback, this);
149}156}
150157
@@ -155,6 +162,7 @@
155 ubuntu_app_launch_observer_delete_app_stop(impl->stopCallback, this);162 ubuntu_app_launch_observer_delete_app_stop(impl->stopCallback, this);
156 ubuntu_app_launch_observer_delete_app_focus(impl->focusCallback, this);163 ubuntu_app_launch_observer_delete_app_focus(impl->focusCallback, this);
157 ubuntu_app_launch_observer_delete_app_resume(impl->resumeCallback, this);164 ubuntu_app_launch_observer_delete_app_resume(impl->resumeCallback, this);
165 ubuntu_app_launch_observer_delete_app_paused(impl->pausedCallback, this);
158 ubuntu_app_launch_observer_delete_app_failed(impl->failureCallback, this);166 ubuntu_app_launch_observer_delete_app_failed(impl->failureCallback, this);
159}167}
160168
161169
=== modified file 'src/platforms/mirserver/CMakeLists.txt'
--- src/platforms/mirserver/CMakeLists.txt 2015-05-19 15:10:48 +0000
+++ src/platforms/mirserver/CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -31,6 +31,8 @@
31 ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS}31 ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS}
32 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}32 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
33 ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS}33 ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS}
34
35 ${APPLICATION_API_INCLUDE_DIRS}
34)36)
3537
36# We have to remove -pedantic for tracepoints.c38# We have to remove -pedantic for tracepoints.c
3739
=== modified file 'src/platforms/mirserver/mirserver.cpp'
--- src/platforms/mirserver/mirserver.cpp 2015-02-09 16:28:40 +0000
+++ src/platforms/mirserver/mirserver.cpp 2015-07-13 21:57:32 +0000
@@ -92,8 +92,7 @@
92 the_input_targeter(),92 the_input_targeter(),
93 the_surface_coordinator(),93 the_surface_coordinator(),
94 the_session_coordinator(),94 the_session_coordinator(),
95 the_prompt_session_manager(),95 the_prompt_session_manager());
96 the_shell_display_layout());
9796
98 m_shell = shell;97 m_shell = shell;
99 return shell;98 return shell;
10099
=== modified file 'src/platforms/mirserver/mirshell.cpp'
--- src/platforms/mirserver/mirshell.cpp 2015-05-13 09:40:03 +0000
+++ src/platforms/mirserver/mirshell.cpp 2015-07-13 21:57:32 +0000
@@ -71,37 +71,42 @@
71 const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter,71 const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter,
72 const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator,72 const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator,
73 const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator,73 const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator,
74 const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager,74 const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager) :
75 const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout) :
76 AbstractShell(inputTargeter, surfaceCoordinator, sessionCoordinator, promptSessionManager,75 AbstractShell(inputTargeter, surfaceCoordinator, sessionCoordinator, promptSessionManager,
77 [](mir::shell::FocusController*) { return std::make_shared<NullWindowManager>(); }),76 [](mir::shell::FocusController*) { return std::make_shared<NullWindowManager>(); })
78 m_displayLayout{displayLayout}
79{77{
80 qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::MirShell";78 qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::MirShell";
81}79}
8280
83mir::frontend::SurfaceId MirShell::create_surface(const std::shared_ptr<ms::Session> &session, const ms::SurfaceCreationParameters &requestParameters)81mir::frontend::SurfaceId MirShell::create_surface(const std::shared_ptr<ms::Session> &session,
82 const ms::SurfaceCreationParameters &requestParameters)
84{83{
84 using namespace mir::geometry;
85 using namespace qtmir;
85 tracepoint(qtmirserver, surfacePlacementStart);86 tracepoint(qtmirserver, surfacePlacementStart);
8687
87 // TODO: Callback unity8 so that it can make a decision on that.88 SurfaceParameters params;
88 // unity8 must bear in mind that the called function will be on a Mir thread though.89 params.geometry.setWidth(requestParameters.size.width.as_int());
89 // The QPA shouldn't be deciding for itself on such things.90 params.geometry.setHeight(requestParameters.size.height.as_int());
9091 if (requestParameters.state.is_set()) {
91 ms::SurfaceCreationParameters placedParameters = requestParameters;92 params.state = static_cast<Globals::SurfaceState>(requestParameters.state.value());
9293 } else {
93 // Just make it fullscreen for now94 params.state = Globals::SurfaceState::Unknown;
94 mir::geometry::Rectangle rect{requestParameters.top_left, requestParameters.size};95 }
95 m_displayLayout->size_to_output(rect);96
96 placedParameters.size = rect.size;97 Q_EMIT sessionAboutToCreateSurface(session, params); // can be connected to via Qt::BlockingQueuedConnection
9798 // to alter surface initial geometry and state
98 qCDebug(QTMIR_MIR_MESSAGES) << "MirShell::create_surface(): size requested ("99
99 << requestParameters.size.width.as_int() << "," << requestParameters.size.height.as_int() << ") and placed ("100 ms::SurfaceCreationParameters placedParameters = requestParameters;
100 << placedParameters.size.width.as_int() << "," << placedParameters.size.height.as_int() << ")";101 placedParameters.size = Size{ Width{params.geometry.width()}, Height{params.geometry.height()} };
101102
102 tracepoint(qtmirserver, surfacePlacementEnd);103 qCDebug(QTMIR_MIR_MESSAGES) << "MirPlacementStrategy: requested ("
103104 << requestParameters.size.width.as_int() << "," << requestParameters.size.height.as_int() << ") and returned ("
104 return AbstractShell::create_surface(session, placedParameters);105 << placedParameters.size.width.as_int() << "," << placedParameters.size.height.as_int() << ")";
106
107 tracepoint(qtmirserver, surfacePlacementEnd);
108
109 return AbstractShell::create_surface(session, placedParameters);
105}110}
106111
107void NullWindowManager::add_session(std::shared_ptr<ms::Session> const& /*session*/)112void NullWindowManager::add_session(std::shared_ptr<ms::Session> const& /*session*/)
108113
=== modified file 'src/platforms/mirserver/mirshell.h'
--- src/platforms/mirserver/mirshell.h 2015-03-11 10:10:49 +0000
+++ src/platforms/mirserver/mirshell.h 2015-07-13 21:57:32 +0000
@@ -17,14 +17,15 @@
17#ifndef QPAMIRSERVER_SHELL_H17#ifndef QPAMIRSERVER_SHELL_H
18#define QPAMIRSERVER_SHELL_H18#define QPAMIRSERVER_SHELL_H
1919
20// Mir
20#include <mir/shell/abstract_shell.h>21#include <mir/shell/abstract_shell.h>
22
23// Qt
21#include <QObject>24#include <QObject>
2225
23namespace mir {26// local
24 namespace shell {27#include "globals.h"
25 class DisplayLayout;28
26 }
27}
2829
29class MirShell : public QObject, public mir::shell::AbstractShell30class MirShell : public QObject, public mir::shell::AbstractShell
30{31{
@@ -35,13 +36,16 @@
35 const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter,36 const std::shared_ptr<mir::shell::InputTargeter> &inputTargeter,
36 const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator,37 const std::shared_ptr<mir::scene::SurfaceCoordinator> &surfaceCoordinator,
37 const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator,38 const std::shared_ptr<mir::scene::SessionCoordinator> &sessionCoordinator,
38 const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager,39 const std::shared_ptr<mir::scene::PromptSessionManager> &promptSessionManager);
39 const std::shared_ptr<mir::shell::DisplayLayout> &displayLayout);40
4041 virtual mir::frontend::SurfaceId create_surface(const std::shared_ptr<mir::scene::Session> &session,
41 virtual mir::frontend::SurfaceId create_surface(const std::shared_ptr<mir::scene::Session>& session, const mir::scene::SurfaceCreationParameters &params);42 const mir::scene::SurfaceCreationParameters &params);
4243
43private:44Q_SIGNALS:
44 std::shared_ptr<mir::shell::DisplayLayout> const m_displayLayout;45 void sessionAboutToCreateSurface(const std::shared_ptr<mir::scene::Session> &session,
46 qtmir::SurfaceParameters &params); // requires Qt::BlockingQueuedConnection!!
47
48 void surfaceAttributeChanged(mir::scene::Surface const*, const MirSurfaceAttrib, const int);
45};49};
4650
47#endif /* QPAMIRSERVER_SHELL_H */51#endif /* QPAMIRSERVER_SHELL_H */
4852
=== modified file 'tests/modules/Application/CMakeLists.txt'
--- tests/modules/Application/CMakeLists.txt 2015-05-21 18:48:59 +0000
+++ tests/modules/Application/CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -1,9 +1,12 @@
1set(1set(
2 APPLICATION_TEST_SOURCES2 APPLICATION_TEST_SOURCES
3 application_test.cpp3 application_test.cpp
4 ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp
4)5)
56
6include_directories(7include_directories(
8 ${APPLICATION_API_INCLUDE_DIRS}
9 ${CMAKE_SOURCE_DIR}/src/common
7 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver10 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
8 ${CMAKE_SOURCE_DIR}/src/modules11 ${CMAKE_SOURCE_DIR}/src/modules
9 ${CMAKE_SOURCE_DIR}/tests/modules/common12 ${CMAKE_SOURCE_DIR}/tests/modules/common
@@ -18,6 +21,8 @@
18 unityapplicationplugin21 unityapplicationplugin
19 qpa-mirserver22 qpa-mirserver
2023
24 Qt5::Test
25
21 ${GTEST_BOTH_LIBRARIES}26 ${GTEST_BOTH_LIBRARIES}
22 ${GMOCK_LIBRARIES}27 ${GMOCK_LIBRARIES}
23)28)
2429
=== modified file 'tests/modules/Application/application_test.cpp'
--- tests/modules/Application/application_test.cpp 2015-03-04 22:13:06 +0000
+++ tests/modules/Application/application_test.cpp 2015-07-13 21:57:32 +0000
@@ -20,6 +20,10 @@
2020
21#include "qtmir_test.h"21#include "qtmir_test.h"
2222
23#include <mock_session.h>
24
25#include <QScopedPointer>
26#include <QSignalSpy>
2327
24using namespace qtmir;28using namespace qtmir;
2529
@@ -30,93 +34,163 @@
30 {}34 {}
31};35};
3236
33TEST_F(ApplicationTests, checkFocusAcquiresWakeLock)37TEST_F(ApplicationTests, acquiresWakelockWhenRunningAndReleasesWhenSuspended)
34{38{
35 using namespace ::testing;39 using namespace ::testing;
3640 QString appId("foo-app");
37 EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);41
3842 auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
39 startApplication(123, "app");43 ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
40 applicationManager.focusApplication("app");44
41}45 QScopedPointer<Application> application(new Application(
4246 QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
43TEST_F(ApplicationTests, checkSuspendReleasesWakeLock)47 desktopFileReader, QStringList(), nullptr));
44{48
45 using namespace ::testing;49 application->setProcessState(Application::ProcessRunning);
4650
47 auto app = startApplication(123, "app");51 NiceMock<MockSession> *session = new NiceMock<MockSession>;
48 auto session = app->session();52
4953 EXPECT_CALL(*session, setApplication(_));
50 applicationManager.focusApplication("app");54 EXPECT_CALL(*session, fullscreen()).WillRepeatedly(Return(false));
5155
52 Q_EMIT session->suspended();56 application->setSession(session);
57
58 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
59
60 session->setState(SessionInterface::Running);
61
62 EXPECT_TRUE(sharedWakelock.enabled());
63
64 ASSERT_EQ(Application::InternalState::Running, application->internalState());
65
66 application->setRequestedState(Application::RequestedSuspended);
67
68 ASSERT_EQ(SessionInterface::Suspending, session->state());
69 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
70
71 session->setState(SessionInterface::Suspended);
72
73 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
74
75 application->setProcessState(Application::ProcessSuspended);
76
77 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
78
53 EXPECT_FALSE(sharedWakelock.enabled());79 EXPECT_FALSE(sharedWakelock.enabled());
54}80}
5581
56TEST_F(ApplicationTests, checkResumeAcquiresWakeLock)82TEST_F(ApplicationTests, checkResumeAcquiresWakeLock)
57{83{
58 using namespace ::testing;84 using namespace ::testing;
5985 QString appId("foo-app");
60 EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);86
6187 auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
62 auto app = startApplication(123, "app");88 ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
63 auto session = app->session();89
6490 QScopedPointer<Application> application(new Application(
65 Q_EMIT session->resumed();91 QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
92 desktopFileReader, QStringList(), nullptr));
93 NiceMock<MockSession> *session = new NiceMock<MockSession>;
94
95 // Get it running and then suspend it
96 application->setProcessState(Application::ProcessRunning);
97 application->setSession(session);
98 session->setState(SessionInterface::Running);
99 application->setRequestedState(Application::RequestedSuspended);
100 session->setState(SessionInterface::Suspended);
101 application->setProcessState(Application::ProcessSuspended);
102 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
103
104 EXPECT_FALSE(sharedWakelock.enabled());
105
106 application->setRequestedState(Application::RequestedRunning);
107
108 ASSERT_EQ(Application::InternalState::Running, application->internalState());
109
110 EXPECT_TRUE(sharedWakelock.enabled());
66}111}
67112
68TEST_F(ApplicationTests, checkRespawnAcquiresWakeLock)113TEST_F(ApplicationTests, checkRespawnAcquiresWakeLock)
69{114{
70 using namespace ::testing;115 using namespace ::testing;
71116 QString appId("foo-app");
72 EXPECT_CALL(sharedWakelock, acquire(_)).Times(1);117
73 const QString appId = "app";118 auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
74119 ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
75 auto app = startApplication(123, "app");120
76121 QScopedPointer<Application> application(new Application(
77 // as respawn fires startApplicationWithAppIdAndArgs again, keep gmock quiet about another call122 QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
78 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))123 desktopFileReader, QStringList(), nullptr));
79 .Times(1)124 NiceMock<MockSession> *session = new NiceMock<MockSession>;
80 .WillRepeatedly(Return(true));125
81126 // Get it running, suspend it, and finally stop it
82 // respawn by setting app state as Stopped, delete the Session associated, then set to Running state127 application->setProcessState(Application::ProcessRunning);
83 app->setState(Session::State::Stopped);128 application->setSession(session);
84 delete app->session();129 session->setState(SessionInterface::Running);
85 app->setState(Session::State::Running);130 application->setRequestedState(Application::RequestedSuspended);
86}131 session->setState(SessionInterface::Suspended);
87132 application->setProcessState(Application::ProcessSuspended);
88TEST_F(ApplicationTests, checkDashFocusDoesNotAcquireWakeLock)133 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
89{134 session->setState(SessionInterface::Stopped);
90 using namespace ::testing;135 application->setProcessState(Application::ProcessStopped);
91136 ASSERT_EQ(Application::InternalState::StoppedUnexpectedly, application->internalState());
92 EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);137
93
94 startApplication(123, "unity8-dash");
95 applicationManager.focusApplication("unity8-dash");
96}
97
98TEST_F(ApplicationTests, checkDashSuspendDoesNotImpactWakeLock)
99{
100 using namespace ::testing;
101
102 auto app = startApplication(123, "unity8-dash");
103 auto session = app->session();
104
105 applicationManager.focusApplication("unity8-dash");
106
107 Q_EMIT session->suspended();
108 EXPECT_FALSE(sharedWakelock.enabled());138 EXPECT_FALSE(sharedWakelock.enabled());
139
140 QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested()));
141 application->setRequestedState(Application::RequestedRunning);
142 ASSERT_EQ(1, spyStartProcess.count());
143 application->setProcessState(Application::ProcessRunning);
144
145 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
146
147 EXPECT_TRUE(sharedWakelock.enabled());
109}148}
110149
111TEST_F(ApplicationTests, checkDashResumeDoesNotAcquireWakeLock)150TEST_F(ApplicationTests, checkDashDoesNotImpactWakeLock)
112{151{
113 using namespace ::testing;152 using namespace ::testing;
153 QString appId("unity8-dash");
114154
115 EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);155 EXPECT_CALL(sharedWakelock, acquire(_)).Times(0);
116156 EXPECT_CALL(sharedWakelock, release(_)).Times(0);
117 auto app = startApplication(123, "unity8-dash");157
118 auto session = app->session();158 auto desktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
119159 ON_CALL(*desktopFileReader, loaded()).WillByDefault(Return(true));
120 Q_EMIT session->resumed();160
161 QScopedPointer<Application> application(new Application(
162 QSharedPointer<MockSharedWakelock>(&sharedWakelock, [](MockSharedWakelock *){}),
163 desktopFileReader, QStringList(), nullptr));
164
165 application->setProcessState(Application::ProcessRunning);
166
167 NiceMock<MockSession> *session = new NiceMock<MockSession>;
168
169 EXPECT_CALL(*session, setApplication(_));
170 EXPECT_CALL(*session, fullscreen()).WillRepeatedly(Return(false));
171
172 application->setSession(session);
173
174 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
175
176 session->setState(SessionInterface::Running);
177
178 ASSERT_EQ(Application::InternalState::Running, application->internalState());
179
180 application->setRequestedState(Application::RequestedSuspended);
181
182 ASSERT_EQ(SessionInterface::Suspending, session->state());
183 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
184
185 session->setState(SessionInterface::Suspended);
186
187 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
188
189 application->setProcessState(Application::ProcessSuspended);
190
191 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
192
193 application->setRequestedState(Application::RequestedRunning);
194
195 ASSERT_EQ(Application::InternalState::Running, application->internalState());
121}196}
122
123197
=== modified file 'tests/modules/ApplicationManager/CMakeLists.txt'
--- tests/modules/ApplicationManager/CMakeLists.txt 2015-05-21 18:48:59 +0000
+++ tests/modules/ApplicationManager/CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -2,9 +2,13 @@
2 APPLICATION_MANAGER_TEST_SOURCES2 APPLICATION_MANAGER_TEST_SOURCES
3 application_manager_test.cpp3 application_manager_test.cpp
4 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp4 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
5 ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp
6 ../common/fake_mirsurfaceitem.h
5)7)
68
7include_directories(9include_directories(
10 ${APPLICATION_API_INCLUDE_DIRS}
11 ${CMAKE_SOURCE_DIR}/src/common
8 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver12 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
9 ${CMAKE_SOURCE_DIR}/src/modules13 ${CMAKE_SOURCE_DIR}/src/modules
10 ${CMAKE_SOURCE_DIR}/tests/modules/common14 ${CMAKE_SOURCE_DIR}/tests/modules/common
@@ -15,7 +19,7 @@
1519
16target_link_libraries(20target_link_libraries(
17 applicationmanager_test21 applicationmanager_test
18 22
19 qpa-mirserver23 qpa-mirserver
20 unityapplicationplugin24 unityapplicationplugin
2125
2226
=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
--- tests/modules/ApplicationManager/application_manager_test.cpp 2015-05-13 17:18:45 +0000
+++ tests/modules/ApplicationManager/application_manager_test.cpp 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013 Canonical, Ltd.2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -23,8 +23,9 @@
2323
24#include <Unity/Application/applicationscreenshotprovider.h>24#include <Unity/Application/applicationscreenshotprovider.h>
2525
26 #include "mock_surface.h"26#include "fake_mirsurfaceitem.h"
27 #include "qtmir_test.h"27#include "mock_surface.h"
28#include "qtmir_test.h"
2829
29using namespace qtmir;30using namespace qtmir;
30using mir::scene::MockSession;31using mir::scene::MockSession;
@@ -45,100 +46,41 @@
45 applicationManager.onSessionStopping(session);46 applicationManager.onSessionStopping(session);
46 sessionManager.onSessionStopping(session);47 sessionManager.onSessionStopping(session);
47 }48 }
49 inline void onSessionCreatedSurface(const mir::scene::Session *mirSession,
50 MirSurfaceItemInterface *qmlSurface) {
51
52 SessionInterface* qmlSession = sessionManager.findSession(mirSession);
53 if (qmlSession) {
54 qmlSession->setSurface(qmlSurface);
55 }
56
57 // I assume that applicationManager ignores the mirSurface parameter, so sending
58 // a null shared pointer must suffice
59 std::shared_ptr<mir::scene::Surface> mirSurface(nullptr);
60 applicationManager.onSessionCreatedSurface(mirSession, mirSurface);
61 }
62
63 inline void suspend(Application *application) {
64 application->setRequestedState(Application::RequestedSuspended);
65 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
66 static_cast<qtmir::Session*>(application->session())->doSuspend();
67 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
68 applicationManager.onProcessSuspended(application->appId());
69 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
70 }
48};71};
4972
50TEST_F(ApplicationManagerTests, SuspendingAndResumingARunningApplicationResultsInOomScoreAdjustment)
51{
52 using namespace ::testing;
53
54 const QString appId("com.canonical.does.not.exist");
55
56 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(_, _)).Times(1);
57 EXPECT_CALL(appController, findDesktopFileForAppId(appId)).Times(1);
58
59 EXPECT_CALL(desktopFileReaderFactory, createInstance(_, _)).Times(1);
60
61 auto application = applicationManager.startApplication(
62 appId,
63 ApplicationManager::NoFlag,
64 QStringList());
65
66 // FIXME - this is doesn't really excerise the actualt behaviour since suspend/resume should be
67 // controlled by state changes. Requires using suspend timer.
68 QMetaObject::invokeMethod(application, "onSessionSuspended");
69 QMetaObject::invokeMethod(application, "onSessionResumed");
70}
71
72TEST_F(ApplicationManagerTests, SuspendingAndResumingDashResultsInOomScoreAdjustment)
73{
74 using namespace ::testing;
75
76 quint64 procId = 5921;
77 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
78 QByteArray cmdLine( "/usr/bin/app1 --desktop_file_hint=unity8-dash");
79
80 EXPECT_CALL(procInfo,command_line(procId))
81 .Times(1)
82 .WillOnce(Return(cmdLine));
83
84 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
85
86 bool authed = true;
87
88 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("Oo", procId);
89 applicationManager.authorizeSession(procId, authed);
90 onSessionStarting(session);
91
92 auto application = applicationManager.findApplication("unity8-dash");
93 applicationManager.onSessionCreatedSurface(session.get(), aSurface);
94
95 // FIXME - this is doesn't really excerise the actualt behaviour since suspend/resume should be
96 // controlled by state changes. Requires using suspend timer.
97 QMetaObject::invokeMethod(application, "onSessionSuspended");
98 QMetaObject::invokeMethod(application, "onSessionResumed");
99}
100
101// Currently disabled as we need to make sure that we have a corresponding mir session, too.
102TEST_F(ApplicationManagerTests, DISABLED_FocusingRunningApplicationResultsInOomScoreAdjustment)
103{
104 using namespace ::testing;
105
106 const QString appId("com.canonical.does.not.exist");
107
108 QSet<QString> appIds;
109
110 for (unsigned int i = 0; i < 50; i++)
111 {
112 QString appIdFormat("%1.does.not.exist");
113 auto appId = appIdFormat.arg(i);
114
115 auto application = applicationManager.startApplication(
116 appId,
117 ApplicationManager::NoFlag,
118 QStringList());
119
120 std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(appIdFormat.toStdString(), i);
121 onSessionStarting( mirSession );
122
123 EXPECT_NE(nullptr, application);
124 }
125
126 for (auto appId : appIds)
127 {
128 applicationManager.focusApplication(appId);
129 }
130}
131
132TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_authorize_and_gets_mixed_up_with_first_one)73TEST_F(ApplicationManagerTests,bug_case_1240400_second_dialer_app_fails_to_authorize_and_gets_mixed_up_with_first_one)
133{74{
134 using namespace ::testing;75 using namespace ::testing;
135 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
136 quint64 firstProcId = 5921;76 quint64 firstProcId = 5921;
137 quint64 secondProcId = 5922;77 quint64 secondProcId = 5922;
138 const char dialer_app_id[] = "dialer-app";78 const char dialer_app_id[] = "dialer-app";
139 QByteArray cmdLine( "/usr/bin/dialer-app --desktop_file_hint=dialer-app");79 QByteArray cmdLine( "/usr/bin/dialer-app --desktop_file_hint=dialer-app");
140 QByteArray secondcmdLine( "/usr/bin/dialer-app");80 QByteArray secondcmdLine( "/usr/bin/dialer-app");
14181
82 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
83
142 EXPECT_CALL(procInfo,command_line(firstProcId))84 EXPECT_CALL(procInfo,command_line(firstProcId))
143 .Times(1)85 .Times(1)
144 .WillOnce(Return(cmdLine));86 .WillOnce(Return(cmdLine));
@@ -150,18 +92,20 @@
15092
151 std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(dialer_app_id, firstProcId);93 std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(dialer_app_id, firstProcId);
152 applicationManager.authorizeSession(firstProcId, authed);94 applicationManager.authorizeSession(firstProcId, authed);
153 EXPECT_EQ(true, authed);95 ASSERT_EQ(true, authed);
154 onSessionStarting(mirSession);96 onSessionStarting(mirSession);
155 applicationManager.onSessionCreatedSurface(mirSession.get(),aSurface);97 onSessionCreatedSurface(mirSession.get(), surface);
156 Application * app = applicationManager.findApplication(dialer_app_id);98 surface->drawFirstFrame();
157 EXPECT_NE(nullptr,app);99 Application * application = applicationManager.findApplication(dialer_app_id);
100 ASSERT_NE(nullptr,application);
101 ASSERT_EQ(Application::InternalState::Running, application->internalState());
158102
159 // now a second session without desktop file is launched:103 // now a second session without desktop file is launched:
160 applicationManager.authorizeSession(secondProcId, authed);104 applicationManager.authorizeSession(secondProcId, authed);
161 applicationManager.onProcessStarting(dialer_app_id);105 applicationManager.onProcessStarting(dialer_app_id);
162106
163 EXPECT_FALSE(authed);107 EXPECT_FALSE(authed);
164 EXPECT_EQ(app,applicationManager.findApplication(dialer_app_id));108 EXPECT_EQ(application, applicationManager.findApplication(dialer_app_id));
165}109}
166110
167TEST_F(ApplicationManagerTests,application_dies_while_starting)111TEST_F(ApplicationManagerTests,application_dies_while_starting)
@@ -182,6 +126,7 @@
182 onSessionStarting(mirSession);126 onSessionStarting(mirSession);
183 Application * beforeFailure = applicationManager.findApplication(app_id);127 Application * beforeFailure = applicationManager.findApplication(app_id);
184 applicationManager.onProcessStarting(app_id);128 applicationManager.onProcessStarting(app_id);
129 onSessionStopping(mirSession);
185 applicationManager.onProcessFailed(app_id, true);130 applicationManager.onProcessFailed(app_id, true);
186 Application * afterFailure = applicationManager.findApplication(app_id);131 Application * afterFailure = applicationManager.findApplication(app_id);
187132
@@ -190,34 +135,6 @@
190 EXPECT_EQ(nullptr, afterFailure);135 EXPECT_EQ(nullptr, afterFailure);
191}136}
192137
193TEST_F(ApplicationManagerTests,application_start_failure_after_starting)
194{
195 using namespace ::testing;
196 quint64 procId = 5921;
197 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
198 const char app_id[] = "my-app";
199 QByteArray cmdLine( "/usr/bin/my-app --desktop_file_hint=my-app");
200
201 EXPECT_CALL(procInfo,command_line(procId))
202 .Times(1)
203 .WillOnce(Return(cmdLine));
204
205 bool authed = true;
206
207 std::shared_ptr<mir::scene::Session> mirSession = std::make_shared<MockSession>(app_id, procId);
208 applicationManager.authorizeSession(procId, authed);
209 onSessionStarting(mirSession);
210 Application * beforeFailure = applicationManager.findApplication(app_id);
211 applicationManager.onSessionCreatedSurface(mirSession.get(), aSurface);
212 applicationManager.onProcessStarting(app_id);
213 applicationManager.onProcessFailed(app_id, false);
214 Application * afterFailure = applicationManager.findApplication(app_id);
215
216 EXPECT_EQ(true, authed);
217 EXPECT_NE(nullptr, beforeFailure);
218 EXPECT_EQ(beforeFailure, afterFailure);
219}
220
221TEST_F(ApplicationManagerTests,startApplicationSupportsShortAppId)138TEST_F(ApplicationManagerTests,startApplicationSupportsShortAppId)
222{139{
223 using namespace ::testing;140 using namespace ::testing;
@@ -400,7 +317,7 @@
400 quint64 a_procId = 5921;317 quint64 a_procId = 5921;
401 const char an_app_id[] = "some_app";318 const char an_app_id[] = "some_app";
402 QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");319 QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");
403 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);320 FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
404321
405 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));322 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
406323
@@ -413,8 +330,8 @@
413 applicationManager.authorizeSession(a_procId, authed);330 applicationManager.authorizeSession(a_procId, authed);
414331
415 onSessionStarting(first_session);332 onSessionStarting(first_session);
416 applicationManager.focusApplication(an_app_id);333 onSessionCreatedSurface(first_session.get(), aSurface);
417 applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);334 aSurface->drawFirstFrame();
418 onSessionStarting(second_session);335 onSessionStarting(second_session);
419336
420 Application * the_app = applicationManager.findApplication(an_app_id);337 Application * the_app = applicationManager.findApplication(an_app_id);
@@ -430,211 +347,62 @@
430 quint64 a_procId = 5921;347 quint64 a_procId = 5921;
431 const char an_app_id[] = "some_app";348 const char an_app_id[] = "some_app";
432 QByteArray a_cmd("/usr/bin/app1 --desktop_file_hint=some_app");349 QByteArray a_cmd("/usr/bin/app1 --desktop_file_hint=some_app");
433 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);350 FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
434351
435 ON_CALL(procInfo, command_line(_)).WillByDefault(Return(a_cmd));352 ON_CALL(procInfo, command_line(_)).WillByDefault(Return(a_cmd));
436 ON_CALL(appController, appIdHasProcessId(_,_)).WillByDefault(Return(false));353 ON_CALL(appController, appIdHasProcessId(_,_)).WillByDefault(Return(false));
437 354
438 bool authed = true;355 bool authed = true;
439356
440 std::shared_ptr<mir::scene::Session> a_session = std::make_shared<MockSession>("Oo", a_procId);357 std::shared_ptr<mir::scene::Session> a_session = std::make_shared<MockSession>("Oo", a_procId);
441 358
442 applicationManager.authorizeSession(a_procId, authed);359 applicationManager.authorizeSession(a_procId, authed);
443 onSessionStarting(a_session);360 onSessionStarting(a_session);
444 applicationManager.onSessionCreatedSurface(a_session.get(), aSurface);361 onSessionCreatedSurface(a_session.get(), aSurface);
445362 aSurface->drawFirstFrame();
446 Application * the_app = applicationManager.findApplication(an_app_id);363
447 applicationManager.focusApplication(an_app_id);364 Application * the_app = applicationManager.findApplication(an_app_id);
448365 applicationManager.focusApplication(an_app_id);
449 EXPECT_EQ(Application::Running, the_app->state());366
450 EXPECT_EQ(true, the_app->focused());367 EXPECT_EQ(Application::Running, the_app->state());
451368 EXPECT_EQ(true, the_app->focused());
452 applicationManager.focusApplication(an_app_id);369
453 EXPECT_EQ(true, the_app->focused());370 applicationManager.focusApplication(an_app_id);
454}371 EXPECT_EQ(true, the_app->focused());
455372}
456TEST_F(ApplicationManagerTests,suspended_suspends_focused_app_and_marks_it_unfocused_in_the_model)373
457{374TEST_F(ApplicationManagerTests,starting_app_is_suspended_when_it_gets_ready_if_requested)
458 using namespace ::testing;375{
459 quint64 a_procId = 5921;376 using namespace ::testing;
460 const char an_app_id[] = "some_app";377 quint64 procId = 5921;
461 QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");378 FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
462 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);379 QByteArray cmdLine( "/usr/bin/app --desktop_file_hint=app");
463380
464 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));381 EXPECT_CALL(procInfo,command_line(procId))
465382 .Times(1)
466 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));383 .WillOnce(Return(cmdLine));
467384
468 bool authed = true;385 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
469386
470 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);387 bool authed = true;
471 std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId);388
472 applicationManager.authorizeSession(a_procId, authed);389 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("Oo", procId);
473390 applicationManager.authorizeSession(procId, authed);
474 onSessionStarting(first_session);391 onSessionStarting(session);
475 applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);392
476 onSessionStarting(second_session);393 Application * app = applicationManager.findApplication("app");
477394 app->setRequestedState(Application::RequestedSuspended);
478 Application * the_app = applicationManager.findApplication(an_app_id);
479 applicationManager.focusApplication(an_app_id);
480
481 EXPECT_EQ(Application::Running, the_app->state());
482
483 applicationManager.setSuspended(true);
484
485 EXPECT_EQ(Application::Suspended, the_app->state());
486 EXPECT_FALSE(the_app->focused());
487
488 applicationManager.setSuspended(false);
489
490 EXPECT_EQ(Application::Running, the_app->state());
491 EXPECT_EQ(true, the_app->focused());
492}
493
494TEST_F(ApplicationManagerTests,suspended_suspends_starting_app_when_it_gets_ready)
495{
496 using namespace ::testing;
497 quint64 a_procId = 5921;
498 const char an_app_id[] = "some_app";
499 QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");
500 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
501
502 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
503
504 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
505
506 bool authed = true;
507
508 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);
509 applicationManager.authorizeSession(a_procId, authed);
510
511 onSessionStarting(first_session);
512
513 Application * the_app = applicationManager.findApplication(an_app_id);
514 applicationManager.focusApplication(an_app_id);
515 EXPECT_EQ(Application::Starting, the_app->state());
516
517 applicationManager.setSuspended(true);
518
519 // Not suspending yet, as it's still starting
520 EXPECT_EQ(Application::Starting, the_app->state());
521
522 // This signals the app is ready now
523 applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
524
525 // And given that the AppManager is suspended now, this should go to suspended too
526 EXPECT_EQ(Application::Suspended, the_app->state());
527 EXPECT_FALSE(the_app->focused());
528
529 applicationManager.setSuspended(false);
530
531 EXPECT_EQ(Application::Running, the_app->state());
532 EXPECT_EQ(true, the_app->focused());
533}
534
535TEST_F(ApplicationManagerTests,focus_change_suspends_starting_app_when_it_gets_ready)
536{
537 using namespace ::testing;
538 quint64 first_procId = 5921;
539 quint64 second_procId = 5922;
540 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
541 QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=app1");
542 QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2");
543
544 EXPECT_CALL(procInfo,command_line(first_procId))
545 .Times(1)
546 .WillOnce(Return(first_cmdLine));
547
548 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
549
550 EXPECT_CALL(procInfo,command_line(second_procId))
551 .Times(1)
552 .WillOnce(Return(second_cmdLine));
553
554 bool authed = true;
555
556 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", first_procId);
557 std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", second_procId);
558 applicationManager.authorizeSession(first_procId, authed);
559 applicationManager.authorizeSession(second_procId, authed);
560 onSessionStarting(first_session);
561
562 Application * app1 = applicationManager.findApplication("app1");
563 applicationManager.focusApplication("app1");
564395
565 // First app starting...396 // First app starting...
566 EXPECT_EQ(Application::Starting, app1->state());397 EXPECT_EQ(Application::Starting, app->state());
567398
568 onSessionStarting(second_session);399 // Signal app is ready now
569 Application * app2 = applicationManager.findApplication("app2");400 applicationManager.onProcessStarting("app");
570 applicationManager.focusApplication("app2");401 onSessionCreatedSurface(session.get(), aSurface);
571402 aSurface->drawFirstFrame();
572 // Second app starting...403
573 EXPECT_EQ(Application::Starting, app2->state());404 // now that its ready, suspend process should have begun
574405 EXPECT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());
575 // Make sure first one is still in starting state
576 EXPECT_EQ(Application::Starting, app1->state());
577
578 // Signal app1 is ready now
579 applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
580
581 // Make sure AppMan suspended it now that its ready
582 EXPECT_EQ(Application::Suspended, app1->state());
583}
584
585TEST_F(ApplicationManagerTests,forceDashActive_activates_dash_while_not_focused)
586{
587 using namespace ::testing;
588 quint64 first_procId = 5921;
589 quint64 second_procId = 5922;
590 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
591 QByteArray first_cmdLine( "/usr/bin/app1 --desktop_file_hint=unity8-dash");
592 QByteArray second_cmdLine( "/usr/bin/app2--desktop_file_hint=app2");
593
594 EXPECT_CALL(procInfo,command_line(first_procId))
595 .Times(1)
596 .WillOnce(Return(first_cmdLine));
597
598 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
599
600 EXPECT_CALL(procInfo,command_line(second_procId))
601 .Times(1)
602 .WillOnce(Return(second_cmdLine));
603
604 bool authed = true;
605
606 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", first_procId);
607 std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", second_procId);
608 applicationManager.authorizeSession(first_procId, authed);
609 applicationManager.authorizeSession(second_procId, authed);
610 onSessionStarting(first_session);
611
612 Application * dashApp = applicationManager.findApplication("unity8-dash");
613 applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);
614 applicationManager.focusApplication("unity8-dash");
615
616 // Dash app should be ready now...
617 EXPECT_EQ(Application::Running, dashApp->state());
618
619 // Launch second app
620 onSessionStarting(second_session);
621 applicationManager.onSessionCreatedSurface(second_session.get(), aSurface);
622 applicationManager.focusApplication("app2");
623 EXPECT_EQ(applicationManager.focusedApplicationId(), "app2");
624
625 // Make sure the dash is suspended
626 EXPECT_EQ(dashApp->state(), Application::Suspended);
627
628 // Now set the dashactive flag
629 applicationManager.setForceDashActive(true);
630
631 // And make sure the dash is woken up but not focused
632 EXPECT_EQ(applicationManager.focusedApplicationId(), "app2");
633 EXPECT_EQ(dashApp->state(), Application::Running);
634
635 // Unset the dashactive flag
636 applicationManager.setForceDashActive(false);
637 EXPECT_EQ(dashApp->state(), Application::Suspended);
638}406}
639407
640TEST_F(ApplicationManagerTests,requestFocusApplication)408TEST_F(ApplicationManagerTests,requestFocusApplication)
@@ -713,20 +481,20 @@
713 Application *theApp = applicationManager.startApplication(appId, ApplicationManager::NoFlag);481 Application *theApp = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
714482
715 // check application data483 // check application data
716 EXPECT_EQ(theApp->state(), Application::Starting);484 EXPECT_EQ(Application::Starting, theApp->state());
717 EXPECT_EQ(theApp->appId(), appId);485 EXPECT_EQ(appId, theApp->appId());
718 EXPECT_EQ(theApp->name(), name);486 EXPECT_EQ(name, theApp->name());
719 EXPECT_EQ(theApp->canBeResumed(), true);487 EXPECT_FALSE(theApp->canBeResumed());
720488
721 // check signals were emitted489 // check signals were emitted
722 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)490 EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
723 EXPECT_EQ(applicationManager.count(), 1);491 EXPECT_EQ(1, applicationManager.count());
724 EXPECT_EQ(addedSpy.count(), 1);492 EXPECT_EQ(1, addedSpy.count());
725 EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId);493 EXPECT_EQ(appId, addedSpy.takeFirst().at(0).toString());
726494
727 // check application in list of apps495 // check application in list of apps
728 Application *theAppAgain = applicationManager.findApplication(appId);496 Application *theAppAgain = applicationManager.findApplication(appId);
729 EXPECT_EQ(theAppAgain, theApp);497 EXPECT_EQ(theApp, theAppAgain);
730}498}
731499
732/*500/*
@@ -757,18 +525,18 @@
757 Application *theApp = applicationManager.findApplication(appId);525 Application *theApp = applicationManager.findApplication(appId);
758526
759 // check application data527 // check application data
760 EXPECT_EQ(theApp->state(), Application::Starting);528 EXPECT_EQ(Application::Starting, theApp->state());
761 EXPECT_EQ(theApp->appId(), appId);529 EXPECT_EQ(appId, theApp->appId());
762 EXPECT_EQ(theApp->name(), name);530 EXPECT_EQ(name, theApp->name());
763 EXPECT_EQ(theApp->canBeResumed(), true);531 EXPECT_EQ(true, theApp->canBeResumed());
764532
765 // check signals were emitted533 // check signals were emitted
766 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)534 EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
767 EXPECT_EQ(applicationManager.count(), 1);535 EXPECT_EQ(1, applicationManager.count());
768 EXPECT_EQ(addedSpy.count(), 1);536 EXPECT_EQ(1, addedSpy.count());
769 EXPECT_EQ(addedSpy.takeFirst().at(0).toString(), appId);537 EXPECT_EQ(appId, addedSpy.takeFirst().at(0).toString());
770 EXPECT_EQ(focusSpy.count(), 1);538 EXPECT_EQ(1, focusSpy.count());
771 EXPECT_EQ(focusSpy.takeFirst().at(0).toString(), appId);539 EXPECT_EQ(appId, focusSpy.takeFirst().at(0).toString());
772}540}
773541
774/*542/*
@@ -1067,7 +835,6 @@
1067835
1068 applicationManager.startApplication(appId, ApplicationManager::NoFlag);836 applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1069 applicationManager.onProcessStarting(appId);837 applicationManager.onProcessStarting(appId);
1070 applicationManager.focusApplication(appId);
1071838
1072 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);839 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1073840
@@ -1075,9 +842,10 @@
1075 applicationManager.authorizeSession(procId, authed);842 applicationManager.authorizeSession(procId, authed);
1076 onSessionStarting(session);843 onSessionStarting(session);
1077844
1078 std::shared_ptr<mir::scene::Surface> surface(nullptr);845 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1079846
1080 applicationManager.onSessionCreatedSurface(session.get(), surface);847 onSessionCreatedSurface(session.get(), surface);
848 surface->drawFirstFrame();
1081849
1082 // Check application state is correctly set850 // Check application state is correctly set
1083 Application *theApp = applicationManager.findApplication(appId);851 Application *theApp = applicationManager.findApplication(appId);
@@ -1124,7 +892,7 @@
1124}892}
1125893
1126/*894/*
1127 * Test that the foreground application is stopped correctly (is in Running state, has surface)895 * Test that a running application is stopped correctly (is in Running state, has surface)
1128 */896 */
1129TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly)897TEST_F(ApplicationManagerTests,shellStopsForegroundAppCorrectly)
1130{898{
@@ -1150,100 +918,61 @@
1150 applicationManager.authorizeSession(procId, authed);918 applicationManager.authorizeSession(procId, authed);
1151 onSessionStarting(session);919 onSessionStarting(session);
1152920
1153 std::shared_ptr<mir::scene::Surface> surface(nullptr);921 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1154 applicationManager.onSessionCreatedSurface(session.get(), surface);922 onSessionCreatedSurface(session.get(), surface);
1155 applicationManager.focusApplication(appId);923 surface->drawFirstFrame();
1156 EXPECT_EQ(applicationManager.focusedApplicationId(), appId);924
1157925 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1158 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));926 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
1159 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));927
1160928 // Stop app
1161 // Stop app929 applicationManager.stopApplication(appId);
1162 applicationManager.stopApplication(appId);930
1163931 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
1164 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)932 EXPECT_EQ(applicationManager.count(), 0);
1165 EXPECT_EQ(applicationManager.count(), 0);933 EXPECT_EQ(removedSpy.count(), 1);
1166 EXPECT_EQ(removedSpy.count(), 1);934 EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
1167 EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);935}
1168}936
1169937/*
1170/*938 * Test that a suspended application is stopped correctly
1171 * Test that the background application is stopped correctly939 */
1172 */940TEST_F(ApplicationManagerTests,shellStopsSuspendedAppCorrectly)
1173TEST_F(ApplicationManagerTests,shellStopsBackgroundAppCorrectly)941{
1174{942 using namespace ::testing;
1175 using namespace ::testing;943 const QString appId("testAppId");
1176 const QString appId("testAppId");944 quint64 procId = 5551;
1177 quint64 procId = 5551;945
1178946 // Set up Mocks & signal watcher
1179 // Set up Mocks & signal watcher947 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1180 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());948 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1181 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));949 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1182 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));950
1183951 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1184 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));952
1185953 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1186 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))954 .Times(1)
1187 .Times(1)955 .WillOnce(Return(true));
1188 .WillOnce(Return(true));956
1189957 Application *application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1190 applicationManager.startApplication(appId, ApplicationManager::NoFlag);958 applicationManager.onProcessStarting(appId);
1191 applicationManager.onProcessStarting(appId);959 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1192 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);960 bool authed = true;
1193 bool authed = true;961 applicationManager.authorizeSession(procId, authed);
1194 applicationManager.authorizeSession(procId, authed);962 onSessionStarting(session);
1195 onSessionStarting(session);963 applicationManager.onProcessStarting(appId);
1196964
1197 std::shared_ptr<mir::scene::Surface> surface(nullptr);965 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1198 applicationManager.onSessionCreatedSurface(session.get(), surface);966 onSessionCreatedSurface(session.get(), surface);
1199 applicationManager.unfocusCurrentApplication();967 surface->drawFirstFrame();
1200968
1201 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());969 suspend(application);
1202970
1203 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));971 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1204 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));972 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
1205973
1206 // Stop app974 // Stop app
1207 applicationManager.stopApplication(appId);975 applicationManager.stopApplication(appId);
1208
1209 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
1210 EXPECT_EQ(applicationManager.count(), 0);
1211 EXPECT_EQ(removedSpy.count(), 1);
1212 EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
1213}
1214
1215/*
1216 * Test that if an application is stopped by upstart, before it has created a surface, AppMan cleans up after it ok
1217 */
1218TEST_F(ApplicationManagerTests,upstartNotificationOfStartingAppBeingStopped)
1219{
1220 using namespace ::testing;
1221 const QString appId("testAppId");
1222 quint64 procId = 5551;
1223
1224 // Set up Mocks & signal watcher
1225 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1226 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1227 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1228
1229 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1230
1231 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1232 .Times(1)
1233 .WillOnce(Return(true));
1234
1235 applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1236 applicationManager.onProcessStarting(appId);
1237 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1238 bool authed = true;
1239 applicationManager.authorizeSession(procId, authed);
1240 onSessionStarting(session);
1241
1242 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1243 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
1244
1245 // Upstart notifies of stopping app
1246 applicationManager.onProcessStopped(appId);
1247976
1248 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)977 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
1249 EXPECT_EQ(applicationManager.count(), 0);978 EXPECT_EQ(applicationManager.count(), 0);
@@ -1278,28 +1007,28 @@
1278 applicationManager.authorizeSession(procId, authed);1007 applicationManager.authorizeSession(procId, authed);
1279 onSessionStarting(session);1008 onSessionStarting(session);
12801009
1281 std::shared_ptr<mir::scene::Surface> surface(nullptr);1010 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1282 applicationManager.onSessionCreatedSurface(session.get(), surface);1011 onSessionCreatedSurface(session.get(), surface);
1283 applicationManager.focusApplication(appId);1012 surface->drawFirstFrame();
1284 EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
12851013
1286 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1014 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1287 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1015 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
12881016
1017 onSessionStopping(session);
1289 // Upstart notifies of stopping app1018 // Upstart notifies of stopping app
1290 applicationManager.onProcessStopped(appId);1019 applicationManager.onProcessStopped(appId);
12911020
1292 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)1021 EXPECT_EQ(2, countSpy.count()); //FIXME(greyback)
1293 EXPECT_EQ(applicationManager.count(), 0);1022 EXPECT_EQ(0, applicationManager.count());
1294 EXPECT_EQ(removedSpy.count(), 1);1023 EXPECT_EQ(1, removedSpy.count());
1295 EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);1024 EXPECT_EQ(appId, removedSpy.takeFirst().at(0).toString());
1296}1025}
12971026
1298/*1027/*
1299 * Test that if the foreground Running application is reported to unexpectedly stop by upstart, AppMan1028 * Test that if a running application is reported to have stopped unexpectedly by upstart, AppMan
1300 * cleans up after it ok (as was not in background, had not lifecycle saved its state, so cannot be resumed)1029 * cleans up after it ok (as was not suspended, had not lifecycle saved its state, so cannot be resumed)
1301 */1030 */
1302TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfForegroundApp)1031TEST_F(ApplicationManagerTests,upstartNotifiesOfUnexpectedStopOfRunningApp)
1303{1032{
1304 using namespace ::testing;1033 using namespace ::testing;
1305 const QString appId("testAppId");1034 const QString appId("testAppId");
@@ -1323,14 +1052,15 @@
1323 applicationManager.authorizeSession(procId, authed);1052 applicationManager.authorizeSession(procId, authed);
1324 onSessionStarting(session);1053 onSessionStarting(session);
13251054
1326 std::shared_ptr<mir::scene::Surface> surface(nullptr);1055 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1327 applicationManager.onSessionCreatedSurface(session.get(), surface);1056 onSessionCreatedSurface(session.get(), surface);
1328 applicationManager.focusApplication(appId);1057 surface->drawFirstFrame();
1329 EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
13301058
1331 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1059 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1332 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1060 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
13331061
1062 onSessionStopping(session);
1063
1334 // Upstart notifies of crashing / OOM killed app1064 // Upstart notifies of crashing / OOM killed app
1335 applicationManager.onProcessFailed(appId, false);1065 applicationManager.onProcessFailed(appId, false);
13361066
@@ -1344,53 +1074,6 @@
1344}1074}
13451075
1346/*1076/*
1347 * Test that if a background application is stopped by upstart, AppMan removes it from the app list
1348 * as the event is a result of direct user interaction
1349 */
1350TEST_F(ApplicationManagerTests,upstartNotifiesOfStoppingBackgroundApp)
1351{
1352 using namespace ::testing;
1353 const QString appId("testAppId");
1354 quint64 procId = 5551;
1355
1356 // Set up Mocks & signal watcher
1357 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1358 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1359 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1360
1361 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1362
1363 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1364 .Times(1)
1365 .WillOnce(Return(true));
1366
1367 applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1368 applicationManager.onProcessStarting(appId);
1369 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1370 bool authed = true;
1371 applicationManager.authorizeSession(procId, authed);
1372 onSessionStarting(session);
1373
1374 std::shared_ptr<mir::scene::Surface> surface(nullptr);
1375 applicationManager.onSessionCreatedSurface(session.get(), surface);
1376 applicationManager.unfocusCurrentApplication();
1377 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
1378
1379 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1380 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));
1381 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
1382
1383 // Upstart notifies of stopping app
1384 applicationManager.onProcessStopped(appId);
1385
1386 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
1387 EXPECT_EQ(applicationManager.count(), 0);
1388 EXPECT_EQ(focusSpy.count(), 0);
1389 EXPECT_EQ(removedSpy.count(), 1);
1390 EXPECT_EQ(removedSpy.takeFirst().at(0).toString(), appId);
1391}
1392
1393/*
1394 * Test that if a background application is reported to unexpectedly stop by upstart, AppMan does not remove1077 * Test that if a background application is reported to unexpectedly stop by upstart, AppMan does not remove
1395 * it from the app lists but instead considers it Stopped, ready to be resumed. This is due to the fact the1078 * it from the app lists but instead considers it Stopped, ready to be resumed. This is due to the fact the
1396 * app should have saved its state, so can be resumed. This situation can occur due to the OOM killer, or1079 * app should have saved its state, so can be resumed. This situation can occur due to the OOM killer, or
@@ -1413,18 +1096,18 @@
1413 .Times(1)1096 .Times(1)
1414 .WillOnce(Return(true));1097 .WillOnce(Return(true));
14151098
1416 applicationManager.startApplication(appId, ApplicationManager::NoFlag);1099 Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1417 applicationManager.onProcessStarting(appId);1100 applicationManager.onProcessStarting(appId);
1418 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);1101 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1419 bool authed = true;1102 bool authed = true;
1420 applicationManager.authorizeSession(procId, authed);1103 applicationManager.authorizeSession(procId, authed);
1421 onSessionStarting(session);1104 onSessionStarting(session);
14221105
1423 std::shared_ptr<mir::scene::Surface> surface(nullptr);1106 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1424 applicationManager.onSessionCreatedSurface(session.get(), surface);1107 onSessionCreatedSurface(session.get(), surface);
1425 applicationManager.focusApplication(appId);1108 surface->drawFirstFrame();
1426 applicationManager.unfocusCurrentApplication();1109
1427 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());1110 suspend(app);
14281111
1429 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1112 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1430 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));1113 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));
@@ -1436,14 +1119,14 @@
1436 // Upstart notifies of crashing / OOM-killed app1119 // Upstart notifies of crashing / OOM-killed app
1437 applicationManager.onProcessFailed(appId, false);1120 applicationManager.onProcessFailed(appId, false);
14381121
1439 EXPECT_EQ(focusSpy.count(), 0);1122 EXPECT_EQ(0, focusSpy.count());
14401123
1441 // Upstart finally notifies the app stopped1124 // Upstart finally notifies the app stopped
1442 applicationManager.onProcessStopped(appId);1125 applicationManager.onProcessStopped(appId);
14431126
1444 EXPECT_EQ(countSpy.count(), 0);1127 EXPECT_EQ(0, countSpy.count());
1445 EXPECT_EQ(applicationManager.count(), 1);1128 EXPECT_EQ(1, applicationManager.count());
1446 EXPECT_EQ(removedSpy.count(), 0);1129 EXPECT_EQ(0, removedSpy.count());
1447}1130}
14481131
1449/*1132/*
@@ -1471,18 +1154,18 @@
1471 .Times(1)1154 .Times(1)
1472 .WillOnce(Return(true));1155 .WillOnce(Return(true));
14731156
1474 applicationManager.startApplication(appId, ApplicationManager::NoFlag);1157 Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1475 applicationManager.onProcessStarting(appId);1158 applicationManager.onProcessStarting(appId);
1476 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);1159 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1477 bool authed = true;1160 bool authed = true;
1478 applicationManager.authorizeSession(procId, authed);1161 applicationManager.authorizeSession(procId, authed);
1479 onSessionStarting(session);1162 onSessionStarting(session);
14801163
1481 std::shared_ptr<mir::scene::Surface> surface(nullptr);1164 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1482 applicationManager.onSessionCreatedSurface(session.get(), surface);1165 onSessionCreatedSurface(session.get(), surface);
1483 applicationManager.focusApplication(appId);1166 surface->drawFirstFrame();
1484 applicationManager.unfocusCurrentApplication();1167
1485 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());1168 suspend(app);
14861169
1487 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1170 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1488 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));1171 QSignalSpy focusSpy(&applicationManager, SIGNAL(focusedApplicationIdChanged()));
@@ -1571,10 +1254,9 @@
1571 onSessionStarting(session);1254 onSessionStarting(session);
15721255
1573 // Associate a surface so AppMan considers app Running, check focused1256 // Associate a surface so AppMan considers app Running, check focused
1574 std::shared_ptr<mir::scene::Surface> surface(nullptr);1257 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1575 applicationManager.onSessionCreatedSurface(session.get(), surface);1258 onSessionCreatedSurface(session.get(), surface);
1576 applicationManager.focusApplication(appId);1259 surface->drawFirstFrame();
1577 EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
15781260
1579 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1261 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1580 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1262 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
@@ -1589,10 +1271,10 @@
1589}1271}
15901272
1591/*1273/*
1592 * Test that if a foreground application (one launched via desktop_file_hint) is reported to be stopping by1274 * Test that if an application (one launched via desktop_file_hint) is reported to be stopping by
1593 * Mir, AppMan removes it from the model immediately1275 * Mir, AppMan removes it from the model immediately
1594 */1276 */
1595TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingForegroundAppLaunchedWithDesktopFileHint)1277TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingAppLaunchedWithDesktopFileHint)
1596{1278{
1597 using namespace ::testing;1279 using namespace ::testing;
1598 const QString appId("testAppId");1280 const QString appId("testAppId");
@@ -1621,10 +1303,9 @@
1621 onSessionStarting(session);1303 onSessionStarting(session);
16221304
1623 // Associate a surface so AppMan considers app Running, check focused1305 // Associate a surface so AppMan considers app Running, check focused
1624 std::shared_ptr<mir::scene::Surface> surface(nullptr);1306 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1625 applicationManager.onSessionCreatedSurface(session.get(), surface);1307 onSessionCreatedSurface(session.get(), surface);
1626 applicationManager.focusApplication(appId);1308 surface->drawFirstFrame();
1627 EXPECT_EQ(applicationManager.focusedApplicationId(), appId);
16281309
1629 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1310 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1630 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1311 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
@@ -1661,86 +1342,43 @@
1661 .Times(1)1342 .Times(1)
1662 .WillOnce(Return(true));1343 .WillOnce(Return(true));
16631344
1664 applicationManager.startApplication(appId, ApplicationManager::NoFlag);1345 Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1665 applicationManager.onProcessStarting(appId);1346 applicationManager.onProcessStarting(appId);
1666 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);1347 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1667 bool authed = true;1348 bool authed = true;
1668 applicationManager.authorizeSession(procId, authed);1349 applicationManager.authorizeSession(procId, authed);
1669 onSessionStarting(session);1350 onSessionStarting(session);
16701351 EXPECT_EQ(Application::Starting, app->state());
1671 // Associate a surface so AppMan considers app Running, check in background1352
1672 std::shared_ptr<mir::scene::Surface> surface(nullptr);1353 app->setRequestedState(Application::RequestedSuspended);
1673 applicationManager.onSessionCreatedSurface(session.get(), surface);1354
1674 applicationManager.focusApplication(appId);1355 // should not suspend an app that`s still starting up
1675 applicationManager.unfocusCurrentApplication();1356 ASSERT_EQ(Application::InternalState::Starting, app->internalState());
1676 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());1357
16771358 // Associate a surface so AppMan considers app Running
1678 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1359 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1679 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1360 onSessionCreatedSurface(session.get(), surface);
16801361 surface->drawFirstFrame();
1681 // Mir notifies of stopping app1362
1682 onSessionStopping(session);1363 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());
16831364
1684 EXPECT_EQ(countSpy.count(), 0);1365 static_cast<qtmir::Session*>(app->session())->doSuspend();
1685 EXPECT_EQ(applicationManager.count(), 1);1366 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, app->internalState());
1686 EXPECT_EQ(removedSpy.count(), 0);1367
16871368 applicationManager.onProcessSuspended(app->appId());
1688 Application * app = applicationManager.findApplication(appId);1369 ASSERT_EQ(Application::InternalState::Suspended, app->internalState());
1689 EXPECT_NE(nullptr,app);1370
1690 EXPECT_EQ(app->state(), Application::Stopped);1371 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1691}1372 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
16921373
1693/*1374 // Mir notifies of stopping app
1694 * Test that if a background application (one launched via desktop_file_hint) is reported to be stopping by1375 onSessionStopping(session);
1695 * Mir, AppMan removes it from the model immediately1376
1696 */1377 EXPECT_EQ(0, countSpy.count());
1697TEST_F(ApplicationManagerTests,mirNotifiesOfStoppingBackgroundAppLaunchedWithDesktopFileHint)1378 EXPECT_EQ(1, applicationManager.count());
1698{1379 EXPECT_EQ(0, removedSpy.count());
1699 using namespace ::testing;1380
1700 const QString appId("testAppId");1381 EXPECT_EQ(Application::Stopped, app->state());
1701 const QString name("Test App");
1702 quint64 procId = 5551;
1703 QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint=");
1704 cmdLine = cmdLine.append(appId);
1705
1706 // Set up Mocks & signal watcher
1707 EXPECT_CALL(procInfo,command_line(procId))
1708 .Times(1)
1709 .WillOnce(Return(cmdLine));
1710
1711 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1712 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1713 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1714 ON_CALL(*mockDesktopFileReader, name()).WillByDefault(Return(name));
1715
1716 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1717
1718 // Mir requests authentication for an application that was started
1719 bool authed = true;
1720 applicationManager.authorizeSession(procId, authed);
1721 EXPECT_EQ(authed, true);
1722 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1723 onSessionStarting(session);
1724
1725 // Associate a surface so AppMan considers app Running, check in background
1726 std::shared_ptr<mir::scene::Surface> surface(nullptr);
1727 applicationManager.onSessionCreatedSurface(session.get(), surface);
1728 applicationManager.focusApplication(appId);
1729 applicationManager.unfocusCurrentApplication();
1730 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());
1731
1732 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1733 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
1734
1735 // Mir notifies of stopping app
1736 onSessionStopping(session);
1737
1738 EXPECT_EQ(countSpy.count(), 2); //FIXME(greyback)
1739 EXPECT_EQ(applicationManager.count(), 0);
1740 EXPECT_EQ(removedSpy.count(), 1);
1741
1742 Application * app = applicationManager.findApplication(appId);
1743 EXPECT_EQ(nullptr,app);
1744}1382}
17451383
1746/*1384/*
@@ -1822,45 +1460,6 @@
1822}1460}
18231461
1824/*1462/*
1825 * Test that if an application is stopped by upstart, the Mir stopping event is ignored
1826 */
1827TEST_F(ApplicationManagerTests,appStoppedByUpstart_mirSessionStoppingEventIgnored)
1828{
1829 using namespace ::testing;
1830 const QString appId("testAppId");
1831 quint64 procId = 5551;
1832
1833 // Set up Mocks & signal watcher
1834 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1835 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1836 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1837
1838 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1839
1840 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1841 .Times(1)
1842 .WillOnce(Return(true));
1843
1844 applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1845 applicationManager.onProcessStarting(appId);
1846 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1847 bool authed = true;
1848 applicationManager.authorizeSession(procId, authed);
1849 onSessionStarting(session);
1850
1851 applicationManager.onProcessStopped(appId);
1852
1853 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1854 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
1855
1856 // Mir notifies of stopping app
1857 onSessionStopping(session);
1858
1859 EXPECT_EQ(countSpy.count(), 0);
1860 EXPECT_EQ(removedSpy.count(), 0);
1861}
1862
1863/*
1864 * Webapps have multiple sessions, but only one is linked to the application (other is considered a hidden session).1463 * Webapps have multiple sessions, but only one is linked to the application (other is considered a hidden session).
1865 * If webapp in foreground stops unexpectedly, remove it and it alone from app list1464 * If webapp in foreground stops unexpectedly, remove it and it alone from app list
1866 */1465 */
@@ -1903,8 +1502,9 @@
1903 applicationManager.authorizeSession(procId2, authed);1502 applicationManager.authorizeSession(procId2, authed);
1904 onSessionStarting(session2);1503 onSessionStarting(session2);
1905 EXPECT_EQ(authed, true);1504 EXPECT_EQ(authed, true);
1906 std::shared_ptr<mir::scene::Surface> surface(nullptr);1505 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
1907 applicationManager.onSessionCreatedSurface(session2.get(), surface);1506 onSessionCreatedSurface(session2.get(), surface);
1507 surface->drawFirstFrame();
19081508
1909 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1509 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1910 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1510 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
@@ -1950,7 +1550,7 @@
1950 .Times(1)1550 .Times(1)
1951 .WillOnce(Return(true));1551 .WillOnce(Return(true));
19521552
1953 applicationManager.startApplication(appId, ApplicationManager::NoFlag);1553 Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1954 applicationManager.onProcessStarting(appId);1554 applicationManager.onProcessStarting(appId);
1955 std::shared_ptr<mir::scene::Session> session1 = std::make_shared<MockSession>("", procId1);1555 std::shared_ptr<mir::scene::Session> session1 = std::make_shared<MockSession>("", procId1);
1956 std::shared_ptr<mir::scene::Session> session2 = std::make_shared<MockSession>("", procId2);1556 std::shared_ptr<mir::scene::Session> session2 = std::make_shared<MockSession>("", procId2);
@@ -1958,19 +1558,20 @@
1958 bool authed = false;1558 bool authed = false;
1959 applicationManager.authorizeSession(procId1, authed);1559 applicationManager.authorizeSession(procId1, authed);
1960 onSessionStarting(session1);1560 onSessionStarting(session1);
1961 EXPECT_EQ(authed, true);1561 EXPECT_EQ(true, authed);
1962 applicationManager.authorizeSession(procId2, authed);1562 applicationManager.authorizeSession(procId2, authed);
1963 onSessionStarting(session2);1563 onSessionStarting(session2);
1964 EXPECT_EQ(authed, true);1564 EXPECT_EQ(true, authed);
19651565
1966 // both sessions create surfaces, then unfocus everything.1566 // both sessions create surfaces, then get them all suspended
1967 std::shared_ptr<mir::scene::Surface> surface1(nullptr);1567 FakeMirSurfaceItem *surface1 = new FakeMirSurfaceItem;
1968 applicationManager.onSessionCreatedSurface(session1.get(), surface1);1568 onSessionCreatedSurface(session1.get(), surface1);
1969 std::shared_ptr<mir::scene::Surface> surface2(nullptr);1569 surface1->drawFirstFrame();
1970 applicationManager.onSessionCreatedSurface(session2.get(), surface2);1570 FakeMirSurfaceItem *surface2 = new FakeMirSurfaceItem;
1971 applicationManager.focusApplication(appId);1571 onSessionCreatedSurface(session2.get(), surface2);
1972 applicationManager.unfocusCurrentApplication();1572 surface2->drawFirstFrame();
1973 EXPECT_EQ(applicationManager.focusedApplicationId(), QString());1573 suspend(app);
1574 EXPECT_EQ(Application::Suspended, app->state());
19741575
1975 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));1576 QSignalSpy countSpy(&applicationManager, SIGNAL(countChanged()));
1976 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));1577 QSignalSpy removedSpy(&applicationManager, SIGNAL(applicationRemoved(const QString &)));
@@ -1979,8 +1580,104 @@
1979 onSessionStopping(session2);1580 onSessionStopping(session2);
1980 onSessionStopping(session1);1581 onSessionStopping(session1);
19811582
1982 EXPECT_EQ(countSpy.count(), 0);1583 EXPECT_EQ(0, countSpy.count());
1983 EXPECT_EQ(removedSpy.count(), 0);1584 EXPECT_EQ(0, removedSpy.count());
1585}
1586
1587/*
1588 * Test requested surface size unchanged if no surfaceSizer registered
1589 */
1590TEST_F(ApplicationManagerTests, registerSurfaceSizeUnchangedWhenNoSizerCallbackRegistered)
1591{
1592 using namespace ::testing;
1593 quint64 procId = 5551;
1594 SurfaceParameters params;
1595 params.geometry = {400, 250}; // can be overridden
1596
1597 const auto session = std::make_shared<MockSession>("", procId);
1598 applicationManager.onSessionAboutToCreateSurface(session, params);
1599
1600 EXPECT_EQ(params.geometry, QSize(400, 250));
1601}
1602
1603/*
1604 * Test registerSurfaceSizerCallback functionality
1605 */
1606TEST_F(ApplicationManagerTests, registerSurfaceSizerFunctional)
1607{
1608 using namespace ::testing;
1609 quint64 procId = 5551;
1610 SurfaceParameters params;
1611 params.geometry = {400, 250}; // can be overridden
1612
1613 QJSValue callback = jsEngine->evaluate("(function(surface) { surface.width = 111; return surface; })");
1614 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1615
1616 const auto session = std::make_shared<MockSession>("", procId);
1617 applicationManager.onSessionAboutToCreateSurface(session, params);
1618
1619 EXPECT_EQ(params.geometry, QSize(111, 250));
1620}
1621
1622/*
1623 * Test deregisterSurfaceSizerCallback correctly removes the callback
1624 */
1625TEST_F(ApplicationManagerTests, deregisterSurfaceSizerWorks)
1626{
1627 using namespace ::testing;
1628 quint64 procId = 5551;
1629 SurfaceParameters params;
1630 params.geometry = {400, 250}; // can be overridden
1631
1632 QJSValue callback = jsEngine->evaluate("(function(surface) { surface.width = 111; return surface; })");
1633 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1634
1635 const auto session = std::make_shared<MockSession>("", procId);
1636
1637 applicationManager.setSurfaceAboutToBeCreatedCallback(false);
1638 applicationManager.onSessionAboutToCreateSurface(session, params);
1639
1640 EXPECT_EQ(params.geometry, QSize(400, 250)); // should be unchanged
1641}
1642
1643/*
1644 * Test registerSurfaceSizerCallback rejects an uncallable QJSValue (i.e. something that's not a function was passed)
1645 */
1646TEST_F(ApplicationManagerTests, registerSurfaceSizerRejectsNonCallableJSValues)
1647{
1648 using namespace ::testing;
1649
1650 QJSValue callback = jsEngine->evaluate("21");
1651 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1652
1653 callback = jsEngine->evaluate("true");
1654 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1655
1656 callback = jsEngine->evaluate("{}");
1657 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1658
1659 callback = jsEngine->evaluate("new Array()");
1660 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1661}
1662
1663/*
1664 * Test in case that registered SurfaceSizerCallback does not return a type with a 'width' (or 'height') property,
1665 * we return the requested surface width/height unchanged
1666 */
1667TEST_F(ApplicationManagerTests, registeredSurfaceSizerDealswithBadReturns)
1668{
1669 using namespace ::testing;
1670 quint64 procId = 5551;
1671 SurfaceParameters params;
1672 params.geometry = {400, 250}; // can be overridden
1673
1674 QJSValue callback = jsEngine->evaluate("(function(s) { var out = new Object(); out.height = 333; return out; })");
1675 applicationManager.setSurfaceAboutToBeCreatedCallback(callback);
1676
1677 const auto session = std::make_shared<MockSession>("", procId);
1678 applicationManager.onSessionAboutToCreateSurface(session, params);
1679
1680 EXPECT_EQ(params.geometry, QSize(400, 333));
1984}1681}
19851682
1986/*1683/*
@@ -2007,34 +1704,33 @@
2007 .Times(1)1704 .Times(1)
2008 .WillOnce(Return(true));1705 .WillOnce(Return(true));
20091706
2010 applicationManager.startApplication(appId, ApplicationManager::NoFlag);1707 Application *app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
2011 applicationManager.onProcessStarting(appId);1708 applicationManager.onProcessStarting(appId);
2012 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);1709 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
2013 bool authed = true;1710 bool authed = true;
2014 applicationManager.authorizeSession(procId, authed);1711 applicationManager.authorizeSession(procId, authed);
2015 onSessionStarting(session);1712 onSessionStarting(session);
20161713
2017 // App creates surface, focuses it, puts it in background, then is OOM killed.1714 // App creates surface, puts it in background, then is OOM killed.
2018 std::shared_ptr<mir::scene::Surface> surface(nullptr);1715 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
2019 applicationManager.onSessionCreatedSurface(session.get(), surface);1716 onSessionCreatedSurface(session.get(), surface);
2020 applicationManager.focusApplication(appId);1717 surface->drawFirstFrame();
2021 applicationManager.unfocusCurrentApplication();1718 suspend(app);
20221719
2023 onSessionStopping(session);1720 onSessionStopping(session);
2024 applicationManager.onProcessFailed(appId, false);1721 applicationManager.onProcessFailed(appId, false);
2025 applicationManager.onProcessStopped(appId);1722 applicationManager.onProcessStopped(appId);
20261723
2027 Application *app = applicationManager.findApplication(appId);1724 EXPECT_EQ(Application::Stopped, app->state());
2028 EXPECT_EQ(app->state(), Application::Stopped);
20291725
2030 QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &)));1726 QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &)));
20311727
2032 // Upstart re-launches app1728 // Upstart re-launches app
2033 applicationManager.onProcessStarting(appId);1729 applicationManager.onProcessStarting(appId);
20341730
2035 EXPECT_EQ(app->state(), Application::Starting);1731 EXPECT_EQ(Application::Starting, app->state());
2036 EXPECT_EQ(focusRequestSpy.count(), 1);1732 EXPECT_EQ(1, focusRequestSpy.count());
2037 EXPECT_EQ(applicationManager.count(), 1);1733 EXPECT_EQ(1, applicationManager.count());
2038}1734}
20391735
2040/*1736/*
@@ -2138,142 +1834,61 @@
2138 }1834 }
2139}1835}
21401836
2141/*
2142 1 - launch and focus a main stage app
2143 * main stage app is running and focused
2144 2 - launch and focus a side stage app
2145 * main stage app is running but is not focused
2146 * side stage app is running and has focus
2147 3 - focus the main stage app
2148 * main stage app is running and has focus
2149 * side stage app is running but is not focused
2150
2151 This is a regression test for the bug where on step 3, the main stage app was momentarily
2152 suspended and then resumed again.
2153 */
2154TEST_F(ApplicationManagerTests, focusMainStageAfterSideStage)
2155{
2156 using namespace testing;
2157
2158 QString webbrowserAppId("webbrowser-app");
2159 quint64 webbrowserPID = 123;
2160 std::shared_ptr<mir::scene::Surface> webbrowserSurface(nullptr);
2161
2162 QString dialerAppId("dialer-app");
2163 quint64 dialerPID = 456;
2164 std::shared_ptr<mir::scene::Surface> dialerSurface(nullptr);
2165
2166 /*** Start webbrowser-app (main stage) ***/
2167
2168 ON_CALL(appController,appIdHasProcessId(webbrowserPID, webbrowserAppId)).WillByDefault(Return(true));
2169
2170 {
2171 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(webbrowserAppId, QFileInfo());
2172 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
2173 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(webbrowserAppId));
2174 ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("MainStage"));
2175
2176 ON_CALL(desktopFileReaderFactory, createInstance(webbrowserAppId, _))
2177 .WillByDefault(Return(mockDesktopFileReader));
2178 }
2179
2180 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(webbrowserAppId, _))
2181 .Times(1)
2182 .WillOnce(Return(true));
2183
2184 /*auto application =*/ applicationManager.startApplication(webbrowserAppId, ApplicationManager::NoFlag);
2185 applicationManager.onProcessStarting(webbrowserAppId);
2186
2187 {
2188 bool authed = false;
2189 applicationManager.authorizeSession(webbrowserPID, authed);
2190 EXPECT_EQ(authed, true);
2191 }
2192
2193 auto webbrowserSession = std::make_shared<mir::scene::MockSession>(webbrowserAppId.toStdString(), webbrowserPID);
2194 sessionManager.onSessionStarting(webbrowserSession);
2195 applicationManager.focusApplication(webbrowserAppId);
2196 applicationManager.onSessionCreatedSurface(webbrowserSession.get(), webbrowserSurface);
2197
2198 /*** Start dialer-app (side stage) ***/
2199
2200 ON_CALL(appController, appIdHasProcessId(dialerPID, dialerAppId)).WillByDefault(Return(true));
2201
2202 {
2203 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(dialerAppId, QFileInfo());
2204 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
2205 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(dialerAppId));
2206 ON_CALL(*mockDesktopFileReader, stageHint()).WillByDefault(Return("SideStage"));
2207
2208 ON_CALL(desktopFileReaderFactory, createInstance(dialerAppId, _))
2209 .WillByDefault(Return(mockDesktopFileReader));
2210 }
2211
2212 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(dialerAppId, _))
2213 .Times(1)
2214 .WillOnce(Return(true));
2215
2216 /*auto application =*/ applicationManager.startApplication(dialerAppId, ApplicationManager::NoFlag);
2217 applicationManager.onProcessStarting(dialerAppId);
2218
2219 {
2220 bool authed = false;
2221 applicationManager.authorizeSession(dialerPID, authed);
2222 EXPECT_EQ(authed, true);
2223 }
2224
2225 auto dialerSession = std::make_shared<mir::scene::MockSession>(dialerAppId.toStdString(), dialerPID);
2226 sessionManager.onSessionStarting(dialerSession);
2227 applicationManager.focusApplication(dialerAppId);
2228 applicationManager.onSessionCreatedSurface(dialerSession.get(), dialerSurface);
2229
2230 /*** Focus webbrowser ***/
2231
2232 // Nothing should happen as it's already the running main stage app
2233 EXPECT_CALL(*webbrowserSession.get(), set_lifecycle_state(_))
2234 .Times(0);
2235 applicationManager.focusApplication(webbrowserAppId);
2236}
2237
2238TEST_F(ApplicationManagerTests,lifecycle_exempt_appId_is_not_suspended)1837TEST_F(ApplicationManagerTests,lifecycle_exempt_appId_is_not_suspended)
2239{1838{
2240 using namespace ::testing;1839 using namespace ::testing;
2241 quint64 a_procId = 5921;1840 quint64 a_procId = 5921;
2242 const char an_app_id[] = "some_app";1841 const QString appId("some_app");
2243 QByteArray a_cmd( "/usr/bin/app1 --desktop_file_hint=some_app");1842 QByteArray a_cmd("/usr/bin/app1");
2244 std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
22451843
2246 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));1844 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
22471845
2248 ON_CALL(appController,appIdHasProcessId(_,_)).WillByDefault(Return(false));1846 ON_CALL(appController,appIdHasProcessId(a_procId, appId)).WillByDefault(Return(true));
22491847
2250 bool authed = true;1848 // Set up Mocks & signal watcher
1849 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1850 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1851 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1852
1853 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1854
1855
1856 Application *the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1857 applicationManager.onProcessStarting(appId);
22511858
2252 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);1859 std::shared_ptr<mir::scene::Session> first_session = std::make_shared<MockSession>("Oo", a_procId);
2253 std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId);1860 std::shared_ptr<mir::scene::Session> second_session = std::make_shared<MockSession>("oO", a_procId);
2254 applicationManager.authorizeSession(a_procId, authed);1861 {
1862 bool authed = false;
1863 applicationManager.authorizeSession(a_procId, authed);
1864 ASSERT_EQ(authed, true);
1865 }
22551866
2256 onSessionStarting(first_session);1867 onSessionStarting(first_session);
2257 applicationManager.onSessionCreatedSurface(first_session.get(), aSurface);1868 FakeMirSurfaceItem *aSurface = new FakeMirSurfaceItem;
1869 onSessionCreatedSurface(first_session.get(), aSurface);
1870 aSurface->drawFirstFrame();
2258 onSessionStarting(second_session);1871 onSessionStarting(second_session);
22591872
2260 Application * the_app = applicationManager.findApplication(an_app_id);
2261 applicationManager.focusApplication(an_app_id);
2262
2263 // Add to other apps to the list (Not "some_app")1873 // Add to other apps to the list (Not "some_app")
2264 QVariantList lifecycleExemptAppIds;1874 QVariantList lifecycleExemptAppIds;
2265 lifecycleExemptAppIds << "one_app" << "another_app";1875 lifecycleExemptAppIds << "one_app" << "another_app";
2266 ON_CALL(settings,get(_)).WillByDefault(Return(lifecycleExemptAppIds));1876 ON_CALL(settings,get(_)).WillByDefault(Return(lifecycleExemptAppIds));
2267 settings.changed("lifecycleExemptAppids");1877 settings.changed("lifecycleExemptAppids");
22681878
2269 EXPECT_EQ(Application::Running, the_app->state());1879 ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
22701880
2271 applicationManager.setSuspended(true);1881 EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(mir_lifecycle_state_will_suspend));
22721882 the_app->setRequestedState(Application::RequestedSuspended);
2273 // And expect "some_app" to get suspended1883 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());
2274 EXPECT_EQ(Application::Suspended, the_app->state());1884
22751885 static_cast<qtmir::Session*>(the_app->session())->doSuspend();
2276 applicationManager.setSuspended(false);1886 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());
1887 applicationManager.onProcessSuspended(the_app->appId());
1888 ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());
1889
1890 EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(mir_lifecycle_state_resumed));
1891 the_app->setRequestedState(Application::RequestedRunning);
22771892
2278 EXPECT_EQ(Application::Running, the_app->state());1893 EXPECT_EQ(Application::Running, the_app->state());
22791894
@@ -2284,14 +1899,16 @@
22841899
2285 EXPECT_EQ(Application::Running, the_app->state());1900 EXPECT_EQ(Application::Running, the_app->state());
22861901
2287 applicationManager.setSuspended(true);1902 EXPECT_CALL(*(mir::scene::MockSession*)first_session.get(), set_lifecycle_state(_)).Times(0);
1903 the_app->setRequestedState(Application::RequestedSuspended);
22881904
2289 // And expect it to be running still1905 // And expect it to be running still
2290 EXPECT_EQ(Application::Running, the_app->state());1906 ASSERT_EQ(Application::InternalState::RunningInBackground, the_app->internalState());
22911907
2292 applicationManager.setSuspended(false);1908 the_app->setRequestedState(Application::RequestedRunning);
22931909
2294 EXPECT_EQ(Application::Running, the_app->state());1910 EXPECT_EQ(Application::Running, the_app->state());
1911 ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
2295}1912}
22961913
2297/*1914/*
@@ -2323,51 +1940,13 @@
2323 onSessionStarting(session);1940 onSessionStarting(session);
23241941
2325 // App creates surface, focuses it so state is running1942 // App creates surface, focuses it so state is running
2326 std::shared_ptr<mir::scene::Surface> surface(nullptr);1943 FakeMirSurfaceItem *surface = new FakeMirSurfaceItem;
2327 applicationManager.onSessionCreatedSurface(session.get(), surface);1944 onSessionCreatedSurface(session.get(), surface);
2328 applicationManager.focusApplication(appId);1945 surface->drawFirstFrame();
23291946
2330 applicationManager.unfocusCurrentApplication();1947 application->setRequestedState(Application::RequestedSuspended);
23311948
2332 EXPECT_FALSE(sharedWakelock.enabled());1949 EXPECT_FALSE(sharedWakelock.enabled());
2333 EXPECT_EQ(application->state(), Application::Running);1950 ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState());
2334}1951 EXPECT_EQ(Application::Running, application->state());
2335
2336/*
2337 * Test lifecycle exempt applications have their wakelocks released on suspend
2338 */
2339TEST_F(ApplicationManagerTests,lifecycleExemptAppsHaveWakelockReleasedOnUnSuspend)
2340{
2341 using namespace ::testing;
2342
2343 const QString appId("com.ubuntu.music"); // member of lifecycle exemption list
2344 const quint64 procId = 12345;
2345
2346 // Set up Mocks & signal watcher
2347 auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
2348 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
2349 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
2350
2351 ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
2352
2353 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
2354 .Times(1)
2355 .WillOnce(Return(true));
2356
2357 auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
2358 applicationManager.onProcessStarting(appId);
2359 std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
2360 bool authed = true;
2361 applicationManager.authorizeSession(procId, authed);
2362 onSessionStarting(session);
2363
2364 // App creates surface, focuses it so state is running
2365 std::shared_ptr<mir::scene::Surface> surface(nullptr);
2366 applicationManager.onSessionCreatedSurface(session.get(), surface);
2367 applicationManager.focusApplication(appId);
2368
2369 applicationManager.setSuspended(true);
2370
2371 EXPECT_FALSE(sharedWakelock.enabled());
2372 EXPECT_EQ(application->state(), Application::Running);
2373}1952}
23741953
=== modified file 'tests/modules/MirSurfaceItem/CMakeLists.txt'
--- tests/modules/MirSurfaceItem/CMakeLists.txt 2014-12-03 08:56:35 +0000
+++ tests/modules/MirSurfaceItem/CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -5,7 +5,9 @@
5)5)
66
7include_directories(7include_directories(
8 ${CMAKE_SOURCE_DIR}/src/modules/Unity/Application8 ${CMAKE_SOURCE_DIR}/src/common
9# ${CMAKE_SOURCE_DIR}/src/modules/Unity/Application
10 ${CMAKE_SOURCE_DIR}/src/modules
9 ${CMAKE_SOURCE_DIR}/tests/modules/common11 ${CMAKE_SOURCE_DIR}/tests/modules/common
10 ${MIRSERVER_INCLUDE_DIRS}12 ${MIRSERVER_INCLUDE_DIRS}
11)13)
1214
=== modified file 'tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp'
--- tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp 2015-05-01 13:37:03 +0000
+++ tests/modules/MirSurfaceItem/mirsurfaceitem_test.cpp 2015-07-13 21:57:32 +0000
@@ -23,7 +23,7 @@
23#include <QTest>23#include <QTest>
2424
25// the test subject25// the test subject
26#include <mirsurfaceitem.h>26#include <Unity/Application/mirsurfaceitem.h>
2727
28// mocks28// mocks
29#include <mock_surface.h>29#include <mock_surface.h>
3030
=== modified file 'tests/modules/SessionManager/CMakeLists.txt'
--- tests/modules/SessionManager/CMakeLists.txt 2015-05-21 18:48:59 +0000
+++ tests/modules/SessionManager/CMakeLists.txt 2015-07-13 21:57:32 +0000
@@ -3,9 +3,13 @@
3 session_manager_test.cpp3 session_manager_test.cpp
4 session_test.cpp4 session_test.cpp
5 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp5 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
6 ${CMAKE_SOURCE_DIR}/tests/modules/common/qtmir_test.cpp
7 ${CMAKE_SOURCE_DIR}/tests/modules/common/fake_mirsurfaceitem.h
6)8)
79
8include_directories(10include_directories(
11 ${APPLICATION_API_INCLUDE_DIRS}
12 ${CMAKE_SOURCE_DIR}/src/common
9 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver13 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
10 ${CMAKE_SOURCE_DIR}/src/modules14 ${CMAKE_SOURCE_DIR}/src/modules
11 ${CMAKE_SOURCE_DIR}/tests/modules/common15 ${CMAKE_SOURCE_DIR}/tests/modules/common
1216
=== modified file 'tests/modules/SessionManager/session_manager_test.cpp'
--- tests/modules/SessionManager/session_manager_test.cpp 2014-12-01 11:05:01 +0000
+++ tests/modules/SessionManager/session_manager_test.cpp 2015-07-13 21:57:32 +0000
@@ -21,7 +21,7 @@
2121
22#include <Unity/Application/session.h>22#include <Unity/Application/session.h>
2323
24 #include "qtmir_test.h"24#include "qtmir_test.h"
2525
26using namespace qtmir;26using namespace qtmir;
27using mir::scene::MockSession;27using mir::scene::MockSession;
2828
=== modified file 'tests/modules/SessionManager/session_test.cpp'
--- tests/modules/SessionManager/session_test.cpp 2015-01-09 15:13:29 +0000
+++ tests/modules/SessionManager/session_test.cpp 2015-07-13 21:57:32 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2014 Canonical, Ltd.2 * Copyright (C) 2014,2015 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -15,16 +15,17 @@
15 *15 *
16 */16 */
1717
18#include <qtmir_test.h>
19#include <fake_mirsurfaceitem.h>
20
18#include <Unity/Application/application.h>21#include <Unity/Application/application.h>
19#include <Unity/Application/mirsurfaceitem.h>22#include <Unity/Application/mirsurfaceitem.h>
2023
21#include "qtmir_test.h"
22#include "stub_scene_surface.h"24#include "stub_scene_surface.h"
2325
24using namespace qtmir;26using namespace qtmir;
25using mir::scene::MockSession;27using mir::scene::MockSession;
2628
27
28namespace ms = mir::scene;29namespace ms = mir::scene;
29namespace mtd = mir::test::doubles;30namespace mtd = mir::test::doubles;
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches