Merge lp:~nick-dedekind/unity-mir/trusted-sessions into lp:unity-mir

Proposed by Nick Dedekind
Status: Merged
Approved by: Gerry Boland
Approved revision: 236
Merged at revision: 241
Proposed branch: lp:~nick-dedekind/unity-mir/trusted-sessions
Merge into: lp:unity-mir
Prerequisite: lp:~nick-dedekind/unity-mir/thready-screenshotting
Diff against target: 1480 lines (+659/-97)
23 files modified
debian/changelog (+7/-0)
src/modules/Unity/Application/application.cpp (+105/-6)
src/modules/Unity/Application/application.h (+26/-5)
src/modules/Unity/Application/application_manager.cpp (+175/-58)
src/modules/Unity/Application/application_manager.h (+21/-12)
src/modules/Unity/Application/applicationscreenshotprovider.cpp (+2/-1)
src/modules/Unity/Application/dbuswindowstack.cpp (+1/-1)
src/modules/Unity/Application/inputarea.cpp (+5/-4)
src/modules/Unity/Application/mirsurfacemanager.cpp (+3/-3)
src/modules/Unity/Application/proc_info.cpp (+13/-0)
src/modules/Unity/Application/proc_info.h (+3/-0)
src/unity-mir/CMakeLists.txt (+2/-0)
src/unity-mir/focussetter.cpp (+3/-2)
src/unity-mir/promptsessionlistener.cpp (+62/-0)
src/unity-mir/promptsessionlistener.h (+47/-0)
src/unity-mir/shellserverconfiguration.cpp (+20/-1)
src/unity-mir/shellserverconfiguration.h (+4/-0)
tests/CMakeLists.txt (+1/-0)
tests/application_manager_test.cpp (+62/-0)
tests/mock_proc_info.h (+8/-3)
tests/mock_prompt_session.h (+31/-0)
tests/mock_prompt_session_manager.h (+57/-0)
tests/mock_session.h (+1/-1)
To merge this branch: bzr merge lp:~nick-dedekind/unity-mir/trusted-sessions
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Gerry Boland (community) Approve
Review via email: mp+226283@code.launchpad.net

This proposal supersedes a proposal from 2014-06-25.

Commit message

Shell implementation of Mir trust sessions.

Description of the change

Shell implementation of Mir trust sessions.

A mir trust session consists of any number of mir sessions arranged into a parent hierarchy. The last child of the hierarchy is the session which is considered to be the top focused session at all times if the unity-mir Application is in focus.

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

Is this actually ready for review? If not, please mark as Work in Progress

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:205
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~nick-dedekind/unity-mir/trusted-sessions/+merge/223432/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/unity-mir-devel-ci/16/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-amd64-ci/16/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-armhf-ci/16/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-i386-ci/16/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity-mir-devel-ci/16/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:206
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~nick-dedekind/unity-mir/trusted-sessions/+merge/223432/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/unity-mir-devel-ci/17/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-amd64-ci/17/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-armhf-ci/17/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-i386-ci/17/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity-mir-devel-ci/17/rebuild

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

Commit message please. Checklist too.

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

- std::shared_ptr<::mir::scene::Session> m_session;
+ mutable mir::scene::Session* m_lastTopSession;

So now Application doesn't have a shared_ptr to a Mir Session object. Reason unity-mir held onto that was so that the Session object was only deleted when both Mir and unity-mir had released it, as it may take time for the Application to be removed from the app list, and QML could be asking for properties of that Session after Mir was done with it.

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

=== modified file 'debian/control'
not necessary change for this MR, please revert

Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal
Download full text (3.2 KiB)

Taking the smaller bits first

=== added file 'src/modules/Unity/Application/promptsession.h'
+ // Qt
+#include <QSharedPointer>
not needed in header

namespace mir
+{
+namespace scene
+{
+class Session;
+class PromptSession;
+class PromptSessionManager;
please indent like in ApplicationManager

In PromptSession constructor, line 45, please line up the arguments (space missing)

+ virtual ~PromptSession();
Why virtual? Could use "= default" to have default deconstructor generated, saving you these few lines:
 +Session::~Session()
+{
+}

+ std::shared_ptr<mir::scene::Session> topProvider() const;
I find the name confusing. What is a "provider"? It returns a Session. "top" also isn't useful, unless you happen to know the internals of unity-mir::PromptSession is a stack.

+ std::shared_ptr<mir::scene::PromptSession> mirPromptSession() const;
shows that we can now easily confuse mir's PromptSession with unity-mir's PromptSession. Perhaps a slightly different class name? PromptSessionWrapper?

+Q_DECLARE_METATYPE(unitymir::PromptSession*)
Why do you need to register it as a type with MOC?

=== added file 'src/modules/Unity/Application/promptsession.cpp'
+#include <QDebug>
not used

+PromptSession::PromptSession(const std::shared_ptr<mir::scene::PromptSession>& promptSession, const std::shared_ptr<mir::scene::PromptSessionManager>& promptSessionManager)
please wrap. I'm not strict, but 120 chars is reasonable.

+ m_promptSessionManager->for_each_provider_in(m_promptSession,
+ [&child](std::shared_ptr<mir::scene::Session> const& provider) {
+ child = provider;
+ });
Really? Is there no way to identify the "top" one other than iterating the whole list/stack?!

+ auto helper = m_promptSessionManager->helper_for(m_promptSession);
I only like using auto if the type is really obvious. In this case it's not, I'd prefer you either declare the type, or use name "helperSession"

+ [&](std::shared_ptr<mir::scene::Session> const& provider) {
You only need to capture "found" here, not all locals.

+ }
please add comment to the namespace closing brace, it's a style I'm trying to maintain in this project.

=== added file 'src/modules/Unity/Application/session.h'
What's the point of this entire class? Do you need to share a mir Session with multiple Applications??

=== added file 'src/unity-mir/promptsessionlistener.cpp'
+ * Copyright (C) 2013 Canonical, Ltd.
2014

+ DLOG("PromptSessionListener::prompt_provider_added (this=%p, prompt_session=%p, prompt_provider=%p)", this, &prompt_session, (void*)prompt_provider.get());
+ DLOG("PromptSessionListener::prompt_provider_removed (this=%p, prompt_session=%p, prompt_provider=%p)", this, &prompt_session, (void*)prompt_provider.get());
please wrap

Same for the header file, prompt_provider_{added,removed} should be wrapped

=== modified file 'src/unity-mir/qmirserver.cpp'
Unnecessary change, please revert

=== modified file 'src/unity-mir/sessionauthorizer.cpp'
Unnecessary change, please revert

=== modified file 'src/modules/Unity/Application/proc_info.h'
+ virtual quint64 ppid(quint64 pid);
what does that mean? Parent process id? Does process-cpp not have a method to get that?

=== modified file 'src/mo...

Read more...

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

=== modified file 'src/modules/Unity/Application/mirsurface.h'
- explicit MirSurface(std::shared_ptr<mir::scene::Surface> surface, Application* application, QQuickItem *parent = 0);
1069 + explicit MirSurface(std::shared_ptr<mir::scene::Surface> surface, QSharedPointer<Session> const& session, QQuickItem *parent = 0);

I object strongly, why create distinction between an Application and a Session? Why does a shell need to know about Sessions?

In my vocabulary, Applications have Surfaces, and that's all a shell needs to know to operate. Those Surfaces can have relationships and properties, which shell can use to treat them differently.

Why do you need this?

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal
Download full text (3.6 KiB)

> Taking the smaller bits first
>
>
>
> === added file 'src/modules/Unity/Application/promptsession.h'
> + // Qt
> +#include <QSharedPointer>
> not needed in header
>
> namespace mir
> +{
> +namespace scene
> +{
> +class Session;
> +class PromptSession;
> +class PromptSessionManager;
> please indent like in ApplicationManager
>
> In PromptSession constructor, line 45, please line up the arguments (space
> missing)
>
> + virtual ~PromptSession();
> Why virtual? Could use "= default" to have default deconstructor generated,
> saving you these few lines:
> +Session::~Session()
> +{
> +}
>
> + std::shared_ptr<mir::scene::Session> topProvider() const;
> I find the name confusing. What is a "provider"? It returns a Session. "top"
> also isn't useful, unless you happen to know the internals of unity-
> mir::PromptSession is a stack.
>
> + std::shared_ptr<mir::scene::PromptSession> mirPromptSession() const;
> shows that we can now easily confuse mir's PromptSession with unity-mir's
> PromptSession. Perhaps a slightly different class name? PromptSessionWrapper?
>
> +Q_DECLARE_METATYPE(unitymir::PromptSession*)
> Why do you need to register it as a type with MOC?
>
>
> === added file 'src/modules/Unity/Application/promptsession.cpp'
> +#include <QDebug>
> not used
>
> +PromptSession::PromptSession(const
> std::shared_ptr<mir::scene::PromptSession>& promptSession, const
> std::shared_ptr<mir::scene::PromptSessionManager>& promptSessionManager)
> please wrap. I'm not strict, but 120 chars is reasonable.

removed unitymir::PromptSession

>
> + m_promptSessionManager->for_each_provider_in(m_promptSession,
> + [&child](std::shared_ptr<mir::scene::Session> const& provider) {
> + child = provider;
> + });
> Really? Is there no way to identify the "top" one other than iterating the
> whole list/stack?!

nope. but there won't be many (like only one/two...)

>
> + auto helper = m_promptSessionManager->helper_for(m_promptSession);
> I only like using auto if the type is really obvious. In this case it's not,
> I'd prefer you either declare the type, or use name "helperSession"
>
> + [&](std::shared_ptr<mir::scene::Session> const& provider) {
> You only need to capture "found" here, not all locals.
>
> + }
> please add comment to the namespace closing brace, it's a style I'm trying to
> maintain in this project.
>
>
>
>
> === added file 'src/modules/Unity/Application/session.h'
> What's the point of this entire class? Do you need to share a mir Session with
> multiple Applications??
>

removed unitymir::Session

>
> === added file 'src/unity-mir/promptsessionlistener.cpp'
> + * Copyright (C) 2013 Canonical, Ltd.
> 2014
>
> + DLOG("PromptSessionListener::prompt_provider_added (this=%p,
> prompt_session=%p, prompt_provider=%p)", this, &prompt_session,
> (void*)prompt_provider.get());
> + DLOG("PromptSessionListener::prompt_provider_removed (this=%p,
> prompt_session=%p, prompt_provider=%p)", this, &prompt_session,
> (void*)prompt_provider.get());
> please wrap
>
> Same for the header file, prompt_provider_{added,removed} should be wrapped
>

done

>
>
> === modified file 'src/unity-mir/qmirserver.cpp'
> Unnecessary change, please reve...

Read more...

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

Thanks for the design changes, I'm much happier with it now. I've mostly small annoying things to request. Taking everything except AppManager and the tests (will check them last):

=== modified file 'src/modules/Unity/Application/application.h'
+ std::shared_ptr<mir::scene::PromptSessionManager> const promptSessionManager,
Could pass by reference to save a copy?

Also, you've seen that the current variable declaration syntax mess that is the "const Type& name" and "Type const& name" confusion. I need to tidy it up, but I'm slowly standardizing everything to "const Type &name" - would you hate me if I asked you to do the same, just in the bits you added? It's ok to say no :)

+ std::shared_ptr<mir::scene::Session> effectiveSession() const;
I think "foregroundSession" is a clearer name than effectiveSession.

session() is also not a great name now, but changing it everywhere would be lots more work, so we can leave it for now.

std::shared_ptr<mir::scene::PromptSession> Application::promptSession() const;
There could be multiple such prompt sessions, but this returns only the most recently created one. Maybe better name is "foremostPromptSession" or "activePromptSession"?

+ mutable mir::scene::Session* m_lastTopSession;
"Top" -> "Foreground" please.

Note that ApplicationManager is a friend of Application, so all your additional public methods can be made private, as they're not useful to shell/QML.

+ void pushPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session);
+ void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session);
Could you rename the first to "append" - if I see "push" I expect a "pop", else "add/append" and "remove" pair nicely

