Merge lp:~dandrader/qtmir/multiSessionApp into lp:qtmir

Proposed by Daniel d'Andrada
Status: Merged
Approved by: Gerry Boland
Approved revision: 639
Merged at revision: 638
Proposed branch: lp:~dandrader/qtmir/multiSessionApp
Merge into: lp:qtmir
Diff against target: 1193 lines (+292/-239)
9 files modified
src/modules/Unity/Application/application.cpp (+146/-90)
src/modules/Unity/Application/application.h (+12/-13)
src/modules/Unity/Application/application_manager.cpp (+29/-58)
src/modules/Unity/Application/application_manager.h (+3/-7)
src/modules/Unity/Application/dbusfocusinfo.cpp (+28/-25)
src/modules/Unity/Application/session.cpp (+1/-1)
src/modules/Unity/Application/session_interface.h (+6/-5)
tests/modules/Application/application_test.cpp (+15/-15)
tests/modules/ApplicationManager/application_manager_test.cpp (+52/-25)
To merge this branch: bzr merge lp:~dandrader/qtmir/multiSessionApp
Reviewer Review Type Date Requested Status
Michał Sawicz Approve
Gerry Boland (community) Approve
Unity8 CI Bot (community) continuous-integration Approve
Review via email: mp+321581@code.launchpad.net

Commit message

Support multiple sessions per Application

Description of the change

* Are there any related MPs required for this MP to build/function as expected? Please list.
No

* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

To post a comment you must log in.
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :

PASSED: Continuous integration, rev:636
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/652/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4810
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4838
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4650
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4650/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4650
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4650/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4650
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4650/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4650
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4650/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4650
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4650/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4650
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4650/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

+ if (session->state() > combinedState) {
+ combinedState = session->state();
+ }
I guess this is as good a "combined" state as any.

The code, you're using the convenience to the order of the states in its enum? Strikes me as something easily broken in future - but I'm glad to see you've added a comment to make that less likely.

+void Application::die()
terminate() would match the "kill" term

Going from memory, the setApplicationPid stuff was a workaround for supporting apps that are not launched via UAL - we had to save the pid we got from Mir. Are apps launched with desktop_file_hint still working?

How are you testing this? I made this simple client which might be handy: git clone -b multiple-connections https://git.launchpad.net/~gerboland/+git/basic_mir_client

Is moving the InitialSurfaceSize calls into the Application required for this MP?

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

On 31/03/2017 13:26, Gerry Boland wrote:
> +void Application::die()
> terminate() would match the "kill" term

Ok. Renamed.

> Going from memory, the setApplicationPid stuff was a workaround for supporting apps that are not launched via UAL - we had to save the pid we got from Mir. Are apps launched with desktop_file_hint still working?

Ooops! You're right. Being able to remove this bookkeeping of pids from
ApplicationManager was too good to be true.
Fixed.

> How are you testing this? I made this simple client which might be handy: git clone -b multiple-connections https://git.launchpad.net/~gerboland/+git/basic_mir_client

That's not handy. Launching some random app from the application drawer
is handy. I tried with nautilus and mahjongg.
As a general rule: try to launch some apps from the app drawer without
this patch. You should get endless splash screens for them. Then install
this patch and try the same ones again. They should now work.

You can also add debug-level logging to surface and application
categories and check in unity8.log that such applications are indeed
getting *3* separate mir sessions each (at least the gtk ones I checked).

> Is moving the InitialSurfaceSize calls into the Application required for this MP?

That was how things were being done before the latest landing (had the
patch ready) and that sure is simpler as well as the application has
many sessions and thus entries in that InitialSurfaceSize singleton.

Application knows his own pids and his own initialSurfaceSize. So it can
update InitialSurfaceSize on his own. No need to involve
ApplicationManager in that. Don't know what you gain from it.
Furthermore, ApplicationManager no longer keeps tracks of all pids going
around. It just momentarily holds them between the moment a session is
authorized and the moment it shows up to be assigned to an Application
object.

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

PASSED: Continuous integration, rev:638
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/655/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4816
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4844
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4655
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4655/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4655
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4655/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4655
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4655/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4655
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4655/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4655
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4655/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4655
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4655/artifact/output/*zip*/output.zip

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

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

PASSED: Continuous integration, rev:639
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/656/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4818
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4846
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4657
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4657/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4657
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4657/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4657
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4657/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4657
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4657/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4657
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4657/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4657
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4657/artifact/output/*zip*/output.zip

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

review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Ok, this does improve the situation a lot.

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

<bregma> Saviq, the first way to test the multiple-connection situation would probably be to create a custom .desktop file that removes GDK_BACKEND from the app's environment, which will force it to autoprobe and use Mir instead

Saviq tried this and got http://pastebin.ubuntu.com/24319827/ where the second mir connection from the process is rejected. I suspect things changed under our feet, can you re-test

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

Still fixes the main thing, will keep

review: Approve
Revision history for this message
Michał Sawicz (saviq) wrote :

