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
=== modified file 'src/modules/Unity/Application/application.cpp'
--- src/modules/Unity/Application/application.cpp 2017-03-28 17:12:03 +0000
+++ src/modules/Unity/Application/application.cpp 2017-03-31 21:11:21 +0000
@@ -28,10 +28,14 @@
2828
29// QPA mirserver29// QPA mirserver
30#include "logging.h"30#include "logging.h"
31#include "initialsurfacesizes.h"
3132
32// Unity API33// Unity API
33#include <unity/shell/application/MirSurfaceInterface.h>34#include <unity/shell/application/MirSurfaceInterface.h>
3435
36// std
37#include <csignal>
38
35namespace unityapp = unity::shell::application;39namespace unityapp = unity::shell::application;
3640
37#define DEBUG_MSG qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::" << __func__41#define DEBUG_MSG qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::" << __func__
@@ -50,12 +54,10 @@
50 , m_supportedStages(Application::MainStage|Application::SideStage)54 , m_supportedStages(Application::MainStage|Application::SideStage)
51 , m_state(InternalState::Starting)55 , m_state(InternalState::Starting)
52 , m_arguments(arguments)56 , m_arguments(arguments)
53 , m_session(nullptr)
54 , m_requestedState(RequestedRunning)57 , m_requestedState(RequestedRunning)
55 , m_processState(ProcessUnknown)58 , m_processState(ProcessUnknown)
56 , m_stopTimer(nullptr)59 , m_stopTimer(nullptr)
57 , m_exemptFromLifecycle(false)60 , m_exemptFromLifecycle(false)
58 , m_proxySurfaceList(new ProxySurfaceListModel(this))
59 , m_proxyPromptSurfaceList(new ProxySurfaceListModel(this))61 , m_proxyPromptSurfaceList(new ProxySurfaceListModel(this))
60{62{
61 INFO_MSG << "()";63 INFO_MSG << "()";
@@ -69,7 +71,7 @@
6971
70 setStopTimer(new Timer);72 setStopTimer(new Timer);
7173
72 connect(m_proxySurfaceList, &unityapp::MirSurfaceListInterface::countChanged, this, &unityapp::ApplicationInfoInterface::surfaceCountChanged);74 connect(&m_surfaceList, &unityapp::MirSurfaceListInterface::countChanged, this, &unityapp::ApplicationInfoInterface::surfaceCountChanged);
73}75}
7476
75Application::~Application()77Application::~Application()
@@ -100,10 +102,11 @@
100 break;102 break;
101 }103 }
102104
103 if (m_session) {105 for (SessionInterface *session : m_sessions) {
104 m_session->setApplication(nullptr);106 session->setApplication(nullptr);
105 delete m_session;107 delete session;
106 }108 }
109 m_sessions.clear();
107110
108 delete m_stopTimer;111 delete m_stopTimer;
109}112}
@@ -281,20 +284,22 @@
281284
282void Application::updateState()285void Application::updateState()
283{286{
284 if ((!m_session && m_state != InternalState::Starting && m_state != InternalState::StoppedResumable)287 SessionInterface *singleSession = m_sessions.count() == 1 ? m_sessions[0] : nullptr;
288
289 if ((m_sessions.isEmpty() && m_state != InternalState::Starting && m_state != InternalState::StoppedResumable)
285 ||290 ||
286 (m_session && m_session->surfaceList()->isEmpty() && m_session->hasClosingSurfaces())) {291 (singleSession && singleSession->surfaceList()->isEmpty() && singleSession->hasClosingSurfaces())) {
287 // As we might not be able to go to Closing state right now (eg, SuspendingWaitProcess),292 // As we might not be able to go to Closing state right now (eg, SuspendingWaitProcess),
288 // store the intent in a separate variable.293 // store the intent in a separate variable.
289 m_closing = true;294 m_closing = true;
290 }295 }
291296
292 bool lostAllSurfaces = m_session && m_session->surfaceList()->isEmpty() && m_session->hadSurface()297 bool lostAllSurfaces = singleSession && singleSession->surfaceList()->isEmpty() && singleSession->hadSurface()
293 && !m_session->hasClosingSurfaces();298 && !singleSession->hasClosingSurfaces();
294299
295 if (m_closing || (lostAllSurfaces && m_state != InternalState::StoppedResumable)) {300 if (m_closing || (lostAllSurfaces && m_state != InternalState::StoppedResumable)) {
296 applyClosing();301 applyClosing();
297 } else if (m_requestedState == RequestedRunning || (m_session && m_session->hasClosingSurfaces())) {302 } else if (m_requestedState == RequestedRunning || (singleSession && singleSession->hasClosingSurfaces())) {
298 applyRequestedRunning();303 applyRequestedRunning();
299 } else {304 } else {
300 applyRequestedSuspended();305 applyRequestedSuspended();
@@ -410,12 +415,22 @@
410415
411bool Application::focused() const416bool Application::focused() const
412{417{
413 return m_session && m_session->focused();418 for (auto session : m_sessions) {
419 if (session->focused()) {
420 return true;
421 }
422 }
423 return false;
414}424}
415425
416bool Application::fullscreen() const426bool Application::fullscreen() const
417{427{
418 return m_session ? m_session->fullscreen() : false;428 for (auto session : m_sessions) {
429 if (session->fullscreen()) {
430 return true;
431 }
432 }
433 return false;
419}434}
420435
421void Application::close()436void Application::close()
@@ -433,7 +448,9 @@
433 case InternalState::SuspendingWaitSession:448 case InternalState::SuspendingWaitSession:
434 case InternalState::SuspendingWaitProcess:449 case InternalState::SuspendingWaitProcess:
435 case InternalState::Suspended:450 case InternalState::Suspended:
436 m_session->close();451 for (auto session : m_sessions) {
452 session->close();
453 }
437 break;454 break;
438 case InternalState::Closing:455 case InternalState::Closing:
439 // already on the way456 // already on the way
@@ -453,69 +470,73 @@
453 m_arguments = arguments;470 m_arguments = arguments;
454}471}
455472
456void Application::setSession(SessionInterface *newSession)473void Application::removeSession(SessionInterface *session)
474{
475 if (!m_sessions.contains(session))
476 return;
477
478 m_surfaceList.removeSurfaceList(session->surfaceList());
479 m_proxyPromptSurfaceList->setSourceList(nullptr);
480 session->disconnect(this);
481 session->surfaceList()->disconnect(this);
482 session->setApplication(nullptr);
483 session->setParent(nullptr);
484
485 m_sessions.removeAll(session);
486
487 InitialSurfaceSizes::remove(session->pid());
488}
489
490void Application::addSession(SessionInterface *newSession)
457{491{
458 INFO_MSG << "(session=" << newSession << ")";492 INFO_MSG << "(session=" << newSession << ")";
459493
460 if (newSession == m_session)494 if (!newSession || m_sessions.contains(newSession))
461 return;495 return;
462496
463 if (m_session) {
464 m_proxySurfaceList->setSourceList(nullptr);
465 m_proxyPromptSurfaceList->setSourceList(nullptr);
466 m_session->disconnect(this);
467 m_session->surfaceList()->disconnect(this);
468 m_session->setApplication(nullptr);
469 m_session->setParent(nullptr);
470 }
471
472 bool oldFullscreen = fullscreen();497 bool oldFullscreen = fullscreen();
473 m_session = newSession;498 m_sessions << newSession;
474499
475 if (m_session) {500 newSession->setParent(this);
476 m_session->setParent(this);501 newSession->setApplication(this);
477 m_session->setApplication(this);502
478503 switch (m_state) {
479 switch (m_state) {504 case InternalState::Starting:
480 case InternalState::Starting:505 case InternalState::Running:
481 case InternalState::Running:506 case InternalState::RunningInBackground:
482 case InternalState::RunningInBackground:507 case InternalState::Closing:
483 case InternalState::Closing:508 newSession->resume();
484 m_session->resume();509 break;
485 break;510 case InternalState::SuspendingWaitSession:
486 case InternalState::SuspendingWaitSession:511 case InternalState::SuspendingWaitProcess:
487 case InternalState::SuspendingWaitProcess:512 case InternalState::Suspended:
488 case InternalState::Suspended:513 newSession->suspend();
489 m_session->suspend();514 break;
490 break;515 case InternalState::Stopped:
491 case InternalState::Stopped:516 default:
492 default:517 newSession->stop();
493 m_session->stop();518 break;
494 break;519 }
495 }520
496521 connect(newSession, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged);
497 connect(m_session, &SessionInterface::stateChanged, this, &Application::onSessionStateChanged);522 connect(newSession, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);
498 connect(m_session, &SessionInterface::fullscreenChanged, this, &Application::fullscreenChanged);523 connect(newSession, &SessionInterface::hasClosingSurfacesChanged, this, &Application::updateState);
499 connect(m_session, &SessionInterface::hasClosingSurfacesChanged, this, &Application::updateState);524 connect(newSession, &SessionInterface::focusRequested, this, &Application::focusRequested);
500 connect(m_session, &SessionInterface::focusRequested, this, &Application::focusRequested);525 connect(newSession->surfaceList(), &MirSurfaceListModel::emptyChanged, this, &Application::updateState);
501 connect(m_session->surfaceList(), &MirSurfaceListModel::emptyChanged, this, &Application::updateState);526 connect(newSession, &SessionInterface::focusedChanged, this, [&](bool focused) {
502 connect(m_session, &SessionInterface::focusedChanged, this, [&](bool focused) {527 qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::focusedChanged(" << focused << ")";
503 qCDebug(QTMIR_APPLICATIONS).nospace() << "Application[" << appId() <<"]::focusedChanged(" << focused << ")";528 Q_EMIT focusedChanged(focused);
504 Q_EMIT focusedChanged(focused);529 });
505 });530
506531 if (m_initialSurfaceSize.isValid() && newSession->pid() != 0) {
507 if (oldFullscreen != fullscreen())532 InitialSurfaceSizes::set(newSession->pid(), m_initialSurfaceSize);
508 Q_EMIT fullscreenChanged(fullscreen());533 }
509534
510 m_proxySurfaceList->setSourceList(m_session->surfaceList());535 if (oldFullscreen != fullscreen())
511 m_proxyPromptSurfaceList->setSourceList(m_session->promptSurfaceList());536 Q_EMIT fullscreenChanged(fullscreen());
512 } else {537
513 // this can only happen after the session has stopped538 m_surfaceList.addSurfaceList(newSession->surfaceList());
514 Q_ASSERT(m_state == InternalState::Stopped || m_state == InternalState::StoppedResumable539 m_proxyPromptSurfaceList->setSourceList(newSession->promptSurfaceList());
515 || m_state == InternalState::Closing);
516 }
517
518 Q_EMIT sessionChanged(m_session);
519}540}
520541
521void Application::setInternalState(Application::InternalState state)542void Application::setInternalState(Application::InternalState state)
@@ -587,7 +608,7 @@
587 break;608 break;
588 case ProcessFailed:609 case ProcessFailed:
589 // we assume the session always stop before the process610 // we assume the session always stop before the process
590 Q_ASSERT(!m_session || m_session->state() == Session::Stopped);611 Q_ASSERT(m_sessions.isEmpty() || combinedSessionState() == Session::Stopped);
591612
592 if (m_state == InternalState::Starting) {613 if (m_state == InternalState::Starting) {
593 // that was way too soon. let it go away614 // that was way too soon. let it go away
@@ -599,7 +620,7 @@
599 break;620 break;
600 case ProcessStopped:621 case ProcessStopped:
601 // we assume the session always stop before the process622 // we assume the session always stop before the process
602 Q_ASSERT(!m_session || m_session->state() == Session::Stopped);623 Q_ASSERT(m_sessions.isEmpty() || combinedSessionState() == Session::Stopped);
603624
604 if (m_state == InternalState::Starting) {625 if (m_state == InternalState::Starting) {
605 // that was way too soon. let it go away626 // that was way too soon. let it go away
@@ -622,7 +643,7 @@
622 INFO_MSG << "()";643 INFO_MSG << "()";
623644
624 Q_ASSERT(m_state == InternalState::Running);645 Q_ASSERT(m_state == InternalState::Running);
625 Q_ASSERT(m_session != nullptr);646 Q_ASSERT(!m_sessions.isEmpty());
626647
627 if (exemptFromLifecycle()) {648 if (exemptFromLifecycle()) {
628 // There's no need to keep the wakelock as the process is never suspended649 // There's no need to keep the wakelock as the process is never suspended
@@ -631,7 +652,9 @@
631 setInternalState(InternalState::RunningInBackground);652 setInternalState(InternalState::RunningInBackground);
632 } else {653 } else {
633 setInternalState(InternalState::SuspendingWaitSession);654 setInternalState(InternalState::SuspendingWaitSession);
634 m_session->suspend();655 for (auto session : m_sessions) {
656 session->suspend();
657 }
635 }658 }
636}659}
637660
@@ -645,12 +668,14 @@
645 if (m_processState == ProcessSuspended) {668 if (m_processState == ProcessSuspended) {
646 setProcessState(ProcessRunning); // should we wait for a resumed() signal?669 setProcessState(ProcessRunning); // should we wait for a resumed() signal?
647 }670 }
648 if (m_session) {671 for (auto session : m_sessions) {
649 m_session->resume();672 session->resume();
650 }673 }
651 } else if (m_state == InternalState::SuspendingWaitSession) {674 } else if (m_state == InternalState::SuspendingWaitSession) {
652 setInternalState(InternalState::Running);675 setInternalState(InternalState::Running);
653 m_session->resume();676 for (auto session : m_sessions) {
677 session->resume();
678 }
654 } else if (m_state == InternalState::RunningInBackground) {679 } else if (m_state == InternalState::RunningInBackground) {
655 setInternalState(InternalState::Running);680 setInternalState(InternalState::Running);
656 }681 }
@@ -704,11 +729,6 @@
704 return m_rotatesWindowContents;729 return m_rotatesWindowContents;
705}730}
706731
707SessionInterface* Application::session() const
708{
709 return m_session;
710}
711
712void Application::acquireWakelock() const732void Application::acquireWakelock() const
713{733{
714 if (appId() == QLatin1String("unity8-dash"))734 if (appId() == QLatin1String("unity8-dash"))
@@ -725,9 +745,28 @@
725 m_sharedWakelock->release(this);745 m_sharedWakelock->release(this);
726}746}
727747
728void Application::onSessionStateChanged(Session::State sessionState)748SessionInterface::State Application::combinedSessionState()
729{749{
730 switch (sessionState) {750 // This doesn't make sense when there are no sessions
751 Q_ASSERT(m_sessions.count() > 0);
752
753 if (m_sessions.count() == 1) {
754 // easy case
755 return m_sessions[0]->state();
756 }
757
758 SessionInterface::State combinedState = SessionInterface::Stopped;
759 for (auto session : m_sessions) {
760 if (session->state() > combinedState) {
761 combinedState = session->state();
762 }
763 }
764 return combinedState;
765}
766
767void Application::onSessionStateChanged()
768{
769 switch (combinedSessionState()) {
731 case Session::Starting:770 case Session::Starting:
732 break;771 break;
733 case Session::Running:772 case Session::Running:
@@ -825,13 +864,18 @@
825864
826 if (size != m_initialSurfaceSize) {865 if (size != m_initialSurfaceSize) {
827 m_initialSurfaceSize = size;866 m_initialSurfaceSize = size;
867 if (m_initialSurfaceSize.isValid()) {
868 for (auto session : m_sessions) {
869 InitialSurfaceSizes::set(session->pid(), size);
870 }
871 }
828 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);872 Q_EMIT initialSurfaceSizeChanged(m_initialSurfaceSize);
829 }873 }
830}874}
831875
832unityapp::MirSurfaceListInterface* Application::surfaceList() const876unityapp::MirSurfaceListInterface* Application::surfaceList() const
833{877{
834 return m_proxySurfaceList;878 return &m_surfaceList;
835}879}
836880
837unityapp::MirSurfaceListInterface* Application::promptSurfaceList() const881unityapp::MirSurfaceListInterface* Application::promptSurfaceList() const
@@ -841,11 +885,11 @@
841885
842void Application::requestFocus()886void Application::requestFocus()
843{887{
844 if (m_proxySurfaceList->rowCount() > 0) {888 if (m_surfaceList.rowCount() > 0) {
845 INFO_MSG << "() - Requesting focus for most recent toplevel app surface";889 INFO_MSG << "() - Requesting focus for most recent toplevel app surface";
846890
847 for (int i = 0; i < m_proxySurfaceList->count(); ++i) {891 for (int i = 0; i < m_surfaceList.count(); ++i) {
848 auto surface = static_cast<MirSurfaceInterface*>(m_proxySurfaceList->get(i));892 auto surface = static_cast<MirSurfaceInterface*>(m_surfaceList.get(i));
849 if (!surface->parentSurface()) {893 if (!surface->parentSurface()) {
850 surface->requestFocus();894 surface->requestFocus();
851 break;895 break;
@@ -857,4 +901,16 @@
857 }901 }
858}902}
859903
904void Application::terminate()
905{
906 for (auto session : m_sessions) {
907 kill(session->pid(), SIGTERM);
908 }
909}
910
911QVector<SessionInterface*> Application::sessions() const
912{
913 return m_sessions;
914}
915
860} // namespace qtmir916} // namespace qtmir
861917
=== modified file 'src/modules/Unity/Application/application.h'
--- src/modules/Unity/Application/application.h 2017-03-22 14:57:19 +0000
+++ src/modules/Unity/Application/application.h 2017-03-31 21:11:21 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright (C) 2013-2016 Canonical, Ltd.2 * Copyright (C) 2013-2017 Canonical, Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it under4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by5 * the terms of the GNU Lesser General Public License version 3, as published by
@@ -24,6 +24,7 @@
24#include <QtCore/QtCore>24#include <QtCore/QtCore>
25#include <QImage>25#include <QImage>
26#include <QSharedPointer>26#include <QSharedPointer>
27#include <QVector>
2728
28// Unity API29// Unity API
29#include <unity/shell/application/ApplicationInfoInterface.h>30#include <unity/shell/application/ApplicationInfoInterface.h>
@@ -109,9 +110,11 @@
109 void setProcessState(ProcessState value);110 void setProcessState(ProcessState value);
110111
111 QStringList arguments() const { return m_arguments; }112 QStringList arguments() const { return m_arguments; }
113 void setArguments(const QStringList &arguments);
112114
113 SessionInterface* session() const;115 void addSession(SessionInterface *session);
114 void setSession(SessionInterface *session);116 void removeSession(SessionInterface *session);
117 QVector<SessionInterface*> sessions() const;
115118
116 bool isValid() const;119 bool isValid() const;
117 bool fullscreen() const;120 bool fullscreen() const;
@@ -123,13 +126,13 @@
123126
124 void requestFocus();127 void requestFocus();
125128
129 void terminate();
130
126 // for tests131 // for tests
127 void setStopTimer(AbstractTimer *timer);132 void setStopTimer(AbstractTimer *timer);
128 AbstractTimer *stopTimer() const { return m_stopTimer; }133 AbstractTimer *stopTimer() const { return m_stopTimer; }
129
130Q_SIGNALS:134Q_SIGNALS:
131 void fullscreenChanged(bool fullscreen);135 void fullscreenChanged(bool fullscreen);
132 void sessionChanged(SessionInterface *session);
133136
134 void startProcessRequested();137 void startProcessRequested();
135 void stopProcessRequested();138 void stopProcessRequested();
@@ -139,7 +142,7 @@
139 void closing();142 void closing();
140143
141private Q_SLOTS:144private Q_SLOTS:
142 void onSessionStateChanged(SessionInterface::State sessionState);145 void onSessionStateChanged();
143146
144 void respawn();147 void respawn();
145148
@@ -147,7 +150,6 @@
147150
148 void acquireWakelock() const;151 void acquireWakelock() const;
149 void releaseWakelock() const;152 void releaseWakelock() const;
150 void setArguments(const QStringList &arguments);
151 void setInternalState(InternalState state);153 void setInternalState(InternalState state);
152 void wipeQMLCache();154 void wipeQMLCache();
153 void suspend();155 void suspend();
@@ -160,6 +162,7 @@
160 void applyRequestedSuspended();162 void applyRequestedSuspended();
161 void applyClosing();163 void applyClosing();
162 void onSessionStopped();164 void onSessionStopped();
165 SessionInterface::State combinedSessionState();
163166
164 QSharedPointer<SharedWakelock> m_sharedWakelock;167 QSharedPointer<SharedWakelock> m_sharedWakelock;
165 QSharedPointer<ApplicationInfo> m_appInfo;168 QSharedPointer<ApplicationInfo> m_appInfo;
@@ -168,7 +171,7 @@
168 QStringList m_arguments;171 QStringList m_arguments;
169 Qt::ScreenOrientations m_supportedOrientations;172 Qt::ScreenOrientations m_supportedOrientations;
170 bool m_rotatesWindowContents;173 bool m_rotatesWindowContents;
171 SessionInterface *m_session;174 QVector<SessionInterface*> m_sessions;
172 RequestedState m_requestedState;175 RequestedState m_requestedState;
173 ProcessState m_processState;176 ProcessState m_processState;
174 AbstractTimer *m_stopTimer;177 AbstractTimer *m_stopTimer;
@@ -176,12 +179,8 @@
176 QSize m_initialSurfaceSize;179 QSize m_initialSurfaceSize;
177 bool m_closing{false};180 bool m_closing{false};
178181
179 ProxySurfaceListModel *m_proxySurfaceList;182 mutable MirSurfaceListModel m_surfaceList;
180 ProxySurfaceListModel *m_proxyPromptSurfaceList;183 ProxySurfaceListModel *m_proxyPromptSurfaceList;
181
182 friend class ApplicationManager;
183 friend class SessionManager;
184 friend class Session;
185};184};
186185
187} // namespace qtmir186} // namespace qtmir
188187
=== modified file 'src/modules/Unity/Application/application_manager.cpp'
--- src/modules/Unity/Application/application_manager.cpp 2017-03-28 17:12:54 +0000
+++ src/modules/Unity/Application/application_manager.cpp 2017-03-31 21:11:21 +0000
@@ -28,7 +28,6 @@
28#include "settings.h"28#include "settings.h"
2929
30// mirserver30// mirserver
31#include "initialsurfacesizes.h"
32#include "nativeinterface.h"31#include "nativeinterface.h"
33#include "logging.h"32#include "logging.h"
3433
@@ -425,7 +424,6 @@
425424
426 Q_UNUSED(error); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??425 Q_UNUSED(error); // FIXME(greyback) upstart reports app that fully started up & crashes as failing during startup??
427 application->setProcessState(Application::ProcessFailed);426 application->setProcessState(Application::ProcessFailed);
428 setApplicationPid(application, 0);
429}427}
430428
431void ApplicationManager::onProcessStopped(const QString &appId)429void ApplicationManager::onProcessStopped(const QString &appId)
@@ -450,7 +448,6 @@
450 // we don't want to override what onProcessFailed already set.448 // we don't want to override what onProcessFailed already set.
451 if (application->processState() != Application::ProcessFailed) {449 if (application->processState() != Application::ProcessFailed) {
452 application->setProcessState(Application::ProcessStopped);450 application->setProcessState(Application::ProcessStopped);
453 setApplicationPid(application, 0);
454 }451 }
455}452}
456453
@@ -527,8 +524,8 @@
527 if (app->state() == Application::Starting) {524 if (app->state() == Application::Starting) {
528 tracepoint(qtmir, appIdHasProcessId_start);525 tracepoint(qtmir, appIdHasProcessId_start);
529 if (m_taskController->appIdHasProcessId(app->appId(), pid)) {526 if (m_taskController->appIdHasProcessId(app->appId(), pid)) {
530 setApplicationPid(app, pid);
531 authorized = true;527 authorized = true;
528 m_authorizedPids.insertMulti(pid, app->appId());
532 tracepoint(qtmir, appIdHasProcessId_end, 1); //found529 tracepoint(qtmir, appIdHasProcessId_end, 1); //found
533 return;530 return;
534 }531 }
@@ -581,17 +578,18 @@
581 // some naughty applications use a script to launch the actual application. Check for the578 // some naughty applications use a script to launch the actual application. Check for the
582 // case where shell actually launched the script.579 // case where shell actually launched the script.
583 Application *application = findApplicationMutexHeld(appInfo->appId());580 Application *application = findApplicationMutexHeld(appInfo->appId());
584 if (application && application->state() == Application::Starting) {581 if (application) {
585 qCDebug(QTMIR_APPLICATIONS) << "Process with pid" << pid << "appeared, attaching to existing entry"582 qCDebug(QTMIR_APPLICATIONS) << "Process with pid" << pid << "appeared, attaching to existing entry"
586 << "in application list with appId:" << application->appId();583 << "in application list with appId:" << application->appId();
587 setApplicationPid(application, pid);
588 authorized = true;584 authorized = true;
585 m_authorizedPids.insertMulti(pid, appInfo->appId());
589 return;586 return;
590 }587 }
591588
592 const QStringList arguments(info->asStringList());589 const QStringList arguments(info->asStringList());
593 queuedAddApp(appInfo, arguments, pid);590 queuedAddApp(appInfo, arguments, pid);
594 authorized = true;591 authorized = true;
592 m_authorizedPids.insertMulti(pid, appInfo->appId());
595}593}
596594
597595
@@ -603,26 +601,22 @@
603 auto qtmirSurface = static_cast<qtmir::MirSurfaceInterface*>(surface);601 auto qtmirSurface = static_cast<qtmir::MirSurfaceInterface*>(surface);
604602
605 QMutexLocker locker(&m_mutex);603 QMutexLocker locker(&m_mutex);
606 return findApplicationWithPid(miral::pid_of(qtmirSurface->session()->session()));604 return findApplicationWithSession(qtmirSurface->session()->session());
607}605}
608606
609Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session)607Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session) const
610{608{
611 if (!session)609 if (!session)
612 return nullptr;610 return nullptr;
613 return findApplicationWithPid(miral::pid_of(session));611
614}612 for (auto *application : m_applications) {
615613 for (auto *qmlSession : application->sessions()) {
616Application* ApplicationManager::findApplicationWithPid(const pid_t pid) const614 if (qmlSession->session() == session) {
617{615 return application;
618 if (pid <= 0)616 }
619 return nullptr;
620
621 for (Application *app : m_applications) {
622 if (m_applicationsPid.value(app) == pid) {
623 return app;
624 }617 }
625 }618 }
619
626 return nullptr;620 return nullptr;
627}621}
628622
@@ -638,7 +632,6 @@
638 appInfo,632 appInfo,
639 arguments,633 arguments,
640 this);634 this);
641 setApplicationPid(application, pid);
642 add(application);635 add(application);
643}636}
644637
@@ -653,22 +646,8 @@
653 DEBUG_MSG << "(appId=" << application->appId() << ")";646 DEBUG_MSG << "(appId=" << application->appId() << ")";
654647
655 connect(application, &QObject::destroyed, this, [this, application] {648 connect(application, &QObject::destroyed, this, [this, application] {
656 const pid_t pid = m_applicationsPid.value(application);
657 if (pid != 0) {
658 InitialSurfaceSizes::remove(pid);
659 m_applicationsPid.remove(application);
660 }
661 m_closingApplications.removeAll(application);649 m_closingApplications.removeAll(application);
662 });650 });
663 connect(application, &Application::initialSurfaceSizeChanged, this, [this, application] {
664 const pid_t pid = m_applicationsPid.value(application);
665 if (pid != 0) {
666 const QSize size = application->initialSurfaceSize();
667 if (size.isValid()) {
668 InitialSurfaceSizes::set(pid, size);
669 }
670 }
671 });
672651
673 Q_ASSERT(!m_modelUnderChange);652 Q_ASSERT(!m_modelUnderChange);
674 m_modelUnderChange = true;653 m_modelUnderChange = true;
@@ -707,13 +686,10 @@
707686
708 connect(application, &Application::stopProcessRequested, this, [=]() {687 connect(application, &Application::stopProcessRequested, this, [=]() {
709 if (!m_taskController->stop(appId)) {688 if (!m_taskController->stop(appId)) {
710 const pid_t pid = m_applicationsPid.value(application);689 qWarning() << "FAILED to ask Upstart to stop application with appId" << appId
711 if (pid > 0) {690 << "Sending SIGTERM to process:" << appId;
712 qWarning() << "FAILED to ask Upstart to stop application with appId" << appId691 application->terminate();
713 << "Sending SIGTERM to process:" << appId;692 application->setProcessState(Application::ProcessStopped);
714 kill(pid, SIGTERM);
715 application->setProcessState(Application::ProcessStopped);
716 }
717 }693 }
718 });694 });
719695
@@ -805,27 +781,22 @@
805 return nullptr;781 return nullptr;
806}782}
807783
808void ApplicationManager::setApplicationPid(Application *app, pid_t pid)
809{
810 const pid_t oldPid = m_applicationsPid.value(app);
811 if (oldPid != 0) {
812 InitialSurfaceSizes::remove(oldPid);
813 }
814
815 m_applicationsPid.insert(app, pid);
816
817 if (app->initialSurfaceSize().isValid() && pid != 0) {
818 InitialSurfaceSizes::set(pid, app->initialSurfaceSize());
819 }
820}
821
822void ApplicationManager::onSessionStarting(SessionInterface *qmlSession)784void ApplicationManager::onSessionStarting(SessionInterface *qmlSession)
823{785{
824 QMutexLocker locker(&m_mutex);786 QMutexLocker locker(&m_mutex);
825787
826 Application* application = findApplicationWithSession(qmlSession->session());788 Application* application = nullptr;
827 if (application && application->state() != Application::Running) {789 {
828 application->setSession(qmlSession);790 auto iter = m_authorizedPids.find(miral::pid_of(qmlSession->session()));
791 if (iter != m_authorizedPids.end()) {
792 QString appId = iter.value();
793 application = findApplication(appId);
794 m_authorizedPids.erase(iter);
795 }
796 }
797
798 if (application) {
799 application->addSession(qmlSession);
829 }800 }
830}801}
831802
832803
=== modified file 'src/modules/Unity/Application/application_manager.h'
--- src/modules/Unity/Application/application_manager.h 2017-03-24 08:44:32 +0000
+++ src/modules/Unity/Application/application_manager.h 2017-03-31 21:11:21 +0000
@@ -110,10 +110,9 @@
110110
111private:111private:
112 // All calls to private functions happen with the mutex held112 // All calls to private functions happen with the mutex held
113 qtmir::Application* findApplicationWithPid(const pid_t pid) const;
114 Application* findApplicationMutexHeld(const QString &inputAppId) const;113 Application* findApplicationMutexHeld(const QString &inputAppId) const;
115114
116 Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);115 Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session) const;
117 void setFocused(Application *application);116 void setFocused(Application *application);
118 void add(Application *application);117 void add(Application *application);
119 void remove(Application* application);118 void remove(Application* application);
@@ -125,10 +124,7 @@
125 Application* findApplicationWithPromptSession(const mir::scene::PromptSession* promptSession);124 Application* findApplicationWithPromptSession(const mir::scene::PromptSession* promptSession);
126 Application *findClosingApplication(const QString &inputAppId) const;125 Application *findClosingApplication(const QString &inputAppId) const;
127126
128 void setApplicationPid(Application *application, pid_t pid);
129
130 QList<Application*> m_applications;127 QList<Application*> m_applications;
131 QHash<Application*, pid_t> m_applicationsPid;
132 DBusFocusInfo *m_dbusFocusInfo;128 DBusFocusInfo *m_dbusFocusInfo;
133 QSharedPointer<TaskController> m_taskController;129 QSharedPointer<TaskController> m_taskController;
134 QSharedPointer<ProcInfo> m_procInfo;130 QSharedPointer<ProcInfo> m_procInfo;
@@ -139,8 +135,8 @@
139 bool m_modelUnderChange{false};135 bool m_modelUnderChange{false};
140 static ApplicationManager* the_application_manager;136 static ApplicationManager* the_application_manager;
141137
142 friend class Application;138 QHash<pid_t, QString> m_authorizedPids;
143 friend class DBusWindowStack;139
144 mutable QMutex m_mutex;140 mutable QMutex m_mutex;
145};141};
146142
147143
=== modified file 'src/modules/Unity/Application/dbusfocusinfo.cpp'
--- src/modules/Unity/Application/dbusfocusinfo.cpp 2017-03-07 20:20:21 +0000
+++ src/modules/Unity/Application/dbusfocusinfo.cpp 2017-03-31 21:11:21 +0000
@@ -75,18 +75,20 @@
75SessionInterface* DBusFocusInfo::findSessionWithPid(const QSet<pid_t> &pidSet)75SessionInterface* DBusFocusInfo::findSessionWithPid(const QSet<pid_t> &pidSet)
76{76{
77 Q_FOREACH (Application* application, m_applications) {77 Q_FOREACH (Application* application, m_applications) {
78 auto session = application->session();78 QVector<SessionInterface*> sessions = application->sessions();
79 if (pidSet.contains(session->pid())) {79 for (auto session : sessions) {
80 return session;80 if (pidSet.contains(session->pid())) {
81 }81 return session;
82 SessionInterface *chosenChildSession = nullptr;82 }
83 session->foreachChildSession([&](SessionInterface* childSession) {83 SessionInterface *chosenChildSession = nullptr;
84 if (pidSet.contains(childSession->pid())) {84 session->foreachChildSession([&](SessionInterface* childSession) {
85 chosenChildSession = childSession;85 if (pidSet.contains(childSession->pid())) {
86 }86 chosenChildSession = childSession;
87 });87 }
88 if (chosenChildSession) {88 });
89 return chosenChildSession;89 if (chosenChildSession) {
90 return chosenChildSession;
91 }
90 }92 }
91 }93 }
92 return nullptr;94 return nullptr;
@@ -111,21 +113,22 @@
111MirSurfaceInterface *DBusFocusInfo::findQmlSurface(const QString &serializedId)113MirSurfaceInterface *DBusFocusInfo::findQmlSurface(const QString &serializedId)
112{114{
113 for (Application* application : m_applications) {115 for (Application* application : m_applications) {
114 auto session = application->session();116 for (SessionInterface *session : application->sessions()) {
115 if (session) {117 if (session) {
116 auto surfaceList = static_cast<MirSurfaceListModel*>(session->surfaceList());118 auto surfaceList = static_cast<MirSurfaceListModel*>(session->surfaceList());
117 for (int i = 0; i < surfaceList->count(); ++i) {119 for (int i = 0; i < surfaceList->count(); ++i) {
118 auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));120 auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));
119 if (qmlSurface->persistentId() == serializedId) {121 if (qmlSurface->persistentId() == serializedId) {
120 return qmlSurface;122 return qmlSurface;
123 }
121 }124 }
122 }
123125
124 surfaceList = static_cast<MirSurfaceListModel*>(session->promptSurfaceList());126 surfaceList = static_cast<MirSurfaceListModel*>(session->promptSurfaceList());
125 for (int i = 0; i < surfaceList->count(); ++i) {127 for (int i = 0; i < surfaceList->count(); ++i) {
126 auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));128 auto qmlSurface = static_cast<MirSurfaceInterface*>(surfaceList->get(i));
127 if (qmlSurface->persistentId() == serializedId) {129 if (qmlSurface->persistentId() == serializedId) {
128 return qmlSurface;130 return qmlSurface;
131 }
129 }132 }
130 }133 }
131 }134 }
132135
=== modified file 'src/modules/Unity/Application/session.cpp'
--- src/modules/Unity/Application/session.cpp 2017-02-21 18:39:45 +0000
+++ src/modules/Unity/Application/session.cpp 2017-03-31 21:11:21 +0000
@@ -92,7 +92,7 @@
92 delete child;92 delete child;
93 }93 }
94 if (m_application) {94 if (m_application) {
95 m_application->setSession(nullptr);95 m_application->removeSession(this);
96 }96 }
9797
98 delete m_children; m_children = nullptr;98 delete m_children; m_children = nullptr;
9999
=== modified file 'src/modules/Unity/Application/session_interface.h'
--- src/modules/Unity/Application/session_interface.h 2017-02-21 18:46:30 +0000
+++ src/modules/Unity/Application/session_interface.h 2017-03-31 21:11:21 +0000
@@ -53,12 +53,13 @@
53 SessionInterface(QObject *parent = 0) : QObject(parent) {}53 SessionInterface(QObject *parent = 0) : QObject(parent) {}
54 virtual ~SessionInterface() {}54 virtual ~SessionInterface() {}
5555
56 // Ordered by importance/activity. Used for calculating the combined state of multiple sessions
56 enum State {57 enum State {
57 Starting,58 Running = 4,
58 Running,59 Starting = 3,
59 Suspending,60 Suspending = 2,
60 Suspended,61 Suspended = 1,
61 Stopped62 Stopped = 0
62 };63 };
6364
64 //getters65 //getters
6566
=== modified file 'tests/modules/Application/application_test.cpp'
--- tests/modules/Application/application_test.cpp 2017-02-21 18:46:30 +0000
+++ tests/modules/Application/application_test.cpp 2017-03-31 21:11:21 +0000
@@ -49,7 +49,7 @@
49 inline void suspend(Application *application)49 inline void suspend(Application *application)
50 {50 {
51 application->setRequestedState(Application::RequestedSuspended);51 application->setRequestedState(Application::RequestedSuspended);
52 auto session = dynamic_cast<Session*>(application->session());52 auto session = dynamic_cast<Session*>(application->sessions()[0]);
5353
54 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());54 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
55 ASSERT_EQ(Session::Suspending, session->state());55 ASSERT_EQ(Session::Suspending, session->state());
@@ -115,7 +115,7 @@
115115
116 FakeSession *session = new FakeSession;116 FakeSession *session = new FakeSession;
117117
118 application->setSession(session);118 application->addSession(session);
119119
120 ASSERT_EQ(Application::InternalState::Starting, application->internalState());120 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
121121
@@ -150,7 +150,7 @@
150150
151 // Get it running and then suspend it151 // Get it running and then suspend it
152 application->setProcessState(Application::ProcessRunning);152 application->setProcessState(Application::ProcessRunning);
153 application->setSession(session);153 application->addSession(session);
154 session->setState(SessionInterface::Running);154 session->setState(SessionInterface::Running);
155 application->setRequestedState(Application::RequestedSuspended);155 application->setRequestedState(Application::RequestedSuspended);
156 session->setState(SessionInterface::Suspended);156 session->setState(SessionInterface::Suspended);
@@ -175,7 +175,7 @@
175175
176 // Get it running, suspend it, and finally stop it176 // Get it running, suspend it, and finally stop it
177 application->setProcessState(Application::ProcessRunning);177 application->setProcessState(Application::ProcessRunning);
178 application->setSession(session);178 application->addSession(session);
179 session->setState(SessionInterface::Running);179 session->setState(SessionInterface::Running);
180 application->setRequestedState(Application::RequestedSuspended);180 application->setRequestedState(Application::RequestedSuspended);
181 session->setState(SessionInterface::Suspended);181 session->setState(SessionInterface::Suspended);
@@ -215,7 +215,7 @@
215215
216 FakeSession *session = new FakeSession;216 FakeSession *session = new FakeSession;
217217
218 application->setSession(session);218 application->addSession(session);
219219
220 ASSERT_EQ(Application::InternalState::Starting, application->internalState());220 ASSERT_EQ(Application::InternalState::Starting, application->internalState());
221221
@@ -250,7 +250,7 @@
250 application->setProcessState(Application::ProcessRunning);250 application->setProcessState(Application::ProcessRunning);
251251
252 FakeSession *session = new FakeSession;252 FakeSession *session = new FakeSession;
253 application->setSession(session);253 application->addSession(session);
254254
255 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));255 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));
256256
@@ -283,7 +283,7 @@
283 application->setProcessState(Application::ProcessRunning);283 application->setProcessState(Application::ProcessRunning);
284284
285 Session *session = createSessionWithFakes();285 Session *session = createSessionWithFakes();
286 application->setSession(session);286 application->addSession(session);
287287
288 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));288 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));
289289
@@ -318,7 +318,7 @@
318 application->setProcessState(Application::ProcessRunning);318 application->setProcessState(Application::ProcessRunning);
319319
320 FakeSession *session = new FakeSession;320 FakeSession *session = new FakeSession;
321 application->setSession(session);321 application->addSession(session);
322322
323 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));323 QSignalSpy spyAppStopped(application.data(), SIGNAL(stopped()));
324324
@@ -380,7 +380,7 @@
380380
381 Session *session = createSessionWithFakes();381 Session *session = createSessionWithFakes();
382382
383 application->setSession(session);383 application->addSession(session);
384384
385 FakeMirSurface *surface = new FakeMirSurface;385 FakeMirSurface *surface = new FakeMirSurface;
386 session->registerSurface(surface);386 session->registerSurface(surface);
@@ -437,7 +437,7 @@
437437
438 Session *session = createSessionWithFakes();438 Session *session = createSessionWithFakes();
439439
440 application->setSession(session);440 application->addSession(session);
441441
442 FakeMirSurface *surface = new FakeMirSurface;442 FakeMirSurface *surface = new FakeMirSurface;
443 session->registerSurface(surface);443 session->registerSurface(surface);
@@ -488,7 +488,7 @@
488488
489 QPointer<Session> session(createSessionWithFakes());489 QPointer<Session> session(createSessionWithFakes());
490490
491 application->setSession(session);491 application->addSession(session);
492492
493 FakeMirSurface *surface = new FakeMirSurface;493 FakeMirSurface *surface = new FakeMirSurface;
494 session->registerSurface(surface);494 session->registerSurface(surface);
@@ -532,7 +532,7 @@
532 application->setProcessState(Application::ProcessRunning);532 application->setProcessState(Application::ProcessRunning);
533533
534 QPointer<Session> session(createSessionWithFakes());534 QPointer<Session> session(createSessionWithFakes());
535 application->setSession(session);535 application->addSession(session);
536536
537 FakeMirSurface *surface = new FakeMirSurface;537 FakeMirSurface *surface = new FakeMirSurface;
538 session->registerSurface(surface);538 session->registerSurface(surface);
@@ -570,7 +570,7 @@
570570
571 Session *session = createSessionWithFakes();571 Session *session = createSessionWithFakes();
572572
573 application->setSession(session);573 application->addSession(session);
574574
575 FakeMirSurface *surface = new FakeMirSurface;575 FakeMirSurface *surface = new FakeMirSurface;
576 session->registerSurface(surface);576 session->registerSurface(surface);
@@ -602,7 +602,7 @@
602 application->setProcessState(Application::ProcessRunning);602 application->setProcessState(Application::ProcessRunning);
603 Session *session = createSessionWithFakes();603 Session *session = createSessionWithFakes();
604604
605 application->setSession(session);605 application->addSession(session);
606606
607 QSignalSpy surfaceCountChangedSpy(application.data(), &Application::surfaceCountChanged);607 QSignalSpy surfaceCountChangedSpy(application.data(), &Application::surfaceCountChanged);
608608
@@ -641,7 +641,7 @@
641641
642 FakeSession *session = new FakeSession;642 FakeSession *session = new FakeSession;
643643
644 application->setSession(session);644 application->addSession(session);
645645
646 QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested()));646 QSignalSpy spyStartProcess(application.data(), SIGNAL(startProcessRequested()));
647647
648648
=== modified file 'tests/modules/ApplicationManager/application_manager_test.cpp'
--- tests/modules/ApplicationManager/application_manager_test.cpp 2017-02-21 18:46:30 +0000
+++ tests/modules/ApplicationManager/application_manager_test.cpp 2017-03-31 21:11:21 +0000
@@ -59,7 +59,7 @@
59 inline void suspend(Application *application) {59 inline void suspend(Application *application) {
60 application->setRequestedState(Application::RequestedSuspended);60 application->setRequestedState(Application::RequestedSuspended);
61 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());61 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, application->internalState());
62 static_cast<qtmir::Session*>(application->session())->doSuspend();62 static_cast<qtmir::Session*>(application->sessions()[0])->doSuspend();
63 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());63 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, application->internalState());
64 applicationManager.onProcessSuspended(application->appId());64 applicationManager.onProcessSuspended(application->appId());
65 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());65 ASSERT_EQ(Application::InternalState::Suspended, application->internalState());
@@ -85,6 +85,11 @@
8585
86 FakeMirSurface surface;86 FakeMirSurface surface;
8787
88 EXPECT_CALL(*taskController,appIdHasProcessId(QString(dialer_app_id), firstProcId))
89 .WillRepeatedly(Return(true));
90 EXPECT_CALL(*taskController,appIdHasProcessId(QString(dialer_app_id), secondProcId))
91 .WillRepeatedly(Return(true));
92
88 EXPECT_CALL(procInfo,command_line(firstProcId))93 EXPECT_CALL(procInfo,command_line(firstProcId))
89 .Times(1)94 .Times(1)
90 .WillOnce(Return(cmdLine));95 .WillOnce(Return(cmdLine));
@@ -226,12 +231,22 @@
226 const char third_app_id[] = "app3";231 const char third_app_id[] = "app3";
227 QByteArray third_cmdLine( "/usr/bin/app3 --desktop_file_hint=app3");232 QByteArray third_cmdLine( "/usr/bin/app3 --desktop_file_hint=app3");
228233
234 ON_CALL(*taskController,appIdHasProcessId(QString(first_app_id), first_procId)).WillByDefault(Return(true));
235 ON_CALL(*taskController,appIdHasProcessId(QString(first_app_id), second_procId)).WillByDefault(Return(false));
236 ON_CALL(*taskController,appIdHasProcessId(QString(first_app_id), third_procId)).WillByDefault(Return(false));
237
238 ON_CALL(*taskController,appIdHasProcessId(QString(second_app_id), second_procId)).WillByDefault(Return(true));
239 ON_CALL(*taskController,appIdHasProcessId(QString(second_app_id), first_procId)).WillByDefault(Return(false));
240 ON_CALL(*taskController,appIdHasProcessId(QString(second_app_id), third_procId)).WillByDefault(Return(false));
241
242 ON_CALL(*taskController,appIdHasProcessId(QString(third_app_id), third_procId)).WillByDefault(Return(true));
243 ON_CALL(*taskController,appIdHasProcessId(QString(third_app_id), first_procId)).WillByDefault(Return(false));
244 ON_CALL(*taskController,appIdHasProcessId(QString(third_app_id), second_procId)).WillByDefault(Return(false));
245
229 EXPECT_CALL(procInfo,command_line(first_procId))246 EXPECT_CALL(procInfo,command_line(first_procId))
230 .Times(1)247 .Times(1)
231 .WillOnce(Return(first_cmdLine));248 .WillOnce(Return(first_cmdLine));
232249
233 ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));
234
235 EXPECT_CALL(procInfo,command_line(second_procId))250 EXPECT_CALL(procInfo,command_line(second_procId))
236 .Times(1)251 .Times(1)
237 .WillOnce(Return(second_cmdLine));252 .WillOnce(Return(second_cmdLine));
@@ -257,12 +272,12 @@
257 Application * secondApp = applicationManager.findApplication(second_app_id);272 Application * secondApp = applicationManager.findApplication(second_app_id);
258 Application * thirdApp = applicationManager.findApplication(third_app_id);273 Application * thirdApp = applicationManager.findApplication(third_app_id);
259274
260 EXPECT_EQ(firstAppInfo.application(), firstApp->session()->session());275 EXPECT_EQ(firstAppInfo.application(), firstApp->sessions()[0]->session());
261 EXPECT_EQ(secondAppInfo.application(), secondApp->session()->session());276 EXPECT_EQ(secondAppInfo.application(), secondApp->sessions()[0]->session());
262 EXPECT_EQ(thirdAppInfo.application(), thirdApp->session()->session());277 EXPECT_EQ(thirdAppInfo.application(), thirdApp->sessions()[0]->session());
263}278}
264279
265TEST_F(ApplicationManagerTests,two_session_on_one_application)280TEST_F(ApplicationManagerTests,two_sessions_on_one_application)
266{281{
267 int argc = 0;282 int argc = 0;
268 char* argv[0];283 char* argv[0];
@@ -275,28 +290,30 @@
275290
276 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));291 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
277292
278 ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));293 ON_CALL(*taskController,appIdHasProcessId(QString(an_app_id), a_procId)).WillByDefault(Return(true));
279294
280 bool authed = true;295 bool authed = true;
281296
282 auto firstAppInfo = createApplicationInfoFor("Oo", a_procId);297 auto firstAppInfo = createApplicationInfoFor("Oo", a_procId);
283 auto secondAppInfo = createApplicationInfoFor("oO", a_procId);298 auto secondAppInfo = createApplicationInfoFor("oO", a_procId);
299
284 applicationManager.authorizeSession(a_procId, authed);300 applicationManager.authorizeSession(a_procId, authed);
285
286 taskController->onSessionStarting(firstAppInfo);301 taskController->onSessionStarting(firstAppInfo);
302
303 applicationManager.authorizeSession(a_procId, authed);
287 taskController->onSessionStarting(secondAppInfo);304 taskController->onSessionStarting(secondAppInfo);
288305
289 Application * the_app = applicationManager.findApplication(an_app_id);306 Application * the_app = applicationManager.findApplication(an_app_id);
290307
291 EXPECT_EQ(true, authed);308 EXPECT_EQ(true, authed);
292 EXPECT_EQ(secondAppInfo.application(), the_app->session()->session());309 EXPECT_EQ(2, the_app->sessions().count());
293310
294 taskController->onSessionStopping(firstAppInfo);311 taskController->onSessionStopping(firstAppInfo);
295 taskController->onSessionStopping(secondAppInfo);312 taskController->onSessionStopping(secondAppInfo);
296 qtApp.sendPostedEvents(nullptr, QEvent::DeferredDelete);313 qtApp.sendPostedEvents(nullptr, QEvent::DeferredDelete);
297}314}
298315
299TEST_F(ApplicationManagerTests,two_session_on_one_application_after_starting)316TEST_F(ApplicationManagerTests,two_sessions_on_one_application_after_starting)
300{317{
301 int argc = 0;318 int argc = 0;
302 char* argv[0];319 char* argv[0];
@@ -310,24 +327,30 @@
310327
311 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));328 ON_CALL(procInfo,command_line(_)).WillByDefault(Return(a_cmd));
312329
313 ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));330 ON_CALL(*taskController,appIdHasProcessId(QString(an_app_id), a_procId)).WillByDefault(Return(true));
314331
315 bool authed = true;332 bool authed = true;
316333
317 auto firstAppInfo = createApplicationInfoFor("Oo", a_procId);334 auto firstAppInfo = createApplicationInfoFor("Oo", a_procId);
318 auto secondAppInfo = createApplicationInfoFor("oO", a_procId);335 auto secondAppInfo = createApplicationInfoFor("oO", a_procId);
336
319 applicationManager.authorizeSession(a_procId, authed);337 applicationManager.authorizeSession(a_procId, authed);
320
321 taskController->onSessionStarting(firstAppInfo);338 taskController->onSessionStarting(firstAppInfo);
339
340 EXPECT_EQ(true, authed);
341
322 onSessionCreatedSurface(firstAppInfo, &aSurface);342 onSessionCreatedSurface(firstAppInfo, &aSurface);
323 aSurface.setReady();343 aSurface.setReady();
344
345 Application * the_app = applicationManager.findApplication(an_app_id);
346 EXPECT_EQ(1, the_app->sessions().count());
347
348 applicationManager.authorizeSession(a_procId, authed);
324 taskController->onSessionStarting(secondAppInfo);349 taskController->onSessionStarting(secondAppInfo);
325350
326 Application * the_app = applicationManager.findApplication(an_app_id);
327
328 EXPECT_EQ(true, authed);351 EXPECT_EQ(true, authed);
329 EXPECT_EQ(Application::Running, the_app->state());352 EXPECT_EQ(Application::Running, the_app->state());
330 EXPECT_EQ(firstAppInfo.application(), the_app->session()->session());353 EXPECT_EQ(2, the_app->sessions().count());
331354
332 taskController->onSessionStopping(firstAppInfo);355 taskController->onSessionStopping(firstAppInfo);
333 taskController->onSessionStopping(secondAppInfo);356 taskController->onSessionStopping(secondAppInfo);
@@ -345,7 +368,7 @@
345 .Times(1)368 .Times(1)
346 .WillOnce(Return(cmdLine));369 .WillOnce(Return(cmdLine));
347370
348 ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));371 ON_CALL(*taskController,appIdHasProcessId(QString("app"), procId)).WillByDefault(Return(true));
349372
350 bool authed = true;373 bool authed = true;
351374
@@ -383,7 +406,9 @@
383 .Times(1)406 .Times(1)
384 .WillOnce(Return(first_cmdLine));407 .WillOnce(Return(first_cmdLine));
385408
386 ON_CALL(*taskController,appIdHasProcessId(_,_)).WillByDefault(Return(false));409 ON_CALL(*taskController,appIdHasProcessId(QString("app1"), first_procId)).WillByDefault(Return(true));
410 ON_CALL(*taskController,appIdHasProcessId(QString("app2"), second_procId)).WillByDefault(Return(true));
411 ON_CALL(*taskController,appIdHasProcessId(QString("app3"), third_procId)).WillByDefault(Return(true));
387412
388 EXPECT_CALL(procInfo,command_line(second_procId))413 EXPECT_CALL(procInfo,command_line(second_procId))
389 .Times(1)414 .Times(1)
@@ -718,7 +743,7 @@
718743
719 // Check application state and session are correctly set744 // Check application state and session are correctly set
720 Application *theApp = applicationManager.findApplication(appId);745 Application *theApp = applicationManager.findApplication(appId);
721 EXPECT_EQ(theApp->session()->session(), appInfo.application());746 EXPECT_EQ(theApp->sessions()[0]->session(), appInfo.application());
722 EXPECT_EQ(theApp->focused(), false);747 EXPECT_EQ(theApp->focused(), false);
723}748}
724749
@@ -1115,6 +1140,8 @@
1115 QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint=");1140 QByteArray cmdLine("/usr/bin/testApp --desktop_file_hint=");
1116 cmdLine = cmdLine.append(appId);1141 cmdLine = cmdLine.append(appId);
11171142
1143 ON_CALL(*taskController,appIdHasProcessId(appId, procId)).WillByDefault(Return(true));
1144
1118 // Set up Mocks & signal watcher1145 // Set up Mocks & signal watcher
1119 EXPECT_CALL(procInfo,command_line(procId))1146 EXPECT_CALL(procInfo,command_line(procId))
1120 .Times(1)1147 .Times(1)
@@ -1181,7 +1208,7 @@
11811208
1182 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());1209 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, app->internalState());
11831210
1184 static_cast<qtmir::Session*>(app->session())->doSuspend();1211 static_cast<qtmir::Session*>(app->sessions()[0])->doSuspend();
1185 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, app->internalState());1212 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, app->internalState());
11861213
1187 applicationManager.onProcessSuspended(app->appId());1214 applicationManager.onProcessSuspended(app->appId());
@@ -1412,10 +1439,10 @@
14121439
1413 // Session should have called deleteLater() on itself, as it's zombie and doesn't hold any surface1440 // Session should have called deleteLater() on itself, as it's zombie and doesn't hold any surface
1414 // But DeferredDelete is special: likes to be called out specifically or it won't come out1441 // But DeferredDelete is special: likes to be called out specifically or it won't come out
1415 qtApp.sendPostedEvents(app->session(), QEvent::DeferredDelete);1442 qtApp.sendPostedEvents(app->sessions()[0], QEvent::DeferredDelete);
1416 qtApp.sendPostedEvents();1443 qtApp.sendPostedEvents();
14171444
1418 ASSERT_EQ(app->session(), nullptr);1445 ASSERT_EQ(app->sessions().count(), 0);
14191446
1420 QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &)));1447 QSignalSpy focusRequestSpy(&applicationManager, SIGNAL(focusRequested(const QString &)));
14211448
@@ -1461,7 +1488,7 @@
1461 the_app->setRequestedState(Application::RequestedSuspended);1488 the_app->setRequestedState(Application::RequestedSuspended);
1462 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());1489 ASSERT_EQ(Application::InternalState::SuspendingWaitSession, the_app->internalState());
14631490
1464 static_cast<qtmir::Session*>(the_app->session())->doSuspend();1491 static_cast<qtmir::Session*>(the_app->sessions()[0])->doSuspend();
1465 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());1492 ASSERT_EQ(Application::InternalState::SuspendingWaitProcess, the_app->internalState());
1466 applicationManager.onProcessSuspended(the_app->appId());1493 applicationManager.onProcessSuspended(the_app->appId());
1467 ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());1494 ASSERT_EQ(Application::InternalState::Suspended, the_app->internalState());
@@ -1838,7 +1865,7 @@
1838 taskController->onSessionStarting(appInfo1);1865 taskController->onSessionStarting(appInfo1);
18391866
1840 FakeMirSurface surface1;1867 FakeMirSurface surface1;
1841 surface1.setSession(app1->session());1868 surface1.setSession(app1->sessions()[0]);
1842 onSessionCreatedSurface(appInfo1, &surface1);1869 onSessionCreatedSurface(appInfo1, &surface1);
1843 surface1.setReady();1870 surface1.setReady();
18441871
@@ -1864,7 +1891,7 @@
1864 taskController->onSessionStarting(appInfo2);1891 taskController->onSessionStarting(appInfo2);
18651892
1866 FakeMirSurface surface2;1893 FakeMirSurface surface2;
1867 surface2.setSession(app2->session());1894 surface2.setSession(app2->sessions()[0]);
1868 onSessionCreatedSurface(appInfo2, &surface2);1895 onSessionCreatedSurface(appInfo2, &surface2);
1869 surface2.setReady();1896 surface2.setReady();
18701897

Subscribers

People subscribed via source and target branches