=== modified file 'src/modules/Unity/Application/application.cpp'
+ mir::scene::PromptSession
please add "namespace ms = mir::scene" to the top of the file and write "ms::PromptSession" everywhere, helps to hide some of Mir's namespace verbosity.

+ std::shared_ptr<mir::scene::PromptSession> prompt_session
camelCase please.

+ std::shared_ptr<mir::scene::Session> child;
'child' could be declared outside the while loop, save create/destroy on each iteration.

+ if (m_pid == pid) {
2 spaces after m_pid. Braces not really needed either for this 1-liner (which is your usual style).

+void Application::pushPromptSession(const std::shared_ptr<mir::scene::PromptSession>& prompt_session)
camelCase please

+void Application::removePromptSession
A warning if no matching prompt session was found would not hurt, and might save future debugging pain.

=== modified file 'src/modules/Unity/Application/mirsurface.h'
Whitespace change, not needed in this MR.

Ok, last need to read AppMan, the tests, and do functional testing.

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

=== added file 'src/unity-mir/promptsessionlistener.cpp'
+#include <QThread>
needed?

Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal

+void Application::removePromptSession
A warning if no matching prompt session was found would not hurt, and might save future debugging pain

It calls this on applications which may not necessarily have the prompt session (loops through all apps). More efficient than checking, then removing.

Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal

Fixed others.

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

ApplicationManager:

=== modified file 'src/modules/Unity/Application/application_manager.h'
+ std::shared_ptr<mir::shell::FocusController> const& focusController,
+ const std::shared_ptr<mir::scene::PromptSessionManager>& promptSessionManager,
Consistency please

+ Application* findApplicationWithPid(const qint64 pid, bool checkSessions)
+ Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session, bool checkSessions);
+ Application* findApplicationWithSession(const mir::scene::Session *session, bool checkSessions);
"checkSessions" - better name would be more like "includeChildSessions" or something like that, WDYT?

+ void onApplicationEffectiveSessionChanged();
s/Effective/Foreground/

+ if (info->contains("trust-session-demo-trusted-helper") ||
+ info->contains("unity8") ||
+ (ppid != 0 && m_procInfo->commandLine(ppid)->contains("trust-session-demo-trusted-helper"))) {
That's pretty messy. Why considering an app with "unity8" in the process name as a trusted helper? So you're allowing any process directly spawned by the trusted helper. I guess that's all we can do, until our internal discussions conclude.

+ info->asStringList()[0].toLatin1().data());
qPrintable(info->asStringList().first()) an alternative, up to you

+ if (m_fencePIDs.contains(pid)) {
It looks like you've changed m_fencePIDs contains the PIDs of all whitelisted and trusted helpers while they are running. That's ok, but "fence" is not a good name now - perhaps "ignored" or "hidden" ?

+ if (!session) { return NULL; }
2 lines, no braces please. "nullptr" too.

Ok, just tests to go

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

Tests
=== modified file 'tests/application_manager_test.cpp'

+ Application* startApplication(quint64 procId, QString const& appId)
Not a bad idea.

+ auto session1 = std::make_shared<MockSession>(appId.toStdString(), procId);
+ auto promptSession = std::make_shared<MockPromptSession>();

I don't see these being used anywhere though.

TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_mirSurfaceCreatedEventHandled)
+ std::shared_ptr<mir::scene::Session> session = std::make_shared<MockSession>("", procId);
+++
why did you move these lines before the focusSpy?

+ std::shared_ptr<mir::scene::Surface> providerSurface((mir::scene::Surface*)__LINE__, [](mir::scene::Surface*) {}); // use mock!
What does that do? Is that comment a TODO?

