Merge lp:qtmir/devel-mir-next into lp:qtmir

Proposed by Kevin DuBois on 2015-12-01
Status: Merged
Approved by: Gerry Boland on 2015-12-14
Approved revision: no longer in the source branch.
Merged at revision: 429
Proposed branch: lp:qtmir/devel-mir-next
Merge into: lp:qtmir
Diff against target: 1865 lines (+736/-139)
36 files modified
CMakeLists.txt (+1/-1)
debian/changelog (+21/-0)
debian/control (+5/-5)
src/modules/Unity/Application/application.cpp (+79/-11)
src/modules/Unity/Application/application.h (+11/-0)
src/modules/Unity/Application/application_manager.cpp (+54/-12)
src/modules/Unity/Application/application_manager.h (+4/-1)
src/modules/Unity/Application/mirsurface.cpp (+23/-20)
src/modules/Unity/Application/mirsurface.h (+2/-0)
src/modules/Unity/Application/mirsurfaceinterface.h (+2/-0)
src/modules/Unity/Application/mirsurfaceitem.cpp (+35/-6)
src/modules/Unity/Application/mirsurfaceitem.h (+5/-0)
src/modules/Unity/Application/mirsurfacemanager.cpp (+1/-1)
src/modules/Unity/Application/session.cpp (+115/-56)
src/modules/Unity/Application/session.h (+9/-4)
src/modules/Unity/Application/session_interface.h (+17/-4)
src/platforms/mirserver/mirwindowmanager.cpp (+12/-0)
src/platforms/mirserver/sessionauthorizer.cpp (+10/-0)
src/platforms/mirserver/sessionauthorizer.h (+1/-0)
src/platforms/mirserver/surfaceobserver.cpp (+6/-1)
src/platforms/mirserver/surfaceobserver.h (+2/-1)
tests/common/mock_display_configuration.h (+1/-0)
tests/mirserver/ScreenController/stub_display.h (+6/-0)
tests/mirserver/WindowManager/stub_session.cpp (+4/-0)
tests/mirserver/WindowManager/stub_session.h (+1/-1)
tests/mirserver/WindowManager/window_manager.cpp (+1/-1)
tests/modules/Application/CMakeLists.txt (+5/-0)
tests/modules/ApplicationManager/application_manager_test.cpp (+264/-1)
tests/modules/SessionManager/session_test.cpp (+3/-3)
tests/modules/SurfaceManager/mirsurface_test.cpp (+1/-1)
tests/modules/common/fake_mirsurface.h (+7/-0)
tests/modules/common/fake_session.h (+8/-2)
tests/modules/common/mock_mir_session.h (+1/-0)
tests/modules/common/mock_session.h (+9/-6)
tests/modules/common/qtmir_test.cpp (+3/-0)
tests/modules/common/qtmir_test.h (+7/-1)
To merge this branch: bzr merge lp:qtmir/devel-mir-next
Reviewer Review Type Date Requested Status
Michał Sawicz Approve on 2015-12-14
Gerry Boland (community) 2015-12-01 Approve on 2015-12-14
PS Jenkins bot (community) continuous-integration Needs Fixing on 2015-12-14
Review via email: mp+279174@code.launchpad.net

Commit message

compatibility branch for Mir 0.18

Description of the change

compatibility branch for Mir 0.18

To post a comment you must log in.
lp:qtmir/devel-mir-next updated on 2015-12-10
424. By Nick Dedekind on 2015-12-10

Politely asks processes to close before resorting to killing Fixes: #1434584
Approved by: Daniel d'Andrada

425. By Daniel d'Andrada on 2015-12-10

Add MirSurfaceItem.fillMode and ensure items and buffer are in sync

Ensure that by the time we enter the phase of updating the scene graph, all qml items are up to date regarding the size of the buffer about to be rendered.

NB: This rendering scheme needs triple buffering to work.
Approved by: Michael Zanetti

426. By Daniel d'Andrada on 2015-12-10

Make Session hold multiple surfaces

+ Standardize MirSurface debug messages and account for multiple surfaces per app
Approved by: Gerry Boland

427. By Michael Terry on 2015-12-10

Don't hold a wakelock on apps that are exempt from lifecycle management.

This supports the new exemptFromLifecycle flag for applications as well as bringing back the internal RunningInBackground state.

The tests were revived from the same branch that removed them when we removed RunningInBackground (revision 400) Fixes: #1518764

428. By CI Train Bot Account on 2015-12-10

Releasing 0.4.7+16.04.20151210-0ubuntu1

Kevin DuBois (kdub) wrote :

10: [ FAILED ] ClientLatency.triple_buffered_client_uses_all_buffers

lp: #1522031

Kevin DuBois (kdub) wrote :

^^wrong tab, intended for another MP target to mir

Gerry Boland (gerboland) wrote :

launchpad diff wrong. But comparing myself locally, looks ok

review: Approve
Michał Sawicz (saviq) wrote :

/me too

review: Approve
lp:qtmir/devel-mir-next updated on 2015-12-17
429. By Cemil Azizoglu on 2015-12-17