Sorry, apparently GTK_BACKEND="" is incorrect, ="*" or unsetting it altogether showed GTK apps launch fine.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/modules/Unity/Application/application.cpp'
2--- src/modules/Unity/Application/application.cpp 2017-03-28 17:12:03 +0000
3+++ src/modules/Unity/Application/application.cpp 2017-03-31 21:11:21 +0000
4@@ -28,10 +28,14 @@
5
6 // QPA mirserver
7 #include "logging.h"
8+#include "initialsurfacesizes.h"
9
10 // Unity API
11 #include <unity/shell/application/MirSurfaceInterface.h>
12
13+// std
14+#include <csignal>
15+
16 namespace unityapp = unity::shell::application;
17
18 #define DEBUG_MSG qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::" << __func__
19@@ -50,12 +54,10 @@
20 , m_supportedStages(Application::MainStage|Application::SideStage)
21 , m_state(InternalState::Starting)
22 , m_arguments(arguments)
23- , m_session(nullptr)
24 , m_requestedState(RequestedRunning)
25 , m_processState(ProcessUnknown)
26 , m_stopTimer(nullptr)
27 , m_exemptFromLifecycle(false)
28- , m_proxySurfaceList(new ProxySurfaceListModel(this))
29 , m_proxyPromptSurfaceList(new ProxySurfaceListModel(this))
30 {
31 INFO_MSG << "()";
32@@ -69,7 +71,7 @@
33
34 setStopTimer(new Timer);
35
36- connect(m_proxySurfaceList, &unityapp::MirSurfaceListInterface::countChanged, this, &unityapp::ApplicationInfoInterface::surfaceCountChanged);
37+ connect(&m_surfaceList, &unityapp::MirSurfaceListInterface::countChanged, this, &unityapp::ApplicationInfoInterface::surfaceCountChanged);
38 }
39
40 Application::~Application()
41@@ -100,10 +102,11 @@
42 break;
43 }
44
45- if (m_session) {
46- m_session->setApplication(nullptr);
47- delete m_session;
48+ for (SessionInterface *session : m_sessions) {
49+ session->setApplication(nullptr);
50+ delete session;
51 }
52+ m_sessions.clear();
53
54 delete m_stopTimer;
55 }
56@@ -281,20 +284,22 @@
57
58 void Application::updateState()
59 {
60- if ((!m_session && m_state != InternalState::Starting && m_state != InternalState::StoppedResumable)
61+ SessionInterface *singleSession = m_sessions.count() == 1 ? m_sessions[0] : nullptr;
62+
63+ if ((m_sessions.isEmpty() && m_state != InternalState::Starting && m_state != InternalState::StoppedResumable)
64 ||
65- (m_session && m_session->surfaceList()->isEmpty() && m_session->hasClosingSurfaces())) {
66+ (singleSession && singleSession->surfaceList()->isEmpty() && singleSession->hasClosingSurfaces())) {
67 // As we might not be able to go to Closing state right now (eg, SuspendingWaitProcess),
68 // store the intent in a separate variable.
69 m_closing = true;
70 }
71
72- bool lostAllSurfaces = m_session && m_session->surfaceList()->isEmpty() && m_session->hadSurface()
73- && !m_session->hasClosingSurfaces();
74+ bool lostAllSurfaces = singleSession && singleSession->surfaceList()->isEmpty() && singleSession->hadSurface()
75+ && !singleSession->hasClosingSurfaces();
76
77 if (m_closing || (lostAllSurfaces && m_state != InternalState::StoppedResumable)) {
78 applyClosing();
79- } else if (m_requestedState == RequestedRunning || (m_session && m_session->hasClosingSurfaces())) {
80+ } else if (m_requestedState == RequestedRunning || (singleSession && singleSession->hasClosingSurfaces())) {
81 applyRequestedRunning();
82 } else {
83 applyRequestedSuspended();
84@@ -410,12 +415,22 @@
85
86 bool Application::focused() const
87 {
88- return m_session && m_session->focused();
89+ for (auto session : m_sessions) {
90+ if (session->focused()) {
91+ return true;
92+ }
93+ }
94+ return false;
95 }
96
97 bool Application::fullscreen() const
98 {
99- return m_session ? m_session->fullscreen() : false;
100+ for (auto session : m_sessions) {
101+ if (session->fullscreen()) {
102+ return true;
103+ }
104+ }
105+ return false;
106 }
107
108 void Application::close()
109@@ -433,7 +448,9 @@
110 case InternalState::SuspendingWaitSession:
111 case InternalState::SuspendingWaitProcess:
112 case InternalState::Suspended:
113- m_session->close();
114+ for (auto session : m_sessions) {
115+ session->close();
116+ }
117 break;
118 case InternalState::Closing:
119 // already on the way
120@@ -453,69 +470,73 @@
121 m_arguments = arguments;
122 }
123
124-void Application::setSession(SessionInterface *newSession)
125+void Application::removeSession(SessionInterface *session)
126+{
127+ if (!m_sessions.contains(session))
128+ return;
129+
130+ m_surfaceList.removeSurfaceList(session->surfaceList());
131+ m_proxyPromptSurfaceList->setSourceList(nullptr);
132+ session->disconnect(this);
133+ session->surfaceList()->disconnect(this);
134+ session->setApplication(nullptr);
135+ session->setParent(nullptr);
136+
137+ m_sessions.removeAll(session);
138+
139+ InitialSurfaceSizes::remove(session->pid());
140+}
141+
142+void Application::addSession(SessionInterface *newSession)
143 {
144 INFO_MSG << "(session=" << newSession << ")";
145
146- if (newSession == m_session)
147+ if (!newSession || m_sessions.contains(newSession))
148 return;
149
150- if (m_session) {
151- m_proxySurfaceList->setSourceList(nullptr);
152- m_proxyPromptSurfaceList->setSourceList(nullptr);
153- m_session->disconnect(this);
154- m_session->surfaceList()->disconnect(this);
155- m_session->setApplication(nullptr);
156- m_session->setParent(nullptr);
157- }
158-
159 bool oldFullscreen = fullscreen();
160- m_session = newSession;
161-
162- if (m_session) {
163- m_session->setParent(this);
164- m_session->setApplication(this);
165-
166- switch (m_state) {
167- case InternalState::Starting:
168- case InternalState::Running:
169- case InternalState::RunningInBackground:
170- case InternalState::Closing:
171- m_session->resume();
172- break;
173- case InternalState::SuspendingWaitSession:
174- case InternalState::SuspendingWaitProcess:
175- case InternalState::Suspended:
176- m_session->suspend();
177- break;
178- case InternalState::Stopped:
179- default:
180- m_session->stop();
181- break;
182- }
183-
184- connect(m_session, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged);
185- connect(m_session, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);
186- connect(m_session, &SessionInterface::hasClosingSurfacesChanged, this, &Application::updateState);
187- connect(m_session, &SessionInterface::focusRequested, this, &Application::focusRequested);
188- connect(m_session->surfaceList(), &MirSurfaceListModel::emptyChanged, this, &Application::updateState);
189- connect(m_session, &SessionInterface::focusedChanged, this, [&](bool focused) {
190- qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::focusedChanged(" << focused << ")";
191- Q_EMIT focusedChanged(focused);
192- });
193-
194- if (oldFullscreen != fullscreen())
195- Q_EMIT fullscreenChanged(fullscreen());
196-
197- m_proxySurfaceList->setSourceList(m_session->surfaceList());
198- m_proxyPromptSurfaceList->setSourceList(m_session->promptSurfaceList());
199- } else {
200- // this can only happen after the session has stopped
201- Q_ASSERT(m_state == InternalState::Stopped || m_state == InternalState::StoppedResumable
202- || m_state == InternalState::Closing);
203- }
204-
205- Q_EMIT sessionChanged(m_session);
206+ m_sessions << newSession;
207+
208+ newSession->setParent(this);
209+ newSession->setApplication(this);
210+
211+ switch (m_state) {
212+ case InternalState::Starting:
213+ case InternalState::Running:
214+ case InternalState::RunningInBackground:
215+ case InternalState::Closing:
216+ newSession->resume();
217+ break;
218+ case InternalState::SuspendingWaitSession:
219+ case InternalState::SuspendingWaitProcess:
220+ case InternalState::Suspended:
221+ newSession->suspend();
222+ break;
223+ case InternalState::Stopped:
224+ default:
225+ newSession->stop();
226+ break;
227+ }
228+
229+ connect(newSession, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged);
230+ connect(newSession, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);
231+ connect(newSession, &SessionInterface::hasClosingSurfacesChanged, this, &Application::updateState);
232+ connect(newSession, &SessionInterface::focusRequested, this, &Application::focusRequested);
233+ connect(newSession->surfaceList(), &MirSurfaceListModel::emptyChanged, this, &Application::updateState);
234+ connect(newSession, &SessionInterface::focusedChanged, this, [&](bool focused) {
235+ qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::focusedChanged(" << focused << ")";
236+ Q_EMIT focusedChanged(focused);
237+ });
238+
239+ if (m_initialSurfaceSize.isValid() && newSession->pid() != 0) {
240+ InitialSurfaceSizes::set(newSession->pid(), m_initialSurfaceSize);
241+ }
242+
243+ if (oldFullscreen != fullscreen())
244+ Q_EMIT fullscreenChanged(fullscreen());
245+
246+ m_surfaceList.addSurfaceList(newSession->surfaceList());
247+ m_proxyPromptSurfaceList->setSourceList(newSession->promptSurfaceList());
248 }
249
250 void Application::setInternalState(Application::InternalState state)
251@@ -587,7 +608,7 @@
252 break;
253 case ProcessFailed:
254 // we assume the session always stop before the process
255- Q_ASSERT(!m_session || m_session->state() == Session::Stopped);
256+ Q_ASSERT(m_sessions.isEmpty() || combinedSessionState() == Session::Stopped);
257
258 if (m_state == InternalState::Starting) {
259 // that was way too soon. let it go away
260@@ -599,7 +620,7 @@
261 break;
262 case ProcessStopped:
263 // we assume the session always stop before the process
264- Q_ASSERT(!m_session || m_session->state() == Session::Stopped);
265+ Q_ASSERT(m_sessions.isEmpty() || combinedSessionState() == Session::Stopped);
266
267 if (m_state == InternalState::Starting) {
268 // that was way too soon. let it go away
269@@ -622,7 +643,7 @@
270 INFO_MSG << "()";
271
272 Q_ASSERT(m_state == InternalState::Running);
273- Q_ASSERT(m_session != nullptr);
274+ Q_ASSERT(!m_sessions.isEmpty());
275
276 if (exemptFromLifecycle()) {
277 // There's no need to keep the wakelock as the process is never suspended
278@@ -631,7 +652,9 @@
279 setInternalState(InternalState::RunningInBackground);
280 } else {
281 setInternalState(InternalState::SuspendingWaitSession);
282- m_session->suspend();
283+ for (auto session : m_sessions) {
284+ session->suspend();
285+ }
286 }
287 }
288
289@@ -645,12 +668,14 @@
290 if (m_processState == ProcessSuspended) {
291 setProcessState(ProcessRunning); // should we wait for a resumed() signal?
292 }
293- if (m_session) {
294- m_session->resume();
295+ for (auto session : m_sessions) {
296+ session->resume();
297 }
298 } else if (m_state == InternalState::SuspendingWaitSession) {
299 setInternalState(InternalState::Running);
300- m_session->resume();
301+ for (auto session : m_sessions) {
302+ session->resume();
303+ }
304 } else if (m_state == InternalState::RunningInBackground) {
305 setInternalState(InternalState::Running);
306 }
307@@ -704,11 +729,6 @@
308 return m_rotatesWindowContents;
309 }
310
311-SessionInterface* Application::session() const
312-{
313- return m_session;
314-}
315-
316 void Application::acquireWakelock() const
317 {
318 if (appId() == QLatin1String("unity8-dash"))
319@@ -725,9 +745,28 @@
320 m_sharedWakelock->release(this);
321 }
322
323-void Application::onSessionStateChanged(Session::State sessionState)
324-{
325- switch (sessionState) {
326+SessionInterface::State Application::combinedSessionState()
327+{
328+ // This doesn't make sense when there are no sessions
329+ Q_ASSERT(m_sessions.count() > 0);
330+
331+ if (m_sessions.count() == 1) {
332+ // easy case
333+ return m_sessions[0]->state();
334+ }
335+
336+ SessionInterface::State combinedState = SessionInterface::Stopped;
337+ for (auto session : m_sessions) {
338+ if (session->state() > combinedState) {
339+ combinedState = session->state();
340+ }
341+ }
342+ return combinedState;
343+}
344+
345+void Application::onSessionStateChanged()
346+{
347+ switch (combinedSessionState()) {
348 case Session::Starting:
349 break;
350 case Session::Running:
351@@ -825,13 +864,18 @@
352
353 if (size != m_initialSurfaceSize) {
354 m_initialSurfaceSize = size;
355+ if (m_initialSurfaceSize.isValid()) {
356+ for (auto session : m_sessions) {
357+ InitialSurfaceSizes::set(session->pid(), size);
358+ }
359+ }
360 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
361 }
362 }
363
364 unityapp::MirSurfaceListInterface* Application::surfaceList() const
365 {
366- return m_proxySurfaceList;
367+ return &m_surfaceList;
368 }
369
370 unityapp::MirSurfaceListInterface* Application::promptSurfaceList() const
371@@ -841,11 +885,11 @@
372
373 void Application::requestFocus()
374 {
375- if (m_proxySurfaceList->rowCount() > 0) {
376+ if (m_surfaceList.rowCount() > 0) {
377 INFO_MSG << "() - Requesting focus for most recent toplevel app surface";
378
379- for (int i = 0; i < m_proxySurfaceList->count(); ++i) {
380- auto surface = static_cast<MirSurfaceInterface*>(m_proxySurfaceList->get(i));
381+ for (int i = 0; i < m_surfaceList.count(); ++i) {
382+ auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
383 if (!surface->parentSurface()) {
384 surface->requestFocus();
385 break;
386@@ -857,4 +901,16 @@
387 }
388 }
389
390+void Application::terminate()
391+{
392+ for (auto session : m_sessions) {
393+ kill(session->pid(), SIGTERM);
394+ }
395+}
396+
397+QVector<SessionInterface*> Application::sessions() const
398+{
399+ return m_sessions;
400+}
401+
402 } // namespace qtmir
403
404=== modified file 'src/modules/Unity/Application/application.h'
405--- src/modules/Unity/Application/application.h 2017-03-22 14:57:19 +0000
406+++ src/modules/Unity/Application/application.h 2017-03-31 21:11:21 +0000
407@@ -1,5 +1,5 @@
408 /*
409- * Copyright (C) 2013-2016 Canonical, Ltd.
410+ * Copyright (C) 2013-2017 Canonical, Ltd.
411 *
412 * This program is free software: you can redistribute it and/or modify it under
413 * the terms of the GNU Lesser General Public License version 3, as published by
414@@ -24,6 +24,7 @@
415 #include <QtCore/QtCore>
416 #include <QImage>
417 #include <QSharedPointer>
418+#include <QVector>
419
420 // Unity API
421 #include <unity/shell/application/ApplicationInfoInterface.h>
422@@ -109,9 +110,11 @@
423 void setProcessState(ProcessState value);
424
425 QStringList arguments() const { return m_arguments; }
426+ void setArguments(const QStringList &arguments);
427
428- SessionInterface* session() const;
429- void setSession(SessionInterface *session);
430+ void addSession(SessionInterface *session);
431+ void removeSession(SessionInterface *session);
432+ QVector<SessionInterface*> sessions() const;
433
434 bool isValid() const;
435 bool fullscreen() const;
436@@ -123,13 +126,13 @@
437
438 void requestFocus();
439
440+ void terminate();
441+
442 // for tests
443 void setStopTimer(AbstractTimer *timer);
444 AbstractTimer *stopTimer() const { return m_stopTimer; }
445-
446 Q_SIGNALS:
447 void fullscreenChanged(bool fullscreen);
448- void sessionChanged(SessionInterface *session);
449
450 void startProcessRequested();
451 void stopProcessRequested();
452@@ -139,7 +142,7 @@
453 void closing();
454
455 private Q_SLOTS:
456- void onSessionStateChanged(SessionInterface::State sessionState);
457+ void onSessionStateChanged();
458
459 void respawn();
460
461@@ -147,7 +150,6 @@
462
463 void acquireWakelock() const;
464 void releaseWakelock() const;
465- void setArguments(const QStringList &arguments);
466 void setInternalState(InternalState state);
467 void wipeQMLCache();
468 void suspend();
469@@ -160,6 +162,7 @@
470 void applyRequestedSuspended();
471 void applyClosing();
472 void onSessionStopped();
473+ SessionInterface::State combinedSessionState();
474
475 QSharedPointer<SharedWakelock> m_sharedWakelock;
476 QSharedPointer<ApplicationInfo> m_appInfo;
477@@ -168,7 +171,7 @@
478 QStringList m_arguments;
479 Qt::ScreenOrientations m_supportedOrientations;
480 bool m_rotatesWindowContents;
481- SessionInterface *m_session;
482+ QVector<SessionInterface*> m_sessions;
483 RequestedState m_requestedState;
484 ProcessState m_processState;
485 AbstractTimer *m_stopTimer;
486@@ -176,12 +179,8 @@
487 QSize m_initialSurfaceSize;
488 bool m_closing{false};
489
490- ProxySurfaceListModel *m_proxySurfaceList;
491+ mutable MirSurfaceListModel m_surfaceList;
492 ProxySurfaceListModel *m_proxyPromptSurfaceList;
493-
494- friend class ApplicationManager;
495- friend class SessionManager;
496- friend class Session;
497 };
498
499 } // namespace qtmir
500
501=== modified file 'src/modules/Unity/Application/application_manager.cpp'
502--- src/modules/Unity/Application/application_manager.cpp 2017-03-28 17:12:54 +0000
503+++ src/modules/Unity/Application/application_manager.cpp 2017-03-31 21:11:21 +0000
504@@ -28,7 +28,6 @@
505 #include "settings.h"
506
507 // mirserver
508-#include "initialsurfacesizes.h"
509 #include "nativeinterface.h"
510 #include "logging.h"
511
512@@ -425,7 +424,6 @@
513
514 Q_UNUSED(error); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
515 application->setProcessState(Application::ProcessFailed);
516- setApplicationPid(application, 0);
517 }
518
519 void ApplicationManager::onProcessStopped(const QString &appId)
520@@ -450,7 +448,6 @@
521 // we don't want to override what onProcessFailed already set.
522 if (application->processState() != Application::ProcessFailed) {
523 application->setProcessState(Application::ProcessStopped);
524- setApplicationPid(application, 0);
525 }
526 }
527
528@@ -527,8 +524,8 @@
529 if (app->state() == Application::Starting) {
530 tracepoint(qtmir, appIdHasProcessId_start);
531 if (m_taskController->appIdHasProcessId(app->appId(), pid)) {
532- setApplicationPid(app, pid);
533 authorized = true;
534+ m_authorizedPids.insertMulti(pid, app->appId());
535 tracepoint(qtmir, appIdHasProcessId_end, 1); //found
536 return;
537 }
538@@ -581,17 +578,18 @@
539 // some naughty applications use a script to launch the actual application. Check for the
540 // case where shell actually launched the script.
541 Application *application = findApplicationMutexHeld(appInfo->appId());
542- if (application && application->state() == Application::Starting) {
543+ if (application) {
544 qCDebug(QTMIR_APPLICATIONS) << "Process with pid" << pid << "appeared, attaching to existing entry"
545 << "in application list with appId:" << application->appId();
546- setApplicationPid(application, pid);
547 authorized = true;
548+ m_authorizedPids.insertMulti(pid, appInfo->appId());
549 return;
550 }
551
552 const QStringList arguments(info->asStringList());
553 queuedAddApp(appInfo, arguments, pid);
554 authorized = true;
555+ m_authorizedPids.insertMulti(pid, appInfo->appId());
556 }
557
558
559@@ -603,26 +601,22 @@
560 auto qtmirSurface = static_cast<qtmir::MirSurfaceInterface*>(surface);
561
562 QMutexLocker locker(&m_mutex);
563- return findApplicationWithPid(miral::pid_of(qtmirSurface->session()->session()));
564+ return findApplicationWithSession(qtmirSurface->session()->session());
565 }
566
567-Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session)
568+Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session) const
569 {
570 if (!session)
571 return nullptr;
572- return findApplicationWithPid(miral::pid_of(session));
573-}
574-
575-Application* ApplicationManager::findApplicationWithPid(const pid_t pid) const
576-{
577- if (pid <= 0)
578- return nullptr;
579-
580- for (Application *app : m_applications) {
581- if (m_applicationsPid.value(app) == pid) {
582- return app;
583+
584+ for (auto *application : m_applications) {
585+ for (auto *qmlSession : application->sessions()) {
586+ if (qmlSession->session() == session) {
587+ return application;
588+ }
589 }
590 }
591+
592 return nullptr;
593 }
594
595@@ -638,7 +632,6 @@
596 appInfo,
597 arguments,
598 this);
599- setApplicationPid(application, pid);
600 add(application);
601 }
602
603@@ -653,22 +646,8 @@
604 DEBUG_MSG << "(appId=" << application->appId() << ")";
605
606 connect(application, &QObject::destroyed, this, [this, application] {
607- const pid_t pid = m_applicationsPid.value(application);
608- if (pid != 0) {
609- InitialSurfaceSizes::remove(pid);
610- m_applicationsPid.remove(application);
611- }
612 m_closingApplications.removeAll(application);
613 });
614- connect(application, &Application::initialSurfaceSizeChanged, this, [this, application] {
615- const pid_t pid = m_applicationsPid.value(application);
616- if (pid != 0) {
617- const QSize size = application->initialSurfaceSize();
618- if (size.isValid()) {
619- InitialSurfaceSizes::set(pid, size);
620- }
621- }
622- });
623
624 Q_ASSERT(!m_modelUnderChange);
625 m_modelUnderChange = true;
626@@ -707,13 +686,10 @@
627
628 connect(application, &Application::stopProcessRequested, this, [=]() {
629 if (!m_taskController->stop(appId)) {
630- const pid_t pid = m_applicationsPid.value(application);
631- if (pid > 0) {
632- qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
633- << "Sending SIGTERM to process:" << appId;
634- kill(pid, SIGTERM);
635- application->setProcessState(Application::ProcessStopped);
636- }
637+ qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
638+ << "Sending SIGTERM to process:" << appId;
639+ application->terminate();
640+ application->setProcessState(Application::ProcessStopped);
641 }
642 });
643
644@@ -805,27 +781,22 @@
645 return nullptr;
646 }
647
648-void ApplicationManager::setApplicationPid(Application *app, pid_t pid)
649-{
650- const pid_t oldPid = m_applicationsPid.value(app);
651- if (oldPid != 0) {
652- InitialSurfaceSizes::remove(oldPid);
653- }
654-
655- m_applicationsPid.insert(app, pid);
656-
657- if (app->initialSurfaceSize().isValid() && pid != 0) {
658- InitialSurfaceSizes::set(pid, app->initialSurfaceSize());
659- }
660-}
661-
662 void ApplicationManager::onSessionStarting(SessionInterface *qmlSession)
663 {
664 QMutexLocker locker(&m_mutex);
665
666- Application* application = findApplicationWithSession(qmlSession->session());
667- if (application && application->state() != Application::Running) {
668- application->setSession(qmlSession);
669+ Application* application = nullptr;
670+ {
671+ auto iter = m_authorizedPids.find(miral::pid_of(qmlSession->session()));
672+ if (iter != m_authorizedPids.end()) {
673+ QString appId = iter.value();
674+ application = findApplication(appId);
675+ m_authorizedPids.erase(iter);
676+ }
677+ }
678+
679+ if (application) {
680+ application->addSession(qmlSession);
681 }
682 }
683
684
685=== modified file 'src/modules/Unity/Application/application_manager.h'
686--- src/modules/Unity/Application/application_manager.h 2017-03-24 08:44:32 +0000
687+++ src/modules/Unity/Application/application_manager.h 2017-03-31 21:11:21 +0000
688@@ -110,10 +110,9 @@
689
690 private:
691 // All calls to private functions happen with the mutex held
692- qtmir::Application* findApplicationWithPid(const pid_t pid) const;
693 Application* findApplicationMutexHeld(const QString &inputAppId) const;
694
695- Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);
696+ Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session) const;
697 void setFocused(Application *application);
698 void add(Application *application);
699 void remove(Application* application);
700@@ -125,10 +124,7 @@
701 Application* findApplicationWithPromptSession(const mir::scene::PromptSession* promptSession);
702 Application *findClosingApplication(const QString &inputAppId) const;
703
704- void setApplicationPid(Application *application, pid_t pid);
705-
706 QList<Application*> m_applications;
707- QHash<Application*, pid_t> m_applicationsPid;
708 DBusFocusInfo *m_dbusFocusInfo;
709 QSharedPointer<TaskController> m_taskController;
710 QSharedPointer<ProcInfo> m_procInfo;
711@@ -139,8 +135,8 @@
712 bool m_modelUnderChange{false};
713 static ApplicationManager* the_application_manager;
714
715- friend class Application;
716- friend class DBusWindowStack;
717+ QHash<pid_t, QString> m_authorizedPids;
718+
719 mutable QMutex m_mutex;
720 };
721
722
723=== modified file 'src/modules/Unity/Application/dbusfocusinfo.cpp'
724--- src/modules/Unity/Application/dbusfocusinfo.cpp 2017-03-07 20:20:21 +0000
725+++ src/modules/Unity/Application/dbusfocusinfo.cpp 2017-03-31 21:11:21 +0000
726@@ -75,18 +75,20 @@
727 SessionInterface* DBusFocusInfo::findSessionWithPid(const QSet<pid_t> &pidSet)
728 {
729 Q_FOREACH (Application* application, m_applications) {
730- auto session = application->session();
731- if (pidSet.contains(session->pid())) {
732- return session;
733- }
734- SessionInterface *chosenChildSession = nullptr;
735- session->foreachChildSession([&](SessionInterface* childSession) {
736- if (pidSet.contains(childSession->pid())) {
737- chosenChildSession = childSession;
738- }
739- });
740- if (chosenChildSession) {
741- return chosenChildSession;
742+ QVector<SessionInterface*> sessions = application->sessions();
743+ for (auto session : sessions) {
744+ if (pidSet.contains(session->pid())) {
745+ return session;
746+ }
747+ SessionInterface *chosenChildSession = nullptr;
748+ session->foreachChildSession([&](SessionInterface* childSession) {
749+ if (pidSet.contains(childSession->pid())) {
750+ chosenChildSession = childSession;
751+ }
752+ });
753+ if (chosenChildSession) {
754+ return chosenChildSession;
755+ }
756 }
757 }
758 return nullptr;
759@@ -111,21 +113,22 @@
760 MirSurfaceInterface *DBusFocusInfo::findQmlSurface(const QString &serializedId)
761 {
762 for (Application* application : m_applications) {
763- auto session = application->session();
764- if (session) {
765- auto surfaceList = static_cast<MirSurfaceListModel*>(session->surfaceList());
766- for (int i = 0; i < surfaceList->count(); ++i) {
767- auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));
768- if (qmlSurface->persistentId() == serializedId) {
769- return qmlSurface;
770+ for (SessionInterface *session : application->sessions()) {
771+ if (session) {
772+ auto surfaceList = static_cast<MirSurfaceListModel*>(session->surfaceList());
773+ for (int i = 0; i < surfaceList->count(); ++i) {
774+ auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));
775+ if (qmlSurface->persistentId() == serializedId) {
776+ return qmlSurface;
777+ }
778 }
779- }
780
781- surfaceList = static_cast<MirSurfaceListModel*>(session->promptSurfaceList());
782- for (int i = 0; i < surfaceList->count(); ++i) {
783- auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));
784- if (qmlSurface->persistentId() == serializedId) {
785- return qmlSurface;
786+ surfaceList = static_cast<MirSurfaceListModel*>(session->promptSurfaceList());
787+ for (int i = 0; i < surfaceList->count(); ++i) {
788+ auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));
789+ if (qmlSurface->persistentId() == serializedId) {
790+ return qmlSurface;
791+ }
792 }
793 }
794 }
795
796=== modified file 'src/modules/Unity/Application/session.cpp'
797--- src/modules/Unity/Application/session.cpp 2017-02-21 18:39:45 +0000
798+++ src/modules/Unity/Application/session.cpp 2017-03-31 21:11:21 +0000
799@@ -92,7 +92,7 @@
800 delete child;
801 }
802 if (m_application) {
803- m_application->setSession(nullptr);
804+ m_application->removeSession(this);
805 }
806
807 delete m_children; m_children = nullptr;
808
809=== modified file 'src/modules/Unity/Application/session_interface.h'
810--- src/modules/Unity/Application/session_interface.h 2017-02-21 18:46:30 +0000
811+++ src/modules/Unity/Application/session_interface.h 2017-03-31 21:11:21 +0000
812@@ -53,12 +53,13 @@
813 SessionInterface(QObject *parent = 0) : QObject(parent) {}
814 virtual ~SessionInterface() {}
815
816+ // Ordered by importance/activity. Used for calculating the combined state of multiple sessions
817 enum State {
818- Starting,
819- Running,
820- Suspending,
821- Suspended,
822- Stopped
823+ Running = 4,
824+ Starting = 3,
825+ Suspending = 2,
826+ Suspended = 1,
827+ Stopped = 0
828 };
829
830 //getters
831
832=== modified file 'tests/modules/Application/application_test.cpp'
833--- tests/modules/Application/application_test.cpp 2017-02-21 18:46:30 +0000
834+++ tests/modules/Application/application_test.cpp 2017-03-31 21:11:21 +0000
835@@ -49,7 +49,7 @@
836 inline void suspend(Application *application)
837 {
838 application->setRequestedState(Application::RequestedSuspended);
839- auto session = dynamic_cast<Session*>(application->session());
840+ auto session = dynamic_cast<Session*>(application->sessions()[0]);
841
842 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
843 ASSERT_EQ(Session::Suspending, session->state());
844@@ -115,7 +115,7 @@
845
846 FakeSession *session = new FakeSession;
847
848- application->setSession(session);
849+ application->addSession(session);
850
851 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
852
853@@ -150,7 +150,7 @@
854
855 // Get it running and then suspend it
856 application->setProcessState(Application::ProcessRunning);
857- application->setSession(session);
858+ application->addSession(session);
859 session->setState(SessionInterface::Running);
860 application->setRequestedState(Application::RequestedSuspended);
861 session->setState(SessionInterface::Suspended);
862@@ -175,7 +175,7 @@
863
864 // Get it running, suspend it, and finally stop it
865 application->setProcessState(Application::ProcessRunning);
866- application->setSession(session);
867+ application->addSession(session);
868 session->setState(SessionInterface::Running);
869 application->setRequestedState(Application::RequestedSuspended);
870 session->setState(SessionInterface::Suspended);
871@@ -215,7 +215,7 @@
872
873 FakeSession *session = new FakeSession;
874
875- application->setSession(session);
876+ application->addSession(session);
877
878 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
879
880@@ -250,7 +250,7 @@
881 application->setProcessState(Application::ProcessRunning);
882
883 FakeSession *session = new FakeSession;
884- application->setSession(session);
885+ application->addSession(session);
886
887 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));
888
889@@ -283,7 +283,7 @@
890 application->setProcessState(Application::ProcessRunning);
891
892 Session *session = createSessionWithFakes();
893- application->setSession(session);
894+ application->addSession(session);
895
896 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));
897
898@@ -318,7 +318,7 @@
899 application->setProcessState(Application::ProcessRunning);
900
901 FakeSession *session = new FakeSession;
902- application->setSession(session);
903+ application->addSession(session);
904
905 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));
906
907@@ -380,7 +380,7 @@
908
909 Session *session = createSessionWithFakes();
910
911- application->setSession(session);
912+ application->addSession(session);
913
914 FakeMirSurface *surface = new FakeMirSurface;
915 session->registerSurface(surface);
916@@ -437,7 +437,7 @@
917
918 Session *session = createSessionWithFakes();
919
920- application->setSession(session);
921+ application->addSession(session);
922
923 FakeMirSurface *surface = new FakeMirSurface;
924 session->registerSurface(surface);
925@@ -488,7 +488,7 @@
926
927 QPointer<Session> session(createSessionWithFakes());
928
929- application->setSession(session);
930+ application->addSession(session);
931
932 FakeMirSurface *surface = new FakeMirSurface;
933 session->registerSurface(surface);
934@@ -532,7 +532,7 @@
935 application->setProcessState(Application::ProcessRunning);
936
937 QPointer<Session> session(createSessionWithFakes());
938- application->setSession(session);
939+ application->addSession(session);
940
941 FakeMirSurface *surface = new FakeMirSurface;
942 session->registerSurface(surface);
943@@ -570,7 +570,7 @@
944
945 Session *session = createSessionWithFakes();
946
947- application->setSession(session);
948+ application->addSession(session);
949
950 FakeMirSurface *surface = new FakeMirSurface;
951 session->registerSurface(surface);
952@@ -602,7 +602,7 @@
953 application->setProcessState(Application::ProcessRunning);
954 Session *session = createSessionWithFakes();
955
956- application->setSession(session);
957+ application->addSession(session);
958
959 QSignalSpy surfaceCountChangedSpy(application.data(), &Application::surfaceCountChanged);
960
961@@ -641,7 +641,7 @@
962
963 FakeSession *session = new FakeSession;
964
965- application->setSession(session);
966+ application->addSession(session);
967
968 QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested()));
969
970
971=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
972--- tests/modules/ApplicationManager/application_manager_test.cpp 2017-02-21 18:46:30 +0000
973+++ tests/modules/ApplicationManager/application_manager_test.cpp 2017-03-31 21:11:21 +0000
974@@ -59,7 +59,7 @@
975 inline void suspend(Application *application) {
976 application->setRequestedState(Application::RequestedSuspended);
977 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
978- static_cast<qtmir::Session*>(application->session())->doSuspend();
979+ static_cast<qtmir::Session*>(application->sessions()[0])->doSuspend();
980 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
981 applicationManager.onProcessSuspended(application->appId());
982 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
983@@ -85,6 +85,11 @@
984
985 FakeMirSurface surface;
986
987+ EXPECT_CALL(*taskController,appIdHasProcessId(QString(dialer_app_id), firstProcId))
988+ .WillRepeatedly(Return(true));
989+ EXPECT_CALL(*taskController,appIdHasProcessId(QString(dialer_app_id), secondProcId))
990+ .WillRepeatedly(Return(true));
991+
992 EXPECT_CALL(procInfo,command_line(firstProcId))
993 .Times(1)
994 .WillOnce(Return(cmdLine));
995@@ -226,12 +231,22 @@
996 const char third_app_id[] = "app3";
997 QByteArray third_cmdLine( "/usr/bin/app3 --desktop_file_hint=app3");
998
999+ ON_CALL(*taskController,appIdHasProcessId(QString(first_app_id), first_procId)).WillByDefault(Return(true));
1000+ ON_CALL(*taskController,appIdHasProcessId(QString(first_app_id), second_procId)).WillByDefault(Return(false));
1001+ ON_CALL(*taskController,appIdHasProcessId(QString(first_app_id), third_procId)).WillByDefault(Return(false));
1002+
1003+ ON_CALL(*taskController,appIdHasProcessId(QString(second_app_id), second_procId)).WillByDefault(Return(true));
1004+ ON_CALL(*taskController,appIdHasProcessId(QString(second_app_id), first_procId)).WillByDefault(Return(false));
1005+ ON_CALL(*taskController,appIdHasProcessId(QString(second_app_id), third_procId)).WillByDefault(Return(false));
1006+
1007+ ON_CALL(*taskController,appIdHasProcessId(QString(third_app_id), third_procId)).WillByDefault(Return(true));
1008+ ON_CALL(*taskController,appIdHasProcessId(QString(third_app_id), first_procId)).WillByDefault(Return(false));
1009+ ON_CALL(*taskController,appIdHasProcessId(QString(third_app_id), second_procId)).WillByDefault(Return(false));
1010+
1011 EXPECT_CALL(procInfo,command_line(first_procId))
1012 .Times(1)
1013 .WillOnce(Return(first_cmdLine));
1014
1015- ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
1016-
1017 EXPECT_CALL(procInfo,command_line(second_procId))
1018 .Times(1)
1019 .WillOnce(Return(second_cmdLine));
1020@@ -257,12 +272,12 @@
1021 Application * secondApp = applicationManager.findApplication(second_app_id);
1022 Application * thirdApp = applicationManager.findApplication(third_app_id);
1023
1024- EXPECT_EQ(firstAppInfo.application(), firstApp->session()->session());
1025- EXPECT_EQ(secondAppInfo.application(), secondApp->session()->session());
1026- EXPECT_EQ(thirdAppInfo.application(), thirdApp->session()->session());
1027+ EXPECT_EQ(firstAppInfo.application(), firstApp->sessions()[0]->session());
1028+ EXPECT_EQ(secondAppInfo.application(), secondApp->sessions()[0]->session());
1029+ EXPECT_EQ(thirdAppInfo.application(), thirdApp->sessions()[0]->session());
1030 }
1031
1032-TEST_F(ApplicationManagerTests,two_session_on_one_application)
1033+TEST_F(ApplicationManagerTests,two_sessions_on_one_application)
1034 {
1035 int argc = 0;
1036 char* argv[0];
1037@@ -275,28 +290,30 @@
1038
1039 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
1040
1041- ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
1042+ ON_CALL(*taskController,appIdHasProcessId(QString(an_app_id), a_procId)).WillByDefault(Return(true));
1043
1044 bool authed = true;
1045
1046 auto firstAppInfo = createApplicationInfoFor("Oo", a_procId);
1047 auto secondAppInfo = createApplicationInfoFor("oO", a_procId);
1048+
1049 applicationManager.authorizeSession(a_procId, authed);
1050-
1051 taskController->onSessionStarting(firstAppInfo);
1052+
1053+ applicationManager.authorizeSession(a_procId, authed);
1054 taskController->onSessionStarting(secondAppInfo);
1055
1056 Application * the_app = applicationManager.findApplication(an_app_id);
1057
1058 EXPECT_EQ(true, authed);
1059- EXPECT_EQ(secondAppInfo.application(), the_app->session()->session());
1060+ EXPECT_EQ(2, the_app->sessions().count());
1061
1062 taskController->onSessionStopping(firstAppInfo);
1063 taskController->onSessionStopping(secondAppInfo);
1064 qtApp.sendPostedEvents(nullptr, QEvent::DeferredDelete);
1065 }
1066
1067-TEST_F(ApplicationManagerTests,two_session_on_one_application_after_starting)
1068+TEST_F(ApplicationManagerTests,two_sessions_on_one_application_after_starting)
1069 {
1070 int argc = 0;
1071 char* argv[0];
1072@@ -310,24 +327,30 @@
1073
1074 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
1075
1076- ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
1077+ ON_CALL(*taskController,appIdHasProcessId(QString(an_app_id), a_procId)).WillByDefault(Return(true));
1078
1079 bool authed = true;
1080
1081 auto firstAppInfo = createApplicationInfoFor("Oo", a_procId);
1082 auto secondAppInfo = createApplicationInfoFor("oO", a_procId);
1083+
1084 applicationManager.authorizeSession(a_procId, authed);
1085-
1086 taskController->onSessionStarting(firstAppInfo);
1087+
1088+ EXPECT_EQ(true, authed);
1089+
1090 onSessionCreatedSurface(firstAppInfo, &aSurface);
1091 aSurface.setReady();
1092+
1093+ Application * the_app = applicationManager.findApplication(an_app_id);
1094+ EXPECT_EQ(1, the_app->sessions().count());
1095+
1096+ applicationManager.authorizeSession(a_procId, authed);
1097 taskController->onSessionStarting(secondAppInfo);
1098
1099- Application * the_app = applicationManager.findApplication(an_app_id);
1100-
1101 EXPECT_EQ(true, authed);
1102 EXPECT_EQ(Application::Running, the_app->state());
1103- EXPECT_EQ(firstAppInfo.application(), the_app->session()->session());
1104+ EXPECT_EQ(2, the_app->sessions().count());
1105
1106 taskController->onSessionStopping(firstAppInfo);
1107 taskController->onSessionStopping(secondAppInfo);
1108@@ -345,7 +368,7 @@
1109 .Times(1)
1110 .WillOnce(Return(cmdLine));
1111
1112- ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
1113+ ON_CALL(*taskController,appIdHasProcessId(QString("app"), procId)).WillByDefault(Return(true));
1114
1115 bool authed = true;
1116
1117@@ -383,7 +406,9 @@
1118 .Times(1)
1119 .WillOnce(Return(first_cmdLine));
1120
1121- ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
1122+ ON_CALL(*taskController,appIdHasProcessId(QString("app1"), first_procId)).WillByDefault(Return(true));
1123+ ON_CALL(*taskController,appIdHasProcessId(QString("app2"), second_procId)).WillByDefault(Return(true));
1124+ ON_CALL(*taskController,appIdHasProcessId(QString("app3"), third_procId)).WillByDefault(Return(true));
1125
1126 EXPECT_CALL(procInfo,command_line(second_procId))
1127 .Times(1)
1128@@ -718,7 +743,7 @@
1129
1130 // Check application state and session are correctly set
1131 Application *theApp = applicationManager.findApplication(appId);
1132- EXPECT_EQ(theApp->session()->session(), appInfo.application());
1133+ EXPECT_EQ(theApp->sessions()[0]->session(), appInfo.application());
1134 EXPECT_EQ(theApp->focused(), false);
1135 }
1136
1137@@ -1115,6 +1140,8 @@
1138 QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint=");
1139 cmdLine = cmdLine.append(appId);
1140
1141+ ON_CALL(*taskController,appIdHasProcessId(appId, procId)).WillByDefault(Return(true));
1142+
1143 // Set up Mocks & signal watcher
1144 EXPECT_CALL(procInfo,command_line(procId))
1145 .Times(1)
1146@@ -1181,7 +1208,7 @@
1147
1148 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());
1149
1150- static_cast<qtmir::Session*>(app->session())->doSuspend();
1151+ static_cast<qtmir::Session*>(app->sessions()[0])->doSuspend();
1152 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, app->internalState());
1153
1154 applicationManager.onProcessSuspended(app->appId());
1155@@ -1412,10 +1439,10 @@
1156
1157 // Session should have called deleteLater() on itself, as it's zombie and doesn't hold any surface
1158 // But DeferredDelete is special: likes to be called out specifically or it won't come out
1159- qtApp.sendPostedEvents(app->session(), QEvent::DeferredDelete);
1160+ qtApp.sendPostedEvents(app->sessions()[0], QEvent::DeferredDelete);
1161 qtApp.sendPostedEvents();
1162
1163- ASSERT_EQ(app->session(), nullptr);
1164+ ASSERT_EQ(app->sessions().count(), 0);
1165
1166 QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &)));
1167
1168@@ -1461,7 +1488,7 @@
1169 the_app->setRequestedState(Application::RequestedSuspended);
1170 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());
1171
1172- static_cast<qtmir::Session*>(the_app->session())->doSuspend();
1173+ static_cast<qtmir::Session*>(the_app->sessions()[0])->doSuspend();
1174 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());
1175 applicationManager.onProcessSuspended(the_app->appId());
1176 ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());
1177@@ -1838,7 +1865,7 @@
1178 taskController->onSessionStarting(appInfo1);
1179
1180 FakeMirSurface surface1;
1181- surface1.setSession(app1->session());
1182+ surface1.setSession(app1->sessions()[0]);
1183 onSessionCreatedSurface(appInfo1, &surface1);
1184 surface1.setReady();
1185
1186@@ -1864,7 +1891,7 @@
1187 taskController->onSessionStarting(appInfo2);
1188
1189 FakeMirSurface surface2;
1190- surface2.setSession(app2->session());
1191+ surface2.setSession(app2->sessions()[0]);
1192 onSessionCreatedSurface(appInfo2, &surface2);
1193 surface2.setReady();
1194

Subscribers

People subscribed via source and target branches