+ {
+ return 0;
strange indent

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

Ah, can you re-propose against lp:unity-mir please?

lp:unity-mir/devel really only for Mir devel compatibility changes

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal

> Tests
> === modified file 'tests/application_manager_test.cpp'
>
> + Application* startApplication(quint64 procId, QString const& appId)
> Not a bad idea.
>
> + auto session1 = std::make_shared<MockSession>(appId.toStdString(), procId);
> + auto promptSession = std::make_shared<MockPromptSession>();
>
> I don't see these being used anywhere though.
>

Moved AppMan.onSessionstart(session1) to startApplication.
Removed other.

>
>
> TEST_F(ApplicationManagerTests,onceAppAddedToApplicationLists_mirSurfaceCreate
> dEventHandled)
> + std::shared_ptr<mir::scene::Session> session =
> std::make_shared<MockSession>("", procId);
> +++
> why did you move these lines before the focusSpy?
>

This was because of the foregroundSessionChanged signal causing a screenshot dataChanged. However, there was a bit of code duplication in the AppMan which I removed. So I've changed it back.

>
> + std::shared_ptr<mir::scene::Surface>
> providerSurface((mir::scene::Surface*)__LINE__, [](mir::scene::Surface*) {});
> // use mock!
> What does that do? Is that comment a TODO?

Made comment a TODO
mmmm. that's a shared_ptr with a custom deleter (an empty deleter in this case. Ever seen mir::test::fake_shared?).
It's using the line number as a pointer address so that calling Session::default_surface does not return nullptr, which is a condition for a Application::foregroundSession sourced by a PromptSession.
Not ideal, but that's why the TODO is there.

>
> + {
> + return 0;
> strange indent

Done

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:228
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~nick-dedekind/unity-mir/trusted-sessions/+merge/224397/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/unity-mir-devel-ci/31/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-amd64-ci/31/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-armhf-ci/31/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-devel-utopic-i386-ci/31/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity-mir-devel-ci/31/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:227
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~nick-dedekind/unity-mir/trusted-sessions/+merge/224397/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/unity-mir-ci/383/
Executed test runs:
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-amd64-ci/47
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-armhf-ci/47
        deb: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-armhf-ci/47/artifact/work/output/*zip*/output.zip
    SUCCESS: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-i386-ci/47

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity-mir-ci/383/rebuild

review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal

FAILED: Continuous integration, rev:228
No commit message was specified in the merge proposal. Click on the following link and set the commit message (if you want a jenkins rebuild you need to trigger it yourself):
https://code.launchpad.net/~nick-dedekind/unity-mir/trusted-sessions/+merge/224397/+edit-commit-message

http://jenkins.qa.ubuntu.com/job/unity-mir-ci/386/
Executed test runs:
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-amd64-ci/50/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-armhf-ci/50/console
    FAILURE: http://jenkins.qa.ubuntu.com/job/unity-mir-utopic-i386-ci/50/console

Click here to trigger a rebuild:
http://s-jenkins.ubuntu-ci:8080/job/unity-mir-ci/386/rebuild

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

This needed?
+ info->contains("unity8") ||

Functional testing with your demo trust sessions app. For anyone else playing along at home, I did:
  bzr branch lp:~nick-dedekind/+junk/trusted_sessions_app
  sudo apt-get build-dep unity-mir
  mkdir BUILD && cd BUILD
  cmake .. -DCMAKE_INSTALL_PREFIX=/usr
  make -j4
On the phone I launched calculator & clock. Calculator in the foreground, I ran:
  ./trust-session-demo-trusted-helper -p `pgrep -f calculator`

Which worked fine.

Problem cases:
1. if calculator in the background (clock in foreground) and its trust helper opened, the trust helper appears on top of the clock app.
2. continuing problem 1, if you do a right-edge swipe to reveal spread, the preview for calculator is empty (just see a weird shadow). Tapping it brings back the trust helper screen though
3. if you open calculator's trust helper, then left-edge swipe, the trust helper closes. Intended?
4. strange things happen if I try to open 2 trust prompt sessions, one for 2 different apps. I guess that's kinda unlikely.
5. For 1 app, I opened 2 trust helpers running simultaneously. When I returned to Dash, then went back to the app, one trust helper had quit, but the other had not. Another odd situation I'm creating?
6. Clock in foreground, opened trust helper for it. Then in shell, caused app to die unexpectedly with "kill -9 `pgrep -f clock`" - shell crashed

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal

> This needed?
> + info->contains("unity8") ||

Yes for now. When we create new sockets for the providers, the mir authorizes with the server pid, which is unity8 (since there is nothing connected to the socket yet).
It's possible that this is not right in mir and the session needs auth only when the connection is made by a client...
This will go away when we start using the trust socket (which skips the auth), as children inherit their parent's permission.

>
> Functional testing with your demo trust sessions app. For anyone else playing
> along at home, I did:
> bzr branch lp:~nick-dedekind/+junk/trusted_sessions_app
> sudo apt-get build-dep unity-mir
> mkdir BUILD && cd BUILD
> cmake .. -DCMAKE_INSTALL_PREFIX=/usr
> make -j4
> On the phone I launched calculator & clock. Calculator in the foreground, I
> ran:
> ./trust-session-demo-trusted-helper -p `pgrep -f calculator`
>
> Which worked fine.
>
>
>
> Problem cases:
> 1. if calculator in the background (clock in foreground) and its trust helper
> opened, the trust helper appears on top of the clock app.

Will take a look.
Technically I think this is a mir issue. It shouldn't be focusing to newly created sessions; that should be focus policy controlled by shell... Bad Mir!
Will need to jiggery poke it.

> 2. continuing problem 1, if you do a right-edge swipe to reveal spread, the
> preview for calculator is empty (just see a weird shadow). Tapping it brings
> back the trust helper screen though

Hm. When we add a provider to a prompt session, I take a new application snapshot. This however does not guarantee that anything has yet been drawn on the surface of the prompt provider surface... Not sure how to fix this.

> 3. if you open calculator's trust helper, then left-edge swipe, the trust
> helper closes. Intended?

yes. at the moment, trust helpers do not survive context switches.

> 4. strange things happen if I try to open 2 trust prompt sessions, one for 2
> different apps. I guess that's kinda unlikely.

Mmm.. multiple prompt sessions are not yet supported.

> 5. For 1 app, I opened 2 trust helpers running simultaneously. When I returned
> to Dash, then went back to the app, one trust helper had quit, but the other
> had not. Another odd situation I'm creating?

As above

> 6. Clock in foreground, opened trust helper for it. Then in shell, caused app
> to die unexpectedly with "kill -9 `pgrep -f clock`" - shell crashed

I've taken a quick look and it seems to be crashing when taking a screenshot of a session which is being closed. Haven't dug into to it, but it doesn't seem related to trust sessions, only a result of their usage.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
233. By Nick Dedekind

remerged with thready branch

234. By Nick Dedekind

remove authorise dodgeys for prompt sessions

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
235. By Nick Dedekind

reverted changes to debian/control & debian/changelog

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

Comments addressed to my satisfaction.

One last thing: I think such a big change needs a mention in the debian/changelog and a version bump would do no harm

236. By Nick Dedekind

bumped to 0.5

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

 * Did you perform an exploratory manual test run of the code change and any related functionality?
Y
 * Did CI run pass? If not, please explain why.
Y

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

Nice work, thank you!

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'debian/changelog'
2--- debian/changelog 2014-07-01 14:19:04 +0000
3+++ debian/changelog 2014-07-15 14:51:53 +0000
4@@ -1,3 +1,10 @@
5+unity-mir (0.5-0ubuntu1) UNRELEASED; urgency=medium
6+
7+ [ Nick Dedekind ]
8+ * Added mir prompt session support
9+
10+ -- Nicholas Dedekind <nicholas.dedekind@gmail.com> Tue, 15 Jul 2014 15:47:28 +0100
11+
12 unity-mir (0.4+14.10.20140701.1-0ubuntu1) utopic; urgency=low
13
14 [ Alexandros Frantzis ]
15
16=== modified file 'src/modules/Unity/Application/application.cpp'
17--- src/modules/Unity/Application/application.cpp 2014-07-15 14:51:53 +0000
18+++ src/modules/Unity/Application/application.cpp 2014-07-15 14:51:53 +0000
19@@ -26,6 +26,10 @@
20 // mir
21 #include <mir/scene/session.h>
22 #include <mir/scene/snapshot.h>
23+#include <mir/scene/prompt_session.h>
24+#include <mir/scene/prompt_session_manager.h>
25+
26+namespace ms = mir::scene;
27
28 namespace unitymir
29 {
30@@ -36,6 +40,7 @@
31 DesktopFileReader *desktopFileReader,
32 State state,
33 const QStringList &arguments,
34+ const std::shared_ptr<ms::PromptSessionManager>& promptSessionManager,
35 QObject *parent)
36 : ApplicationInfoInterface(desktopFileReader->appId(), parent)
37 , m_taskController(taskController)
38@@ -49,6 +54,7 @@
39 , m_visible(false)
40 , m_arguments(arguments)
41 , m_suspendTimer(new QTimer(this))
42+ , m_promptSessionManager(promptSessionManager)
43 , m_screenShotGuard(new Guard)
44 {
45 DLOG("Application::Application (this=%p, appId=%s, state=%d", this, qPrintable(desktopFileReader->appId()),
46@@ -59,6 +65,8 @@
47
48 // FIXME(greyback) need to save long appId internally until upstart-app-launch can hide it from us
49 m_longAppId = desktopFileReader->file().remove(QRegExp(".desktop$")).split('/').last();
50+
51+ connect(this, &Application::foregroundSessionChanged, this, &Application::updateScreenshot);
52 }
53
54 Application::~Application()
55@@ -142,11 +150,66 @@
56 return m_fullscreen;
57 }
58
59-std::shared_ptr<mir::scene::Session> Application::session() const
60+std::shared_ptr<ms::Session> Application::session() const
61 {
62 return m_session;
63 }
64
65+std::shared_ptr<ms::Session> Application::foregroundSession() const
66+{
67+ std::shared_ptr<ms::Session> child;
68+
69+ QListIterator<std::shared_ptr<ms::PromptSession>> iter(m_promptSessions);
70+ iter.toBack();
71+ while (iter.hasPrevious()) {
72+ std::shared_ptr<ms::PromptSession> promptSession = iter.previous();
73+
74+ // get top session
75+ m_promptSessionManager->for_each_provider_in(promptSession,
76+ [&child](std::shared_ptr<ms::Session> const& provider) {
77+ if (provider->default_surface() != nullptr)
78+ child = provider;
79+ });
80+
81+ if (child)
82+ return child;
83+ }
84+
85+ return session();
86+}
87+
88+std::shared_ptr<ms::PromptSession> Application::activePromptSession() const
89+{
90+ if (m_promptSessions.count() > 0)
91+ return m_promptSessions.back();
92+ return nullptr;
93+}
94+
95+bool Application::containsProcess(pid_t pid) const
96+{
97+ if (m_pid == pid)
98+ return true;
99+
100+ QListIterator<std::shared_ptr<ms::PromptSession>> iter(m_promptSessions);
101+ while(iter.hasNext()) {
102+ std::shared_ptr<ms::PromptSession> promptSession = iter.next();
103+
104+ std::shared_ptr<ms::Session> helper = m_promptSessionManager->helper_for(promptSession);
105+ if (helper && helper->process_id() == pid)
106+ return true;
107+
108+ bool found = false;
109+ m_promptSessionManager->for_each_provider_in(promptSession,
110+ [&found, pid](std::shared_ptr<ms::Session> const& provider) {
111+ if (provider->process_id() == pid)
112+ found = true;
113+ });
114+ if (found)
115+ return true;
116+ }
117+ return false;
118+}
119+
120 bool Application::visible() const
121 {
122 return m_visible;
123@@ -172,13 +235,35 @@
124 m_pid = pid;
125 }
126
127-void Application::setSession(const std::shared_ptr<mir::scene::Session>& session)
128+void Application::setSession(const std::shared_ptr<ms::Session>& session)
129 {
130 DLOG("Application::setSession (this=%p, session=%p)", this, session.get());
131
132 // TODO(greyback) what if called with new surface?
133 m_session = session;
134 m_visible = true; // bit of an assumption that, but no other way to deduce an actual Surface has been created
135+ checkSessionChanges();
136+}
137+
138+void Application::appendPromptSession(const std::shared_ptr<ms::PromptSession>& promptSession)
139+{
140+ DLOG("Application::setPromptSession (this=%p, promptSession=%p)", this, promptSession ? promptSession.get() : nullptr);
141+
142+ m_promptSessions.append(promptSession);
143+ checkSessionChanges();
144+}
145+
146+void Application::removePromptSession(const std::shared_ptr<ms::PromptSession>& promptSession)
147+{
148+ DLOG("Application::setPromptSession (this=%p, promptSession=%p)", this, promptSession ? promptSession.get() : nullptr);
149+
150+ QMutableListIterator<std::shared_ptr<ms::PromptSession>> iter(m_promptSessions);
151+ while(iter.hasNext()) {
152+ if (iter.next() == promptSession) {
153+ iter.remove();
154+ checkSessionChanges();
155+ }
156+ }
157 }
158
159 void Application::setSessionName(const QString& name)
160@@ -207,9 +292,11 @@
161
162 void Application::updateScreenshot()
163 {
164+ if (!foregroundSession())
165+ return;
166 QWeakPointer<Guard> wk(m_screenShotGuard.toWeakRef());
167
168- session()->take_snapshot(
169+ foregroundSession()->take_snapshot(
170 [&, wk](mir::scene::Snapshot const& snapshot)
171 {
172 // In case we get a threaded screenshot callback once the application is deleted.
173@@ -238,7 +325,8 @@
174 {
175 case Application::Suspended:
176 if (m_state == Application::Running) {
177- session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
178+ if (session())
179+ session()->set_lifecycle_state(mir_lifecycle_state_will_suspend);
180 m_suspendTimer->start(3000);
181 }
182 break;
183@@ -248,7 +336,8 @@
184
185 if (m_state == Application::Suspended) {
186 resume();
187- session()->set_lifecycle_state(mir_lifecycle_state_resumed);
188+ if (session())
189+ session()->set_lifecycle_state(mir_lifecycle_state_resumed);
190 } else if (m_state == Application::Stopped) {
191 respawn();
192 state = Application::Starting;
193@@ -288,7 +377,7 @@
194 {
195 DLOG("Application::setVisible (this=%p, visible=%s)", this, visible ? "yes" : "no");
196 // FIXME: this is bad, as should a MirSurface of this app exist, it won't be notified of the visiblity change.
197- if (visible != m_visible) {
198+ if (m_session && visible != m_visible) {
199 if (visible) {
200 m_session->show();
201 } else {
202@@ -322,4 +411,14 @@
203 return m_longAppId;
204 }
205
206+void Application::checkSessionChanges()
207+{
208+ ms::Session* lastTopSession = m_lastForegroundSession;
209+ ms::Session* currentTopSession = foregroundSession().get();
210+ m_lastForegroundSession = currentTopSession;
211+ if (currentTopSession && currentTopSession != lastTopSession) {
212+ Q_EMIT foregroundSessionChanged();
213+ }
214+}
215+
216 } // namespace unitymir
217
218=== modified file 'src/modules/Unity/Application/application.h'
219--- src/modules/Unity/Application/application.h 2014-07-15 14:51:53 +0000
220+++ src/modules/Unity/Application/application.h 2014-07-15 14:51:53 +0000
221@@ -29,7 +29,13 @@
222 #include <unity/shell/application/ApplicationInfoInterface.h>
223
224 class QImage;
225-namespace mir { namespace scene { class Session; }}
226+namespace mir {
227+ namespace scene {
228+ class Session;
229+ class PromptSession;
230+ class PromptSessionManager;
231+ }
232+}
233
234 namespace unitymir
235 {
236@@ -50,6 +56,7 @@
237 DesktopFileReader *desktopFileReader,
238 State state,
239 const QStringList &arguments,
240+ const std::shared_ptr<mir::scene::PromptSessionManager>& promptSessionManager,
241 QObject *parent = 0);
242 virtual ~Application();
243
244@@ -69,7 +76,6 @@
245 void setVisible(const bool);
246
247 QImage screenshotImage() const;
248- void updateScreenshot();
249
250 bool canBeResumed() const;
251 void setCanBeResumed(const bool);
252@@ -78,17 +84,26 @@
253 QString desktopFile() const;
254 QString exec() const;
255 bool fullscreen() const;
256- std::shared_ptr<::mir::scene::Session> session() const;
257 pid_t pid() const;
258
259+ std::shared_ptr<mir::scene::Session> session() const;
260+ std::shared_ptr<mir::scene::Session> foregroundSession() const;
261+ std::shared_ptr<mir::scene::PromptSession> activePromptSession() const;
262+
263+ bool containsProcess(pid_t pid) const;
264+
265+ void checkSessionChanges();
266+
267 public Q_SLOTS:
268 void suspend();
269 void resume();
270 void respawn();
271+ void updateScreenshot();
272
273 Q_SIGNALS:
274 void fullscreenChanged();
275 void stageChanged(Stage stage);
276+ void foregroundSessionChanged();
277 void visibleChanged();
278
279 private:
280@@ -97,9 +112,12 @@
281 void setState(State state);
282 void setFocused(bool focus);
283 void setFullscreen(bool fullscreen);
284- void setSession(const std::shared_ptr<::mir::scene::Session>& session);
285 void setSessionName(const QString& name);
286
287+ void setSession(const std::shared_ptr<mir::scene::Session>& session);
288+ void appendPromptSession(const std::shared_ptr<mir::scene::PromptSession>& session);
289+ void removePromptSession(const std::shared_ptr<mir::scene::PromptSession>& session);
290+
291 QSharedPointer<TaskController> m_taskController;
292 DesktopFileReader* m_desktopData;
293 QString m_longAppId;
294@@ -111,11 +129,14 @@
295 QImage m_screenshotImage;
296 bool m_canBeResumed;
297 bool m_fullscreen;
298+ std::shared_ptr<mir::scene::Session> m_session;
299+ QList<std::shared_ptr<mir::scene::PromptSession>> m_promptSessions;
300 bool m_visible; // duplicating internal Mir data :(
301- std::shared_ptr<::mir::scene::Session> m_session;
302 QString m_sessionName;
303 QStringList m_arguments;
304 QTimer* m_suspendTimer;
305+ mutable mir::scene::Session* m_lastForegroundSession;
306+ std::shared_ptr<mir::scene::PromptSessionManager> const m_promptSessionManager;
307
308 class Guard {};
309 QSharedPointer<Guard> m_screenShotGuard;
310
311=== modified file 'src/modules/Unity/Application/application_manager.cpp'
312--- src/modules/Unity/Application/application_manager.cpp 2014-06-25 15:30:50 +0000
313+++ src/modules/Unity/Application/application_manager.cpp 2014-07-15 14:51:53 +0000
314@@ -31,18 +31,23 @@
315 #include "initialsurfaceplacementstrategy.h"
316 #include "taskcontroller.h"
317 #include "logging.h"
318+#include "promptsessionlistener.h"
319
320 // mir
321 #include <mir/scene/depth_id.h>
322 #include <mir/scene/session.h>
323 #include <mir/shell/focus_controller.h>
324 #include <mir/scene/surface.h>
325+#include <mir/scene/prompt_session.h>
326 #include <mir/graphics/display.h>
327 #include <mir/graphics/display_buffer.h>
328 #include <mircommon/mir/geometry/rectangles.h>
329+#include <mir/frontend/shell.h>
330+#include <mir/scene/prompt_session_manager.h>
331
332 // Qt
333 #include <QCoreApplication>
334+#include <QThread>
335
336 // std
337 #include <csignal>
338@@ -102,6 +107,20 @@
339 manager, &ApplicationManager::onSessionCreatedSurface);
340 }
341
342+void connectToPromptSessionListener(ApplicationManager * manager, PromptSessionListener * listener)
343+{
344+ QObject::connect(listener, &PromptSessionListener::promptSessionStarting,
345+ manager, &ApplicationManager::onPromptSessionStarting);
346+ QObject::connect(listener, &PromptSessionListener::promptSessionStopping,
347+ manager, &ApplicationManager::onPromptSessionStopping);
348+
349+ QObject::connect(listener, &PromptSessionListener::promptProviderAdded,
350+ manager, &ApplicationManager::onPromptProviderAdded);
351+ QObject::connect(listener, &PromptSessionListener::promptProviderRemoved,
352+ manager, &ApplicationManager::onPromptProviderRemoved);
353+
354+}
355+
356 void connectToSessionAuthorizer(ApplicationManager * manager, SessionAuthorizer * authorizer)
357 {
358 QObject::connect(authorizer, &SessionAuthorizer::requestAuthorizationForSession,
359@@ -151,17 +170,19 @@
360 // FIXME: We should use a QSharedPointer to wrap this ApplicationManager object, which requires us
361 // to use the data() method to pass the raw pointer to the QML engine. However the QML engine appears
362 // to take ownership of the object, and deletes it when it wants to. This conflicts with the purpose
363- // of the QSharedPointer, and a double-delete results. Trying QQmlEngine::setObjectOwnership on the
364+ // of the QSharedPointer, and a double-delete results. Trying QQmlEngine::setObjectOwnership on the
365 // object no effect, which it should. Need to investigate why.
366 ApplicationManager* appManager = new ApplicationManager(
367 taskController,
368 fileReaderFactory,
369 procInfo,
370 mirServer->the_focus_controller(),
371+ mirServer->the_prompt_session_manager(),
372 displaySize
373 );
374
375 connectToSessionListener(appManager, mirServer->sessionListener());
376+ connectToPromptSessionListener(appManager, mirServer->promptSessionListener());
377 connectToSessionAuthorizer(appManager, mirServer->sessionAuthorizer());
378 connectToPlacementStrategy(appManager, mirServer->placementStrategy());
379 connectToTaskController(appManager, taskController.data());
380@@ -183,7 +204,8 @@
381 const QSharedPointer<TaskController>& taskController,
382 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
383 const QSharedPointer<ProcInfo>& procInfo,
384- const std::shared_ptr<mir::shell::FocusController>& controller,
385+ const std::shared_ptr<mir::shell::FocusController>& focusController,
386+ const std::shared_ptr<mir::scene::PromptSessionManager>& promptSessionManager,
387 const QSize& displaySize,
388 QObject *parent)
389 : ApplicationManagerInterface(parent)
390@@ -193,13 +215,13 @@
391 , m_msApplicationToBeFocused(nullptr)
392 , m_ssApplicationToBeFocused(nullptr)
393 , m_lifecycleExceptions(QStringList() << "com.ubuntu.music")
394- , m_focusController(controller)
395+ , m_focusController(focusController)
396+ , m_promptSessionManager(promptSessionManager)
397 , m_dbusWindowStack(new DBusWindowStack(this))
398 , m_taskController(taskController)
399 , m_desktopFileReaderFactory(desktopFileReaderFactory)
400 , m_procInfo(procInfo)
401 , m_gridUnitPx(8)
402- , m_fenceNext(false)
403 , m_displaySize(displaySize)
404 , m_panelHeight(54)
405 {
406@@ -385,15 +407,15 @@
407 DLOG("No such running application '%s'", qPrintable(appId));
408 return false;
409 }
410-
411+
412 if (application->stage() == Application::MainStage && m_sideStageApplication)
413 suspendApplication(m_sideStageApplication);
414-
415+
416 if (application->stage() == Application::MainStage)
417 m_msApplicationToBeFocused = application;
418 else
419 m_ssApplicationToBeFocused = application;
420-
421+
422 if (application->state() == Application::Stopped) {
423 // Respawning this app, move to end of application list so onSessionStarting works ok
424 // FIXME: this happens pretty late, shell could request respawn earlier
425@@ -401,8 +423,9 @@
426 int from = m_applications.indexOf(application);
427 move(from, m_applications.length()-1);
428 } else {
429- if (application->session()) {
430- m_focusController->set_focus_to(application->session());
431+ auto mirSession = application->foregroundSession();
432+ if (mirSession) {
433+ m_focusController->set_focus_to(mirSession);
434 int from = m_applications.indexOf(application);
435 move(from, 0);
436 }
437@@ -474,6 +497,7 @@
438 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
439 Application::Starting,
440 arguments,
441+ m_promptSessionManager,
442 this);
443 if (!application->isValid()) {
444 DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId));
445@@ -499,7 +523,9 @@
446 m_taskController,
447 m_desktopFileReaderFactory->createInstance(appId, m_taskController->findDesktopFileForAppId(appId)),
448 Application::Starting,
449- QStringList(), this);
450+ QStringList(),
451+ m_promptSessionManager,
452+ this);
453 if (!application->isValid()) {
454 DLOG("Unable to instantiate application with appId '%s'", qPrintable(appId));
455 return;
456@@ -703,14 +729,15 @@
457
458 if (info->startsWith("maliit-server") || info->contains("qt5/libexec/QtWebProcess")) {
459 authorized = true;
460- m_fenceNext = true;
461+ m_hiddenPIDs << pid;
462 return;
463 }
464
465 boost::optional<QString> desktopFileName{ info->getParameter("--desktop_file_hint=") };
466
467 if (!desktopFileName) {
468- LOG("ApplicationManager REJECTED connection from app with pid %lld as no desktop_file_hint specified", pid);
469+ LOG("ApplicationManager REJECTED connection from app with pid %lld as no desktop_file_hint specified (%s)", pid,
470+ qPrintable(info->asStringList().first()));
471 return;
472 }
473
474@@ -758,7 +785,13 @@
475 DLOG("Existing process with pid %lld appeared, adding '%s' to application lists", pid, desktopData->name().toLatin1().data());
476
477 QStringList arguments(info->asStringList());
478- application = new Application(m_taskController, desktopData, Application::Starting, arguments, this);
479+ application = new Application(
480+ m_taskController,
481+ desktopData,
482+ Application::Starting,
483+ arguments,
484+ m_promptSessionManager,
485+ this);
486 application->setPid(pid);
487 application->setStage(stage);
488 application->setCanBeResumed(false);
489@@ -768,11 +801,11 @@
490
491 void ApplicationManager::placeSession(ms::Session const* session, uint32_t &x, uint32_t &y)
492 {
493- Application* application = findApplicationWithSession(session);
494+ Application* application = findApplicationWithSession(session, true);
495 DLOG("ApplicationManager::placeSession (this=%p, application=%p, session=%p, name=%s)", this, application, session,
496 session?(session->name().c_str()):"null");
497
498- // Application defaults
499+ // Application defaults
500 x = 0;
501 y = m_panelHeight;
502
503@@ -789,7 +822,7 @@
504 // SideStage override
505 if (application && application->stage() == Application::SideStage)
506 x = m_displaySize.width() - (SIDE_STAGE_WIDTH_GU * m_gridUnitPx);
507-
508+
509 DLOG("ApplicationManager::placeSession (x=%d, y=%d)", x, y);
510 }
511
512@@ -797,12 +830,9 @@
513 {
514 DLOG("ApplicationManager::onSessionStarting (this=%p, application=%s)", this, session?session->name().c_str():"null");
515
516- if (m_fenceNext) {
517- m_fenceNext = false;
518- return;
519- }
520+ pid_t pid = session->process_id();
521
522- Application* application = findApplicationWithPid(session->process_id());
523+ Application* application = findApplicationWithPid(session->process_id(), false);
524 if (application && application->state() != Application::Running) {
525 application->setSession(session);
526 if (application->stage() == Application::MainStage)
527@@ -810,18 +840,18 @@
528 else
529 m_ssApplicationToBeFocused = application;
530 } else {
531+ if (m_hiddenPIDs.contains(pid)) {
532+ return;
533+ }
534 DLOG("ApplicationManager::onSessionStarting - unauthorized application!!");
535 }
536 }
537
538 void ApplicationManager::onSessionStopping(const std::shared_ptr<ms::Session>& session)
539 {
540- // in case application closed not by hand of shell, check again here:
541- Application* application = findApplicationWithSession(session);
542-
543- DLOG("ApplicationManager::onSessionStopping (this=%p, application=%p, appId=%s, session name=%s)", this, application,
544- application?qPrintable(application->appId()):"null", session?session->name().c_str():"null");
545-
546+ DLOG("ApplicationManager::onSessionStopping (this=%p, session name=%s)", this, session?session->name().c_str():"null");
547+
548+ Application* application = findApplicationWithSession(session, false); // only check app sessions
549 if (application) {
550 /* Can remove the application from the running apps list immediately in these curcumstances:
551 * 1. application is not managed by upstart (this message from Mir is only notice the app has stopped, must do
552@@ -844,17 +874,19 @@
553 application->setState(Application::Stopped);
554 }
555 }
556+
557+ m_hiddenPIDs.removeOne(session->process_id());
558 }
559
560 void ApplicationManager::onSessionFocused(const std::shared_ptr<ms::Session>& session)
561 {
562- Application* application = findApplicationWithSession(session);
563+ Application* application = findApplicationWithSession(session, true);
564 DLOG("ApplicationManager::onSessionFocused (this=%p, application=%p, appId=%s, session name=%s)", this, application,
565 application?qPrintable(application->appId()):"null", session?session->name().c_str():"null");
566
567 // Don't give application focus until it has created it's surface, when it is set as state "Running"
568 // and only notify shell of focus changes that it actually expects
569- if (application && application->state() != Application::Starting &&
570+ if (application && application->state() != Application::Starting &&
571 (application == m_msApplicationToBeFocused ||
572 application == m_ssApplicationToBeFocused)
573 && application != m_focusedApplication) {
574@@ -864,10 +896,14 @@
575 } else {
576 if (application == nullptr) {
577 DLOG("Invalid application focused, discarding the event");
578- if (nullptr != m_focusedApplication)
579+ if (nullptr != m_focusedApplication) {
580 focusApplication(m_focusedApplication->appId());
581- else
582+ } else {
583 m_focusController->set_focus_to(nullptr);
584+ }
585+ } else if (application == m_focusedApplication && session != application->foregroundSession()) {
586+ DLOG("Focused session is not on top. Resetting focus");
587+ focusApplication(application->appId());
588 }
589 }
590 }
591@@ -878,7 +914,8 @@
592 if (nullptr != m_focusedApplication) {
593 Q_ASSERT(m_focusedApplication->focused());
594 m_focusedApplication->setFocused(false);
595-
596+
597+ auto lastFocusedApp = m_focusedApplication;
598 suspendApplication(m_focusedApplication);
599
600 m_focusedApplication = nullptr;
601@@ -887,22 +924,87 @@
602
603 QModelIndex appIndex = findIndex(m_focusedApplication);
604 Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused << RoleState);
605+
606+ if (lastFocusedApp && lastFocusedApp->activePromptSession()) {
607+ m_promptSessionManager->stop_prompt_session(lastFocusedApp->activePromptSession());
608+ }
609 }
610 }
611
612 void ApplicationManager::onSessionCreatedSurface(const ms::Session * session,
613 const std::shared_ptr<ms::Surface> & surface)
614 {
615- DLOG("ApplicationManager::onSessionCreatedSurface (this=%p)", this);
616+ DLOG("ApplicationManager::onSessionCreatedSurface (this=%p, appId=%s)", this, session->name().c_str());
617 Q_UNUSED(surface);
618
619- Application* application = findApplicationWithSession(session);
620- if (application && application->state() == Application::Starting) {
621- m_dbusWindowStack->WindowCreated(0, application->appId());
622- // only when Session creates a Surface will we actually mark it focused
623- setFocused(application);
624- QModelIndex appIndex = findIndex(application);
625- Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused);
626+ Application* application = findApplicationWithSession(session, true);
627+ if (application) {
628+ if (application->state() == Application::Starting) {
629+ m_dbusWindowStack->WindowCreated(0, application->appId());
630+ // only when Session creates a Surface will we actually mark it focused
631+ setFocused(application);
632+ QModelIndex appIndex = findIndex(application);
633+ Q_EMIT dataChanged(appIndex, appIndex, QVector<int>() << RoleFocused);
634+ }
635+
636+ application->checkSessionChanges();
637+ }
638+}
639+
640+void ApplicationManager::onPromptSessionStarting(const std::shared_ptr<ms::PromptSession>& promptSession)
641+{
642+ DLOG("ApplicationManager::onPromptSessionStarting (this=%p, prompt_session=%p)", this, promptSession.get());
643+
644+ Application* app = findApplicationWithSession(m_promptSessionManager->application_for(promptSession), false);
645+ if (app) {
646+ app->appendPromptSession(promptSession);
647+ } else {
648+ DLOG("ApplicationManager::onPromptSessionStarting - no app found (prompt_session=%p)", promptSession.get());
649+ }
650+}
651+
652+void ApplicationManager::onPromptSessionStopping(const std::shared_ptr<ms::PromptSession>& promptSession)
653+{
654+ DLOG("ApplicationManager::onPromptSessionStopping (this=%p, prompt_session=%p)", this, promptSession.get());
655+
656+ for (Application *app : m_applications) {
657+ app->removePromptSession(promptSession);
658+ }
659+}
660+
661+void ApplicationManager::onPromptProviderAdded(ms::PromptSession const* promptSession,
662+ const std::shared_ptr<ms::Session>& session)
663+{
664+ Q_UNUSED(promptSession);
665+ DLOG("ApplicationManager::onPromptProviderAdded (this=%p, prompt_session=%p, session=%s)", this, promptSession, session?session->name().c_str():"null");
666+
667+ Application* app = findApplicationWithSession(session, true);
668+ if (app) {
669+ app->checkSessionChanges();
670+
671+ if (app == m_focusedApplication) {
672+ focusApplication(app->appId());
673+ }
674+ } else {
675+ DLOG("ApplicationManager::onPromptProviderAdded - no app found (prompt_session=%p)", promptSession);
676+ }
677+}
678+
679+void ApplicationManager::onPromptProviderRemoved(ms::PromptSession const* promptSession,
680+ const std::shared_ptr<ms::Session>& session)
681+{
682+ Q_UNUSED(session);
683+ DLOG("ApplicationManager::onPromptProviderAdded (this=%p, prompt_session=%p, session=%s)", this, (void*)promptSession, session?session->name().c_str():"null");
684+
685+ // we need to update the focus if the app is in focus.
686+ for (Application *app : m_applications) {
687+ if (promptSession == app->activePromptSession().get()) {
688+ app->checkSessionChanges();
689+
690+ if (app == m_focusedApplication) {
691+ focusApplication(app->appId());
692+ }
693+ }
694 }
695 }
696
697@@ -925,6 +1027,7 @@
698 else
699 m_sideStageApplication = application;
700
701+ auto lastFocusedApp = m_focusedApplication;
702 m_focusedApplication = application;
703 m_focusedApplication->setFocused(true);
704 m_focusedApplication->setVisible(true);
705@@ -932,24 +1035,26 @@
706 move(m_applications.indexOf(application), 0);
707 Q_EMIT focusedApplicationIdChanged();
708 m_dbusWindowStack->FocusedWindowChanged(0, application->appId(), application->stage());
709-}
710-
711-Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session)
712-{
713- return findApplicationWithSession(session.get());
714-}
715-
716-Application* ApplicationManager::findApplicationWithSession(const ms::Session *session)
717-{
718- for (Application *app : m_applications) {
719- if (app->session().get() == session) {
720- return app;
721- }
722+
723+ // stop prompt session when focus changes.
724+ if (lastFocusedApp && lastFocusedApp->activePromptSession()) {
725+ m_promptSessionManager->stop_prompt_session(lastFocusedApp->activePromptSession());
726 }
727- return nullptr;
728-}
729-
730-Application* ApplicationManager::findApplicationWithPid(const qint64 pid)
731+}
732+
733+Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session, bool includeChildSessions)
734+{
735+ return findApplicationWithSession(session.get(), includeChildSessions);
736+}
737+
738+Application* ApplicationManager::findApplicationWithSession(const ms::Session *session, bool includeChildSessions)
739+{
740+ if (!session)
741+ return nullptr;
742+ return findApplicationWithPid(session->process_id(), includeChildSessions);
743+}
744+
745+Application* ApplicationManager::findApplicationWithPid(const qint64 pid, bool includeChildSessions)
746 {
747 if (pid <= 0)
748 return nullptr;
749@@ -958,6 +1063,10 @@
750 if (app->m_pid == pid) {
751 return app;
752 }
753+
754+ if (includeChildSessions && app->containsProcess(pid)) {
755+ return app;
756+ }
757 }
758 return nullptr;
759 }
760@@ -1006,7 +1115,8 @@
761 }
762 }
763
764-void ApplicationManager::move(int from, int to) {
765+void ApplicationManager::move(int from, int to)
766+{
767 DLOG("ApplicationManager::move (this=%p, from=%d, to=%d)", this, from, to);
768 if (from == to) return;
769
770@@ -1036,8 +1146,15 @@
771 {
772 if (application == m_focusedApplication) {
773 // TODO(greyback) What to do?? Focus next app, or unfocus everything??
774+ auto lastFocusedApp = m_focusedApplication;
775 m_focusedApplication = nullptr;
776 Q_EMIT focusedApplicationIdChanged();
777+
778+ // stop prompt session when focus changes.
779+ if (lastFocusedApp && lastFocusedApp->activePromptSession()) {
780+ m_promptSessionManager->stop_prompt_session(lastFocusedApp->activePromptSession());
781+ }
782+
783 return true;
784 }
785 return false;
786
787=== modified file 'src/modules/Unity/Application/application_manager.h'
788--- src/modules/Unity/Application/application_manager.h 2014-05-26 15:54:44 +0000
789+++ src/modules/Unity/Application/application_manager.h 2014-07-15 14:51:53 +0000
790@@ -41,6 +41,8 @@
791 namespace scene {
792 class Session;
793 class Surface;
794+ class PromptSession;
795+ class PromptSessionManager;
796 }
797 namespace shell {
798 class FocusController;
799@@ -79,8 +81,9 @@
800 explicit ApplicationManager(const QSharedPointer<TaskController>& taskController,
801 const QSharedPointer<DesktopFileReader::Factory>& desktopFileReaderFactory,
802 const QSharedPointer<ProcInfo>& processInfo,
803- std::shared_ptr<mir::shell::FocusController> const& controller,
804- QSize const& displaySize,
805+ const std::shared_ptr<mir::shell::FocusController>& focusController,
806+ const std::shared_ptr<mir::scene::PromptSessionManager>& promptSessionManager,
807+ const QSize& displaySize,
808 QObject *parent = 0);
809 virtual ~ApplicationManager();
810
811@@ -106,7 +109,7 @@
812 Q_INVOKABLE void move(int from, int to);
813
814 const QList<Application*> &list() const { return m_applications; }
815- unitymir::Application* findApplicationWithPid(const qint64 pid);
816+ Application* findApplicationWithPid(const qint64 pid, bool includeChildSessions);
817
818 // Internal helpers
819 bool suspendApplication(Application *application);
820@@ -117,13 +120,18 @@
821 public Q_SLOTS:
822 void authorizeSession(const quint64 pid, bool &authorized);
823 void placeSession(::mir::scene::Session const*, uint32_t &x, uint32_t &y);
824-
825- void onSessionStarting(std::shared_ptr<::mir::scene::Session> const& session);
826- void onSessionStopping(std::shared_ptr<::mir::scene::Session> const& session);
827- void onSessionFocused(std::shared_ptr<::mir::scene::Session> const& session);
828+
829+ void onSessionStarting(const std::shared_ptr<::mir::scene::Session>& session);
830+ void onSessionStopping(const std::shared_ptr<::mir::scene::Session>& session);
831+ void onSessionFocused(const std::shared_ptr<::mir::scene::Session>& session);
832 void onSessionUnfocused();
833
834- void onSessionCreatedSurface(::mir::scene::Session const*, std::shared_ptr<::mir::scene::Surface> const&);
835+ void onPromptSessionStarting(const std::shared_ptr<mir::scene::PromptSession>& promptSession);
836+ void onPromptSessionStopping(const std::shared_ptr<mir::scene::PromptSession>& promptSession);
837+ void onPromptProviderAdded(mir::scene::PromptSession const* promptSession, const std::shared_ptr<mir::scene::Session>& session);
838+ void onPromptProviderRemoved(mir::scene::PromptSession const* promptSession, const std::shared_ptr<mir::scene::Session>& session);
839+
840+ void onSessionCreatedSurface(::mir::scene::Session const*, const std::shared_ptr<::mir::scene::Surface>&);
841
842 void onProcessFailed(const QString& appId, const bool duringStartup);
843 void onProcessStarting(const QString& appId);
844@@ -141,8 +149,8 @@
845 void setFocused(Application *application);
846 void add(Application *application);
847 void remove(Application* application);
848- Application* findApplicationWithSession(const std::shared_ptr<::mir::scene::Session> &session);
849- Application* findApplicationWithSession(const ::mir::scene::Session *session);
850+ Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session, bool includeChildSessions);
851+ Application* findApplicationWithSession(const mir::scene::Session *session, bool includeChildSessions);
852 Application* applicationForStage(Application::Stage stage);
853 QModelIndex findIndex(Application* application);
854 bool isFocused(Application* application);
855@@ -155,13 +163,14 @@
856 Application* m_msApplicationToBeFocused; // placeholder store for async focusing
857 Application* m_ssApplicationToBeFocused; // placeholder store for async focusing
858 QStringList m_lifecycleExceptions;
859- std::shared_ptr<mir::shell::FocusController> m_focusController;
860+ std::shared_ptr<mir::shell::FocusController> const m_focusController;
861+ std::shared_ptr<mir::scene::PromptSessionManager> const m_promptSessionManager;
862 DBusWindowStack* m_dbusWindowStack;
863 QSharedPointer<TaskController> m_taskController;
864 QSharedPointer<DesktopFileReader::Factory> m_desktopFileReaderFactory;
865 QSharedPointer<ProcInfo> m_procInfo;
866 int m_gridUnitPx;
867- bool m_fenceNext;
868+ QList<pid_t> m_hiddenPIDs;
869 QString m_nextFocusedAppId;
870 QSize m_displaySize;
871 int m_panelHeight;
872
873=== modified file 'src/modules/Unity/Application/applicationscreenshotprovider.cpp'
874--- src/modules/Unity/Application/applicationscreenshotprovider.cpp 2014-04-15 14:31:02 +0000
875+++ src/modules/Unity/Application/applicationscreenshotprovider.cpp 2014-07-15 14:51:53 +0000
876@@ -52,7 +52,8 @@
877
878 // TODO: if app not ready, return an app-provided splash image. If app has been stopped with saved state
879 // return the screenshot that was saved to disk.
880- if (!app->session() || !app->session()->default_surface()) {
881+ auto mirSession = app->foregroundSession();
882+ if (!mirSession || !mirSession->default_surface()) {
883 LOG("ApplicationScreenshotProvider - app session not found - asking for screenshot too early");
884 return QImage();
885 }
886
887=== modified file 'src/modules/Unity/Application/dbuswindowstack.cpp'
888--- src/modules/Unity/Application/dbuswindowstack.cpp 2014-03-11 03:40:49 +0000
889+++ src/modules/Unity/Application/dbuswindowstack.cpp 2014-07-15 14:51:53 +0000
890@@ -47,7 +47,7 @@
891 {
892 AppIdDesktopFile res;
893 ApplicationManager *appMgr = static_cast<ApplicationManager*>(parent());
894- const Application* app = static_cast<Application*>(appMgr->findApplicationWithPid(pid));
895+ const Application* app = static_cast<Application*>(appMgr->findApplicationWithPid(pid, false));
896 if (app) {
897 res.app_id = app->appId();
898 res.desktop_file = app->desktopFile();
899
900=== modified file 'src/modules/Unity/Application/inputarea.cpp'
901--- src/modules/Unity/Application/inputarea.cpp 2014-06-27 08:36:07 +0000
902+++ src/modules/Unity/Application/inputarea.cpp 2014-07-15 14:51:53 +0000
903@@ -178,11 +178,12 @@
904 {
905 DLOG("InputArea::setMirInputArea (this=%p, x=%lf, y=%lf, width=%lf, height=%lf)", this, relativeGeometry.x(), relativeGeometry.y(), relativeGeometry.width(), relativeGeometry.height());
906 using namespace mir::geometry;
907+ const QRect rect = parentItem()->mapRectToScene(relativeGeometry).toRect();
908
909- m_mirInputArea.top_left.x = X{relativeGeometry.x()};
910- m_mirInputArea.top_left.y = Y{relativeGeometry.y()};
911- m_mirInputArea.size.width = Width{relativeGeometry.width()};
912- m_mirInputArea.size.height = Height{relativeGeometry.height()};
913+ m_mirInputArea.top_left.x = X{rect.x()};
914+ m_mirInputArea.top_left.y = Y{rect.y()};
915+ m_mirInputArea.size.width = Width{rect.width()};
916+ m_mirInputArea.size.height = Height{rect.height()};
917
918 if (m_surface) {
919 m_surface->installInputArea(this);
920
921=== modified file 'src/modules/Unity/Application/mirsurfacemanager.cpp'
922--- src/modules/Unity/Application/mirsurfacemanager.cpp 2014-04-15 14:31:02 +0000
923+++ src/modules/Unity/Application/mirsurfacemanager.cpp 2014-07-15 14:51:53 +0000
924@@ -106,8 +106,8 @@
925 {
926 DLOG("MirSurfaceManager::sessionCreatedSurface (this=%p) with surface name '%s'", this, surface->name().c_str());
927 ApplicationManager* appMgr = static_cast<ApplicationManager*>(ApplicationManager::singleton());
928- Application* application = appMgr->findApplicationWithSession(session);
929-
930+ Application* application = appMgr->findApplicationWithSession(session, false);
931+
932 auto qmlSurface = new MirSurface(surface, application);
933 m_surfaces.insert(surface.get(), qmlSurface);
934 Q_EMIT surfaceCreated(qmlSurface);
935@@ -137,7 +137,7 @@
936 if (fs) {
937 fs->set_default_keyboard_target(surface);
938 }
939-
940+
941 Q_EMIT shellSurfaceChanged(m_shellSurface);
942 }
943
944
945=== modified file 'src/modules/Unity/Application/proc_info.cpp'
946--- src/modules/Unity/Application/proc_info.cpp 2014-03-11 19:11:00 +0000
947+++ src/modules/Unity/Application/proc_info.cpp 2014-07-15 14:51:53 +0000
948@@ -19,6 +19,9 @@
949 #include <QFile>
950 #include <QRegularExpression>
951
952+#include <core/posix/process.h>
953+#include <core/posix/linux/proc/process/stat.h>
954+
955 namespace unitymir
956 {
957
958@@ -33,6 +36,16 @@
959
960 return std::unique_ptr<CommandLine>(new CommandLine{ cmdline.readLine().replace('\0', ' ') });
961 }
962+
963+quint64 ProcInfo::parentProcess(quint64 pid)
964+{
965+ core::posix::Process process(pid);
966+ core::posix::linux::proc::process::Stat stat;
967+ process >> stat;
968+
969+ return stat.parent;
970+}
971+
972 QStringList ProcInfo::CommandLine::asStringList() const {
973 return QString(m_command.data()).split(' ');
974 }
975
976=== modified file 'src/modules/Unity/Application/proc_info.h'
977--- src/modules/Unity/Application/proc_info.h 2014-03-11 19:11:00 +0000
978+++ src/modules/Unity/Application/proc_info.h 2014-07-15 14:51:53 +0000
979@@ -45,6 +45,9 @@
980 QStringList asStringList() const;
981 };
982 virtual std::unique_ptr<CommandLine> commandLine(quint64 pid);
983+
984+ virtual quint64 parentProcess(quint64 pid);
985+
986 virtual ~ProcInfo();
987 };
988
989
990=== modified file 'src/unity-mir/CMakeLists.txt'
991--- src/unity-mir/CMakeLists.txt 2014-05-22 21:37:02 +0000
992+++ src/unity-mir/CMakeLists.txt 2014-07-15 14:51:53 +0000
993@@ -28,6 +28,7 @@
994 logging.h
995 focussetter.h
996 serverstatuslistener.h
997+ promptsessionlistener.h
998 ${GENERATED_PROTOBUF_HDRS}
999 )
1000
1001@@ -47,6 +48,7 @@
1002 unityprotobufservice.cpp
1003 focussetter.cpp
1004 serverstatuslistener.cpp
1005+ promptsessionlistener.cpp
1006
1007 ${UNITY_MIR_HEADERS}
1008 ${GENERATED_PROTOBUF_SRCS}
1009
1010=== modified file 'src/unity-mir/focussetter.cpp'
1011--- src/unity-mir/focussetter.cpp 2014-04-15 14:31:02 +0000
1012+++ src/unity-mir/focussetter.cpp 2014-07-15 14:51:53 +0000
1013@@ -15,6 +15,7 @@
1014 */
1015
1016 #include <mir/scene/surface.h>
1017+#include <mir/scene/session.h>
1018
1019 #include "focussetter.h"
1020 #include "logging.h"
1021@@ -35,7 +36,7 @@
1022
1023 void FocusSetter::set_focus_to(std::shared_ptr<ms::Session> const& session)
1024 {
1025- DLOG("FocusSetter::set_focus_to(session=%p):%d", session.get(), __LINE__);
1026+ DLOG("FocusSetter::set_focus_to(session=%p, session name=%s):%d", session.get(), session?session->name().c_str():"null", __LINE__);
1027 // Ensure we always call the underlying setter to dispatch focus/unfocus notifications.
1028 underlying_setter->set_focus_to(session);
1029 if (session == nullptr)
1030@@ -53,6 +54,6 @@
1031 assert(t);
1032
1033 default_keyboard_target = default_target;
1034-
1035+
1036 t->take_input_focus(keyboard_input_targeter);
1037 }
1038
1039=== added file 'src/unity-mir/promptsessionlistener.cpp'
1040--- src/unity-mir/promptsessionlistener.cpp 1970-01-01 00:00:00 +0000
1041+++ src/unity-mir/promptsessionlistener.cpp 2014-07-15 14:51:53 +0000
1042@@ -0,0 +1,62 @@
1043+/*
1044+ * Copyright (C) 2014 Canonical, Ltd.
1045+ *
1046+ * This program is free software: you can redistribute it and/or modify it under
1047+ * the terms of the GNU Lesser General Public License version 3, as published by
1048+ * the Free Software Foundation.
1049+ *
1050+ * This program is distributed in the hope that it will be useful, but WITHOUT
1051+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1052+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1053+ * Lesser General Public License for more details.
1054+ *
1055+ * You should have received a copy of the GNU Lesser General Public License
1056+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1057+ */
1058+
1059+#include "promptsessionlistener.h"
1060+#include "logging.h"
1061+
1062+namespace ms = mir::scene;
1063+
1064+Q_DECLARE_METATYPE(std::shared_ptr<ms::PromptSession>)
1065+
1066+PromptSessionListener::PromptSessionListener(QObject *parent) :
1067+ QObject(parent)
1068+{
1069+ DLOG("PromptSessionListener::PromptSessionListener (this=%p)", this);
1070+ qRegisterMetaType<std::shared_ptr<ms::PromptSession>>("std::shared_ptr<mir::scene::PromptSession>");
1071+}
1072+
1073+PromptSessionListener::~PromptSessionListener()
1074+{
1075+ DLOG("PromptSessionListener::~PromptSessionListener (this=%p)", this);
1076+}
1077+
1078+void PromptSessionListener::starting(std::shared_ptr<ms::PromptSession> const& prompt_session)
1079+{
1080+ DLOG("PromptSessionListener::starting (this=%p, prompt_session=%p)", this, (void*)prompt_session.get());
1081+ Q_EMIT promptSessionStarting(prompt_session);
1082+}
1083+
1084+void PromptSessionListener::stopping(std::shared_ptr<ms::PromptSession> const& prompt_session)
1085+{
1086+ DLOG("PromptSessionListener::stopping (this=%p, prompt_session=%p)", this, (void*)prompt_session.get());
1087+ Q_EMIT promptSessionStopping(prompt_session);
1088+}
1089+
1090+void PromptSessionListener::prompt_provider_added(ms::PromptSession const& prompt_session,
1091+ std::shared_ptr<ms::Session> const& prompt_provider)
1092+{
1093+ DLOG("PromptSessionListener::prompt_provider_added (this=%p, prompt_session=%p, prompt_provider=%p)",
1094+ this, &prompt_session, (void*)prompt_provider.get());
1095+ Q_EMIT promptProviderAdded(&prompt_session, prompt_provider);
1096+}
1097+
1098+void PromptSessionListener::prompt_provider_removed(ms::PromptSession const& prompt_session,
1099+ std::shared_ptr<ms::Session> const& prompt_provider)
1100+{
1101+ DLOG("PromptSessionListener::prompt_provider_removed (this=%p, prompt_session=%p, prompt_provider=%p)",
1102+ this, &prompt_session, (void*)prompt_provider.get());
1103+ Q_EMIT promptProviderRemoved(&prompt_session, prompt_provider);
1104+}
1105
1106=== added file 'src/unity-mir/promptsessionlistener.h'
1107--- src/unity-mir/promptsessionlistener.h 1970-01-01 00:00:00 +0000
1108+++ src/unity-mir/promptsessionlistener.h 2014-07-15 14:51:53 +0000
1109@@ -0,0 +1,47 @@
1110+/*
1111+ * Copyright (C) 2014 Canonical, Ltd.
1112+ *
1113+ * This program is free software: you can redistribute it and/or modify it under
1114+ * the terms of the GNU Lesser General Public License version 3, as published by
1115+ * the Free Software Foundation.
1116+ *
1117+ * This program is distributed in the hope that it will be useful, but WITHOUT
1118+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1119+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1120+ * Lesser General Public License for more details.
1121+ *
1122+ * You should have received a copy of the GNU Lesser General Public License
1123+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1124+ */
1125+
1126+#ifndef PROMPTSESSIONLISTENER_H
1127+#define PROMPTSESSIONLISTENER_H
1128+
1129+#include <QObject>
1130+
1131+#include "mir/scene/prompt_session_listener.h"
1132+
1133+class PromptSessionListener : public QObject, public mir::scene::PromptSessionListener
1134+{
1135+ Q_OBJECT
1136+public:
1137+ explicit PromptSessionListener(QObject *parent = 0);
1138+ ~PromptSessionListener();
1139+
1140+ void starting(std::shared_ptr<mir::scene::PromptSession> const& prompt_session) override;
1141+ void stopping(std::shared_ptr<mir::scene::PromptSession> const& prompt_session) override;
1142+
1143+ void prompt_provider_added(mir::scene::PromptSession const& prompt_session,
1144+ std::shared_ptr<mir::scene::Session> const& prompt_provider) override;
1145+ void prompt_provider_removed(mir::scene::PromptSession const& prompt_session,
1146+ std::shared_ptr<mir::scene::Session> const& prompt_provider) override;
1147+
1148+Q_SIGNALS:
1149+ void promptSessionStarting(std::shared_ptr<mir::scene::PromptSession> const& session);
1150+ void promptSessionStopping(std::shared_ptr<mir::scene::PromptSession> const& session);
1151+
1152+ void promptProviderAdded(mir::scene::PromptSession const*, std::shared_ptr<mir::scene::Session> const&);
1153+ void promptProviderRemoved(mir::scene::PromptSession const*, std::shared_ptr<mir::scene::Session> const&);
1154+};
1155+
1156+#endif // SESSIONLISTENER_H
1157
1158=== modified file 'src/unity-mir/shellserverconfiguration.cpp'
1159--- src/unity-mir/shellserverconfiguration.cpp 2014-05-08 03:50:31 +0000
1160+++ src/unity-mir/shellserverconfiguration.cpp 2014-07-15 14:51:53 +0000
1161@@ -24,6 +24,7 @@
1162 #include "sessionlistener.h"
1163 #include "surfaceconfigurator.h"
1164 #include "sessionauthorizer.h"
1165+#include "promptsessionlistener.h"
1166 #include "focussetter.h"
1167 #include "logging.h"
1168
1169@@ -117,6 +118,16 @@
1170 });
1171 }
1172
1173+std::shared_ptr<ms::PromptSessionListener>
1174+ShellServerConfiguration::the_prompt_session_listener()
1175+{
1176+ return prompt_session_listener(
1177+ [this]
1178+ {
1179+ return std::make_shared<PromptSessionListener>();
1180+ });
1181+}
1182+
1183 std::shared_ptr<mir::ServerStatusListener>
1184 ShellServerConfiguration::the_server_status_listener()
1185 {
1186@@ -136,7 +147,7 @@
1187 // The rationale is that if when you do
1188 // the_session_authorizer()
1189 // get a pointer that is unique means that Mir is not
1190-// holding the pointer and thus when we return from the
1191+// holding the pointer and thus when we return from the
1192 // sessionAuthorizer()
1193 // scope the unique pointer will be destroyed so we return 0
1194 //
1195@@ -165,6 +176,14 @@
1196 return static_cast<SessionListener*>(sharedPtr.get());
1197 }
1198
1199+PromptSessionListener *ShellServerConfiguration::promptSessionListener()
1200+{
1201+ auto sharedPtr = the_prompt_session_listener();
1202+ if (sharedPtr.unique()) return 0;
1203+
1204+ return static_cast<PromptSessionListener*>(sharedPtr.get());
1205+}
1206+
1207 SurfaceConfigurator *ShellServerConfiguration::surfaceConfigurator()
1208 {
1209 auto sharedPtr = the_surface_configurator();
1210
1211=== modified file 'src/unity-mir/shellserverconfiguration.h'
1212--- src/unity-mir/shellserverconfiguration.h 2014-05-08 03:50:31 +0000
1213+++ src/unity-mir/shellserverconfiguration.h 2014-07-15 14:51:53 +0000
1214@@ -28,6 +28,7 @@
1215
1216 class FocusSetter;
1217 class SessionListener;
1218+class PromptSessionListener;
1219 class SessionAuthorizer;
1220 class SurfaceConfigurator;
1221 class InitialSurfacePlacementStrategy;
1222@@ -38,6 +39,7 @@
1223
1224 Q_PROPERTY(SessionAuthorizer* sessionAuthorizer READ sessionAuthorizer CONSTANT)
1225 Q_PROPERTY(SessionListener* sessionListener READ sessionListener CONSTANT)
1226+ Q_PROPERTY(PromptSessionListener* promptSessionListener READ promptSessionListener CONSTANT)
1227 Q_PROPERTY(SurfaceConfigurator* surfaceConfigurator READ surfaceConfigurator CONSTANT)
1228
1229 public:
1230@@ -50,6 +52,7 @@
1231 std::shared_ptr<mir::scene::SessionListener> the_session_listener() override;
1232 std::shared_ptr<mir::scene::SurfaceConfigurator> the_surface_configurator() override;
1233 std::shared_ptr<mir::shell::FocusSetter> the_shell_focus_setter() override;
1234+ std::shared_ptr<mir::scene::PromptSessionListener> the_prompt_session_listener() override;
1235 std::shared_ptr<mir::ServerStatusListener> the_server_status_listener() override;
1236 std::shared_ptr<mir::frontend::SessionAuthorizer> the_session_authorizer() override;
1237 std::shared_ptr<mir::frontend::ConnectionCreator> the_connection_creator() override;
1238@@ -59,6 +62,7 @@
1239 InitialSurfacePlacementStrategy *placementStrategy();
1240 SessionAuthorizer *sessionAuthorizer();
1241 SessionListener *sessionListener();
1242+ PromptSessionListener *promptSessionListener();
1243 SurfaceConfigurator *surfaceConfigurator();
1244 FocusSetter *focusSetter();
1245
1246
1247=== modified file 'tests/CMakeLists.txt'
1248--- tests/CMakeLists.txt 2014-05-19 12:13:58 +0000
1249+++ tests/CMakeLists.txt 2014-07-15 14:51:53 +0000
1250@@ -33,6 +33,7 @@
1251 mock_process_controller.h
1252 mock_proc_info.h
1253 mock_session.h
1254+ mock_prompt_session_manager.h
1255 )
1256
1257 add_executable(
1258
1259=== modified file 'tests/application_manager_test.cpp'
1260--- tests/application_manager_test.cpp 2014-07-15 14:51:53 +0000
1261+++ tests/application_manager_test.cpp 2014-07-15 14:51:53 +0000
1262@@ -36,6 +36,8 @@
1263 #include "mock_proc_info.h"
1264 #include "mock_session.h"
1265 #include "mock_focus_controller.h"
1266+#include "mock_prompt_session_manager.h"
1267+#include "mock_prompt_session.h"
1268
1269 using namespace unitymir;
1270
1271@@ -68,6 +70,7 @@
1272 [](DesktopFileReader::Factory*){}),
1273 QSharedPointer<ProcInfo>(&procInfo,[](ProcInfo *){}),
1274 std::shared_ptr<mir::shell::FocusController>(&focusController, [](void*){}),
1275+ std::shared_ptr<mir::scene::PromptSessionManager>(&promptSessionManager, [](void*){}),
1276 QSize(400,400)
1277 }
1278 {
1279@@ -109,6 +112,7 @@
1280 testing::NiceMock<testing::MockProcInfo> procInfo;
1281 testing::NiceMock<testing::MockDesktopFileReaderFactory> desktopFileReaderFactory;
1282 testing::NiceMock<testing::MockFocusController> focusController;
1283+ testing::NiceMock<testing::MockPromptSessionManager> promptSessionManager;
1284 QSharedPointer<TaskController> taskController;
1285 ApplicationManager applicationManager;
1286 };
1287@@ -2004,3 +2008,61 @@
1288 cv.wait(lk, [&] { return done; } );
1289 }
1290 }
1291+
1292+TEST_F(ApplicationManagerTests, applicationTracksPromptSession)
1293+{
1294+ using namespace testing;
1295+ quint64 procId1 = 5551;
1296+
1297+ auto application = startApplication(procId1, "webapp");
1298+ auto promptSession = std::make_shared<MockPromptSession>();
1299+
1300+ ON_CALL(promptSessionManager, application_for(_)).WillByDefault(Return(application->session()));
1301+
1302+ applicationManager.onPromptSessionStarting(promptSession);
1303+
1304+ EXPECT_EQ(application->activePromptSession(), promptSession);
1305+
1306+ applicationManager.onPromptSessionStopping(promptSession);
1307+
1308+ EXPECT_EQ(application->activePromptSession(), nullptr);
1309+}
1310+
1311+TEST_F(ApplicationManagerTests, applicationFocusesPromptSessionProviders)
1312+{
1313+ using namespace testing;
1314+ quint64 procId1 = 5551;
1315+ quint64 procId2 = 5552;
1316+
1317+ auto application = startApplication(procId1, "webapp");
1318+
1319+ std::shared_ptr<mir::scene::PromptSession> promptSession = std::make_shared<MockPromptSession>();
1320+
1321+ ON_CALL(promptSessionManager, application_for(promptSession)).WillByDefault(Return(application->session()));
1322+ ON_CALL(promptSessionManager, helper_for(promptSession)).WillByDefault(Return(std::shared_ptr<mir::scene::Session>()));
1323+
1324+ std::shared_ptr<mir::scene::Surface> aSurface(nullptr);
1325+ applicationManager.onSessionCreatedSurface(application->session().get(), aSurface);
1326+
1327+ std::shared_ptr<testing::MockSession> providerSession = std::make_shared<MockSession>("", procId2);
1328+ // TODO: Use mock!
1329+ std::shared_ptr<mir::scene::Surface> providerSurface((mir::scene::Surface*)__LINE__, [](mir::scene::Surface*) {});
1330+ ON_CALL(*providerSession, default_surface()).WillByDefault(Return(providerSurface));
1331+
1332+ applicationManager.onSessionStarting(providerSession);
1333+ EXPECT_EQ(application->foregroundSession(), application->session());
1334+
1335+ InSequence s;
1336+ EXPECT_CALL(focusController, set_focus_to(std::static_pointer_cast<mir::scene::Session>(providerSession))).Times(1);
1337+ EXPECT_CALL(focusController, set_focus_to(application->session())).Times(1);
1338+
1339+ applicationManager.onPromptSessionStarting(promptSession);
1340+
1341+ ON_CALL(promptSessionManager, for_each_provider_in(promptSession,_)).WillByDefault(InvokeArgument<1>(providerSession));
1342+ applicationManager.onPromptProviderAdded(promptSession.get(), providerSession);
1343+ EXPECT_EQ(application->foregroundSession(), providerSession);
1344+
1345+ ON_CALL(promptSessionManager, for_each_provider_in(promptSession,_)).WillByDefault(Return());
1346+ applicationManager.onPromptProviderRemoved(promptSession.get(), providerSession);
1347+ EXPECT_EQ(application->foregroundSession(), application->session());
1348+}
1349
1350=== modified file 'tests/mock_proc_info.h'
1351--- tests/mock_proc_info.h 2014-02-28 17:42:43 +0000
1352+++ tests/mock_proc_info.h 2014-07-15 14:51:53 +0000
1353@@ -27,9 +27,14 @@
1354 struct MockProcInfo : public unitymir::ProcInfo
1355 {
1356 MOCK_METHOD1(command_line, QByteArray(quint64));
1357- std::unique_ptr<CommandLine> commandLine(quint64 pid)
1358- {
1359- return std::unique_ptr<CommandLine>(new CommandLine{command_line(pid)});
1360+ std::unique_ptr<CommandLine> commandLine(quint64 pid) override
1361+ {
1362+ return std::unique_ptr<CommandLine>(new CommandLine{command_line(pid)});
1363+ }
1364+
1365+ quint64 parentProcess(quint64 /* pid */) override
1366+ {
1367+ return 0;
1368 }
1369 };
1370 }
1371
1372=== added file 'tests/mock_prompt_session.h'
1373--- tests/mock_prompt_session.h 1970-01-01 00:00:00 +0000
1374+++ tests/mock_prompt_session.h 2014-07-15 14:51:53 +0000
1375@@ -0,0 +1,31 @@
1376+/*
1377+ * Copyright (C) 2014 Canonical, Ltd.
1378+ *
1379+ * This program is free software: you can redistribute it and/or modify it under
1380+ * the terms of the GNU Lesser General Public License version 3, as published by
1381+ * the Free Software Foundation.
1382+ *
1383+ * This program is distributed in the hope that it will be useful, but WITHOUT
1384+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1385+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1386+ * Lesser General Public License for more details.
1387+ *
1388+ * You should have received a copy of the GNU Lesser General Public License
1389+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1390+ *
1391+ */
1392+
1393+#ifndef MOCK_MIR_PROMPT_SESSION_H
1394+#define MOCK_MIR_PROMPT_SESSION_H
1395+
1396+#include <mir/scene/prompt_session.h>
1397+#include <gmock/gmock.h>
1398+
1399+namespace testing
1400+{
1401+struct MockPromptSession : public mir::scene::PromptSession
1402+{
1403+};
1404+}
1405+
1406+#endif // MOCK_MIR_PROMPT_SESSION_H
1407
1408=== added file 'tests/mock_prompt_session_manager.h'
1409--- tests/mock_prompt_session_manager.h 1970-01-01 00:00:00 +0000
1410+++ tests/mock_prompt_session_manager.h 2014-07-15 14:51:53 +0000
1411@@ -0,0 +1,57 @@
1412+/*
1413+ * Copyright (C) 2014 Canonical, Ltd.
1414+ *
1415+ * This program is free software: you can redistribute it and/or modify it under
1416+ * the terms of the GNU Lesser General Public License version 3, as published by
1417+ * the Free Software Foundation.
1418+ *
1419+ * This program is distributed in the hope that it will be useful, but WITHOUT
1420+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1421+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1422+ * Lesser General Public License for more details.
1423+ *
1424+ * You should have received a copy of the GNU Lesser General Public License
1425+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1426+ *
1427+ */
1428+
1429+#ifndef MOCK_MIR_SCENE_PROMPT_SESSION_MANAGER_H_
1430+#define MOCK_MIR_SCENE_PROMPT_SESSION_MANAGER_H_
1431+
1432+#include "mir/scene/prompt_session_manager.h"
1433+#include "mir/scene/prompt_session_creation_parameters.h"
1434+
1435+#include <gmock/gmock.h>
1436+
1437+namespace testing
1438+{
1439+
1440+class MockPromptSessionManager: public mir::scene::PromptSessionManager
1441+{
1442+public:
1443+ MOCK_CONST_METHOD2(start_prompt_session_for, std::shared_ptr<mir::scene::PromptSession>(std::shared_ptr<mir::scene::Session> const&,
1444+ mir::scene::PromptSessionCreationParameters const&));
1445+
1446+ MOCK_CONST_METHOD1(stop_prompt_session, void(std::shared_ptr<mir::scene::PromptSession> const&));
1447+
1448+ MOCK_CONST_METHOD2(add_prompt_provider, void(std::shared_ptr<mir::scene::PromptSession> const&,
1449+ std::shared_ptr<mir::scene::Session> const&));
1450+
1451+ MOCK_CONST_METHOD2(add_prompt_provider_by_pid, void(std::shared_ptr<mir::scene::PromptSession> const&,
1452+ pid_t));
1453+
1454+ MOCK_CONST_METHOD1(add_expected_session, void(std::shared_ptr<mir::scene::Session> const&));
1455+
1456+ MOCK_CONST_METHOD1(remove_session, void(std::shared_ptr<mir::scene::Session> const&));
1457+
1458+ MOCK_CONST_METHOD1(application_for, std::shared_ptr<mir::scene::Session>(std::shared_ptr<mir::scene::PromptSession> const&));
1459+
1460+ MOCK_CONST_METHOD1(helper_for, std::shared_ptr<mir::scene::Session>(std::shared_ptr<mir::scene::PromptSession> const&));
1461+
1462+ MOCK_CONST_METHOD2(for_each_provider_in, void(std::shared_ptr<mir::scene::PromptSession> const&,
1463+ std::function<void(std::shared_ptr<mir::scene::Session> const&)> const&));
1464+};
1465+
1466+} // namespace testing
1467+
1468+#endif // MOCK_MIR_SCENE_PROMPT_SESSION_MANAGER_H_
1469
1470=== modified file 'tests/mock_session.h'
1471--- tests/mock_session.h 2014-06-18 15:15:39 +0000
1472+++ tests/mock_session.h 2014-07-15 14:51:53 +0000
1473@@ -30,7 +30,7 @@
1474 struct MockSession : public mir::scene::Session
1475 {
1476 MockSession() {}
1477- MockSession(std::string const& sessionName, pid_t processId)
1478+ MockSession(std::string const& sessionName, pid_t processId)
1479 : m_sessionName(sessionName), m_sessionId(processId)
1480 {}
1481

Subscribers

People subscribed via source and target branches