compatibility branch for Mir 0.18
Approved by: Gerry Boland, Michał Sawicz

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2015-11-25 15:38:50 +0000
3+++ CMakeLists.txt 2015-12-11 13:19:15 +0000
4@@ -75,7 +75,7 @@
5 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
6 pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
7 pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=11)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=12)
10
11 include_directories(${APPLICATION_API_INCLUDE_DIRS})
12
13
14=== modified file 'debian/changelog'
15--- debian/changelog 2015-12-01 14:14:00 +0000
16+++ debian/changelog 2015-12-11 13:19:15 +0000
17@@ -1,3 +1,24 @@
18+qtmir (0.4.7+16.04.20151210-0ubuntu1) xenial; urgency=medium
19+
20+ [ Nick Dedekind ]
21+ * [ Nick Dedekind ]
22+ * Request app closing rather than killing.
23+
24+ [ Daniel d'Andrada ]
25+ * Add MirSurfaceItem.fillMode and ensure items and buffer are in sync
26+ * Make Session hold multiple surfaces
27+
28+ [ Michael Terry ]
29+ * Don't hold a wakelock on apps that are exempt from lifecycle
30+ management. (LP: #1518764)
31+
32+ [ Michał Sawicz ]
33+ * Add MirSurfaceItem.fillMode and ensure items and buffer are in sync
34+ * Don't hold a wakelock on apps that are exempt from lifecycle
35+ management. (LP: #1518764)
36+
37+ -- Gerry Boland <ci-train-bot@canonical.com> Thu, 10 Dec 2015 13:08:47 +0000
38+
39 qtmir (0.4.6+16.04.20151125-0ubuntu2) xenial; urgency=medium
40
41 * Rebuild against Qt 5.5.1.
42
43=== modified file 'debian/control'
44--- debian/control 2015-11-25 15:38:50 +0000
45+++ debian/control 2015-12-11 13:19:15 +0000
46@@ -11,9 +11,9 @@
47 libglib2.0-dev,
48 libgsettings-qt-dev,
49 liblttng-ust-dev,
50- libmirclient-dev (>= 0.17.0),
51- libmircommon-dev (>= 0.17.0),
52- libmirserver-dev (>= 0.17.0),
53+ libmirclient-dev (>= 0.18.0),
54+ libmircommon-dev (>= 0.18.0),
55+ libmirserver-dev (>= 0.18.0),
56 libmtdev-dev,
57 libprocess-cpp-dev,
58 libqt5sensors5-dev,
59@@ -22,7 +22,7 @@
60 libubuntu-app-launch2-dev,
61 libubuntu-application-api-dev (>= 2.1.0),
62 libudev-dev,
63- libunity-api-dev (>= 7.103),
64+ libunity-api-dev (>= 7.104),
65 liburl-dispatcher1-dev,
66 libxkbcommon-dev,
67 libxrender-dev,
68@@ -93,7 +93,7 @@
69 Conflicts: libqtmir,
70 libunity-mir1,
71 Provides: unity-application-impl,
72- unity-application-impl-11,
73+ unity-application-impl-12,
74 Description: Qt plugin for Unity specific Mir APIs
75 QtMir provides Qt/QML bindings for Mir features that are exposed through the
76 qtmir-desktop or qtmir-android QPA plugin such as Application management
77
78=== modified file 'src/modules/Unity/Application/application.cpp'
79--- src/modules/Unity/Application/application.cpp 2015-10-01 17:20:40 +0000
80+++ src/modules/Unity/Application/application.cpp 2015-12-11 13:19:15 +0000
81@@ -45,13 +45,15 @@
82 , m_sharedWakelock(sharedWakelock)
83 , m_desktopData(desktopFileReader)
84 , m_pid(0)
85- , m_stage((m_desktopData->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)
86+ , m_stage((desktopFileReader->stageHint() == "SideStage") ? Application::SideStage : Application::MainStage)
87 , m_state(InternalState::Starting)
88 , m_focused(false)
89 , m_arguments(arguments)
90 , m_session(nullptr)
91 , m_requestedState(RequestedRunning)
92 , m_processState(ProcessUnknown)
93+ , m_closeTimer(0)
94+ , m_exemptFromLifecycle(false)
95 {
96 qCDebug(QTMIR_APPLICATIONS) << "Application::Application - appId=" << desktopFileReader->appId();
97
98@@ -78,6 +80,7 @@
99 switch (m_state) {
100 case InternalState::Starting:
101 case InternalState::Running:
102+ case InternalState::RunningInBackground:
103 case InternalState::SuspendingWaitSession:
104 case InternalState::SuspendingWaitProcess:
105 wipeQMLCache();
106@@ -209,6 +212,8 @@
107 return "Starting";
108 case InternalState::Running:
109 return "Running";
110+ case InternalState::RunningInBackground:
111+ return "RunningInBackground";
112 case InternalState::SuspendingWaitSession:
113 return "SuspendingWaitSession";
114 case InternalState::SuspendingWaitProcess:
115@@ -277,6 +282,7 @@
116 case InternalState::Starting:
117 return Starting;
118 case InternalState::Running:
119+ case InternalState::RunningInBackground:
120 case InternalState::SuspendingWaitSession:
121 case InternalState::SuspendingWaitProcess:
122 case InternalState::Closing:
123@@ -327,6 +333,7 @@
124 case InternalState::Running:
125 // already where it's wanted to be
126 break;
127+ case InternalState::RunningInBackground:
128 case InternalState::SuspendingWaitSession:
129 case InternalState::Suspended:
130 resume();
131@@ -359,6 +366,7 @@
132 Q_ASSERT(m_processState == ProcessUnknown);
133 }
134 break;
135+ case InternalState::RunningInBackground:
136 case InternalState::SuspendingWaitSession:
137 case InternalState::SuspendingWaitProcess:
138 case InternalState::Suspended:
139@@ -394,25 +402,23 @@
140 return m_pid;
141 }
142
143-void Application::setPid(pid_t pid)
144-{
145- m_pid = pid;
146-}
147-
148 void Application::close()
149 {
150 qCDebug(QTMIR_APPLICATIONS) << "Application::close - appId=" << appId();
151
152 switch (m_state) {
153 case InternalState::Starting:
154+ stop();
155+ break;
156 case InternalState::Running:
157- setInternalState(InternalState::Closing);
158+ doClose();
159 break;
160+ case InternalState::RunningInBackground:
161 case InternalState::SuspendingWaitSession:
162 case InternalState::SuspendingWaitProcess:
163 case InternalState::Suspended:
164 setRequestedState(RequestedRunning);
165- setInternalState(InternalState::Closing);
166+ doClose();
167 break;
168 case InternalState::Closing:
169 // already on the way
170@@ -422,6 +428,22 @@
171 // too late
172 break;
173 }
174+
175+}
176+
177+void Application::doClose()
178+{
179+ Q_ASSERT(m_closeTimer == 0);
180+ Q_ASSERT(m_session != nullptr);
181+
182+ m_session->close();
183+ m_closeTimer = startTimer(3000);
184+ setInternalState(InternalState::Closing);
185+}
186+
187+void Application::setPid(pid_t pid)
188+{
189+ m_pid = pid;
190 }
191
192 void Application::setArguments(const QStringList arguments)
193@@ -452,6 +474,7 @@
194 switch (m_state) {
195 case InternalState::Starting:
196 case InternalState::Running:
197+ case InternalState::RunningInBackground:
198 case InternalState::Closing:
199 m_session->resume();
200 break;
201@@ -510,6 +533,7 @@
202 case InternalState::Running:
203 acquireWakelock();
204 break;
205+ case InternalState::RunningInBackground:
206 case InternalState::Suspended:
207 releaseWakelock();
208 break;
209@@ -603,15 +627,26 @@
210
211 void Application::suspend()
212 {
213+ qCDebug(QTMIR_APPLICATIONS) << "Application::suspend - appId=" << appId();
214+
215 Q_ASSERT(m_state == InternalState::Running);
216 Q_ASSERT(m_session != nullptr);
217
218- setInternalState(InternalState::SuspendingWaitSession);
219- m_session->suspend();
220+ if (exemptFromLifecycle()) {
221+ // There's no need to keep the wakelock as the process is never suspended
222+ // and thus has no cleanup to perform when (for example) the display is
223+ // blanked.
224+ setInternalState(InternalState::RunningInBackground);
225+ } else {
226+ setInternalState(InternalState::SuspendingWaitSession);
227+ m_session->suspend();
228+ }
229 }
230
231 void Application::resume()
232 {
233+ qCDebug(QTMIR_APPLICATIONS) << "Application::resume - appId=" << appId();
234+
235 if (m_state == InternalState::Suspended) {
236 setInternalState(InternalState::Running);
237 Q_EMIT resumeProcessRequested();
238@@ -622,6 +657,8 @@
239 } else if (m_state == InternalState::SuspendingWaitSession) {
240 setInternalState(InternalState::Running);
241 m_session->resume();
242+ } else if (m_state == InternalState::RunningInBackground) {
243+ setInternalState(InternalState::Running);
244 }
245 }
246
247@@ -634,11 +671,42 @@
248 Q_EMIT startProcessRequested();
249 }
250
251+void Application::stop()
252+{
253+ qCDebug(QTMIR_APPLICATIONS) << "Application::stop - appId=" << appId();
254+
255+ Q_EMIT stopProcessRequested();
256+}
257+
258+void Application::timerEvent(QTimerEvent *event)
259+{
260+ if (event->timerId() == m_closeTimer) {
261+ m_closeTimer = 0;
262+ stop();
263+ }
264+}
265+
266 bool Application::isTouchApp() const
267 {
268 return m_desktopData->isTouchApp();
269 }
270
271+bool Application::exemptFromLifecycle() const
272+{
273+ return m_exemptFromLifecycle;
274+}
275+
276+void Application::setExemptFromLifecycle(bool exemptFromLifecycle)
277+{
278+ if (m_exemptFromLifecycle != exemptFromLifecycle)
279+ {
280+ // We don't adjust current suspension state, we only care about exempt
281+ // status going into a suspend.
282+ m_exemptFromLifecycle = exemptFromLifecycle;
283+ Q_EMIT exemptFromLifecycleChanged(m_exemptFromLifecycle);
284+ }
285+}
286+
287 QString Application::longAppId() const
288 {
289 return m_longAppId;
290@@ -704,7 +772,7 @@
291 * 3. application is managed by upstart and is in foreground (i.e. has
292 * Running state), if Mir reports the application disconnects, it
293 * either crashed or stopped itself.
294- * 4. We're expecting the application to stop after a close request
295+ * 4. We're expecting the application to stop after a close request
296 */
297 setInternalState(InternalState::Stopped);
298 } else {
299
300=== modified file 'src/modules/Unity/Application/application.h'
301--- src/modules/Unity/Application/application.h 2015-11-17 14:16:22 +0000
302+++ src/modules/Unity/Application/application.h 2015-12-11 13:19:15 +0000
303@@ -68,6 +68,7 @@
304 enum class InternalState {
305 Starting,
306 Running,
307+ RunningInBackground,
308 SuspendingWaitSession,
309 SuspendingWaitProcess,
310 Suspended,
311@@ -104,6 +105,8 @@
312 Qt::ScreenOrientations supportedOrientations() const override;
313 bool rotatesWindowContents() const override;
314 bool isTouchApp() const override;
315+ bool exemptFromLifecycle() const override;
316+ void setExemptFromLifecycle(bool) override;
317
318 void setStage(Stage stage);
319
320@@ -137,6 +140,7 @@
321 void sessionChanged(SessionInterface *session);
322
323 void startProcessRequested();
324+ void stopProcessRequested();
325 void suspendProcessRequested();
326 void resumeProcessRequested();
327 void stopped();
328@@ -146,6 +150,9 @@
329
330 void respawn();
331
332+protected:
333+ void timerEvent(QTimerEvent *event);
334+
335 private:
336
337 QString longAppId() const;
338@@ -158,11 +165,13 @@
339 void wipeQMLCache();
340 void suspend();
341 void resume();
342+ void stop();
343 QColor colorFromString(const QString &colorString, const char *colorName) const;
344 static const char* internalStateToStr(InternalState state);
345 void applyRequestedState();
346 void applyRequestedRunning();
347 void applyRequestedSuspended();
348+ void doClose();
349
350 QSharedPointer<SharedWakelock> m_sharedWakelock;
351 DesktopFileReader* m_desktopData;
352@@ -178,6 +187,8 @@
353 SessionInterface *m_session;
354 RequestedState m_requestedState;
355 ProcessState m_processState;
356+ int m_closeTimer;
357+ bool m_exemptFromLifecycle;
358
359 friend class ApplicationManager;
360 friend class SessionManager;
361
362=== modified file 'src/modules/Unity/Application/application_manager.cpp'
363--- src/modules/Unity/Application/application_manager.cpp 2015-11-17 14:16:22 +0000
364+++ src/modules/Unity/Application/application_manager.cpp 2015-12-11 13:19:15 +0000
365@@ -229,6 +229,8 @@
366 return QVariant::fromValue(application->focused());
367 case RoleIsTouchApp:
368 return QVariant::fromValue(application->isTouchApp());
369+ case RoleExemptFromLifecycle:
370+ return QVariant::fromValue(application->exemptFromLifecycle());
371 case RoleSession:
372 return QVariant::fromValue(application->session());
373 case RoleFullscreen:
374@@ -353,6 +355,23 @@
375 return nullptr;
376 }
377
378+ if (m_queuedStartApplications.contains(inputAppId)) {
379+ qWarning() << "ApplicationManager::startApplication - application appId=" << appId << " is queued to start";
380+ return nullptr;
381+ } else {
382+ application = findClosingApplication(inputAppId);
383+ if (application) {
384+ m_queuedStartApplications.append(inputAppId);
385+ qWarning() << "ApplicationManager::startApplication - application appId=" << appId << " is closing. Queuing start";
386+ connect(application, &QObject::destroyed, this, [this, application, inputAppId, flags, arguments]() {
387+ m_queuedStartApplications.removeAll(inputAppId);
388+ // start the app.
389+ startApplication(inputAppId, flags, arguments);
390+ }, Qt::QueuedConnection); // Queued so that we finish the app removal before starting again.
391+ return nullptr;
392+ }
393+ }
394+
395 if (!m_taskController->start(appId, arguments)) {
396 qWarning() << "Upstart failed to start application with appId" << appId;
397 return nullptr;
398@@ -438,17 +457,12 @@
399 application->close();
400 remove(application);
401
402- bool result = m_taskController->stop(application->longAppId());
403-
404- if (!result && application->pid() > 0) {
405- qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
406- << "Sending SIGTERM to process:" << application->pid();
407- kill(application->pid(), SIGTERM);
408- result = true;
409- }
410-
411- delete application;
412- return result;
413+ connect(application, &QObject::destroyed, this, [this, application](QObject*) {
414+ m_closingApplications.removeAll(application);
415+ });
416+ m_closingApplications.append(application);
417+ application->close();
418+ return true;
419 }
420
421 void ApplicationManager::onProcessFailed(const QString &appId, const bool duringStartup)
422@@ -473,7 +487,11 @@
423 {
424 tracepoint(qtmir, onProcessStopped);
425 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::onProcessStopped - appId=" << appId;
426+
427 Application *application = findApplication(appId);
428+ if (!application) {
429+ application = findClosingApplication(appId);
430+ }
431
432 if (!application) {
433 qDebug() << "ApplicationManager::onProcessStopped reports stop of appId=" << appId
434@@ -729,6 +747,15 @@
435 this, [=]() { m_taskController->start(appId, arguments); },
436 Qt::QueuedConnection);
437
438+ connect(application, &Application::stopProcessRequested, this, [=]() {
439+ if (!m_taskController->stop(application->longAppId()) && application->pid() > 0) {
440+ qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
441+ << "Sending SIGTERM to process:" << appId;
442+ kill(application->pid(), SIGTERM);
443+ application->setProcessState(Application::ProcessStopped);
444+ }
445+ });
446+
447 connect(application, &Application::suspendProcessRequested, this, [=]() { m_taskController->suspend(longAppId); } );
448 connect(application, &Application::resumeProcessRequested, this, [=]() { m_taskController->resume(longAppId); } );
449
450@@ -753,7 +780,10 @@
451 Q_ASSERT(application != nullptr);
452 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::remove - appId=" << application->appId();
453
454- application->disconnect(this);
455+ disconnect(application, &Application::fullscreenChanged, this, 0);
456+ disconnect(application, &Application::focusedChanged, this, 0);
457+ disconnect(application, &Application::stateChanged, this, 0);
458+ disconnect(application, &Application::stageChanged, this, 0);
459
460 int i = m_applications.indexOf(application);
461 if (i != -1) {
462@@ -813,4 +843,16 @@
463 return result;
464 }
465
466+Application *ApplicationManager::findClosingApplication(const QString &inputAppId) const
467+{
468+ const QString appId = toShortAppIdIfPossible(inputAppId);
469+
470+ for (Application *app : m_closingApplications) {
471+ if (app->appId() == appId) {
472+ return app;
473+ }
474+ }
475+ return nullptr;
476+}
477+
478 } // namespace qtmir
479
480=== modified file 'src/modules/Unity/Application/application_manager.h'
481--- src/modules/Unity/Application/application_manager.h 2015-11-17 14:16:22 +0000
482+++ src/modules/Unity/Application/application_manager.h 2015-12-11 13:19:15 +0000
483@@ -68,7 +68,7 @@
484
485 // FIXME: these roles should be added to unity-api and removed from here
486 enum MoreRoles {
487- RoleSession = RoleIsTouchApp+1,
488+ RoleSession = RoleExemptFromLifecycle+1,
489 RoleFullscreen,
490 };
491
492@@ -149,6 +149,7 @@
493 QString toString() const;
494
495 Application* findApplicationWithPromptSession(const mir::scene::PromptSession* promptSession);
496+ Application *findClosingApplication(const QString &inputAppId) const;
497
498 QSharedPointer<MirServer> m_mirServer;
499
500@@ -160,6 +161,8 @@
501 QSharedPointer<ProcInfo> m_procInfo;
502 QSharedPointer<SharedWakelock> m_sharedWakelock;
503 QSharedPointer<SettingsInterface> m_settings;
504+ QList<Application*> m_closingApplications;
505+ QList<QString> m_queuedStartApplications;
506 static ApplicationManager* the_application_manager;
507
508 friend class Application;
509
510=== modified file 'src/modules/Unity/Application/mirsurface.cpp'
511--- src/modules/Unity/Application/mirsurface.cpp 2015-11-25 15:38:39 +0000
512+++ src/modules/Unity/Application/mirsurface.cpp 2015-12-11 13:19:15 +0000
513@@ -34,6 +34,8 @@
514
515 using namespace qtmir;
516
517+#define DEBUG_MSG qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << (void*)this << "," << appId() <<"]::" << __func__
518+
519 namespace {
520
521 // Would be better if QMouseEvent had nativeModifiers
522@@ -216,7 +218,7 @@
523 Q_ASSERT(m_views.isEmpty());
524
525 if (m_session) {
526- m_session->setSurface(nullptr);
527+ m_session->removeSurface(this);
528 }
529
530 QMutexLocker locker(&m_mutex);
531@@ -297,15 +299,14 @@
532 int framesPending = m_surface->buffers_ready_for_compositor(userId);
533 if (framesPending > 0) {
534 m_textureUpdated = false;
535-
536+
537 locker.unlock();
538 if (updateTexture()) {
539- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::dropPendingBuffer() dropped=1 left=" << framesPending-1;
540+ DEBUG_MSG << "() dropped=1 left=" << framesPending-1;
541 } else {
542 // If we haven't managed to update the texture, don't keep banging away.
543 m_frameDropperTimer.stop();
544- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::dropPendingBuffer() dropped=0"
545- << " left=" << framesPending << " - failed to upate texture";
546+ DEBUG_MSG << "() dropped=0" << " left=" << framesPending << " - failed to upate texture";
547 }
548 Q_EMIT frameDropped();
549 } else {
550@@ -319,13 +320,13 @@
551
552 void MirSurface::stopFrameDropper()
553 {
554- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::stopFrameDropper()";
555+ DEBUG_MSG << "()";
556 m_frameDropperTimer.stop();
557 }
558
559 void MirSurface::startFrameDropper()
560 {
561- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::startFrameDropper()";
562+ DEBUG_MSG << "()";
563 if (!m_frameDropperTimer.isActive()) {
564 m_frameDropperTimer.start();
565 }
566@@ -406,12 +407,11 @@
567 // Temporary hotfix for http://pad.lv/1483752
568 if (m_session->childSessions()->rowCount() > 0) {
569 // has child trusted session, ignore any focus change attempts
570- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::setFocus(" << focus
571- << ") - has child trusted session, ignore any focus change attempts";
572+ DEBUG_MSG << "(" << focus << ") - has child trusted session, ignore any focus change attempts";
573 return;
574 }
575
576- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::setFocus(" << focus << ")";
577+ DEBUG_MSG << "(" << focus << ")";
578
579 if (focus) {
580 m_shell->set_surface_attribute(m_session->session(), m_surface, mir_surface_attrib_focus, mir_surface_focused);
581@@ -420,6 +420,13 @@
582 }
583 }
584
585+void MirSurface::close()
586+{
587+ if (m_surface) {
588+ m_surface->request_client_surface_close();
589+ }
590+}
591+
592 void MirSurface::resize(int width, int height)
593 {
594 int mirWidth = m_surface->size().width.as_int();
595@@ -430,9 +437,8 @@
596 if (clientIsRunning() && mirSizeIsDifferent) {
597 mir::geometry::Size newMirSize(width, height);
598 m_surface->resize(newMirSize);
599- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::resize"
600- << " old (" << mirWidth << "," << mirHeight << ")"
601- << ", new (" << width << "," << height << ")";
602+ DEBUG_MSG << " old (" << mirWidth << "," << mirHeight << ")"
603+ << ", new (" << width << "," << height << ")";
604 }
605 }
606
607@@ -659,8 +665,7 @@
608 void MirSurface::registerView(qintptr viewId)
609 {
610 m_views.insert(viewId, MirSurface::View{false});
611- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::registerView(" << viewId << ")"
612- << " after=" << m_views.count();
613+ DEBUG_MSG << "(" << viewId << ")" << " after=" << m_views.count();
614 if (m_views.count() == 1) {
615 Q_EMIT isBeingDisplayedChanged();
616 }
617@@ -668,9 +673,8 @@
618
619 void MirSurface::unregisterView(qintptr viewId)
620 {
621- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::unregisterView(" << viewId << ")"
622- << " after=" << m_views.count() << " live=" << m_live;
623 m_views.remove(viewId);
624+ DEBUG_MSG << "(" << viewId << ")" << " after=" << m_views.count() << " live=" << m_live;
625 if (m_views.count() == 0) {
626 Q_EMIT isBeingDisplayedChanged();
627 if (m_session.isNull() || !m_live) {
628@@ -701,7 +705,7 @@
629 }
630
631 if (newVisible != visible()) {
632- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::updateVisibility(" << newVisible << ")";
633+ DEBUG_MSG << "(" << newVisible << ")";
634
635 m_surface->configure(mir_surface_attrib_visibility,
636 newVisible ? mir_surface_visibility_exposed : mir_surface_visibility_occluded);
637@@ -745,8 +749,7 @@
638
639 void MirSurface::setCursor(const QCursor &cursor)
640 {
641- qCDebug(QTMIR_SURFACES).nospace() << "MirSurface[" << appId() << "]::setCursor("
642- << qtCursorShapeToStr(cursor.shape()) << ")";
643+ DEBUG_MSG << "(" << qtCursorShapeToStr(cursor.shape()) << ")";
644
645 m_cursor = cursor;
646 Q_EMIT cursorChanged(m_cursor);
647
648=== modified file 'src/modules/Unity/Application/mirsurface.h'
649--- src/modules/Unity/Application/mirsurface.h 2015-11-25 15:38:39 +0000
650+++ src/modules/Unity/Application/mirsurface.h 2015-12-11 13:19:15 +0000
651@@ -99,6 +99,8 @@
652
653 void setFocus(bool focus) override;
654
655+ void close() override;
656+
657 void mousePressEvent(QMouseEvent *event) override;
658 void mouseMoveEvent(QMouseEvent *event) override;
659 void mouseReleaseEvent(QMouseEvent *event) override;
660
661=== modified file 'src/modules/Unity/Application/mirsurfaceinterface.h'
662--- src/modules/Unity/Application/mirsurfaceinterface.h 2015-11-25 15:38:39 +0000
663+++ src/modules/Unity/Application/mirsurfaceinterface.h 2015-12-11 13:19:15 +0000
664@@ -64,6 +64,8 @@
665
666 virtual void setFocus(bool focus) = 0;
667
668+ virtual void close() = 0;
669+
670 virtual void mousePressEvent(QMouseEvent *event) = 0;
671 virtual void mouseMoveEvent(QMouseEvent *event) = 0;
672 virtual void mouseReleaseEvent(QMouseEvent *event) = 0;
673
674=== modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
675--- src/modules/Unity/Application/mirsurfaceitem.cpp 2015-11-25 15:38:54 +0000
676+++ src/modules/Unity/Application/mirsurfaceitem.cpp 2015-12-11 13:19:15 +0000
677@@ -92,6 +92,7 @@
678 , m_surfaceHeight(0)
679 , m_orientationAngle(nullptr)
680 , m_consumesInput(false)
681+ , m_fillMode(Stretch)
682 {
683 qCDebug(QTMIR_SURFACES) << "MirSurfaceItem::MirSurfaceItem";
684
685@@ -235,20 +236,35 @@
686 QSGDefaultImageNode *node = static_cast<QSGDefaultImageNode*>(oldNode);
687 if (!node) {
688 node = new QSGDefaultImageNode;
689- node->setTexture(m_textureProvider->texture());
690-
691 node->setMipmapFiltering(QSGTexture::None);
692 node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
693 node->setVerticalWrapMode(QSGTexture::ClampToEdge);
694- node->setSubSourceRect(QRectF(0, 0, 1, 1));
695 } else {
696 if (!m_lastFrameNumberRendered || (*m_lastFrameNumberRendered != m_surface->currentFrameNumber())) {
697 node->markDirty(QSGNode::DirtyMaterial);
698 }
699 }
700-
701- node->setTargetRect(QRectF(0, 0, width(), height()));
702- node->setInnerTargetRect(QRectF(0, 0, width(), height()));
703+ node->setTexture(m_textureProvider->texture());
704+
705+ if (m_fillMode == PadOrCrop) {
706+ const QSize &textureSize = m_textureProvider->texture()->textureSize();
707+
708+ QRectF targetRect;
709+ targetRect.setWidth(qMin(width(), static_cast<qreal>(textureSize.width())));
710+ targetRect.setHeight(qMin(height(), static_cast<qreal>(textureSize.height())));
711+
712+ qreal u = targetRect.width() / textureSize.width();
713+ qreal v = targetRect.height() / textureSize.height();
714+ node->setSubSourceRect(QRectF(0, 0, u, v));
715+
716+ node->setTargetRect(targetRect);
717+ node->setInnerTargetRect(targetRect);
718+ } else {
719+ // Stretch
720+ node->setSubSourceRect(QRectF(0, 0, 1, 1));
721+ node->setTargetRect(QRectF(0, 0, width(), height()));
722+ node->setInnerTargetRect(QRectF(0, 0, width(), height()));
723+ }
724
725 node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
726 node->setAntialiasing(antialiasing());
727@@ -730,6 +746,19 @@
728 }
729 }
730
731+MirSurfaceItem::FillMode MirSurfaceItem::fillMode() const
732+{
733+ return m_fillMode;
734+}
735+
736+void MirSurfaceItem::setFillMode(FillMode value)
737+{
738+ if (m_fillMode != value) {
739+ m_fillMode = value;
740+ Q_EMIT fillModeChanged(m_fillMode);
741+ }
742+}
743+
744 } // namespace qtmir
745
746 #include "mirsurfaceitem.moc"
747
748=== modified file 'src/modules/Unity/Application/mirsurfaceitem.h'
749--- src/modules/Unity/Application/mirsurfaceitem.h 2015-11-25 15:38:44 +0000
750+++ src/modules/Unity/Application/mirsurfaceitem.h 2015-12-11 13:19:15 +0000
751@@ -68,6 +68,9 @@
752 int surfaceHeight() const override;
753 void setSurfaceHeight(int value) override;
754
755+ FillMode fillMode() const override;
756+ void setFillMode(FillMode value) override;
757+
758 ////////
759 // QQuickItem
760
761@@ -168,6 +171,8 @@
762 Mir::OrientationAngle *m_orientationAngle;
763
764 bool m_consumesInput;
765+
766+ FillMode m_fillMode;
767 };
768
769 } // namespace qtmir
770
771=== modified file 'src/modules/Unity/Application/mirsurfacemanager.cpp'
772--- src/modules/Unity/Application/mirsurfacemanager.cpp 2015-10-09 15:35:05 +0000
773+++ src/modules/Unity/Application/mirsurfacemanager.cpp 2015-12-11 13:19:15 +0000
774@@ -110,7 +110,7 @@
775 }
776
777 if (session)
778- session->setSurface(qmlSurface);
779+ session->registerSurface(qmlSurface);
780
781 // Only notify QML of surface creation once it has drawn its first frame.
782 connect(qmlSurface, &MirSurfaceInterface::firstFrameDrawn, this, [=]() {
783
784=== modified file 'src/modules/Unity/Application/session.cpp'
785--- src/modules/Unity/Application/session.cpp 2015-08-31 09:51:28 +0000
786+++ src/modules/Unity/Application/session.cpp 2015-12-11 13:19:15 +0000
787@@ -40,13 +40,34 @@
788 namespace qtmir
789 {
790
791+namespace {
792+
793+const char *sessionStateToString(SessionInterface::State state)
794+{
795+ switch (state) {
796+ case SessionInterface::Starting:
797+ return "starting";
798+ case SessionInterface::Running:
799+ return "running";
800+ case SessionInterface::Suspending:
801+ return "suspending";
802+ case SessionInterface::Suspended:
803+ return "suspended";
804+ case SessionInterface::Stopped:
805+ return "stopped";
806+ default:
807+ return "???";
808+ }
809+}
810+
811+}
812+
813 Session::Session(const std::shared_ptr<ms::Session>& session,
814 const std::shared_ptr<ms::PromptSessionManager>& promptSessionManager,
815 QObject *parent)
816 : SessionInterface(parent)
817 , m_session(session)
818 , m_application(nullptr)
819- , m_surface(nullptr)
820 , m_parentSession(nullptr)
821 , m_children(new SessionModel(this))
822 , m_fullscreen(false)
823@@ -86,10 +107,15 @@
824 void Session::doSuspend()
825 {
826 Q_ASSERT(m_state == Session::Suspending);
827- if (m_surface) {
828- m_surface->stopFrameDropper();
829+
830+
831+ auto surfaceList = m_surfaces.list();
832+ if (surfaceList.empty()) {
833+ qCDebug(QTMIR_SESSIONS) << "Application::suspend - no surface to call stopFrameDropper() on!";
834 } else {
835- qDebug() << "Application::suspend - no surface to call stopFrameDropper() on!";
836+ for (int i = 0; i < surfaceList.count(); ++i) {
837+ surfaceList[i]->stopFrameDropper();
838+ }
839 }
840 setState(Suspended);
841 }
842@@ -120,14 +146,9 @@
843 return m_application;
844 }
845
846-MirSurfaceInterface* Session::surface() const
847+const ObjectListModel<MirSurfaceInterface>* Session::surfaces() const
848 {
849- // Only notify QML of surface creation once it has drawn its first frame.
850- if (m_surface && m_surface->isFirstFrameDrawn()) {
851- return m_surface;
852- } else {
853- return nullptr;
854- }
855+ return &m_surfaces;
856 }
857
858 SessionInterface* Session::parentSession() const
859@@ -142,6 +163,9 @@
860
861 void Session::setState(State state) {
862 if (state != m_state) {
863+ qCDebug(QTMIR_SESSIONS) << "Session::setState - session=" << name()
864+ << "state=" << sessionStateToString(state);
865+
866 m_state = state;
867 Q_EMIT stateChanged(m_state);
868 }
869@@ -166,55 +190,57 @@
870 Q_EMIT applicationChanged(application);
871 }
872
873-void Session::setSurface(MirSurfaceInterface *newSurface)
874+void Session::registerSurface(MirSurfaceInterface *newSurface)
875 {
876- qCDebug(QTMIR_SESSIONS) << "Session::setSurface - session=" << name() << "surface=" << newSurface;
877-
878- if (newSurface == m_surface) {
879- return;
880- }
881-
882- if (m_surface) {
883- m_surface->disconnect(this);
884- }
885-
886- MirSurfaceInterface *previousSurface = surface();
887- m_surface = newSurface;
888-
889- if (newSurface) {
890- connect(newSurface, &MirSurfaceInterface::stateChanged,
891- this, &Session::updateFullscreenProperty);
892-
893- // Only notify QML of surface creation once it has drawn its first frame.
894- if (m_surface->isFirstFrameDrawn()) {
895- setState(Running);
896- } else {
897- connect(newSurface, &MirSurfaceInterface::firstFrameDrawn,
898- this, &Session::onFirstSurfaceFrameDrawn);
899- }
900- }
901-
902- if (previousSurface != surface()) {
903- qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this
904- << " surface=" << m_surface;
905- Q_EMIT surfaceChanged(m_surface);
906+ qCDebug(QTMIR_SESSIONS) << "Session::resgisterSurface - session=" << name() << "surface=" << newSurface;
907+
908+ // Only notify QML of surface creation once it has drawn its first frame.
909+ if (newSurface->isFirstFrameDrawn()) {
910+ appendSurface(newSurface);
911+ } else {
912+ connect(newSurface, &MirSurfaceInterface::firstFrameDrawn,
913+ this, [this, newSurface]() { this->appendSurface(newSurface); });
914 }
915
916 updateFullscreenProperty();
917 }
918
919-void Session::onFirstSurfaceFrameDrawn()
920-{
921- qCDebug(QTMIR_SESSIONS).nospace() << "Session::surfaceChanged - session=" << this
922- << " surface=" << m_surface;
923- Q_EMIT surfaceChanged(m_surface);
924- setState(Running);
925+void Session::appendSurface(MirSurfaceInterface *newSurface)
926+{
927+ qCDebug(QTMIR_SESSIONS) << "Session::appendSurface - session=" << name() << "surface=" << newSurface;
928+
929+ connect(newSurface, &MirSurfaceInterface::stateChanged,
930+ this, &Session::updateFullscreenProperty);
931+
932+ m_surfaces.insert(m_surfaces.rowCount(), newSurface);
933+
934+ Q_EMIT lastSurfaceChanged(newSurface);
935+
936+ if (m_state == Starting) {
937+ setState(Running);
938+ }
939+}
940+
941+void Session::removeSurface(MirSurfaceInterface* surface)
942+{
943+ qCDebug(QTMIR_SESSIONS) << "Session::removeSurface - session=" << name() << "surface=" << surface;
944+
945+ surface->disconnect(this);
946+
947+ if (m_surfaces.contains(surface)) {
948+ bool lastSurfaceWasRemoved = lastSurface() == surface;
949+ m_surfaces.remove(surface);
950+ if (lastSurfaceWasRemoved) {
951+ Q_EMIT lastSurfaceChanged(lastSurface());
952+ }
953+ }
954 }
955
956 void Session::updateFullscreenProperty()
957 {
958- if (m_surface) {
959- setFullscreen(m_surface->state() == Mir::FullscreenState);
960+ if (m_surfaces.rowCount() > 0) {
961+ // TODO: Figure out something better
962+ setFullscreen(m_surfaces.list().at(0)->state() == Mir::FullscreenState);
963 } else {
964 // Keep the current value of the fullscreen property until we get a new
965 // surface
966@@ -232,7 +258,7 @@
967
968 void Session::suspend()
969 {
970- qCDebug(QTMIR_SESSIONS) << "Session::suspend - session=" << this << "state=" << applicationStateToStr(m_state);
971+ qCDebug(QTMIR_SESSIONS) << "Session::suspend - session=" << this << "state=" << sessionStateToString(m_state);
972 if (m_state == Running) {
973 session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
974 m_suspendTimer->start(1500);
975@@ -251,7 +277,7 @@
976
977 void Session::resume()
978 {
979- qCDebug(QTMIR_SESSIONS) << "Session::resume - session=" << this << "state=" << applicationStateToStr(m_state);
980+ qCDebug(QTMIR_SESSIONS) << "Session::resume - session=" << this << "state=" << sessionStateToString(m_state);
981
982 if (m_state == Suspending || m_state == Suspended) {
983 doResume();
984@@ -264,8 +290,11 @@
985 Q_ASSERT(m_suspendTimer->isActive());
986 m_suspendTimer->stop();
987 } else if (m_state == Suspended) {
988- Q_ASSERT(m_surface);
989- m_surface->startFrameDropper();
990+ Q_ASSERT(m_surfaces.rowCount() > 0);
991+ auto surfaceList = m_surfaces.list();
992+ for (int i = 0; i < surfaceList.count(); ++i) {
993+ surfaceList[i]->startFrameDropper();
994+ }
995 }
996
997 session()->set_lifecycle_state(mir_lifecycle_state_resumed);
998@@ -281,14 +310,33 @@
999 setState(Running);
1000 }
1001
1002+void Session::close()
1003+{
1004+ qCDebug(QTMIR_SESSIONS) << "Session::close - " << name();
1005+
1006+ auto surfaceList = m_surfaces.list();
1007+ for (int i = 0; i < surfaceList.count(); ++i) {
1008+ surfaceList[i]->close();
1009+ }
1010+}
1011+
1012 void Session::stop()
1013 {
1014+ qCDebug(QTMIR_SESSIONS) << "Session::stop - " << name();
1015+
1016 if (m_state != Stopped) {
1017+
1018 stopPromptSessions();
1019+
1020 if (m_suspendTimer->isActive())
1021 m_suspendTimer->stop();
1022- if (m_surface)
1023- m_surface->stopFrameDropper();
1024+
1025+ {
1026+ auto surfaceList = m_surfaces.list();
1027+ for (int i = 0; i < surfaceList.count(); ++i) {
1028+ surfaceList[i]->stopFrameDropper();
1029+ }
1030+ }
1031
1032 foreachChildSession([](SessionInterface* session) {
1033 session->stop();
1034@@ -304,6 +352,8 @@
1035 void Session::setLive(const bool live)
1036 {
1037 if (m_live != live) {
1038+ qCDebug(QTMIR_SESSIONS) << "Session::setLive - " << name() << "live=" << live;
1039+
1040 m_live = live;
1041 Q_EMIT liveChanged(m_live);
1042 if (!live) {
1043@@ -422,4 +472,13 @@
1044 }
1045 }
1046
1047+MirSurfaceInterface* Session::lastSurface() const
1048+{
1049+ if (m_surfaces.rowCount() > 0) {
1050+ return m_surfaces.list().last();
1051+ } else {
1052+ return nullptr;
1053+ }
1054+}
1055+
1056 } // namespace qtmir
1057
1058=== modified file 'src/modules/Unity/Application/session.h'
1059--- src/modules/Unity/Application/session.h 2015-08-31 09:51:28 +0000
1060+++ src/modules/Unity/Application/session.h 2015-12-11 13:19:15 +0000
1061@@ -52,17 +52,21 @@
1062 //getters
1063 QString name() const override;
1064 unity::shell::application::ApplicationInfoInterface* application() const override;
1065- MirSurfaceInterface* surface() const override;
1066+ MirSurfaceInterface* lastSurface() const override;
1067+ const ObjectListModel<MirSurfaceInterface>* surfaces() const override;
1068 SessionInterface* parentSession() const override;
1069 State state() const override;
1070 bool fullscreen() const override;
1071 bool live() const override;
1072
1073 void setApplication(unity::shell::application::ApplicationInfoInterface* item) override;
1074- void setSurface(MirSurfaceInterface* surface) override;
1075+
1076+ void registerSurface(MirSurfaceInterface* surface) override;
1077+ void removeSurface(MirSurfaceInterface* surface) override;
1078
1079 void suspend() override;
1080 void resume() override;
1081+ void close() override;
1082 void stop() override;
1083
1084 void addChildSession(SessionInterface* session) override;
1085@@ -88,7 +92,6 @@
1086
1087 private Q_SLOTS:
1088 void updateFullscreenProperty();
1089- void onFirstSurfaceFrameDrawn();
1090
1091 private:
1092 void setParentSession(Session* session);
1093@@ -97,9 +100,11 @@
1094
1095 void stopPromptSessions();
1096
1097+ void appendSurface(MirSurfaceInterface* surface);
1098+
1099 std::shared_ptr<mir::scene::Session> m_session;
1100 Application* m_application;
1101- MirSurfaceInterface* m_surface;
1102+ ObjectListModel<MirSurfaceInterface> m_surfaces;
1103 SessionInterface* m_parentSession;
1104 SessionModel* m_children;
1105 bool m_fullscreen;
1106
1107=== modified file 'src/modules/Unity/Application/session_interface.h'
1108--- src/modules/Unity/Application/session_interface.h 2015-08-31 09:51:28 +0000
1109+++ src/modules/Unity/Application/session_interface.h 2015-12-11 13:19:15 +0000
1110@@ -24,8 +24,12 @@
1111 #include <unity/shell/application/ApplicationInfoInterface.h>
1112
1113 // local
1114+#include "objectlistmodel.h"
1115 #include "sessionmodel.h"
1116
1117+// Qt
1118+#include <QQmlListProperty>
1119+
1120 namespace mir {
1121 namespace scene {
1122 class Session;
1123@@ -39,7 +43,12 @@
1124
1125 class SessionInterface : public QObject {
1126 Q_OBJECT
1127- Q_PROPERTY(MirSurfaceInterface* surface READ surface NOTIFY surfaceChanged)
1128+
1129+ // FIXME: Remove this once unity8 starts trully supporting multiple surfaces per applicaton.
1130+ // Ie, remove this once untiy8 moves from using this property to using the surfaces one.
1131+ Q_PROPERTY(MirSurfaceInterface* lastSurface READ lastSurface NOTIFY lastSurfaceChanged);
1132+
1133+ Q_PROPERTY(const ObjectListModel<MirSurfaceInterface>* surfaces READ surfaces CONSTANT);
1134 Q_PROPERTY(unity::shell::application::ApplicationInfoInterface* application READ application NOTIFY applicationChanged DESIGNABLE false)
1135 Q_PROPERTY(SessionInterface* parentSession READ parentSession NOTIFY parentSessionChanged DESIGNABLE false)
1136 Q_PROPERTY(SessionModel* childSessions READ childSessions DESIGNABLE false CONSTANT)
1137@@ -62,7 +71,8 @@
1138 //getters
1139 virtual QString name() const = 0;
1140 virtual unity::shell::application::ApplicationInfoInterface* application() const = 0;
1141- virtual MirSurfaceInterface* surface() const = 0;
1142+ virtual MirSurfaceInterface* lastSurface() const = 0;
1143+ virtual const ObjectListModel<MirSurfaceInterface>* surfaces() const = 0;
1144 virtual SessionInterface* parentSession() const = 0;
1145 virtual SessionModel* childSessions() const = 0;
1146 virtual State state() const = 0;
1147@@ -73,13 +83,15 @@
1148
1149 // For MirSurface and MirSurfaceManager use
1150
1151- virtual void setSurface(MirSurfaceInterface* surface) = 0;
1152+ virtual void registerSurface(MirSurfaceInterface* surface) = 0;
1153+ virtual void removeSurface(MirSurfaceInterface* surface) = 0;
1154
1155 // For Application use
1156
1157 virtual void setApplication(unity::shell::application::ApplicationInfoInterface* item) = 0;
1158 virtual void suspend() = 0;
1159 virtual void resume() = 0;
1160+ virtual void close() = 0;
1161 virtual void stop() = 0;
1162
1163 // For SessionManager use
1164@@ -98,16 +110,17 @@
1165 virtual void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session) = 0;
1166
1167 Q_SIGNALS:
1168- void surfaceChanged(MirSurfaceInterface*);
1169 void parentSessionChanged(SessionInterface*);
1170 void applicationChanged(unity::shell::application::ApplicationInfoInterface* application);
1171 void stateChanged(State state);
1172 void fullscreenChanged(bool fullscreen);
1173 void liveChanged(bool live);
1174+ void lastSurfaceChanged(MirSurfaceInterface* surface);
1175 };
1176
1177 } // namespace qtmir
1178
1179 Q_DECLARE_METATYPE(qtmir::SessionInterface*)
1180+Q_DECLARE_METATYPE(qtmir::ObjectListModel<qtmir::MirSurfaceInterface>*)
1181
1182 #endif // SESSION_INTERFACE_H
1183
1184=== modified file 'src/platforms/mirserver/mirwindowmanager.cpp'
1185--- src/platforms/mirserver/mirwindowmanager.cpp 2015-10-27 10:17:45 +0000
1186+++ src/platforms/mirserver/mirwindowmanager.cpp 2015-12-11 13:19:15 +0000
1187@@ -63,6 +63,11 @@
1188 MirSurfaceAttrib attrib,
1189 int value) override;
1190
1191+ void handle_raise_surface(
1192+ std::shared_ptr<mir::scene::Session> const& session,
1193+ std::shared_ptr<mir::scene::Surface> const& surface,
1194+ uint64_t timestamp) override;
1195+
1196 void modify_surface(
1197 const std::shared_ptr<mir::scene::Session>&,
1198 const std::shared_ptr<mir::scene::Surface>& surface,
1199@@ -144,6 +149,13 @@
1200 return false;
1201 }
1202
1203+void MirWindowManagerImpl::handle_raise_surface(
1204+ std::shared_ptr<mir::scene::Session> const& /*session*/,
1205+ std::shared_ptr<mir::scene::Surface> const& /*surface*/,
1206+ uint64_t /*timestamp*/)
1207+{
1208+}
1209+
1210 int MirWindowManagerImpl::set_surface_attribute(
1211 std::shared_ptr<ms::Session> const& /*session*/,
1212 std::shared_ptr<ms::Surface> const& surface,
1213
1214=== modified file 'src/platforms/mirserver/sessionauthorizer.cpp'
1215--- src/platforms/mirserver/sessionauthorizer.cpp 2015-10-08 11:20:30 +0000
1216+++ src/platforms/mirserver/sessionauthorizer.cpp 2015-12-11 13:19:15 +0000
1217@@ -88,3 +88,13 @@
1218 Q_UNUSED(creds)
1219 return true;
1220 }
1221+
1222+bool SessionAuthorizer::set_base_display_configuration_is_allowed(SessionCredentials const& creds)
1223+{
1224+ qCDebug(QTMIR_MIR_MESSAGES) << "SessionAuthorizer::set_base_display_configuration_is_allowed - this="
1225+ << this << "pid=" << creds.pid();
1226+
1227+ //FIXME Actually mediate this access for clients
1228+ Q_UNUSED(creds)
1229+ return false;
1230+}
1231
1232=== modified file 'src/platforms/mirserver/sessionauthorizer.h'
1233--- src/platforms/mirserver/sessionauthorizer.h 2015-10-08 10:19:02 +0000
1234+++ src/platforms/mirserver/sessionauthorizer.h 2015-12-11 13:19:15 +0000
1235@@ -36,6 +36,7 @@
1236
1237 bool connection_is_allowed(mir::frontend::SessionCredentials const& creds) override;
1238 bool configure_display_is_allowed(mir::frontend::SessionCredentials const& creds) override;
1239+ bool set_base_display_configuration_is_allowed(mir::frontend::SessionCredentials const& creds) override;
1240 bool screencast_is_allowed(mir::frontend::SessionCredentials const& creds) override;
1241 bool prompt_session_is_allowed(mir::frontend::SessionCredentials const& creds) override;
1242
1243
1244=== modified file 'src/platforms/mirserver/surfaceobserver.cpp'
1245--- src/platforms/mirserver/surfaceobserver.cpp 2015-11-11 18:56:13 +0000
1246+++ src/platforms/mirserver/surfaceobserver.cpp 2015-12-11 13:19:15 +0000
1247@@ -60,7 +60,7 @@
1248 }
1249 }
1250
1251-void SurfaceObserver::frame_posted(int /*frames_available*/)
1252+void SurfaceObserver::frame_posted(int /*frames_available*/, mir::geometry::Size const& /*size*/)
1253 {
1254 m_framesPosted = true;
1255 if (m_listener) {
1256@@ -73,6 +73,11 @@
1257 Q_EMIT nameChanged(QString::fromUtf8(name));
1258 }
1259
1260+void SurfaceObserver::cursor_image_removed()
1261+{
1262+ Q_EMIT cursorChanged(QCursor());
1263+}
1264+
1265 void SurfaceObserver::attrib_changed(MirSurfaceAttrib attribute, int value)
1266 {
1267 if (m_listener) {
1268
1269=== modified file 'src/platforms/mirserver/surfaceobserver.h'
1270--- src/platforms/mirserver/surfaceobserver.h 2015-11-10 11:07:23 +0000
1271+++ src/platforms/mirserver/surfaceobserver.h 2015-12-11 13:19:15 +0000
1272@@ -39,7 +39,7 @@
1273 void hidden_set_to(bool) override {}
1274
1275 // Get new frame notifications from Mir, called from a Mir thread.
1276- void frame_posted(int frames_available) override;
1277+ void frame_posted(int frames_available, mir::geometry::Size const& size ) override;
1278
1279 void alpha_set_to(float) override {}
1280 void transformation_set_to(glm::mat4 const&) override {}
1281@@ -49,6 +49,7 @@
1282 void client_surface_close_requested() override {}
1283 void keymap_changed(xkb_rule_names const &) override {}
1284 void renamed(char const * name) override;
1285+ void cursor_image_removed() override;
1286
1287 Q_SIGNALS:
1288 void attributeChanged(const MirSurfaceAttrib attribute, const int value);
1289
1290=== modified file 'tests/common/mock_display_configuration.h'
1291--- tests/common/mock_display_configuration.h 2015-08-20 10:16:54 +0000
1292+++ tests/common/mock_display_configuration.h 2015-12-11 13:19:15 +0000
1293@@ -29,6 +29,7 @@
1294
1295 MOCK_CONST_METHOD1(for_each_output, void(std::function<void(mir::graphics::DisplayConfigurationOutput const&)>));
1296 MOCK_METHOD1(for_each_output, void(std::function<void(mir::graphics::UserDisplayConfigurationOutput&)>));
1297+ MOCK_CONST_METHOD0(clone, std::unique_ptr<mir::graphics::DisplayConfiguration>());
1298
1299 MOCK_CONST_METHOD0(valid, bool());
1300 };
1301
1302=== modified file 'tests/mirserver/ScreenController/stub_display.h'
1303--- tests/mirserver/ScreenController/stub_display.h 2015-10-14 22:59:04 +0000
1304+++ tests/mirserver/ScreenController/stub_display.h 2015-12-11 13:19:15 +0000
1305@@ -38,6 +38,12 @@
1306 }
1307 }
1308
1309+
1310+ std::unique_ptr<mg::DisplayConfiguration> clone() const override
1311+ {
1312+ return std::make_unique<MockDisplayConfiguration>();
1313+ }
1314+
1315 private:
1316 const std::vector<mg::DisplayConfigurationOutput> m_config;
1317 };
1318
1319=== modified file 'tests/mirserver/WindowManager/stub_session.cpp'
1320--- tests/mirserver/WindowManager/stub_session.cpp 2015-10-28 09:55:42 +0000
1321+++ tests/mirserver/WindowManager/stub_session.cpp 2015-12-11 13:19:15 +0000
1322@@ -90,6 +90,10 @@
1323 {
1324 }
1325
1326+void StubSession::destroy_surface(std::weak_ptr<mir::scene::Surface> const& /*surface*/)
1327+{
1328+}
1329+
1330 std::shared_ptr<mir::scene::Surface> StubSession::surface(
1331 mir::frontend::SurfaceId /*surface*/) const
1332 {
1333
1334=== modified file 'tests/mirserver/WindowManager/stub_session.h'
1335--- tests/mirserver/WindowManager/stub_session.h 2015-10-28 09:55:42 +0000
1336+++ tests/mirserver/WindowManager/stub_session.h 2015-12-11 13:19:15 +0000
1337@@ -43,8 +43,8 @@
1338 mir::frontend::SurfaceId create_surface(
1339 mir::scene::SurfaceCreationParameters const& params,
1340 std::shared_ptr<mir::frontend::EventSink> const& sink) override;
1341-
1342 void destroy_surface(mir::frontend::SurfaceId surface) override;
1343+ void destroy_surface(std::weak_ptr<mir::scene::Surface> const& surface) override;
1344
1345 std::shared_ptr<mir::scene::Surface> surface(mir::frontend::SurfaceId surface) const override;
1346 std::shared_ptr<mir::scene::Surface> surface_after(std::shared_ptr<mir::scene::Surface> const&) const override;
1347
1348=== modified file 'tests/mirserver/WindowManager/window_manager.cpp'
1349--- tests/mirserver/WindowManager/window_manager.cpp 2015-10-12 16:28:21 +0000
1350+++ tests/mirserver/WindowManager/window_manager.cpp 2015-12-11 13:19:15 +0000
1351@@ -42,7 +42,7 @@
1352 {
1353 MOCK_METHOD1(clip_to_output, void (Rectangle& rect));
1354 MOCK_METHOD1(size_to_output, void (Rectangle& rect));
1355- MOCK_METHOD2(place_in_output, void (mir::graphics::DisplayConfigurationOutputId id, Rectangle& rect));
1356+ MOCK_METHOD2(place_in_output, bool (mir::graphics::DisplayConfigurationOutputId id, Rectangle& rect));
1357 };
1358
1359 struct MockSurface : StubSurface
1360
1361=== modified file 'tests/modules/Application/CMakeLists.txt'
1362--- tests/modules/Application/CMakeLists.txt 2015-08-19 20:17:56 +0000
1363+++ tests/modules/Application/CMakeLists.txt 2015-12-11 13:19:15 +0000
1364@@ -11,6 +11,11 @@
1365 ${CMAKE_SOURCE_DIR}/src/modules
1366 ${CMAKE_SOURCE_DIR}/tests/modules/common
1367 ${MIRSERVER_INCLUDE_DIRS}
1368+
1369+ ${Qt5Core_INCLUDE_DIRS}
1370+ ${Qt5GUI_INCLUDE_DIRS}
1371+ ${Qt5Quick_INCLUDE_DIRS}
1372+ ${Qt5DBus_INCLUDE_DIRS}
1373 )
1374
1375 add_executable(application_test ${APPLICATION_TEST_SOURCES})
1376
1377=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
1378--- tests/modules/ApplicationManager/application_manager_test.cpp 2015-11-17 14:16:22 +0000
1379+++ tests/modules/ApplicationManager/application_manager_test.cpp 2015-12-11 13:19:15 +0000
1380@@ -50,7 +50,7 @@
1381
1382 SessionInterface* qmlSession = sessionManager.findSession(mirSession);
1383 if (qmlSession) {
1384- qmlSession->setSurface(qmlSurface);
1385+ qmlSession->registerSurface(qmlSurface);
1386 }
1387
1388 // I assume that applicationManager ignores the mirSurface parameter, so sending
1389@@ -1737,6 +1737,114 @@
1390 }
1391 }
1392
1393+TEST_F(ApplicationManagerTests, lifecycleExemptAppIsNotSuspended)
1394+{
1395+ using namespace ::testing;
1396+
1397+ const QString appId("testAppId");
1398+ const quint64 procId = 12345;
1399+
1400+ // Set up Mocks & signal watcher
1401+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1402+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1403+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1404+
1405+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1406+
1407+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1408+ .Times(1)
1409+ .WillOnce(Return(true));
1410+
1411+ auto the_app = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1412+ applicationManager.onProcessStarting(appId);
1413+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1414+ bool authed = true;
1415+ applicationManager.authorizeSession(procId, authed);
1416+ onSessionStarting(session);
1417+
1418+ // App creates surface, focuses it so state is running
1419+ FakeMirSurface *surface = new FakeMirSurface;
1420+ onSessionCreatedSurface(session.get(), surface);
1421+ surface->drawFirstFrame();
1422+
1423+ // Test normal lifecycle management as a control group
1424+ ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
1425+ ASSERT_EQ(Application::ProcessState::ProcessRunning, the_app->processState());
1426+
1427+ EXPECT_CALL(*(mir::scene::MockSession*)session.get(), set_lifecycle_state(mir_lifecycle_state_will_suspend));
1428+ the_app->setRequestedState(Application::RequestedSuspended);
1429+ ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());
1430+
1431+ static_cast<qtmir::Session*>(the_app->session())->doSuspend();
1432+ ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());
1433+ applicationManager.onProcessSuspended(the_app->appId());
1434+ ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());
1435+
1436+ EXPECT_CALL(*(mir::scene::MockSession*)session.get(), set_lifecycle_state(mir_lifecycle_state_resumed));
1437+ the_app->setRequestedState(Application::RequestedRunning);
1438+
1439+ EXPECT_EQ(Application::Running, the_app->state());
1440+
1441+ // Now mark the app as exempt from lifecycle management and retest
1442+ the_app->setExemptFromLifecycle(true);
1443+
1444+ EXPECT_EQ(Application::Running, the_app->state());
1445+
1446+ EXPECT_CALL(*(mir::scene::MockSession*)session.get(), set_lifecycle_state(_)).Times(0);
1447+ the_app->setRequestedState(Application::RequestedSuspended);
1448+
1449+ // And expect it to be running still
1450+ ASSERT_EQ(Application::InternalState::RunningInBackground, the_app->internalState());
1451+
1452+ the_app->setRequestedState(Application::RequestedRunning);
1453+
1454+ EXPECT_EQ(Application::Running, the_app->state());
1455+ ASSERT_EQ(Application::InternalState::Running, the_app->internalState());
1456+}
1457+
1458+/*
1459+ * Test lifecycle exempt applications have their wakelocks released when shell tries to suspend them
1460+ */
1461+TEST_F(ApplicationManagerTests, lifecycleExemptAppHasWakelockReleasedOnAttemptedSuspend)
1462+{
1463+ using namespace ::testing;
1464+
1465+ const QString appId("testAppId");
1466+ const quint64 procId = 12345;
1467+
1468+ // Set up Mocks & signal watcher
1469+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1470+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1471+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1472+
1473+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1474+
1475+ EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1476+ .Times(1)
1477+ .WillOnce(Return(true));
1478+
1479+ auto application = applicationManager.startApplication(appId, ApplicationManager::NoFlag);
1480+ applicationManager.onProcessStarting(appId);
1481+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
1482+ bool authed = true;
1483+ applicationManager.authorizeSession(procId, authed);
1484+ onSessionStarting(session);
1485+
1486+ // App creates surface, focuses it so state is running
1487+ FakeMirSurface *surface = new FakeMirSurface;
1488+ onSessionCreatedSurface(session.get(), surface);
1489+ surface->drawFirstFrame();
1490+
1491+ // Mark app as exempt
1492+ application->setExemptFromLifecycle(true);
1493+
1494+ application->setRequestedState(Application::RequestedSuspended);
1495+
1496+ EXPECT_FALSE(sharedWakelock.enabled());
1497+ ASSERT_EQ(Application::InternalState::RunningInBackground, application->internalState());
1498+ EXPECT_EQ(Application::Running, application->state());
1499+}
1500+
1501 /*
1502 * Test that when user stops an application, application does not delete QML cache
1503 */
1504@@ -1870,3 +1978,158 @@
1505 EXPECT_EQ(0, applicationManager.count());
1506 EXPECT_TRUE(dir.exists());
1507 }
1508+
1509+/*
1510+ * Test that there is an attempt at polite exiting of the app by requesting closure of the surface.
1511+ */
1512+TEST_F(ApplicationManagerTests,requestSurfaceCloseOnStop)
1513+{
1514+ using namespace ::testing;
1515+
1516+ const QString appId("testAppId");
1517+ quint64 procId = 5551;
1518+ Application* app = startApplication(procId, appId);
1519+ std::shared_ptr<mir::scene::Session> session = app->session()->session();
1520+
1521+ FakeMirSurface *surface = new FakeMirSurface;
1522+ onSessionCreatedSurface(session.get(), surface);
1523+ surface->drawFirstFrame();
1524+
1525+ QSignalSpy spy(surface, SIGNAL(closeRequested()));
1526+
1527+ // Stop app
1528+ applicationManager.stopApplication(appId);
1529+
1530+ EXPECT_EQ(1, spy.count());
1531+}
1532+
1533+
1534+// * Test that if there is no surface available to the app when it is stopped, that it is forced to close.
1535+TEST_F(ApplicationManagerTests,forceAppDeleteWhenRemovedWithMissingSurface)
1536+{
1537+ using namespace ::testing;
1538+
1539+ int argc = 0;
1540+ char* argv[0];
1541+ QCoreApplication qtApp(argc, argv); // app for deleteLater event
1542+
1543+ const QString appId("testAppId");
1544+ quint64 procId = 5551;
1545+ Application* app = startApplication(procId, appId);
1546+
1547+ QSignalSpy spy(app, SIGNAL(destroyed(QObject*)));
1548+ QObject::connect(app, &QObject::destroyed, app, [&qtApp](){ qtApp.quit(); });
1549+
1550+ // Stop app
1551+ applicationManager.stopApplication(appId);
1552+ qtApp.exec();
1553+ EXPECT_EQ(1, spy.count());
1554+}
1555+
1556+/*
1557+ * Test that if an application is started while it is still attempting to close, it is queued to start again.
1558+ */
1559+TEST_F(ApplicationManagerTests,applicationStartQueuedOnStartStopStart)
1560+{
1561+ using namespace ::testing;
1562+
1563+ int argc = 0;
1564+ char* argv[0];
1565+ QCoreApplication qtApp(argc, argv); // app for deleteLater event
1566+
1567+ const QString appId("testAppId");
1568+ quint64 procId = 5551;
1569+ Application* app = startApplication(procId, appId);
1570+ std::shared_ptr<mir::scene::Session> session = app->session()->session();
1571+
1572+ FakeMirSurface *surface = new FakeMirSurface;
1573+ onSessionCreatedSurface(session.get(), surface);
1574+ surface->drawFirstFrame();
1575+
1576+ EXPECT_EQ(Application::InternalState::Running, app->internalState());
1577+
1578+ // Stop app
1579+ applicationManager.stopApplication(appId);
1580+
1581+ EXPECT_EQ(Application::InternalState::Closing, app->internalState());
1582+
1583+ // // Set up Mocks & signal watcher
1584+ auto mockDesktopFileReader = new NiceMock<MockDesktopFileReader>(appId, QFileInfo());
1585+ ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1586+ ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1587+ ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1588+
1589+ EXPECT_EQ(nullptr, applicationManager.startApplication(appId, ApplicationManager::NoFlag));
1590+
1591+ QSignalSpy spy(&applicationManager, SIGNAL(applicationAdded(const QString&)));
1592+ applicationManager.onProcessStopped(appId);
1593+
1594+ QObject::connect(&applicationManager, &ApplicationManager::applicationAdded,
1595+ &applicationManager, [&qtApp, appId](const QString& startedApp) {
1596+ if (startedApp == appId) {
1597+ qtApp.quit();
1598+ }
1599+ });
1600+
1601+ qtApp.exec();
1602+ EXPECT_EQ(1, spy.count());
1603+}
1604+
1605+/*
1606+ * Test that there is an attempt at polite exiting of the app by requesting closure of the surface.
1607+ */
1608+TEST_F(ApplicationManagerTests,suspendedApplicationResumesClosesAndDeletes)
1609+{
1610+ using namespace ::testing;
1611+
1612+ const QString appId("testAppId");
1613+ quint64 procId = 5551;
1614+ Application* app = startApplication(procId, appId);
1615+ std::shared_ptr<mir::scene::Session> session = app->session()->session();
1616+
1617+ FakeMirSurface *surface = new FakeMirSurface;
1618+ onSessionCreatedSurface(session.get(), surface);
1619+ surface->drawFirstFrame();
1620+ EXPECT_EQ(Application::InternalState::Running, app->internalState());
1621+ EXPECT_EQ(SessionInterface::Running, app->session()->state());
1622+
1623+ // Suspend the application.
1624+ suspend(app);
1625+ EXPECT_EQ(Application::InternalState::Suspended, app->internalState());
1626+
1627+ // Stop app
1628+ applicationManager.stopApplication(appId);
1629+ EXPECT_EQ(Application::InternalState::Closing, app->internalState());
1630+ EXPECT_EQ(SessionInterface::Running, app->session()->state());
1631+}
1632+
1633+/*
1634+ * Test that a application which fails to close will eventually be forceable closed.
1635+ */
1636+TEST_F(ApplicationManagerTests,failedApplicationCloseEventualyDeletesApplication)
1637+{
1638+ using namespace ::testing;
1639+
1640+ int argc = 0;
1641+ char* argv[0];
1642+ QCoreApplication qtApp(argc, argv); // app for deleteLater event
1643+
1644+ const QString appId("testAppId");
1645+ quint64 procId = 5551;
1646+ Application* app = startApplication(procId, appId);
1647+ std::shared_ptr<mir::scene::Session> session = app->session()->session();
1648+
1649+ FakeMirSurface *surface = new FakeMirSurface;
1650+ onSessionCreatedSurface(session.get(), surface);
1651+ surface->drawFirstFrame();
1652+
1653+ EXPECT_EQ(Application::InternalState::Running, app->internalState());
1654+
1655+ QSignalSpy spy(app, SIGNAL(destroyed(QObject*)));
1656+ QObject::connect(app, &QObject::destroyed, app, [&qtApp](){ qtApp.quit(); });
1657+
1658+ // Stop app
1659+ applicationManager.stopApplication(appId);
1660+ qtApp.exec();
1661+ EXPECT_EQ(1, spy.count());
1662+}
1663
1664=== modified file 'tests/modules/SessionManager/session_test.cpp'
1665--- tests/modules/SessionManager/session_test.cpp 2015-10-08 11:20:30 +0000
1666+++ tests/modules/SessionManager/session_test.cpp 2015-12-11 13:19:15 +0000
1667@@ -58,7 +58,7 @@
1668 EXPECT_EQ(Session::Starting, session->state());
1669
1670 FakeMirSurface *surface = new FakeMirSurface;
1671- session->setSurface(surface);
1672+ session->registerSurface(surface);
1673
1674 // Still on Starting as the surface hasn't drawn its first frame yet
1675 EXPECT_EQ(Session::Starting, session->state());
1676@@ -228,7 +228,7 @@
1677 auto session = std::make_shared<qtmir::Session>(mirSession, mirServer->the_prompt_session_manager());
1678 {
1679 FakeMirSurface *surface = new FakeMirSurface;
1680- session->setSurface(surface);
1681+ session->registerSurface(surface);
1682 surface->drawFirstFrame();
1683 }
1684 EXPECT_EQ(Session::Running, session->state());
1685@@ -259,7 +259,7 @@
1686 auto session = std::make_shared<qtmir::Session>(mirSession, mirServer->the_prompt_session_manager());
1687 {
1688 FakeMirSurface *surface = new FakeMirSurface;
1689- session->setSurface(surface);
1690+ session->registerSurface(surface);
1691 surface->drawFirstFrame();
1692 }
1693 EXPECT_EQ(Session::Running, session->state());
1694
1695=== modified file 'tests/modules/SurfaceManager/mirsurface_test.cpp'
1696--- tests/modules/SurfaceManager/mirsurface_test.cpp 2015-11-17 14:47:38 +0000
1697+++ tests/modules/SurfaceManager/mirsurface_test.cpp 2015-12-11 13:19:15 +0000
1698@@ -60,7 +60,7 @@
1699 .WillRepeatedly(Return(1));
1700
1701 MirSurface *surface = new MirSurface(mockSurface, fakeSession, nullptr, surfaceObserver);
1702- surfaceObserver->frame_posted(1);
1703+ surfaceObserver->frame_posted(1, mir::geometry::Size{1,1});
1704
1705 QSignalSpy spyFrameDropped(surface, SIGNAL(frameDropped()));
1706 QTest::qWait(300);
1707
1708=== modified file 'tests/modules/common/fake_mirsurface.h'
1709--- tests/modules/common/fake_mirsurface.h 2015-11-25 15:38:39 +0000
1710+++ tests/modules/common/fake_mirsurface.h 2015-12-11 13:19:15 +0000
1711@@ -174,6 +174,13 @@
1712
1713 QCursor cursor() const override { return QCursor(); }
1714
1715+ void close() override {
1716+ Q_EMIT closeRequested();
1717+ }
1718+
1719+Q_SIGNALS:
1720+ void closeRequested();
1721+
1722 public Q_SLOTS:
1723 void onCompositorSwappedBuffers() override {}
1724
1725
1726=== modified file 'tests/modules/common/fake_session.h'
1727--- tests/modules/common/fake_session.h 2015-10-20 09:57:17 +0000
1728+++ tests/modules/common/fake_session.h 2015-12-11 13:19:15 +0000
1729@@ -15,6 +15,7 @@
1730 */
1731
1732 #include <Unity/Application/session_interface.h>
1733+#include <QDebug>
1734
1735 #ifndef QTMIR_FAKE_SESSION_H
1736 #define QTMIR_FAKE_SESSION_H
1737@@ -38,7 +39,8 @@
1738
1739 QString name() const override { return QString("foo-session"); }
1740 unity::shell::application::ApplicationInfoInterface* application() const override { return m_application; }
1741- MirSurfaceInterface* surface() const override { return nullptr; }
1742+ MirSurfaceInterface* lastSurface() const override { return nullptr; }
1743+ const ObjectListModel<MirSurfaceInterface>* surfaces() const override { return nullptr; }
1744 SessionInterface* parentSession() const override { return nullptr; }
1745 SessionModel* childSessions() const override { return nullptr; }
1746 State state() const override { return m_state; }
1747@@ -49,7 +51,8 @@
1748
1749 // For MirSurfaceItem and MirSurfaceManager use
1750
1751- void setSurface(MirSurfaceInterface*) override {}
1752+ void registerSurface(MirSurfaceInterface*) override {}
1753+ void removeSurface(MirSurfaceInterface*) override {}
1754
1755 // For Application use
1756
1757@@ -73,6 +76,9 @@
1758 setState(Stopped);
1759 }
1760
1761+ void close() override {
1762+ }
1763+
1764 // For SessionManager use
1765
1766 void addChildSession(SessionInterface*) override {}
1767
1768=== modified file 'tests/modules/common/mock_mir_session.h'
1769--- tests/modules/common/mock_mir_session.h 2015-10-07 14:38:44 +0000
1770+++ tests/modules/common/mock_mir_session.h 2015-12-11 13:19:15 +0000
1771@@ -56,6 +56,7 @@
1772 frontend::SurfaceId(SurfaceCreationParameters const&,
1773 std::shared_ptr<frontend::EventSink> const&));
1774 MOCK_METHOD1(destroy_surface, void (frontend::SurfaceId));
1775+ MOCK_METHOD1(destroy_surface, void (std::weak_ptr<Surface> const& surface));
1776
1777 MOCK_METHOD0(hide, void());
1778 MOCK_METHOD0(show, void());
1779
1780=== modified file 'tests/modules/common/mock_session.h'
1781--- tests/modules/common/mock_session.h 2015-08-31 09:51:28 +0000
1782+++ tests/modules/common/mock_session.h 2015-12-11 13:19:15 +0000
1783@@ -36,19 +36,26 @@
1784
1785 MOCK_CONST_METHOD0(name, QString());
1786 MOCK_CONST_METHOD0(application, unity::shell::application::ApplicationInfoInterface*());
1787- MOCK_CONST_METHOD0(surface, MirSurfaceInterface*());
1788+ MOCK_CONST_METHOD0(lastSurface, MirSurfaceInterface*());
1789+ MOCK_CONST_METHOD0(surfaces, const ObjectListModel<MirSurfaceInterface>*());
1790 MOCK_CONST_METHOD0(parentSession, SessionInterface*());
1791+ MOCK_CONST_METHOD0(childSessions, SessionModel*());
1792
1793 MOCK_CONST_METHOD0(state, State());
1794
1795 MOCK_CONST_METHOD0(fullscreen, bool());
1796 MOCK_CONST_METHOD0(live, bool());
1797
1798+ MOCK_CONST_METHOD0(session, std::shared_ptr<mir::scene::Session>());
1799+
1800+ MOCK_METHOD1(registerSurface, void(MirSurfaceInterface* surface));
1801+ MOCK_METHOD1(removeSurface, void(MirSurfaceInterface* surface));
1802+
1803 MOCK_METHOD1(setApplication, void(unity::shell::application::ApplicationInfoInterface* item));
1804- MOCK_METHOD1(setSurface, void(MirSurfaceInterface* surface));
1805
1806 MOCK_METHOD0(suspend, void());
1807 MOCK_METHOD0(resume, void());
1808+ MOCK_METHOD0(close, void());
1809 MOCK_METHOD0(stop, void());
1810
1811 MOCK_METHOD1(addChildSession, void(SessionInterface* session));
1812@@ -56,13 +63,9 @@
1813 MOCK_METHOD1(removeChildSession, void(SessionInterface* session));
1814 MOCK_CONST_METHOD1(foreachChildSession, void(std::function<void(SessionInterface* session)> f));
1815
1816- MOCK_CONST_METHOD0(session, std::shared_ptr<mir::scene::Session>());
1817-
1818 MOCK_CONST_METHOD0(activePromptSession, std::shared_ptr<mir::scene::PromptSession>());
1819 MOCK_CONST_METHOD1(foreachPromptSession, void(std::function<void(const std::shared_ptr<mir::scene::PromptSession>&)> f));
1820
1821- MOCK_CONST_METHOD0(childSessions, SessionModel*());
1822-
1823 void setState(State state) {
1824 if (m_state != state) {
1825 m_state = state;
1826
1827=== modified file 'tests/modules/common/qtmir_test.cpp'
1828--- tests/modules/common/qtmir_test.cpp 2015-10-01 16:48:38 +0000
1829+++ tests/modules/common/qtmir_test.cpp 2015-12-11 13:19:15 +0000
1830@@ -26,6 +26,9 @@
1831 case Application::InternalState::Running:
1832 *os << "Running";
1833 break;
1834+ case Application::InternalState::RunningInBackground:
1835+ *os << "RunningInBackground";
1836+ break;
1837 case Application::InternalState::SuspendingWaitSession:
1838 *os << "SuspendingWaitSession";
1839 break;
1840
1841=== modified file 'tests/modules/common/qtmir_test.h'
1842--- tests/modules/common/qtmir_test.h 2015-11-19 12:56:02 +0000
1843+++ tests/modules/common/qtmir_test.h 2015-12-11 13:19:15 +0000
1844@@ -139,7 +139,9 @@
1845 ON_CALL(*mockDesktopFileReader, loaded()).WillByDefault(Return(true));
1846 ON_CALL(*mockDesktopFileReader, appId()).WillByDefault(Return(appId));
1847
1848- ON_CALL(desktopFileReaderFactory, createInstance(appId, _)).WillByDefault(Return(mockDesktopFileReader));
1849+ EXPECT_CALL(desktopFileReaderFactory, createInstance(appId, _))
1850+ .Times(1)
1851+ .WillOnce(Return(mockDesktopFileReader));
1852
1853 EXPECT_CALL(appController, startApplicationWithAppIdAndArgs(appId, _))
1854 .Times(1)
1855@@ -153,7 +155,11 @@
1856 EXPECT_EQ(authed, true);
1857
1858 auto appSession = std::make_shared<mir::scene::MockSession>(appId.toStdString(), procId);
1859+ applicationManager.onSessionStarting(appSession);
1860 sessionManager.onSessionStarting(appSession);
1861+
1862+ Mock::VerifyAndClearExpectations(&appController);
1863+ Mock::VerifyAndClearExpectations(&desktopFileReaderFactory);
1864 return application;
1865 }
1866

Subscribers

People subscribed via source and target branches