Merge lp:~dandrader/qtmir/fake-apps into lp:qtmir

Proposed by Daniel d'Andrada
Status: Superseded
Proposed branch: lp:~dandrader/qtmir/fake-apps
Merge into: lp:qtmir
Diff against target: 6497 lines (+4124/-669)
74 files modified
CMakeLists.txt (+3/-2)
debian/control (+2/-2)
debian/gles-patches/convert-to-gles.patch (+1/-1)
src/common/debughelpers.cpp (+47/-1)
src/common/debughelpers.h (+3/-1)
src/common/windowcontrollerinterface.h (+2/-1)
src/modules/Unity/Application/CMakeLists.txt (+9/-1)
src/modules/Unity/Application/application.cpp (+1/-1)
src/modules/Unity/Application/application.h (+0/-1)
src/modules/Unity/Application/application_manager.cpp (+46/-34)
src/modules/Unity/Application/application_manager.h (+3/-1)
src/modules/Unity/Application/fake/fakeapplicationinfo.cpp (+26/-0)
src/modules/Unity/Application/fake/fakeapplicationinfo.h (+93/-0)
src/modules/Unity/Application/fake/fakemaliit.cpp (+76/-0)
src/modules/Unity/Application/fake/fakemaliit.h (+47/-0)
src/modules/Unity/Application/fake/fakemirclient.cpp (+685/-0)
src/modules/Unity/Application/fake/fakemirclient.h (+177/-0)
src/modules/Unity/Application/fake/fakeprompt.cpp (+320/-0)
src/modules/Unity/Application/fake/fakeprompt.h (+155/-0)
src/modules/Unity/Application/fake/faketaskcontroller.cpp (+683/-0)
src/modules/Unity/Application/fake/faketaskcontroller.h (+157/-0)
src/modules/Unity/Application/fake/windowattributes.h (+47/-0)
src/modules/Unity/Application/mirtestsingleton.cpp (+211/-0)
src/modules/Unity/Application/mirtestsingleton.h (+116/-0)
src/modules/Unity/Application/plugin.cpp (+11/-1)
src/modules/Unity/Application/resources/screenshots/gmail-webapp.svg (+343/-0)
src/modules/Unity/Application/resources/screenshots/ubuntu-weather-app.svg (+201/-0)
src/modules/Unity/Application/resources/surfaces.qrc (+39/-0)
src/modules/Unity/Application/session.cpp (+13/-1)
src/modules/Unity/Application/session_interface.h (+1/-1)
src/modules/Unity/Application/sessionmanager.cpp (+0/-200)
src/modules/Unity/Application/sessionmanager.h (+0/-90)
src/modules/Unity/Application/surfacemanager.cpp (+4/-5)
src/modules/Unity/Application/surfacemanager.h (+1/-3)
src/modules/Unity/Application/taskcontroller.cpp (+196/-0)
src/modules/Unity/Application/taskcontroller.h (+63/-5)
src/platforms/mirserver/CMakeLists.txt (+2/-0)
src/platforms/mirserver/argvHelper.h (+0/-52)
src/platforms/mirserver/cursor.cpp (+5/-4)
src/platforms/mirserver/cursor.h (+2/-2)
src/platforms/mirserver/logging.cpp (+2/-1)
src/platforms/mirserver/logging.h (+2/-1)
src/platforms/mirserver/mirserverhooks.cpp (+9/-1)
src/platforms/mirserver/mirserverhooks.h (+2/-1)
src/platforms/mirserver/mirserverintegration.cpp (+2/-2)
src/platforms/mirserver/mirserverintegration.h (+1/-1)
src/platforms/mirserver/mirsingleton.cpp (+29/-1)
src/platforms/mirserver/mirsingleton.h (+8/-1)
src/platforms/mirserver/pidfetcher.cpp (+34/-0)
src/platforms/mirserver/pidfetcher.h (+43/-0)
src/platforms/mirserver/plugin.cpp (+3/-1)
src/platforms/mirserver/qmirserver.cpp (+4/-2)
src/platforms/mirserver/qmirserver.h (+1/-1)
src/platforms/mirserver/qmirserver_p.cpp (+5/-18)
src/platforms/mirserver/qmirserver_p.h (+3/-3)
src/platforms/mirserver/qteventfeeder.cpp (+63/-23)
src/platforms/mirserver/qteventfeeder.h (+4/-0)
src/platforms/mirserver/windowmanagementpolicy.cpp (+7/-1)
src/platforms/mirserver/windowmanagementpolicy.h (+1/-0)
tests/framework/mock_renderable.cpp (+1/-1)
tests/framework/mock_renderable.h (+3/-3)
tests/framework/mock_task_controller.cpp (+2/-1)
tests/framework/mock_task_controller.h (+1/-1)
tests/framework/qtmir_test.cpp (+2/-4)
tests/framework/qtmir_test.h (+2/-5)
tests/mirserver/ArgvHelper/CMakeLists.txt (+0/-18)
tests/mirserver/ArgvHelper/argvHelper_test.cpp (+0/-72)
tests/mirserver/CMakeLists.txt (+0/-1)
tests/modules/Application/application_test.cpp (+3/-0)
tests/modules/ApplicationManager/application_manager_test.cpp (+70/-69)
tests/modules/CMakeLists.txt (+1/-1)
tests/modules/Session/CMakeLists.txt (+6/-6)
tests/modules/Session/session_test.cpp (+2/-2)
tests/modules/Session/taskcontroller_test.cpp (+17/-17)
To merge this branch: bzr merge lp:~dandrader/qtmir/fake-apps
Reviewer Review Type Date Requested Status
Unity8 CI Bot (community) continuous-integration Needs Fixing
Gerry Boland (community) Needs Fixing
Review via email: mp+316845@code.launchpad.net

This proposal has been superseded by a proposal from 2017-02-14.

Commit message

Fake applications using internal mir clients when UNITY_TESTING is defined

Description of the change

Prereq-archive: ppa:ci-train-ppa-service/ubuntu/2457

* Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~dandrader/unity-api/mirInputTweaks/+merge/316844
https://code.launchpad.net/~dandrader/unity8/testWithQtMir/+merge/316851

* 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 :
review: Needs Fixing (continuous-integration)
Revision history for this message
Unity8 CI Bot (unity8-ci-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :
Download full text (3.2 KiB)

=== modified file 'src/common/windowcontrollerinterface.h'
+ virtual void modify(const miral::Window &window, miral::WindowSpecification) = 0;
This is opening large functionality to the shell, which makes me suspicious. Digging I see it only used by MirSurface::setMaximumWidth and friends, which are test-only.

I dislike that, as shell should not be updating MirAL's information on surface min/max/inc limits, instead it should be the client (like it will happen in reality).

Can you redo the min/max/inc limit setting to use the client api instead, and remove this change?

=== modified file 'src/modules/Unity/Application/application_manager.cpp'
+ taskController.reset(fakeTaskController);
+ procInfo = fakeTaskController->procInfo();
reset too, for consistency

=== modified file 'src/modules/Unity/Application/application_manager.h'
+ SessionInterface *findSession(const mir::scene::Session* session) const;
+ Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);
so these you've made public, but are also just for test use. Do comment about that fact then, just to make it clear.

Also, we need to avoid exposing mirserver apis and use MirAL's api instead. So use miral::Application instead of std::shared_ptr<mir::scene::Session>. findSession doesn't need to take a raw pointer either. In fact, are both needed at all?

+ void onSessionStarting(SessionInterface *session);
is this fixing a bug? If so, would be good to separate that bug fix into a separate MP with a test.

I'll look at the Fake stuff last.

=== modified file 'src/modules/Unity/Application/mirsurface.h'
+ Q_PROPERTY(int width READ width NOTIFY widthChanged)
+ Q_PROPERTY(int height READ height NOTIFY heightChanged)
why? size() gives this info out, and a single change signal too. You know I disapprove of this.

+ // Whether there's a MirSurfaceItem with active focus displaying this surface.
+ // This information is useful in shell tests
+ Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
+
+ // Whether the MirSurface is exposed (true) or occluded (false)
+ // This information is useful in shell tests
+ Q_PROPERTY(bool exposed READ exposed NOTIFY exposedChanged)
Can you expose the client API for these? Wiring it through shell, you're missing out on testing the Mir->client communication.

+ // Used only by shell tests
+ Q_INVOKABLE void setMinimumWidth(int);
+ Q_INVOKABLE void setMaximumWidth(int);
+ Q_INVOKABLE void setMinimumHeight(int);
+ Q_INVOKABLE void setMaximumHeight(int);
+ Q_INVOKABLE void setWidthIncrement(int);
+ Q_INVOKABLE void setHeightIncrement(int);
Yeah as I said above, I dislike these here.

=== modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
What is going on here? What is this mouse & touch counting stuff for? Can't these things could be done in the client, and avoid the test code in the shell?

=== modified file 'src/modules/Unity/Application/session.cpp'
+ const bool focusedBefore = focused();
and on.

Are you fixing a bug here? Can you separate that out into a separate MP with a test?

+ for (int i = 0; i < m_closingSurfaces.count(); ++...

Read more...

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

=== modified file 'src/platforms/mirserver/mirserverhooks.h'
+ mir::Server *server() const;
You need to justify this. Again it is exposing mir server api, which we're trying to avoid.

=== modified file 'src/platforms/mirserver/mirserverintegration.cpp'
-MirServerIntegration::MirServerIntegration(int &argc, char **argv)
+MirServerIntegration::MirServerIntegration()
Why?? This prevents Mir from processing any command line args. You really need to justify this reversal.

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

On 09/02/2017 14:17, Gerry Boland wrote:
> === modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
> What is going on here? What is this mouse & touch counting stuff for? Can't these things could be done in the client, and avoid the test code in the shell?

That would be lovely. Any suggestions?

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

On 09/02/2017 14:17, Gerry Boland wrote:
> + for (int i = 0; i < m_closingSurfaces.count(); ++i) {
> foreach nicer

That's debatable. I personally find Q_FOREACH to be an eyesore.

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

Instead of exporting the whole of MirServer through the QPA, would be cleaner if you could just expose the bare minimum api needed for the test bits

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

Can you also split the SessionManager->TaskController transition into a separate MP.

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

> On 09/02/2017 14:17, Gerry Boland wrote:
> > + for (int i = 0; i < m_closingSurfaces.count(); ++i) {
> > foreach nicer
>
> That's debatable. I personally find Q_FOREACH to be an eyesore.

I mis-spoke, I meant the C++11 ranged for is nicer.

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

> On 09/02/2017 14:17, Gerry Boland wrote:
> > === modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
> > What is going on here? What is this mouse & touch counting stuff for? Can't
> these things could be done in the client, and avoid the test code in the
> shell?
>
> That would be lovely. Any suggestions?

Well I see the fake MirClient::windowEvent receives input events, so that can keep a count. That does mean you need to expose the some test-only data about the mir client to the qmltest, but that should be possible. Quite probably a non-trivial amount of work.

I don't mind if the code isn't perfect first time, I understand that shortcuts need to be made for brevity. If you mention that limitations in the description and justify why, then I'll be happy and can review with that in mind. But if not, I will point them out and ask why.

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

=== added file 'src/modules/Unity/Application/fake/fakemaliit.cpp'
+ MirBufferStream* buffer_stream = mir_window_get_buffer_stream(window);
+ // TODO sometimes buffer_stream is nullptr
+ // (Only observed when creating a lot of clients at once)
Just FYI, this is a deprecated method. The null thing is weird, I'd suggest logging a bug but if it is to be deprecated anyway, not worth the trouble.

+ for (int i = 0; i < m_windows.count(); ++i) {
+ MirWindow *window = m_windows[i];
ranged for a little nicer! It's not an eyesore IMO

=== added file 'src/modules/Unity/Application/fake/fakeprompt.cpp'
+ MirClientEvent(MirWindow* window, const MirEvent *event, QEvent::Type type)
+ : QEvent(type), window(window), mirEvent(event) {
+ mirEvent = mir_event_ref(event);
you're setting mirEvent twice afaics.

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

On 10/02/2017 11:53, Gerry Boland wrote:
> === added file 'src/modules/Unity/Application/fake/fakemaliit.cpp'
> + MirBufferStream* buffer_stream = mir_window_get_buffer_stream(window);
> + // TODO sometimes buffer_stream is nullptr
> + // (Only observed when creating a lot of clients at once)
> Just FYI, this is a deprecated method. The null thing is weird, I'd suggest logging a bug but if it is to be deprecated anyway, not worth the trouble.

That's copy-pasted code from alan_g in miral.

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

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

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

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

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

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

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

FAILED: Continuous integration, rev:604
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/498/
Executed test runs:
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build/4095/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4123
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3963
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3963/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3963
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3963/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3963
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3963/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3963/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3963
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3963/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3963/console

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

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

FAILED: Continuous integration, rev:605
https://unity8-jenkins.ubuntu.com/job/lp-qtmir-ci/500/
Executed test runs:
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build/4098/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4126
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3966
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/3966/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3966
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/3966/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3966
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/3966/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/3966/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3966
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/3966/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/3966/console

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

review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :
Download full text (3.7 KiB)

On 09/02/2017 14:17, Gerry Boland wrote:
> Review: Needs Fixing
>
> === modified file 'src/common/windowcontrollerinterface.h'
> + virtual void modify(const miral::Window &window, miral::WindowSpecification) = 0;
> This is opening large functionality to the shell, which makes me suspicious. Digging I see it only used by MirSurface::setMaximumWidth and friends, which are test-only.
>
> I dislike that, as shell should not be updating MirAL's information on surface min/max/inc limits, instead it should be the client (like it will happen in reality).
>
> Can you redo the min/max/inc limit setting to use the client api instead, and remove this change?

Done.

> === modified file 'src/modules/Unity/Application/application_manager.cpp'
> + taskController.reset(fakeTaskController);
> + procInfo = fakeTaskController->procInfo();
> reset too, for consistency
>

Can't do that. fakeTaskController->procInfo() is a QSharedPointer.

> === modified file 'src/modules/Unity/Application/application_manager.h'
> + SessionInterface *findSession(const mir::scene::Session* session) const;
> + Application* findApplicationWithSession(const std::shared_ptr<mir::scene::Session> &session);
> so these you've made public, but are also just for test use. Do comment about that fact then, just to make it clear.

ApplicationManager::findSession() is used by SurfaceManager now that
SessionManager is gone and its responsibilities split between
TaskController (most of it), SurfaceManager (just a little bit) and
ApplicationManager (another tiny piece).
Made findApplicationWithSession() private (I think it was used from
outside earlier in the development but not anymore).

> Also, we need to avoid exposing mirserver apis and use MirAL's api instead. So use miral::Application instead of std::shared_ptr<mir::scene::Session>. findSession doesn't need to take a raw pointer either. In fact, are both needed at all?

findSession() is needed as explained above.

> + void onSessionStarting(SessionInterface *session);
> is this fixing a bug? If so, would be good to separate that bug fix into a separate MP with a test.

No. Again, it's because of the removal of SessionManager.

> === modified file 'src/modules/Unity/Application/mirsurface.h'
> + Q_PROPERTY(int width READ width NOTIFY widthChanged)
> + Q_PROPERTY(int height READ height NOTIFY heightChanged)
> why? size() gives this info out, and a single change signal too. You know I disapprove of this.

Because test code uses it. Easier to copy/paste that from the mock
Unity.Application than to change test code.

Anyway, removed.

> + // Whether there's a MirSurfaceItem with active focus displaying this surface.
> + // This information is useful in shell tests
> + Q_PROPERTY(bool activeFocus READ activeFocus NOTIFY activeFocusChanged)
> +
> + // Whether the MirSurface is exposed (true) or occluded (false)
> + // This information is useful in shell tests
> + Q_PROPERTY(bool exposed READ exposed NOTIFY exposedChanged)
> Can you expose the client API for these? Wiring it through shell, you're missing out on testing the Mir->client communication.

Done.

> + // Used only by shell tests
> + Q_INVOKA...

Read more...

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

On 10/02/2017 11:35, Gerry Boland wrote:
>> On 09/02/2017 14:17, Gerry Boland wrote:
>>> === modified file 'src/modules/Unity/Application/mirsurfaceitem.cpp'
>>> What is going on here? What is this mouse & touch counting stuff for? Can't
>> these things could be done in the client, and avoid the test code in the
>> shell?
>>
>> That would be lovely. Any suggestions?
> Well I see the fake MirClient::windowEvent receives input events, so that can keep a count. That does mean you need to expose the some test-only data about the mir client to the qmltest, but that should be possible. Quite probably a non-trivial amount of work.

Done.

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

On 09/02/2017 16:26, Gerry Boland wrote:
> Can you also split the SessionManager->TaskController transition into a separate MP.

Done:
https://code.launchpad.net/~dandrader/qtmir/byeSessionManager/+merge/317209

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

On 09/02/2017 15:20, Gerry Boland wrote:
> Instead of exporting the whole of MirServer through the QPA, would be cleaner if you could just expose the bare minimum api needed for the test bits

Would it be ok if Unity.Application module got its hands on MirServer
via other means than Qt's NativeInterface? Is the problem that MirServer
is on the NativeInterface and so shell code could access it?

I'm a bit lost here. Not sure where you wanna get to and why. The
internal mir client code is really plain mir and miral has no part in it.

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

On 09/02/2017 14:23, Gerry Boland wrote:
> === modified file 'src/platforms/mirserver/mirserverintegration.cpp'
> -MirServerIntegration::MirServerIntegration(int &argc, char **argv)
> +MirServerIntegration::MirServerIntegration()
> Why?? This prevents Mir from processing any command line args. You really need to justify this reversal.

Yes, I meant to ask about it but ended up forgetting to do so due to the
sea of code and multitude of weeks that took to me make these branches.

The problem is, if I recall correctly, that Mir is getting in the way
when qtmir is used in uqmlscene (make tryFoo) and qmltestrunner (make
testFoo). That was the simplest solution. Please advise.

lp:~dandrader/qtmir/fake-apps updated
596. By Albert Astals Cid

We're at provides 26 already

597. By Iain Lane

Releasing 0.5.1+17.04.20170206-0ubuntu2

598. By Alan Griffiths

Iteration 0 of miral::PersistDisplayConfig. This does nothing yet (and breaks nothing in the process). This MP creates a place (miral-prototypes) to build prototype miral features and sketches out what will need to be implemented for PersistDisplayConfig. (LP: #1644189)

Approved by: Nick Dedekind, Unity8 CI Bot

599. By Nick Dedekind

Added Extended Display Information Data (EDID) parsing.

Approved by: Gerry Boland, Unity8 CI Bot

600. By Gerry Boland

ScreenModel: Only expose windows on displays that are turned on

It seems the GL context Mir gives us for a display that is turned off is invalid. But there is no point rendering to a display that is turned off anyway. (LP: #1521403, #1638611, #1656250)

Approved by: Daniel d'Andrada, Unity8 CI Bot

601. By Gerry Boland

Restore lost LTTng tracepoints, and delete unused ones (LP: #1658084)

Approved by: Daniel d'Andrada, Unity8 CI Bot

602. By CI Train Bot Account

We're at provides 26 already (LP: #1662608)

Approved by: Gerry Boland

603. By Albert Astals Cid

Check we provide the same unity-application-impl that we require

Approved by: Gerry Boland, Lukáš Tinkl, Unity8 CI Bot

604. By Alan Griffiths

Identify the code that depends directly on mirserver-dev headers

Approved by: Lukáš Tinkl, Unity8 CI Bot

605. By Daniel d'Andrada

Resolve mir cursor names using mir symbols instead of plain strings

Since those names under the API symbols change regularly and should be considered opaque. (LP: #1662827)

Approved by: Albert Astals Cid, Unity8 CI Bot

606. By CI Train Bot Account

Releasing 0.5.1+17.04.20170215.1-0ubuntu1

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

On 10/02/2017 11:53, Gerry Boland wrote:
> + for (int i = 0; i < m_windows.count(); ++i) {
> + MirWindow *window = m_windows[i];
> ranged for a little nicer! It's not an eyesore IMO

Done.

lp:~dandrader/qtmir/fake-apps updated
607. By Daniel d'Andrada

Some qtmir::Session fixes

- Emit Session::focusedChanged when adding an already focused window
- When session dies, besides killing all its surfaces, also kill its closing surfaces

608. By Daniel d'Andrada

Make TaskController absorb SessionManager

So that we can later on provide fake, internal, mir clients for tests
just by replacing the TaskController with a fake implementation.

609. By Daniel d'Andrada

Don't pass command line arguments to Mir

Those are application specific, as qtmir can be used by several different
executables (unity8, uqmlscene, qmltestrunner), each taking its own set of
comamnd line args.

Those executables (uqmlscene, qmltestrunner in particular) will quit when
seeing Mir arguments and vice-versa (Mir quits when seeing unrecognized args)

610. By Daniel d'Andrada

Fake applications using internal mir clients when UNITY_TESTING is defined

Unmerged revisions

610. By Daniel d'Andrada

Fake applications using internal mir clients when UNITY_TESTING is defined

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'CMakeLists.txt'
2--- CMakeLists.txt 2017-02-02 09:17:48 +0000
3+++ CMakeLists.txt 2017-02-14 10:17:32 +0000
4@@ -23,8 +23,9 @@
5 # add custom cmake modules
6 set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
7
8-# Instruct CMake to run moc automatically when needed.
9+# Instruct CMake to run moc and rcc automatically when needed.
10 set(CMAKE_AUTOMOC ON)
11+set(CMAKE_AUTORCC ON)
12
13 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Werror")
14 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -fno-strict-aliasing -Werror -Wextra")
15@@ -90,7 +91,7 @@
16 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
17 pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
18 pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
19-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=26)
20+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=27)
21 pkg_check_modules(CGMANAGER libcgmanager REQUIRED)
22 pkg_check_modules(CONTENT_HUB libcontent-hub>=0.2 REQUIRED)
23
24
25=== modified file 'debian/control'
26--- debian/control 2017-02-02 09:17:48 +0000
27+++ debian/control 2017-02-14 10:17:32 +0000
28@@ -25,7 +25,7 @@
29 libubuntu-app-launch2-dev (>= 0.9),
30 libubuntu-application-api-dev (>= 2.1.0),
31 libudev-dev,
32- libunity-api-dev (>= 8.2),
33+ libunity-api-dev (>= 8.3),
34 liburl-dispatcher1-dev,
35 libxkbcommon-dev,
36 libxrender-dev,
37@@ -102,7 +102,7 @@
38 Conflicts: libqtmir,
39 libunity-mir1,
40 Provides: unity-application-impl,
41- unity-application-impl-23,
42+ unity-application-impl-27,
43 Description: Qt plugin for Unity specific Mir APIs
44 QtMir provides Qt/QML bindings for Mir features that are exposed through the
45 qtmir-desktop or qtmir-android QPA plugin such as Application management
46
47=== modified file 'debian/gles-patches/convert-to-gles.patch'
48--- debian/gles-patches/convert-to-gles.patch 2016-12-01 11:45:31 +0000
49+++ debian/gles-patches/convert-to-gles.patch 2017-02-14 10:17:32 +0000
50@@ -84,7 +84,7 @@
51 -Conflicts: libqtmir,
52 - libunity-mir1,
53 -Provides: unity-application-impl,
54-- unity-application-impl-23,
55+- unity-application-impl-27,
56 -Description: Qt plugin for Unity specific Mir APIs
57 - QtMir provides Qt/QML bindings for Mir features that are exposed through the
58 - qtmir-desktop or qtmir-android QPA plugin such as Application management
59
60=== modified file 'src/common/debughelpers.cpp'
61--- src/common/debughelpers.cpp 2017-01-18 21:24:15 +0000
62+++ src/common/debughelpers.cpp 2017-02-14 10:17:32 +0000
63@@ -1,5 +1,5 @@
64 /*
65- * Copyright (C) 2013-2015 Canonical, Ltd.
66+ * Copyright (C) 2013-2017 Canonical, Ltd.
67 *
68 * This program is free software: you can redistribute it and/or modify it under
69 * the terms of the GNU Lesser General Public License version 3, as published by
70@@ -371,3 +371,49 @@
71 return "???";
72 }
73 }
74+
75+const char* mirEventTypeToStr(MirEventType t)
76+{
77+ switch (t) {
78+ case mir_event_type_key:
79+ return "key";
80+ case mir_event_type_motion:
81+ return "motion";
82+ case mir_event_type_window:
83+ return "window";
84+ case mir_event_type_resize:
85+ return "resize";
86+ case mir_event_type_prompt_session_state_change:
87+ return "prompt_session_state_change";
88+ case mir_event_type_orientation:
89+ return "orientation";
90+ case mir_event_type_close_window:
91+ return "close_window";
92+ case mir_event_type_input:
93+ return "input";
94+ case mir_event_type_keymap:
95+ return "keymap";
96+ case mir_event_type_window_output:
97+ return "window_output";
98+ case mir_event_type_input_device_state:
99+ return "input_device_state";
100+ case mir_event_type_window_placement:
101+ return "window_placement";
102+ default:
103+ return "????";
104+ }
105+ Q_UNREACHABLE();
106+}
107+
108+const char *mirLifecycleStateToStr(MirLifecycleState value)
109+{
110+ switch (value) {
111+ case mir_lifecycle_state_will_suspend:
112+ return "will_suspend";
113+ case mir_lifecycle_state_resumed:
114+ return "resumed";
115+ case mir_lifecycle_connection_lost:
116+ return "connection_lost";
117+ }
118+ Q_UNREACHABLE();
119+}
120
121=== modified file 'src/common/debughelpers.h'
122--- src/common/debughelpers.h 2017-01-18 21:24:15 +0000
123+++ src/common/debughelpers.h 2017-02-14 10:17:32 +0000
124@@ -1,5 +1,5 @@
125 /*
126- * Copyright (C) 2013-2015 Canonical, Ltd.
127+ * Copyright (C) 2013-2017 Canonical, Ltd.
128 *
129 * This program is free software: you can redistribute it and/or modify it under
130 * the terms of the GNU Lesser General Public License version 3, as published by
131@@ -28,11 +28,13 @@
132 QString touchEventToString(const QTouchEvent *ev);
133
134 QString mirSurfaceAttribAndValueToString(MirWindowAttrib attrib, int value);
135+const char* mirEventTypeToStr(MirEventType t);
136 const char *mirSurfaceTypeToStr(int value);
137 const char *mirSurfaceStateToStr(int value);
138 const char *mirSurfaceFocusStateToStr(int value);
139 const char *mirSurfaceVisibilityToStr(int value);
140 const char *mirMotionActionToStr(int value);
141+const char *mirLifecycleStateToStr(MirLifecycleState value);
142
143 const char *applicationStateToStr(int state);
144
145
146=== modified file 'src/common/windowcontrollerinterface.h'
147--- src/common/windowcontrollerinterface.h 2016-11-03 20:17:46 +0000
148+++ src/common/windowcontrollerinterface.h 2017-02-14 10:17:32 +0000
149@@ -1,5 +1,5 @@
150 /*
151- * Copyright (C) 2016 Canonical, Ltd.
152+ * Copyright (C) 2016-2017 Canonical, Ltd.
153 *
154 * This program is free software: you can redistribute it and/or modify it under
155 * the terms of the GNU Lesser General Public License version 3, as published by
156@@ -18,6 +18,7 @@
157 #define WINDOWCONTROLLERINTERFACE_H
158
159 #include <miral/window.h>
160+#include <miral/window_specification.h>
161
162 #include <mir_toolkit/event.h>
163
164
165=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
166--- src/modules/Unity/Application/CMakeLists.txt 2016-11-15 17:54:03 +0000
167+++ src/modules/Unity/Application/CMakeLists.txt 2017-02-14 10:17:32 +0000
168@@ -1,6 +1,7 @@
169 include_directories(
170 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
171 ${CMAKE_SOURCE_DIR}/src/common
172+ fake
173 )
174
175 include_directories(
176@@ -35,17 +36,23 @@
177 ../../../common/abstractdbusservicemonitor.cpp
178 ../../../common/debughelpers.cpp
179 dbusfocusinfo.cpp
180+ fake/faketaskcontroller.cpp
181+ fake/fakeapplicationinfo.cpp
182+ fake/fakemaliit.cpp
183+ fake/fakemirclient.cpp
184+ fake/fakeprompt.cpp
185 plugin.cpp
186 mirsurface.cpp
187 mirsurfaceinterface.h
188 mirsurfaceitem.cpp
189 mirsurfacelistmodel.cpp
190+ mirtestsingleton.cpp
191 mirbuffersgtexture.cpp
192 proc_info.cpp
193 session.cpp
194- sessionmanager.cpp
195 sharedwakelock.cpp
196 surfacemanager.cpp
197+ taskcontroller.cpp
198 upstart/applicationinfo.cpp
199 upstart/taskcontroller.cpp
200 timer.cpp
201@@ -66,6 +73,7 @@
202 applicationinfo.h
203 taskcontroller.h
204 settings_interface.h
205+ resources/surfaces.qrc
206 )
207
208 add_library(unityapplicationplugin SHARED
209
210=== modified file 'src/modules/Unity/Application/application.cpp'
211--- src/modules/Unity/Application/application.cpp 2017-02-02 09:36:07 +0000
212+++ src/modules/Unity/Application/application.cpp 2017-02-14 10:17:32 +0000
213@@ -392,7 +392,7 @@
214 if (m_processState == ProcessRunning) {
215 suspend();
216 } else {
217- // we can't suspend it since we have no information on the app process
218+ DEBUG_MSG << "() Can't suspend since we have no information on the app process";
219 Q_ASSERT(m_processState == ProcessUnknown);
220 }
221 break;
222
223=== modified file 'src/modules/Unity/Application/application.h'
224--- src/modules/Unity/Application/application.h 2016-11-15 17:54:03 +0000
225+++ src/modules/Unity/Application/application.h 2017-02-14 10:17:32 +0000
226@@ -184,7 +184,6 @@
227 ProxySurfaceListModel *m_proxyPromptSurfaceList;
228
229 friend class ApplicationManager;
230- friend class SessionManager;
231 friend class Session;
232 };
233
234
235=== modified file 'src/modules/Unity/Application/application_manager.cpp'
236--- src/modules/Unity/Application/application_manager.cpp 2017-02-02 09:36:07 +0000
237+++ src/modules/Unity/Application/application_manager.cpp 2017-02-14 10:17:32 +0000
238@@ -1,5 +1,5 @@
239 /*
240- * Copyright (C) 2013-2016 Canonical, Ltd.
241+ * Copyright (C) 2013-2017 Canonical, Ltd.
242 *
243 * This program is free software: you can redistribute it and/or modify it under
244 * the terms of the GNU Lesser General Public License version 3, as published by
245@@ -24,12 +24,12 @@
246 #include "sharedwakelock.h"
247 #include "proc_info.h"
248 #include "upstart/taskcontroller.h"
249+#include "faketaskcontroller.h"
250 #include "tracepoints.h" // generated from tracepoints.tp
251 #include "settings.h"
252
253 // mirserver
254 #include "nativeinterface.h"
255-#include "sessionauthorizer.h"
256 #include "logging.h"
257
258 //miral
259@@ -73,28 +73,6 @@
260 return appId;
261 }
262
263-void connectToSessionAuthorizer(ApplicationManager *manager, SessionAuthorizer *authorizer)
264-{
265- QObject::connect(authorizer, &SessionAuthorizer::requestAuthorizationForSession,
266- manager, &ApplicationManager::authorizeSession, Qt::BlockingQueuedConnection);
267-}
268-
269-void connectToTaskController(ApplicationManager *manager, TaskController *controller)
270-{
271- QObject::connect(controller, &TaskController::processStarting,
272- manager, &ApplicationManager::onProcessStarting);
273- QObject::connect(controller, &TaskController::processStopped,
274- manager, &ApplicationManager::onProcessStopped);
275- QObject::connect(controller, &TaskController::processSuspended,
276- manager, &ApplicationManager::onProcessSuspended);
277- QObject::connect(controller, &TaskController::processFailed,
278- manager, &ApplicationManager::onProcessFailed);
279- QObject::connect(controller, &TaskController::focusRequested,
280- manager, &ApplicationManager::onFocusRequested);
281- QObject::connect(controller, &TaskController::resumeRequested,
282- manager, &ApplicationManager::onResumeRequested);
283-}
284-
285 } // namespace
286
287 ApplicationManager* ApplicationManager::create()
288@@ -107,10 +85,17 @@
289 return nullptr;
290 }
291
292- SessionAuthorizer *sessionAuthorizer = static_cast<SessionAuthorizer*>(nativeInterface->nativeResourceForIntegration("SessionAuthorizer"));
293+ QSharedPointer<TaskController> taskController;
294+ QSharedPointer<ProcInfo> procInfo;
295+ if (qgetenv("UNITY_TESTING") == "1") {
296+ auto *fakeTaskController = new FakeTaskController();
297+ taskController.reset(fakeTaskController);
298+ procInfo = fakeTaskController->procInfo();
299+ } else {
300+ taskController.reset(new upstart::TaskController());
301+ procInfo.reset(new ProcInfo());
302+ }
303
304- QSharedPointer<TaskController> taskController(new upstart::TaskController());
305- QSharedPointer<ProcInfo> procInfo(new ProcInfo());
306 QSharedPointer<SharedWakelock> sharedWakelock(new SharedWakelock);
307 QSharedPointer<Settings> settings(new Settings());
308
309@@ -126,9 +111,6 @@
310 settings
311 );
312
313- connectToSessionAuthorizer(appManager, sessionAuthorizer);
314- connectToTaskController(appManager, taskController.data());
315-
316 // Emit signal to notify Upstart that Mir is ready to receive client connections
317 // see http://upstart.ubuntu.com/cookbook/#expect-stop
318 // FIXME: should not be qtmir's job, instead should notify the user of this library
319@@ -166,6 +148,23 @@
320 {
321 qCDebug(QTMIR_APPLICATIONS) << "ApplicationManager::ApplicationManager (this=%p)" << this;
322 setObjectName(QStringLiteral("qtmir::ApplicationManager"));
323+
324+ QObject::connect(m_taskController.data(), &TaskController::processStarting,
325+ this, &ApplicationManager::onProcessStarting);
326+ QObject::connect(m_taskController.data(), &TaskController::processStopped,
327+ this, &ApplicationManager::onProcessStopped);
328+ QObject::connect(m_taskController.data(), &TaskController::processSuspended,
329+ this, &ApplicationManager::onProcessSuspended);
330+ QObject::connect(m_taskController.data(), &TaskController::processFailed,
331+ this, &ApplicationManager::onProcessFailed);
332+ QObject::connect(m_taskController.data(), &TaskController::focusRequested,
333+ this, &ApplicationManager::onFocusRequested);
334+ QObject::connect(m_taskController.data(), &TaskController::resumeRequested,
335+ this, &ApplicationManager::onResumeRequested);
336+ QObject::connect(m_taskController.data(), &TaskController::authorizationRequestedForSession,
337+ this, &ApplicationManager::authorizeSession);
338+ QObject::connect(m_taskController.data(), &TaskController::sessionStarting,
339+ this, &ApplicationManager::onSessionStarting);
340 }
341
342 ApplicationManager::~ApplicationManager()
343@@ -580,14 +579,14 @@
344
345 auto qtmirSurface = static_cast<qtmir::MirSurfaceInterface*>(surface);
346
347- return findApplicationWithPid(miral::pid_of(qtmirSurface->session()->session()));
348+ return findApplicationWithPid(PidFetcher::pidOf(qtmirSurface->session()->session()));
349 }
350
351 Application* ApplicationManager::findApplicationWithSession(const std::shared_ptr<ms::Session> &session)
352 {
353 if (!session)
354 return nullptr;
355- return findApplicationWithPid(miral::pid_of(session));
356+ return findApplicationWithPid(PidFetcher::pidOf(session));
357 }
358
359 Application* ApplicationManager::findApplicationWithPid(const pid_t pid) const
360@@ -596,7 +595,7 @@
361 return nullptr;
362
363 for (Application *app : m_applications) {
364- if (app->m_pid == pid) {
365+ if (app->pid() == pid) {
366 return app;
367 }
368 }
369@@ -665,7 +664,6 @@
370 application->deleteLater();
371 });
372
373-
374 beginInsertRows(QModelIndex(), m_applications.count(), m_applications.count());
375 m_applications.append(application);
376 endInsertRows();
377@@ -785,4 +783,18 @@
378 return nullptr;
379 }
380
381+
382+void ApplicationManager::onSessionStarting(SessionInterface *qmlSession)
383+{
384+ Application* application = findApplicationWithSession(qmlSession->session());
385+ if (application && application->state() != Application::Running) {
386+ application->setSession(qmlSession);
387+ }
388+}
389+
390+SessionInterface *ApplicationManager::findSession(const mir::scene::Session* session) const
391+{
392+ return m_taskController->findSession(session);
393+}
394+
395 } // namespace qtmir
396
397=== modified file 'src/modules/Unity/Application/application_manager.h'
398--- src/modules/Unity/Application/application_manager.h 2017-02-02 09:36:07 +0000
399+++ src/modules/Unity/Application/application_manager.h 2017-02-14 10:17:32 +0000
400@@ -94,6 +94,8 @@
401 const QList<Application*> &list() const { return m_applications; }
402 qtmir::Application* findApplicationWithPid(const pid_t pid) const;
403
404+ SessionInterface *findSession(const mir::scene::Session* session) const;
405+
406 public Q_SLOTS:
407 void authorizeSession(const pid_t pid, bool &authorized);
408
409@@ -103,6 +105,7 @@
410 void onProcessFailed(const QString& appId, TaskController::Error error);
411 void onFocusRequested(const QString& appId);
412 void onResumeRequested(const QString& appId);
413+ void onSessionStarting(SessionInterface *session);
414
415 Q_SIGNALS:
416 void emptyChanged();
417@@ -138,7 +141,6 @@
418
419 friend class Application;
420 friend class DBusWindowStack;
421- friend class SessionManager;
422 };
423
424 } // namespace qtmir
425
426=== added directory 'src/modules/Unity/Application/fake'
427=== added file 'src/modules/Unity/Application/fake/fakeapplicationinfo.cpp'
428--- src/modules/Unity/Application/fake/fakeapplicationinfo.cpp 1970-01-01 00:00:00 +0000
429+++ src/modules/Unity/Application/fake/fakeapplicationinfo.cpp 2017-02-14 10:17:32 +0000
430@@ -0,0 +1,26 @@
431+/*
432+ * Copyright (C) 2016 Canonical, Ltd.
433+ *
434+ * This program is free software: you can redistribute it and/or modify it under
435+ * the terms of the GNU Lesser General Public License version 3, as published by
436+ * the Free Software Foundation.
437+ *
438+ * This program is distributed in the hope that it will be useful, but WITHOUT
439+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
440+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
441+ * Lesser General Public License for more details.
442+ *
443+ * You should have received a copy of the GNU Lesser General Public License
444+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
445+ *
446+ */
447+
448+#include "fakeapplicationinfo.h"
449+
450+using namespace qtmir;
451+
452+void FakeApplicationInfo::setIconId(const QString &iconId)
453+{
454+ m_icon = QString("qrc:///Unity/Application/icons/%1@18.png")
455+ .arg(iconId);
456+}
457
458=== added file 'src/modules/Unity/Application/fake/fakeapplicationinfo.h'
459--- src/modules/Unity/Application/fake/fakeapplicationinfo.h 1970-01-01 00:00:00 +0000
460+++ src/modules/Unity/Application/fake/fakeapplicationinfo.h 2017-02-14 10:17:32 +0000
461@@ -0,0 +1,93 @@
462+/*
463+ * Copyright (C) 2016 Canonical, Ltd.
464+ *
465+ * This program is free software: you can redistribute it and/or modify it under
466+ * the terms of the GNU Lesser General Public License version 3, as published by
467+ * the Free Software Foundation.
468+ *
469+ * This program is distributed in the hope that it will be useful, but WITHOUT
470+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
471+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
472+ * Lesser General Public License for more details.
473+ *
474+ * You should have received a copy of the GNU Lesser General Public License
475+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
476+ *
477+ */
478+
479+#ifndef QTMIR_FAKE_APPLICATION_INFO_H
480+#define QTMIR_FAKE_APPLICATION_INFO_H
481+
482+#include "applicationinfo.h"
483+
484+#include <mir_toolkit/common.h>
485+
486+namespace qtmir {
487+
488+class FakeApplicationInfo : public ApplicationInfo
489+{
490+ Q_OBJECT
491+public:
492+ FakeApplicationInfo(const QString &appId) { m_appId = appId; }
493+
494+ QString appId() const override { return m_appId; }
495+
496+ QString name() const override { return m_name; }
497+ void setName(const QString &name) { m_name = name; }
498+
499+ QString comment() const override { return m_comment; }
500+
501+ QUrl icon() const override { return m_icon; }
502+
503+ QString splashTitle() const override { return m_splashTitle; }
504+ QUrl splashImage() const override { return m_splashImage; }
505+ bool splashShowHeader() const override { return m_splashShowHeader; }
506+ QString splashColor() const override { return m_splashColor; }
507+ QString splashColorHeader() const override { return m_splashColorHeader; }
508+ QString splashColorFooter() const override { return m_splashColorFooter; }
509+ Qt::ScreenOrientations supportedOrientations() const override { return m_supportedOrientations; }
510+ bool rotatesWindowContents() const override { return m_rotatesWindowContents; }
511+
512+ void setIsTouchApp(bool value) { m_isTouchApp = value; }
513+ bool isTouchApp() const override { return m_isTouchApp; }
514+
515+ void setScreenshotId(const QString &screenshotId) { m_screenshotId = screenshotId; }
516+ QString screenshotId() const { return m_screenshotId; }
517+
518+ void setIconId(const QString &iconId);
519+ void setSupportedOrientations(Qt::ScreenOrientations orientations) { m_supportedOrientations = orientations; }
520+ void setRotatesWindowContents(bool value) { m_rotatesWindowContents = value; }
521+
522+ void setInitialWindowState(MirWindowState value) { m_initialWindowState = value; }
523+ MirWindowState initialWindowState() const { return m_initialWindowState; }
524+
525+ // Window type to be used when creating top-level windows for this application
526+ void setWindowType(MirWindowType value) { m_windowType = value; }
527+ MirWindowType windowType() const { return m_windowType; }
528+
529+private:
530+ QString m_appId;
531+ QString m_name;
532+ QString m_comment;
533+ QUrl m_icon;
534+ QString m_splashTitle;
535+ QUrl m_splashImage;
536+ bool m_splashShowHeader;
537+ QString m_splashColor;
538+ QString m_splashColorHeader;
539+ QString m_splashColorFooter;
540+ Qt::ScreenOrientations m_supportedOrientations{Qt::PortraitOrientation |
541+ Qt::LandscapeOrientation |
542+ Qt::InvertedPortraitOrientation |
543+ Qt::InvertedLandscapeOrientation};
544+ bool m_rotatesWindowContents{false};
545+ bool m_isTouchApp{true};
546+
547+ QString m_screenshotId;
548+ MirWindowState m_initialWindowState{mir_window_state_restored};
549+ MirWindowType m_windowType{mir_window_type_normal};
550+};
551+
552+} // namespace qtmir
553+
554+#endif // QTMIR_FAKE_APPLICATION_INFO_H
555
556=== added file 'src/modules/Unity/Application/fake/fakemaliit.cpp'
557--- src/modules/Unity/Application/fake/fakemaliit.cpp 1970-01-01 00:00:00 +0000
558+++ src/modules/Unity/Application/fake/fakemaliit.cpp 2017-02-14 10:17:32 +0000
559@@ -0,0 +1,76 @@
560+/*
561+ * Copyright (C) 2017 Canonical, Ltd.
562+ *
563+ * This program is free software: you can redistribute it and/or modify it under
564+ * the terms of the GNU Lesser General Public License version 3, as published by
565+ * the Free Software Foundation.
566+ *
567+ * This program is distributed in the hope that it will be useful, but WITHOUT
568+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
569+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
570+ * Lesser General Public License for more details.
571+ *
572+ * You should have received a copy of the GNU Lesser General Public License
573+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
574+ *
575+ */
576+
577+#include "fakemaliit.h"
578+
579+// Qt
580+#include <QPainter>
581+
582+// mir
583+#include <mir_toolkit/mir_buffer_stream.h>
584+
585+using namespace qtmir;
586+
587+/**************************************************************************************************
588+ * FakeMaliitLauncher
589+ *************************************************************************************************/
590+
591+FakeMaliitLauncher::FakeMaliitLauncher(QSharedPointer<FakeApplicationInfo> appInfo, pid_t pid, QObject *parent)
592+ : FakeMirClientLauncher(appInfo, pid, parent)
593+{
594+}
595+
596+MirClient *FakeMaliitLauncher::createClient()
597+{
598+ return new Maliit(m_appInfo, m_connection);
599+}
600+
601+/**************************************************************************************************
602+ * Maliit
603+ *************************************************************************************************/
604+
605+Maliit::Maliit(QSharedPointer<FakeApplicationInfo> appInfo, miral::toolkit::Connection conn)
606+ : MirClient(appInfo, conn)
607+{
608+}
609+
610+QSize Maliit::update(MirWindow *window)
611+{
612+ MirBufferStream* buffer_stream = mir_window_get_buffer_stream(window);
613+
614+ // TODO sometimes buffer_stream is nullptr
615+ // (Only observed when creating a lot of clients at once)
616+ if (!buffer_stream)
617+ return QSize();
618+
619+ MirGraphicsRegion region;
620+ mir_buffer_stream_get_graphics_region(buffer_stream, &region);
621+
622+ Q_ASSERT(!m_screenshot.isNull());
623+
624+ QImage windowImage((uchar*)region.vaddr, region.width, region.height, QImage::Format_RGB32);
625+ {
626+ QPainter painter(&windowImage);
627+
628+ painter.fillRect(0, 0, windowImage.width(), windowImage.height()*0.6, Qt::transparent);
629+ painter.drawImage(QRectF(0, windowImage.height()*0.6, windowImage.width(), windowImage.height()*0.4), m_screenshot);
630+ }
631+
632+ mir_buffer_stream_swap_buffers_sync(buffer_stream);
633+
634+ return windowImage.size();
635+}
636
637=== added file 'src/modules/Unity/Application/fake/fakemaliit.h'
638--- src/modules/Unity/Application/fake/fakemaliit.h 1970-01-01 00:00:00 +0000
639+++ src/modules/Unity/Application/fake/fakemaliit.h 2017-02-14 10:17:32 +0000
640@@ -0,0 +1,47 @@
641+/*
642+ * Copyright (C) 2017 Canonical, Ltd.
643+ *
644+ * This program is free software: you can redistribute it and/or modify it under
645+ * the terms of the GNU Lesser General Public License version 3, as published by
646+ * the Free Software Foundation.
647+ *
648+ * This program is distributed in the hope that it will be useful, but WITHOUT
649+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
650+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
651+ * Lesser General Public License for more details.
652+ *
653+ * You should have received a copy of the GNU Lesser General Public License
654+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
655+ *
656+ */
657+
658+#ifndef QTMIR_FAKE_MALIIT_H
659+#define QTMIR_FAKE_MALIIT_H
660+
661+#include "fakemirclient.h"
662+
663+namespace qtmir {
664+
665+class Maliit : public MirClient
666+{
667+ Q_OBJECT
668+
669+public:
670+ Maliit(QSharedPointer<FakeApplicationInfo> appInfo, miral::toolkit::Connection);
671+
672+private:
673+ QSize update(MirWindow *window) override;
674+};
675+
676+class FakeMaliitLauncher : public FakeMirClientLauncher
677+{
678+ Q_OBJECT
679+public:
680+ FakeMaliitLauncher(QSharedPointer<FakeApplicationInfo> appInfo, pid_t pid, QObject *parent = nullptr);
681+private:
682+ MirClient *createClient() override;
683+};
684+
685+} // namespace qtmir
686+
687+#endif // QTMIR_FAKE_MALIIT_H
688
689=== added file 'src/modules/Unity/Application/fake/fakemirclient.cpp'
690--- src/modules/Unity/Application/fake/fakemirclient.cpp 1970-01-01 00:00:00 +0000
691+++ src/modules/Unity/Application/fake/fakemirclient.cpp 2017-02-14 10:17:32 +0000
692@@ -0,0 +1,685 @@
693+/*
694+ * Copyright (C) 2017 Canonical, Ltd.
695+ *
696+ * This program is free software: you can redistribute it and/or modify it under
697+ * the terms of the GNU Lesser General Public License version 3, as published by
698+ * the Free Software Foundation.
699+ *
700+ * This program is distributed in the hope that it will be useful, but WITHOUT
701+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
702+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
703+ * Lesser General Public License for more details.
704+ *
705+ * You should have received a copy of the GNU Lesser General Public License
706+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
707+ *
708+ */
709+
710+#include "fakemirclient.h"
711+
712+// src/common
713+#include <debughelpers.h>
714+
715+#include <miral/toolkit/persistent_id.h>
716+#include <miral/toolkit/window_spec.h>
717+
718+// std
719+#include <atomic>
720+#include <chrono>
721+#include <mutex>
722+#include <thread>
723+
724+// Qt
725+#include <qpa/qplatformnativeinterface.h>
726+#include <QGuiApplication>
727+#include <QCoreApplication>
728+#include <QEventLoop>
729+#include <QPainter>
730+
731+// mir
732+#include <mir/main_loop.h>
733+#include <mir_toolkit/mir_buffer_stream.h>
734+
735+// qpa mirserver
736+#include "logging.h"
737+
738+#include <QDebug>
739+
740+using namespace qtmir;
741+using namespace miral::toolkit;
742+using namespace std::chrono_literals;
743+
744+#define DEBUG_MSG qCDebug(QTMIR_FAKECLIENTS).nospace() << "MirClient["<< m_appInfo->appId() <<"]::" << __func__
745+#define CRITICAL_MSG qCCritical(QTMIR_FAKECLIENTS).nospace() << "MirClient["<< m_appInfo->appId() <<"]::" << __func__
746+
747+class MirClientEvent : public QEvent
748+{
749+public:
750+ MirClientEvent(MirWindow* window, const MirEvent *event, QEvent::Type type)
751+ : QEvent(type), window(window), mirEvent(event) {
752+ mirEvent = mir_event_ref(event);
753+ }
754+ ~MirClientEvent()
755+ {
756+ mir_event_unref(mirEvent);
757+ }
758+
759+ MirWindow* window;
760+ const MirEvent *mirEvent;
761+};
762+
763+namespace {
764+void lifecycleEventCallback(MirConnection* /*connection*/, MirLifecycleState state, void* context)
765+{
766+ static_cast<MirClient*>(context)->lifecycleEvent(state);
767+}
768+
769+int mirClientEventType{0};
770+void windowEventHandler(MirWindow* window, MirEvent const* event, void* context)
771+{
772+ if (mirClientEventType == 0) {
773+ mirClientEventType = QEvent::registerEventType();
774+ }
775+ auto client = static_cast<MirClient*>(context);
776+ QEvent *qtEvent = new MirClientEvent(window, event, static_cast<QEvent::Type>(mirClientEventType));
777+ QCoreApplication::postEvent(client, qtEvent);
778+}
779+
780+} // anonymous namespace
781+
782+
783+/**************************************************************************************************
784+ * FakeMirClientLauncher
785+ *************************************************************************************************/
786+
787+FakeMirClientLauncher::FakeMirClientLauncher(QSharedPointer<FakeApplicationInfo> appInfo, pid_t pid, QObject *parent)
788+ : QThread(parent)
789+ , m_pid(pid)
790+ , m_appInfo(appInfo)
791+{
792+ connect(this, &QThread::finished, this, [this]{ m_fd = mir::Fd(); });
793+}
794+
795+void FakeMirClientLauncher::launch()
796+{
797+ auto nativeInterface = QGuiApplication::platformNativeInterface();
798+ m_server = static_cast<mir::Server*>(nativeInterface->nativeResourceForIntegration("MirServer"));
799+
800+ m_server->the_main_loop()->enqueue(this, [this] { launchFromMirServerThread(); });
801+}
802+
803+void FakeMirClientLauncher::launchFromMirServerThread()
804+{
805+ std::condition_variable condition;
806+
807+ m_fd = m_server->open_client_socket([this, &condition](std::shared_ptr<mir::frontend::Session> const& fSession)
808+ {
809+ std::lock_guard<decltype(m_mutex)> lock_guard{m_mutex};
810+ m_session = std::dynamic_pointer_cast<mir::scene::Session>(fSession);
811+ condition.notify_one();
812+ });
813+
814+ char connect_string[64] = {0};
815+ sprintf(connect_string, "fd://%d", m_fd.operator int());
816+
817+ m_connection = Connection{mir_connect_sync(connect_string, m_appInfo->appId().toLatin1().data())};
818+
819+ {
820+ std::unique_lock<decltype(m_mutex)> lock{m_mutex};
821+ condition.wait(lock, [&] { return !!m_session.lock(); });
822+ }
823+
824+ start();
825+}
826+
827+std::shared_ptr<mir::scene::Session> FakeMirClientLauncher::session()
828+{
829+ auto sharedPtr = m_session.lock();
830+ return sharedPtr;
831+}
832+
833+MirClient *FakeMirClientLauncher::createClient()
834+{
835+ return new MirClient(m_appInfo, m_connection);
836+}
837+
838+void FakeMirClientLauncher::run()
839+{
840+ {
841+ std::lock_guard<std::mutex> lock_guard{m_mutex};
842+
843+ m_client = createClient();
844+ connect(m_client, &MirClient::willSuspend, this, &FakeMirClientLauncher::willSuspend);
845+ connect(m_client, &MirClient::resumed, this, &FakeMirClientLauncher::resumed);
846+ connect(m_client, &MirClient::windowCreated, this, &FakeMirClientLauncher::addWindowAttributes);
847+ connect(m_client, &MirClient::windowAttributeChanged, this, &FakeMirClientLauncher::updateWindowAttribute);
848+ connect(m_client, &MirClient::windowClosed, this, &FakeMirClientLauncher::removeWindowAttributes);
849+ connect(m_client, &MirClient::touchPressed, this, &FakeMirClientLauncher::incrementTouchPressCount);
850+ connect(m_client, &MirClient::touchReleased, this, &FakeMirClientLauncher::incrementTouchReleaseCount);
851+ connect(m_client, &MirClient::mousePressed, this, &FakeMirClientLauncher::incrementMousePressCount);
852+ connect(m_client, &MirClient::mouseReleased, this, &FakeMirClientLauncher::incrementMouseReleaseCount);
853+
854+ if (!m_manualWindowCreation) {
855+ // wait a bit so that the splash screen has some time to shine
856+ msleep(350);
857+
858+ m_client->createWindow();
859+ }
860+
861+ // keep only one reference, in MirClient
862+ m_connection.reset();
863+ }
864+
865+
866+ qCDebug(QTMIR_FAKECLIENTS).nospace() << "FakeMirClientLauncher::run[" << m_appInfo->appId() << "] entered loop.";
867+ exec();
868+ qCDebug(QTMIR_FAKECLIENTS).nospace() << "FakeMirClientLauncher::run[" << m_appInfo->appId() << "] exited loop.";
869+
870+ {
871+ std::lock_guard<std::mutex> lock_guard{m_mutex};
872+
873+ delete m_client;
874+ m_client = nullptr;
875+ }
876+}
877+
878+void FakeMirClientLauncher::createWindow()
879+{
880+ std::lock_guard<std::mutex> lock_guard{m_mutex};
881+ QMetaObject::invokeMethod(m_client, "createWindow", Qt::AutoConnection);
882+}
883+
884+void FakeMirClientLauncher::killWindow(const QString &persistentId)
885+{
886+ std::lock_guard<std::mutex> lock_guard{m_mutex};
887+ QMetaObject::invokeMethod(m_client, "killWindow", Qt::AutoConnection, Q_ARG(QString, persistentId));
888+}
889+
890+void FakeMirClientLauncher::setWindowInputBounds(const QString &persistentId, QRect rect)
891+{
892+ std::lock_guard<std::mutex> lock_guard{m_mutex};
893+ QMetaObject::invokeMethod(m_client, "setWindowInputBounds", Qt::AutoConnection,
894+ Q_ARG(QString, persistentId),
895+ Q_ARG(QRect, rect));
896+}
897+
898+void FakeMirClientLauncher::setWindowMinimumWidth(const QString &persistentId, int value)
899+{
900+ std::lock_guard<std::mutex> lock_guard{m_mutex};
901+ QMetaObject::invokeMethod(m_client, "setWindowMinimumWidth", Qt::AutoConnection,
902+ Q_ARG(QString, persistentId), Q_ARG(int, value));
903+}
904+
905+void FakeMirClientLauncher::setWindowMaximumWidth(const QString &persistentId, int value)
906+{
907+ std::lock_guard<std::mutex> lock_guard{m_mutex};
908+ QMetaObject::invokeMethod(m_client, "setWindowMaximumWidth", Qt::AutoConnection,
909+ Q_ARG(QString, persistentId), Q_ARG(int, value));
910+}
911+
912+void FakeMirClientLauncher::setWindowMinimumHeight(const QString &persistentId, int value)
913+{
914+ std::lock_guard<std::mutex> lock_guard{m_mutex};
915+ QMetaObject::invokeMethod(m_client, "setWindowMinimumHeight", Qt::AutoConnection,
916+ Q_ARG(QString, persistentId), Q_ARG(int, value));
917+}
918+
919+void FakeMirClientLauncher::setWindowMaximumHeight(const QString &persistentId, int value)
920+{
921+ std::lock_guard<std::mutex> lock_guard{m_mutex};
922+ QMetaObject::invokeMethod(m_client, "setWindowMaximumHeight", Qt::AutoConnection,
923+ Q_ARG(QString, persistentId), Q_ARG(int, value));
924+}
925+
926+void FakeMirClientLauncher::setWindowWidthIncrement(const QString &persistentId, int value)
927+{
928+ std::lock_guard<std::mutex> lock_guard{m_mutex};
929+ QMetaObject::invokeMethod(m_client, "setWindowWidthIncrement", Qt::AutoConnection,
930+ Q_ARG(QString, persistentId), Q_ARG(int, value));
931+}
932+
933+void FakeMirClientLauncher::setWindowHeightIncrement(const QString &persistentId, int value)
934+{
935+ std::lock_guard<std::mutex> lock_guard{m_mutex};
936+ QMetaObject::invokeMethod(m_client, "setWindowHeightIncrement", Qt::AutoConnection,
937+ Q_ARG(QString, persistentId), Q_ARG(int, value));
938+}
939+
940+void FakeMirClientLauncher::kill()
941+{
942+ m_killed = true;
943+ quit();
944+}
945+
946+const WindowAttributes *FakeMirClientLauncher::windowAttributes(const QString &persistentId) const
947+{
948+ auto iterator = m_windowAttributes.find(persistentId);
949+ if (iterator == m_windowAttributes.end()) {
950+ return nullptr;
951+ } else {
952+ return &(iterator.value());
953+ }
954+}
955+
956+void FakeMirClientLauncher::addWindowAttributes(QString persistentId, WindowAttributes windowAttributes)
957+{
958+ Q_ASSERT(!m_windowAttributes.contains(persistentId));
959+ m_windowAttributes[persistentId] = windowAttributes;
960+}
961+
962+void FakeMirClientLauncher::updateWindowAttribute(QString persistentId, MirWindowAttrib attribute, int value)
963+{
964+ Q_ASSERT(m_windowAttributes.contains(persistentId));
965+
966+ WindowAttributes &windowAttribs = m_windowAttributes[persistentId];
967+
968+ switch (attribute) {
969+ case mir_window_attrib_focus:
970+ windowAttribs.focusState = static_cast<MirWindowFocusState>(value);
971+ break;
972+ case mir_window_attrib_visibility:
973+ windowAttribs.visibility = static_cast<MirWindowVisibility>(value);
974+ break;
975+ default:
976+ // NOOP
977+ break;
978+ };
979+}
980+
981+void FakeMirClientLauncher::incrementTouchPressCount(QString persistentId)
982+{
983+ Q_ASSERT(m_windowAttributes.contains(persistentId));
984+ WindowAttributes &windowAttribs = m_windowAttributes[persistentId];
985+ ++windowAttribs.touchPressCount;
986+}
987+
988+void FakeMirClientLauncher::incrementTouchReleaseCount(QString persistentId)
989+{
990+ Q_ASSERT(m_windowAttributes.contains(persistentId));
991+ WindowAttributes &windowAttribs = m_windowAttributes[persistentId];
992+ ++windowAttribs.touchReleaseCount;
993+}
994+
995+void FakeMirClientLauncher::incrementMousePressCount(QString persistentId)
996+{
997+ Q_ASSERT(m_windowAttributes.contains(persistentId));
998+ WindowAttributes &windowAttribs = m_windowAttributes[persistentId];
999+ ++windowAttribs.mousePressCount;
1000+}
1001+
1002+void FakeMirClientLauncher::incrementMouseReleaseCount(QString persistentId)
1003+{
1004+ Q_ASSERT(m_windowAttributes.contains(persistentId));
1005+ WindowAttributes &windowAttribs = m_windowAttributes[persistentId];
1006+ ++windowAttribs.mouseReleaseCount;
1007+}
1008+
1009+void FakeMirClientLauncher::removeWindowAttributes(QString persistentId)
1010+{
1011+ Q_ASSERT(m_windowAttributes.contains(persistentId));
1012+ m_windowAttributes.remove(persistentId);
1013+}
1014+
1015+/**************************************************************************************************
1016+ * MirClient
1017+ *************************************************************************************************/
1018+
1019+MirClient::MirClient(QSharedPointer<FakeApplicationInfo> appInfo, miral::toolkit::Connection connection)
1020+ : m_connection(connection)
1021+ , m_appInfo(appInfo)
1022+{
1023+ QString screenshotFileName;
1024+ if (m_appInfo->screenshotId().endsWith(".svg") || m_appInfo->screenshotId().endsWith(".png")) {
1025+ screenshotFileName = QString(":/Unity/Application/screenshots/%2")
1026+ .arg(m_appInfo->screenshotId());
1027+ } else {
1028+ screenshotFileName = QString(":/Unity/Application/screenshots/%2@12.png")
1029+ .arg(m_appInfo->screenshotId());
1030+ }
1031+
1032+ m_screenshot = QImage(screenshotFileName);
1033+
1034+ mir_connection_set_lifecycle_event_callback(connection, &lifecycleEventCallback, this);
1035+}
1036+
1037+QSize MirClient::update(MirWindow* window)
1038+{
1039+ MirBufferStream* buffer_stream = mir_window_get_buffer_stream(window);
1040+
1041+ // TODO sometimes buffer_stream is nullptr
1042+ // (Only observed when creating a lot of clients at once)
1043+ if (!buffer_stream)
1044+ return QSize();
1045+
1046+ MirGraphicsRegion region;
1047+ mir_buffer_stream_get_graphics_region(buffer_stream, &region);
1048+
1049+ QImage windowImage((uchar*)region.vaddr, region.width, region.height, QImage::Format_RGB32);
1050+ {
1051+ QPainter painter(&windowImage);
1052+ if (m_screenshot.isNull()) {
1053+ painter.fillRect(0, 0, windowImage.width(), windowImage.height(), Qt::magenta);
1054+ } else {
1055+ painter.drawImage(QRectF(0, 0, windowImage.width(), windowImage.height()), m_screenshot);
1056+ }
1057+ }
1058+
1059+ mir_buffer_stream_swap_buffers_sync(buffer_stream);
1060+
1061+ return windowImage.size();
1062+}
1063+
1064+void MirClient::resizeEvent(MirWindow* window, MirResizeEvent const *event)
1065+{
1066+ // TODO redraw until we get a frame with the size announced in this event
1067+
1068+ QSize targetSize(mir_resize_event_get_width(event), mir_resize_event_get_height(event));
1069+
1070+ QSize size = update(window);
1071+
1072+ int attempts = 1;
1073+ while (size != targetSize && attempts < 5) {
1074+ size = update(window);
1075+ ++attempts;
1076+ }
1077+}
1078+
1079+void MirClient::attributeChangeEvent(MirWindow *window, MirWindowEvent const *event)
1080+{
1081+ MirWindowAttrib attribute = mir_window_event_get_attribute(event);
1082+ int value = mir_window_event_get_attribute_value(event);
1083+
1084+ Q_EMIT windowAttributeChanged(idForWindow(window), attribute, value);
1085+}
1086+
1087+void MirClient::inputEvent(MirWindow* window, const MirInputEvent *event)
1088+{
1089+ switch (mir_input_event_get_type(event)) {
1090+ case mir_input_event_type_touch:
1091+ touchEvent(window, mir_input_event_get_touch_event(event));
1092+ break;
1093+ case mir_input_event_type_pointer:
1094+ pointerEvent(window, mir_input_event_get_pointer_event(event));
1095+ break;
1096+ default:
1097+ // don't care
1098+ break;
1099+ }
1100+}
1101+
1102+void MirClient::touchEvent(MirWindow* window, const MirTouchEvent *event)
1103+{
1104+
1105+ // Don't care about multi-touch
1106+ switch (mir_touch_event_action(event, 0)) {
1107+ case mir_touch_action_up:
1108+ Q_EMIT touchReleased(idForWindow(window));
1109+ break;
1110+ case mir_touch_action_down:
1111+ Q_EMIT touchPressed(idForWindow(window));
1112+ break;
1113+ default:
1114+ // don't care
1115+ break;
1116+ }
1117+}
1118+
1119+void MirClient::pointerEvent(MirWindow* window, const MirPointerEvent *event)
1120+{
1121+ bool isPressed = mir_pointer_event_button_state(event, mir_pointer_button_primary);
1122+
1123+ if (isPressed && !m_primaryButtonPressed) {
1124+ Q_EMIT mousePressed(idForWindow(window));
1125+ } else if (!isPressed && m_primaryButtonPressed) {
1126+ Q_EMIT mouseReleased(idForWindow(window));
1127+ }
1128+
1129+ m_primaryButtonPressed = isPressed;
1130+}
1131+
1132+void MirClient::windowEvent(MirWindow* window, MirEvent const* mirEvent)
1133+{
1134+ switch (mir_event_get_type(mirEvent))
1135+ {
1136+ case mir_event_type_input:
1137+ inputEvent(window, mir_event_get_input_event(mirEvent));
1138+ break;
1139+ case mir_event_type_resize:
1140+ resizeEvent(window, mir_event_get_resize_event(mirEvent));
1141+ break;
1142+ case mir_event_type_window:
1143+ attributeChangeEvent(window, mir_event_get_window_event(mirEvent));
1144+ break;
1145+ case mir_event_type_window_output:
1146+ break;
1147+ case mir_event_type_orientation:
1148+ break;
1149+ case mir_event_type_close_window:
1150+ closeWindow(window);
1151+ break;
1152+ default:
1153+ qDebug() << "MirClient::windowEvent unhandled event type: " << mirEventTypeToStr(mir_event_get_type(mirEvent));
1154+ }
1155+}
1156+
1157+void MirClient::customEvent(QEvent* event)
1158+{
1159+ auto mirClientEvent = static_cast<MirClientEvent*>(event);
1160+ windowEvent(mirClientEvent->window, mirClientEvent->mirEvent);
1161+}
1162+
1163+void MirClient::createWindow()
1164+{
1165+ ++m_windowCounter;
1166+
1167+ QString name = QString("%1 %2")
1168+ .arg(m_appInfo->name())
1169+ .arg(m_windowCounter);
1170+
1171+ MirWindowSpec *spec = mir_create_window_spec(m_connection);
1172+ mir_window_spec_set_type(spec, m_appInfo->windowType());
1173+ mir_window_spec_set_width(spec, 640);
1174+ mir_window_spec_set_height(spec, 480);
1175+ mir_window_spec_set_pixel_format(spec, mir_pixel_format_argb_8888);
1176+ mir_window_spec_set_buffer_usage(spec, mir_buffer_usage_software);
1177+ mir_window_spec_set_name(spec, name.toLatin1().data());
1178+ mir_window_spec_set_event_handler(spec, &windowEventHandler, this /*context*/);
1179+ mir_window_spec_set_state(spec, m_appInfo->initialWindowState());
1180+
1181+ miral::toolkit::Window window{ mir_create_window_sync(spec) };
1182+ m_windows.append(window);
1183+
1184+ mir_window_spec_release(spec);
1185+ spec = nullptr;
1186+
1187+ Q_EMIT windowCreated(idForWindow(window), WindowAttributes(window));
1188+
1189+ // draw the first frame
1190+ update(window);
1191+}
1192+
1193+void MirClient::killWindow(const QString &requestedId)
1194+{
1195+ for (int i = 0; i < m_windows.count(); ++i) {
1196+ MirWindow *window = m_windows[i];
1197+ if (idForWindow(window) == requestedId) {
1198+ window = nullptr;
1199+ m_windows.removeAt(i);
1200+ }
1201+ }
1202+}
1203+
1204+QString MirClient::idForWindow(MirWindow* window)
1205+{
1206+ if (m_surfaceIds.contains(window)) {
1207+ return m_surfaceIds[window];
1208+ } else {
1209+ QString id(PersistentId(window).c_str());
1210+ m_surfaceIds[window] = id;
1211+ return id;
1212+ }
1213+}
1214+
1215+void MirClient::closeWindow(MirWindow *mirWindow)
1216+{
1217+ DEBUG_MSG << "(" << mirWindow << ")";
1218+
1219+ bool found = false;
1220+ for (int i = 0; i < m_windows.count() && !found; ++i) {
1221+ if (m_windows[i] == mirWindow) {
1222+ found = true;
1223+ DEBUG_MSG << "(" << mirWindow << ") - window removed";
1224+ m_windows.remove(i);
1225+ }
1226+ }
1227+
1228+ if (m_windows.isEmpty()) {
1229+ DEBUG_MSG << "(" << mirWindow << ") - quitting";
1230+ QThread::currentThread()->quit();
1231+ }
1232+}
1233+
1234+MirWindow *MirClient::findWindow(const QString &requestedId)
1235+{
1236+ for (int i = 0; i < m_windows.count(); ++i) {
1237+ MirWindow *window = m_windows[i];
1238+
1239+ if (idForWindow(window) == requestedId) {
1240+ return window;
1241+ }
1242+ }
1243+
1244+ return nullptr;
1245+}
1246+
1247+void MirClient::setWindowInputBounds(const QString &persistentId, QRect rect)
1248+{
1249+ MirWindow *window = findWindow(persistentId);
1250+ if (!window) {
1251+ CRITICAL_MSG << "(" << persistentId << ", " << rect << ") - did not find window";
1252+ return;
1253+ }
1254+
1255+ DEBUG_MSG << "(" << persistentId << ", " << rect << ")";
1256+
1257+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1258+
1259+ {
1260+ MirRectangle mirRect;
1261+ mirRect.left = rect.x();
1262+ mirRect.top = rect.y();
1263+ mirRect.width = rect.width();
1264+ mirRect.height = rect.height();
1265+
1266+ mir_window_spec_set_input_shape(spec, &mirRect, 1 /*n_rects*/);
1267+ }
1268+
1269+ mir_window_apply_spec(window, spec);
1270+ mir_window_spec_release(spec);
1271+}
1272+
1273+void MirClient::setWindowMinimumWidth(const QString &persistentId, int value)
1274+{
1275+ MirWindow *window = findWindow(persistentId);
1276+ if (!window) {
1277+ CRITICAL_MSG << "(" << persistentId << ", " << value << ") - did not find window";
1278+ return;
1279+ }
1280+
1281+ DEBUG_MSG << "(" << persistentId << ", " << value << ")";
1282+
1283+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1284+ mir_window_spec_set_min_width(spec, value);
1285+ mir_window_apply_spec(window, spec);
1286+ mir_window_spec_release(spec);
1287+}
1288+
1289+void MirClient::setWindowMaximumWidth(const QString &persistentId, int value)
1290+{
1291+ MirWindow *window = findWindow(persistentId);
1292+ if (!window) {
1293+ CRITICAL_MSG << "(" << persistentId << ", " << value << ") - did not find window";
1294+ return;
1295+ }
1296+
1297+ DEBUG_MSG << "(" << persistentId << ", " << value << ")";
1298+
1299+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1300+ mir_window_spec_set_max_width(spec, value);
1301+ mir_window_apply_spec(window, spec);
1302+ mir_window_spec_release(spec);
1303+}
1304+
1305+void MirClient::setWindowMinimumHeight(const QString &persistentId, int value)
1306+{
1307+ MirWindow *window = findWindow(persistentId);
1308+ if (!window) {
1309+ CRITICAL_MSG << "(" << persistentId << ", " << value << ") - did not find window";
1310+ return;
1311+ }
1312+
1313+ DEBUG_MSG << "(" << persistentId << ", " << value << ")";
1314+
1315+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1316+ mir_window_spec_set_min_height(spec, value);
1317+ mir_window_apply_spec(window, spec);
1318+ mir_window_spec_release(spec);
1319+}
1320+
1321+void MirClient::setWindowMaximumHeight(const QString &persistentId, int value)
1322+{
1323+ MirWindow *window = findWindow(persistentId);
1324+ if (!window) {
1325+ CRITICAL_MSG << "(" << persistentId << ", " << value << ") - did not find window";
1326+ return;
1327+ }
1328+
1329+ DEBUG_MSG << "(" << persistentId << ", " << value << ")";
1330+
1331+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1332+ mir_window_spec_set_max_height(spec, value);
1333+ mir_window_apply_spec(window, spec);
1334+ mir_window_spec_release(spec);
1335+}
1336+
1337+void MirClient::setWindowWidthIncrement(const QString &persistentId, int value)
1338+{
1339+ MirWindow *window = findWindow(persistentId);
1340+ if (!window) {
1341+ CRITICAL_MSG << "(" << persistentId << ", " << value << ") - did not find window";
1342+ return;
1343+ }
1344+
1345+ DEBUG_MSG << "(" << persistentId << ", " << value << ")";
1346+
1347+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1348+ mir_window_spec_set_width_increment(spec, value);
1349+ mir_window_apply_spec(window, spec);
1350+ mir_window_spec_release(spec);
1351+}
1352+
1353+void MirClient::setWindowHeightIncrement(const QString &persistentId, int value)
1354+{
1355+ MirWindow *window = findWindow(persistentId);
1356+ if (!window) {
1357+ CRITICAL_MSG << "(" << persistentId << ", " << value << ") - did not find window";
1358+ return;
1359+ }
1360+
1361+ DEBUG_MSG << "(" << persistentId << ", " << value << ")";
1362+
1363+ MirWindowSpec* spec = mir_create_window_spec(m_connection);
1364+ mir_window_spec_set_height_increment(spec, value);
1365+ mir_window_apply_spec(window, spec);
1366+ mir_window_spec_release(spec);
1367+}
1368+
1369+void MirClient::lifecycleEvent(MirLifecycleState state)
1370+{
1371+ DEBUG_MSG << "(" << mirLifecycleStateToStr(state) << ")";
1372+ if (state == mir_lifecycle_state_will_suspend) {
1373+ Q_EMIT willSuspend();
1374+ } else if (state == mir_lifecycle_state_resumed) {
1375+ Q_EMIT resumed();
1376+ }
1377+}
1378
1379=== added file 'src/modules/Unity/Application/fake/fakemirclient.h'
1380--- src/modules/Unity/Application/fake/fakemirclient.h 1970-01-01 00:00:00 +0000
1381+++ src/modules/Unity/Application/fake/fakemirclient.h 2017-02-14 10:17:32 +0000
1382@@ -0,0 +1,177 @@
1383+/*
1384+ * Copyright (C) 2017 Canonical, Ltd.
1385+ *
1386+ * This program is free software: you can redistribute it and/or modify it under
1387+ * the terms of the GNU Lesser General Public License version 3, as published by
1388+ * the Free Software Foundation.
1389+ *
1390+ * This program is distributed in the hope that it will be useful, but WITHOUT
1391+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1392+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1393+ * Lesser General Public License for more details.
1394+ *
1395+ * You should have received a copy of the GNU Lesser General Public License
1396+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1397+ *
1398+ */
1399+
1400+#ifndef QTMIR_FAKE_MIR_CLIENT_H
1401+#define QTMIR_FAKE_MIR_CLIENT_H
1402+
1403+#include <QObject>
1404+#include <QSharedPointer>
1405+#include <QThread>
1406+
1407+#include <miral/toolkit/connection.h>
1408+#include <miral/toolkit/window.h>
1409+
1410+// Qt
1411+#include <QImage>
1412+#include <QMap>
1413+
1414+// mir
1415+#include <mir/fd.h>
1416+#include <mir/scene/session.h>
1417+#include <mir/server.h>
1418+#include <mir_toolkit/client_types.h>
1419+
1420+// std
1421+#include <condition_variable>
1422+
1423+// local
1424+#include "fakeapplicationinfo.h"
1425+#include "windowattributes.h"
1426+
1427+namespace qtmir {
1428+
1429+// lives in a separate thread
1430+class MirClient : public QObject
1431+{
1432+ Q_OBJECT
1433+
1434+public:
1435+ MirClient(QSharedPointer<FakeApplicationInfo> appInfo, miral::toolkit::Connection);
1436+
1437+ void customEvent(QEvent* event) override;
1438+
1439+Q_SIGNALS:
1440+ void willSuspend();
1441+ void resumed();
1442+
1443+ void windowCreated(QString persistentId, qtmir::WindowAttributes windowAttributes);
1444+ void windowAttributeChanged(QString persistentId, MirWindowAttrib attribute, int value);
1445+ void touchPressed(QString persistentId);
1446+ void touchReleased(QString persistentId);
1447+ void mousePressed(QString persistentId);
1448+ void mouseReleased(QString persistentId);
1449+ void windowClosed(QString persistentId);
1450+
1451+public Q_SLOTS:
1452+ void windowEvent(MirWindow* window, MirEvent const* event);
1453+ void createWindow();
1454+ void killWindow(const QString &persistentId);
1455+ void setWindowInputBounds(const QString &persistentId, QRect rect);
1456+ void setWindowMinimumWidth(const QString &persistentId, int value);
1457+ void setWindowMaximumWidth(const QString &persistentId, int value);
1458+ void setWindowMinimumHeight(const QString &persistentId, int value);
1459+ void setWindowMaximumHeight(const QString &persistentId, int value);
1460+ void setWindowWidthIncrement(const QString &persistentId, int value);
1461+ void setWindowHeightIncrement(const QString &persistentId, int value);
1462+
1463+ void lifecycleEvent(MirLifecycleState state);
1464+
1465+private:
1466+ virtual QSize update(MirWindow *window);
1467+ void closeWindow(MirWindow *window);
1468+ void resizeEvent(MirWindow *window, MirResizeEvent const*);
1469+ void attributeChangeEvent(MirWindow *window, MirWindowEvent const *event);
1470+ void inputEvent(MirWindow* window, const MirInputEvent *);
1471+ void touchEvent(MirWindow* window, const MirTouchEvent *);
1472+ void pointerEvent(MirWindow* window, const MirPointerEvent *);
1473+ MirWindow *findWindow(const QString &persistentId);
1474+ QString idForWindow(MirWindow*);
1475+
1476+ int m_windowCounter{0};
1477+ QMap<MirWindow*, QString> m_surfaceIds; // works around bug LP#1661704
1478+
1479+ bool m_primaryButtonPressed{false};
1480+
1481+protected:
1482+ miral::toolkit::Connection m_connection;
1483+ QVector<miral::toolkit::Window> m_windows;
1484+ QImage m_screenshot;
1485+ QSharedPointer<FakeApplicationInfo> m_appInfo;
1486+};
1487+
1488+// lives in Qt's main/gui thread
1489+class FakeMirClientLauncher : public QThread
1490+{
1491+ Q_OBJECT
1492+public:
1493+ FakeMirClientLauncher(QSharedPointer<FakeApplicationInfo> appInfo, pid_t pid, QObject *parent = nullptr);
1494+
1495+ void launch();
1496+
1497+ std::shared_ptr<mir::scene::Session> session();
1498+ pid_t pid() const { return m_pid; }
1499+ QSharedPointer<FakeApplicationInfo> appInfo() const { return m_appInfo; }
1500+
1501+ void createWindow();
1502+ void killWindow(const QString &persistentId);
1503+ void createPromptWindow();
1504+ void setWindowInputBounds(const QString &persistentId, QRect rect);
1505+ void setWindowMinimumWidth(const QString &persistentId, int value);
1506+ void setWindowMaximumWidth(const QString &persistentId, int value);
1507+ void setWindowMinimumHeight(const QString &persistentId, int value);
1508+ void setWindowMaximumHeight(const QString &persistentId, int value);
1509+ void setWindowWidthIncrement(const QString &persistentId, int value);
1510+ void setWindowHeightIncrement(const QString &persistentId, int value);
1511+ void setManualWindowCreation(bool value) { m_manualWindowCreation = value; }
1512+
1513+ const WindowAttributes *windowAttributes(const QString &persistentId) const;
1514+
1515+ void kill();
1516+ bool killed() const { return m_killed; }
1517+
1518+Q_SIGNALS:
1519+ void willSuspend();
1520+ void resumed();
1521+
1522+protected:
1523+ void run() override;
1524+
1525+private Q_SLOTS:
1526+ void addWindowAttributes(QString persistendId, WindowAttributes windowAttributes);
1527+ void updateWindowAttribute(QString persistentId, MirWindowAttrib attribute, int value);
1528+ void incrementTouchPressCount(QString persistentId);
1529+ void incrementTouchReleaseCount(QString persistentId);
1530+ void incrementMousePressCount(QString persistentId);
1531+ void incrementMouseReleaseCount(QString persistentId);
1532+ void removeWindowAttributes(QString persistendId);
1533+
1534+private:
1535+ // called from the fake mir client thread
1536+ virtual MirClient *createClient();
1537+
1538+ // Called from mir server thread
1539+ void launchFromMirServerThread();
1540+
1541+ mir::Server *m_server{nullptr};
1542+ std::weak_ptr<mir::scene::Session> m_session;
1543+ std::mutex mutable m_mutex;
1544+ mir::Fd m_fd; // gotta hold on to it otherwise connection will close
1545+ pid_t m_pid;
1546+ MirClient *m_client{nullptr};
1547+ bool m_manualWindowCreation{false};
1548+ bool m_killed{false};
1549+
1550+ // Key is persistent window id
1551+ QMap<QString, WindowAttributes> m_windowAttributes;
1552+protected:
1553+ QSharedPointer<FakeApplicationInfo> m_appInfo;
1554+ miral::toolkit::Connection m_connection;
1555+};
1556+
1557+} // namespace qtmir
1558+
1559+#endif // QTMIR_FAKE_MIR_CLIENT_H
1560
1561=== added file 'src/modules/Unity/Application/fake/fakeprompt.cpp'
1562--- src/modules/Unity/Application/fake/fakeprompt.cpp 1970-01-01 00:00:00 +0000
1563+++ src/modules/Unity/Application/fake/fakeprompt.cpp 2017-02-14 10:17:32 +0000
1564@@ -0,0 +1,320 @@
1565+/*
1566+ * Copyright (C) 2017 Canonical, Ltd.
1567+ *
1568+ * This program is free software: you can redistribute it and/or modify it under
1569+ * the terms of the GNU Lesser General Public License version 3, as published by
1570+ * the Free Software Foundation.
1571+ *
1572+ * This program is distributed in the hope that it will be useful, but WITHOUT
1573+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1574+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1575+ * Lesser General Public License for more details.
1576+ *
1577+ * You should have received a copy of the GNU Lesser General Public License
1578+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1579+ *
1580+ */
1581+
1582+#include "fakeprompt.h"
1583+
1584+// Qt
1585+#include <qpa/qplatformnativeinterface.h>
1586+#include <QDebug>
1587+#include <QGuiApplication>
1588+#include <QPainter>
1589+
1590+// mir
1591+#include <mir/main_loop.h>
1592+
1593+#include <unistd.h> // for getpid()
1594+
1595+// mir
1596+#include <mir_toolkit/mir_buffer_stream.h>
1597+#include <mir_toolkit/mir_prompt_session.h>
1598+
1599+// miral
1600+#include <miral/toolkit/window_spec.h>
1601+
1602+using namespace qtmir;
1603+using namespace miral::toolkit;
1604+
1605+namespace {
1606+
1607+class MirClientEvent : public QEvent
1608+{
1609+public:
1610+ MirClientEvent(MirWindow* window, const MirEvent *event, QEvent::Type type)
1611+ : QEvent(type), window(window), mirEvent(event) {
1612+ mirEvent = mir_event_ref(event);
1613+ }
1614+ ~MirClientEvent()
1615+ {
1616+ mir_event_unref(mirEvent);
1617+ }
1618+
1619+ MirWindow* window;
1620+ const MirEvent *mirEvent;
1621+};
1622+
1623+int mirClientEventType{0};
1624+void windowEventHandler(MirWindow* window, MirEvent const* event, void* context)
1625+{
1626+ if (mirClientEventType == 0) {
1627+ mirClientEventType = QEvent::registerEventType();
1628+ }
1629+ auto client = static_cast<QObject*>(context);
1630+ QEvent *qtEvent = new MirClientEvent(window, event, static_cast<QEvent::Type>(mirClientEventType));
1631+ QCoreApplication::postEvent(client, qtEvent);
1632+}
1633+
1634+void mirPromptSessionStateChangeCallback(MirPromptSession* /*promptSession*/, MirPromptSessionState /*state*/, void* /*context*/)
1635+{
1636+}
1637+
1638+void mirClientFdCallback(MirPromptSession* /*promptSession*/, size_t count, int const* fds, void* context)
1639+{
1640+ Q_UNUSED(count); // in case of release builds, as Q_ASSERT won't be compiled
1641+ Q_ASSERT(count == 1);
1642+
1643+ auto fakePromptHelper = static_cast<FakePromptHelper*>(context);
1644+ QMetaObject::invokeMethod(fakePromptHelper->launcher(), "notifyPromptProviderFdReady", Qt::AutoConnection, Q_ARG(int, fds[0]));
1645+}
1646+
1647+} // anonymous namespace
1648+
1649+/**************************************************************************************************
1650+ * FakePromptHelperLauncher
1651+ *************************************************************************************************/
1652+
1653+FakePromptHelperLauncher::FakePromptHelperLauncher(pid_t pid, QObject *parent)
1654+ : QThread(parent)
1655+ , m_pid(pid)
1656+{
1657+ m_targetApplicationPid = getpid(); // shell's own pid, since we're only running in-process mir clients
1658+ m_sessionName = QString("prompt-helper-%1").arg((qintptr)this);
1659+ connect(this, &QThread::finished, this, [this]{ m_fd = mir::Fd(); deleteLater(); });
1660+}
1661+
1662+void FakePromptHelperLauncher::launch()
1663+{
1664+ auto nativeInterface = QGuiApplication::platformNativeInterface();
1665+ m_server = static_cast<mir::Server*>(nativeInterface->nativeResourceForIntegration("MirServer"));
1666+
1667+ m_server->the_main_loop()->enqueue(this, [this] { launchFromMirServerThread(); });
1668+}
1669+
1670+void FakePromptHelperLauncher::launchFromMirServerThread()
1671+{
1672+ m_fd = m_server->open_prompt_socket();
1673+
1674+ char connectString[64] = {0};
1675+ sprintf(connectString, "fd://%d", m_fd.operator int());
1676+
1677+ m_connection = Connection{mir_connect_sync(connectString, m_sessionName.toLatin1().data())};
1678+
1679+ start();
1680+}
1681+
1682+void FakePromptHelperLauncher::run()
1683+{
1684+ FakePromptHelper *promptHelper;
1685+ {
1686+ std::lock_guard<std::mutex> lockGuard{m_mutex};
1687+
1688+ promptHelper = new FakePromptHelper(m_targetApplicationPid, m_connection, this);
1689+
1690+ // keep only one reference, in MirClient
1691+ m_connection.reset();
1692+ }
1693+
1694+ exec();
1695+
1696+ {
1697+ std::lock_guard<std::mutex> lockGuard{m_mutex};
1698+
1699+ delete promptHelper;
1700+ promptHelper = nullptr;
1701+ }
1702+}
1703+
1704+void FakePromptHelperLauncher::notifyPromptProviderFdReady(int fd)
1705+{
1706+ Q_EMIT promptProviderFdReady(fd);
1707+}
1708+
1709+void FakePromptHelperLauncher::setProvider(FakePromptProviderLauncher *providerLauncher)
1710+{
1711+ Q_ASSERT(m_providerLauncher == nullptr);
1712+
1713+ m_providerLauncher = providerLauncher;
1714+
1715+ connect(m_providerLauncher, &QThread::finished, this, [this]() {
1716+ m_providerLauncher->deleteLater();
1717+ m_providerLauncher = nullptr;
1718+ quit();
1719+ });
1720+}
1721+
1722+/**************************************************************************************************
1723+ * FakePromptHelper
1724+ *************************************************************************************************/
1725+
1726+FakePromptHelper::FakePromptHelper(pid_t targetApplicationPid, miral::toolkit::Connection connection, QObject *launcher)
1727+ : m_connection(connection)
1728+ , m_launcher(launcher)
1729+{
1730+ m_promptSession = mir_connection_create_prompt_session_sync(m_connection, targetApplicationPid,
1731+ &mirPromptSessionStateChangeCallback, this);
1732+
1733+ mir_prompt_session_new_fds_for_prompt_providers(m_promptSession, 1 /* no_of_fds */, &mirClientFdCallback, this);
1734+}
1735+
1736+FakePromptHelper::~FakePromptHelper()
1737+{
1738+ mir_prompt_session_release_sync(m_promptSession);
1739+}
1740+
1741+/**************************************************************************************************
1742+ * FakePromptProviderLauncher
1743+ *************************************************************************************************/
1744+
1745+FakePromptProviderLauncher::FakePromptProviderLauncher(pid_t pid, int fd, QObject *parent)
1746+ : QThread(parent)
1747+ , m_fd(fd)
1748+ , m_pid(pid)
1749+{
1750+ m_sessionName = QString("prompt-provider-%1").arg((qintptr)this);
1751+ connect(this, &QThread::finished, this, [this]{ m_fd = mir::Fd(); });
1752+}
1753+
1754+void FakePromptProviderLauncher::launch()
1755+{
1756+ start();
1757+}
1758+
1759+void FakePromptProviderLauncher::run()
1760+{
1761+ {
1762+ char connectString[64] = {0};
1763+ sprintf(connectString, "fd://%d", m_fd.operator int());
1764+ m_connection = Connection{mir_connect_sync(connectString, m_sessionName.toLatin1().data())};
1765+ }
1766+
1767+ {
1768+ std::lock_guard<std::mutex> lockGuard{m_mutex};
1769+
1770+ m_promptProvider = new FakePromptProvider(m_connection);
1771+
1772+ // keep only one reference, in MirClient
1773+ m_connection.reset();
1774+ }
1775+
1776+ exec();
1777+
1778+ {
1779+ std::lock_guard<std::mutex> lockGuard{m_mutex};
1780+
1781+ delete m_promptProvider;
1782+ m_promptProvider = nullptr;
1783+ }
1784+}
1785+
1786+/**************************************************************************************************
1787+ * FakePromptProvider
1788+ *************************************************************************************************/
1789+
1790+FakePromptProvider::FakePromptProvider(miral::toolkit::Connection connection)
1791+ : m_connection(connection)
1792+{
1793+ createWindow();
1794+ update(m_windows[0]);
1795+}
1796+
1797+void FakePromptProvider::update(MirWindow* window)
1798+{
1799+ MirBufferStream* buffer_stream = mir_window_get_buffer_stream(window);
1800+
1801+ // TODO sometimes buffer_stream is nullptr
1802+ // (Only observed when creating a lot of clients at once)
1803+ if (!buffer_stream)
1804+ return;
1805+
1806+ MirGraphicsRegion region;
1807+ mir_buffer_stream_get_graphics_region(buffer_stream, &region);
1808+
1809+ QImage windowImage((uchar*)region.vaddr, region.width, region.height, QImage::Format_RGB32);
1810+ {
1811+ QRect windowRect(0, 0, windowImage.width(), windowImage.height());
1812+
1813+ QPainter painter(&windowImage);
1814+ painter.setBrush(QBrush(QColor(183,25,118)));
1815+ painter.drawRect(windowRect);
1816+
1817+ painter.setPen(QPen(QColor(0,0,0)));
1818+ painter.drawText(windowRect, Qt::AlignCenter, QString("Prompt\nWindow"));
1819+ }
1820+
1821+ mir_buffer_stream_swap_buffers_sync(buffer_stream);
1822+}
1823+
1824+void FakePromptProvider::windowEvent(MirWindow* window, MirEvent const* mirEvent)
1825+{
1826+ switch (mir_event_get_type(mirEvent))
1827+ {
1828+ case mir_event_type_input:
1829+ break;
1830+ case mir_event_type_resize:
1831+ update(window);
1832+ break;
1833+ case mir_event_type_window:
1834+ break;
1835+ case mir_event_type_window_output:
1836+ break;
1837+ case mir_event_type_orientation:
1838+ break;
1839+ case mir_event_type_close_window:
1840+ closeWindow(window);
1841+ break;
1842+ default:
1843+ qDebug() << "FakePromptProvider::windowEvent unhandled event type: %d" << static_cast<int>(mir_event_get_type(mirEvent));
1844+ }
1845+}
1846+
1847+void FakePromptProvider::customEvent(QEvent* event)
1848+{
1849+ auto mirClientEvent = static_cast<MirClientEvent*>(event);
1850+ windowEvent(mirClientEvent->window, mirClientEvent->mirEvent);
1851+}
1852+
1853+void FakePromptProvider::createWindow()
1854+{
1855+ ++m_windowCounter;
1856+
1857+ QString name = QString("%1 %2")
1858+ .arg("prompt provider")
1859+ .arg(m_windowCounter);
1860+
1861+ auto const spec = WindowSpec::for_normal_surface(
1862+ m_connection, 640, 480, mir_pixel_format_xrgb_8888)
1863+ .set_buffer_usage(mir_buffer_usage_software)
1864+ .set_type(mir_window_type_normal)
1865+ .set_name(name.toLatin1().data())
1866+ .set_event_handler(&windowEventHandler, this /*context*/);
1867+
1868+ m_windows.append(spec.create_surface());
1869+}
1870+
1871+void FakePromptProvider::closeWindow(MirWindow *mirWindow)
1872+{
1873+ bool found = false;
1874+ for (int i = 0; i < m_windows.count() && !found; ++i) {
1875+ if (m_windows[i] == mirWindow) {
1876+ found = true;
1877+ m_windows.remove(i);
1878+ }
1879+ }
1880+
1881+ if (m_windows.isEmpty()) {
1882+ QThread::currentThread()->quit();
1883+ }
1884+}
1885
1886=== added file 'src/modules/Unity/Application/fake/fakeprompt.h'
1887--- src/modules/Unity/Application/fake/fakeprompt.h 1970-01-01 00:00:00 +0000
1888+++ src/modules/Unity/Application/fake/fakeprompt.h 2017-02-14 10:17:32 +0000
1889@@ -0,0 +1,155 @@
1890+/*
1891+ * Copyright (C) 2017 Canonical, Ltd.
1892+ *
1893+ * This program is free software: you can redistribute it and/or modify it under
1894+ * the terms of the GNU Lesser General Public License version 3, as published by
1895+ * the Free Software Foundation.
1896+ *
1897+ * This program is distributed in the hope that it will be useful, but WITHOUT
1898+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1899+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1900+ * Lesser General Public License for more details.
1901+ *
1902+ * You should have received a copy of the GNU Lesser General Public License
1903+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1904+ *
1905+ */
1906+
1907+#ifndef QTMIR_FAKE_PROMPT_H
1908+#define QTMIR_FAKE_PROMPT_H
1909+
1910+#include <QObject>
1911+#include <QSharedPointer>
1912+#include <QThread>
1913+
1914+#include <miral/toolkit/connection.h>
1915+#include <miral/toolkit/window.h>
1916+
1917+// Qt
1918+#include <QImage>
1919+
1920+// mir
1921+#include <mir/fd.h>
1922+#include <mir/scene/session.h>
1923+#include <mir/server.h>
1924+#include <mir_toolkit/client_types.h>
1925+
1926+// std
1927+#include <condition_variable>
1928+
1929+// local
1930+#include "fakeapplicationinfo.h"
1931+
1932+namespace qtmir {
1933+
1934+// lives in a separate thread
1935+class FakePromptProvider : public QObject
1936+{
1937+ Q_OBJECT
1938+
1939+public:
1940+ FakePromptProvider(miral::toolkit::Connection);
1941+
1942+ void customEvent(QEvent* event) override;
1943+
1944+public Q_SLOTS:
1945+ void windowEvent(MirWindow* window, MirEvent const* event);
1946+ void createWindow();
1947+
1948+private Q_SLOTS:
1949+ void update(MirWindow *window);
1950+
1951+private:
1952+ void closeWindow(MirWindow *window);
1953+
1954+ miral::toolkit::Connection m_connection;
1955+ QVector<miral::toolkit::Window> m_windows;
1956+
1957+ int m_windowCounter{0};
1958+};
1959+
1960+class FakePromptProviderLauncher : public QThread
1961+{
1962+ Q_OBJECT
1963+
1964+public:
1965+ FakePromptProviderLauncher(pid_t pid, int fd, QObject *parent = nullptr);
1966+
1967+ pid_t pid() const { return m_pid; }
1968+ QString sessionName() const { return m_sessionName; }
1969+ void launch();
1970+
1971+protected:
1972+ void run() override;
1973+
1974+private:
1975+ std::mutex mutable m_mutex;
1976+ miral::toolkit::Connection m_connection;
1977+ mir::Fd m_fd; // gotta hold on to it otherwise connection will close
1978+ pid_t m_pid;
1979+
1980+ FakePromptProvider *m_promptProvider{nullptr};
1981+ QString m_sessionName;
1982+};
1983+
1984+// lives in a separate thread
1985+class FakePromptHelper : public QObject
1986+{
1987+ Q_OBJECT
1988+
1989+public:
1990+ FakePromptHelper(pid_t targetApplicationPid, miral::toolkit::Connection, QObject *launcher);
1991+ virtual ~FakePromptHelper();
1992+
1993+ QObject *launcher() const { return m_launcher; }
1994+
1995+private:
1996+ miral::toolkit::Connection m_connection;
1997+ MirPromptSession *m_promptSession{nullptr};
1998+ QObject *m_launcher;
1999+};
2000+
2001+// lives in Qt's main/gui thread
2002+class FakePromptHelperLauncher : public QThread
2003+{
2004+ Q_OBJECT
2005+public:
2006+ FakePromptHelperLauncher(pid_t pid, QObject *parent = nullptr);
2007+
2008+ void launch();
2009+
2010+ pid_t pid() const { return m_pid; }
2011+
2012+ QString sessionName() const { return m_sessionName; }
2013+
2014+ void setProvider(FakePromptProviderLauncher *providerLauncher);
2015+ FakePromptProviderLauncher *provider() const { return m_providerLauncher; }
2016+
2017+public Q_SLOTS:
2018+ void notifyPromptProviderFdReady(int fd);
2019+
2020+Q_SIGNALS:
2021+ void promptProviderFdReady(int fd);
2022+
2023+protected:
2024+ void run() override;
2025+
2026+private:
2027+ // Called from mir server thread
2028+ void launchFromMirServerThread();
2029+
2030+ mir::Server *m_server{nullptr};
2031+ std::mutex mutable m_mutex;
2032+ miral::toolkit::Connection m_connection;
2033+ mir::Fd m_fd; // gotta hold on to it otherwise connection will close
2034+ pid_t m_pid;
2035+ QString m_sessionName;
2036+
2037+ FakePromptProviderLauncher *m_providerLauncher{nullptr};
2038+
2039+ pid_t m_targetApplicationPid;
2040+};
2041+
2042+} // namespace qtmir
2043+
2044+#endif // QTMIR_FAKE_PROMPT_H
2045
2046=== added file 'src/modules/Unity/Application/fake/faketaskcontroller.cpp'
2047--- src/modules/Unity/Application/fake/faketaskcontroller.cpp 1970-01-01 00:00:00 +0000
2048+++ src/modules/Unity/Application/fake/faketaskcontroller.cpp 2017-02-14 10:17:32 +0000
2049@@ -0,0 +1,683 @@
2050+/*
2051+ * Copyright (C) 2017 Canonical, Ltd.
2052+ *
2053+ * This program is free software: you can redistribute it and/or modify it under
2054+ * the terms of the GNU Lesser General Public License version 3, as published by
2055+ * the Free Software Foundation.
2056+ *
2057+ * This program is distributed in the hope that it will be useful, but WITHOUT
2058+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2059+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2060+ * Lesser General Public License for more details.
2061+ *
2062+ * You should have received a copy of the GNU Lesser General Public License
2063+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2064+ *
2065+ */
2066+
2067+// local
2068+#include "faketaskcontroller.h"
2069+#include "fakemaliit.h"
2070+#include "fakemirclient.h"
2071+#include "fakeprompt.h"
2072+#include "session.h"
2073+#include "mirsurface.h"
2074+#include "windowattributes.h"
2075+
2076+// mirserver
2077+#include <sessionauthorizer.h>
2078+
2079+// qt
2080+#include <QGuiApplication>
2081+#include <QTimer>
2082+#include <qpa/qplatformnativeinterface.h>
2083+
2084+// QPA mirserver
2085+#include <logging.h>
2086+#include <promptsession.h>
2087+
2088+#include <QDebug>
2089+#include <QMutexLocker>
2090+
2091+#define DEBUG_MSG qCDebug(QTMIR_SESSIONS).nospace() << "FakeTaskController::" << __func__
2092+#define WARNING_MSG qCWarning(QTMIR_SESSIONS).nospace() << "FakeTaskController::" << __func__
2093+
2094+using namespace qtmir;
2095+namespace unityapi = unity::shell::application;
2096+
2097+
2098+FakeTaskController *FakeTaskController::m_instance = nullptr;
2099+
2100+FakeTaskController::FakeTaskController()
2101+ : TaskController()
2102+ , m_maliitAppInfo(new FakeApplicationInfo("maliit-server"))
2103+ , m_procInfo(new FakeProcInfo)
2104+{
2105+ Q_ASSERT(m_instance == nullptr);
2106+ m_instance = this;
2107+
2108+ createAvailableApplications();
2109+
2110+ m_maliitAppInfo->setName("Maliit IM Server");
2111+ m_maliitAppInfo->setScreenshotId("vkb_portrait.png");
2112+ m_maliitAppInfo->setWindowType(mir_window_type_inputmethod);
2113+ m_maliitAppInfo->setInitialWindowState(mir_window_state_hidden);
2114+
2115+ m_procInfo->taskController = this;
2116+
2117+ qRegisterMetaType<qtmir::WindowAttributes>();
2118+}
2119+
2120+FakeTaskController::~FakeTaskController()
2121+{
2122+ Q_ASSERT(m_instance != nullptr);
2123+ m_instance = nullptr;
2124+}
2125+
2126+void FakeTaskController::createAvailableApplications()
2127+{
2128+ FakeApplicationInfo *application;
2129+
2130+ application = new FakeApplicationInfo("unity8-dash");
2131+ application->setName("Unity 8 Mock Dash");
2132+ application->setScreenshotId("unity8-dash");
2133+ application->setIconId("dash");
2134+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2135+
2136+ application = new FakeApplicationInfo("dialer-app");
2137+ application->setName("Dialer");
2138+ application->setScreenshotId("dialer");
2139+ application->setIconId("dialer-app");
2140+ application->setSupportedOrientations(Qt::PortraitOrientation
2141+ | Qt::InvertedPortraitOrientation);
2142+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2143+
2144+ application = new FakeApplicationInfo("camera-app");
2145+ application->setName("Camera");
2146+ application->setScreenshotId("camera");
2147+ application->setIconId("camera");
2148+ application->setInitialWindowState(mir_window_state_fullscreen);
2149+ application->setSupportedOrientations(Qt::PortraitOrientation
2150+ | Qt::LandscapeOrientation
2151+ | Qt::InvertedPortraitOrientation
2152+ | Qt::InvertedLandscapeOrientation);
2153+ application->setRotatesWindowContents(true);
2154+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2155+
2156+ application = new FakeApplicationInfo("camera-app2");
2157+ application->setName("Camera2");
2158+ application->setScreenshotId("camera");
2159+ application->setIconId("camera");
2160+ application->setSupportedOrientations(Qt::PortraitOrientation
2161+ | Qt::LandscapeOrientation
2162+ | Qt::InvertedPortraitOrientation
2163+ | Qt::InvertedLandscapeOrientation);
2164+ application->setRotatesWindowContents(true);
2165+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2166+
2167+ application = new FakeApplicationInfo("gallery-app");
2168+ application->setName("Gallery");
2169+ application->setScreenshotId("gallery");
2170+ application->setIconId("gallery");
2171+ //application->setShellChrome(Mir::LowChrome);
2172+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2173+
2174+ application = new FakeApplicationInfo("facebook-webapp");
2175+ application->setName("Facebook");
2176+ application->setScreenshotId("facebook");
2177+ application->setIconId("facebook");
2178+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2179+
2180+ application = new FakeApplicationInfo("webbrowser-app");
2181+ //application->setShellChrome(Mir::LowChrome);
2182+ application->setName("Browser");
2183+ application->setScreenshotId("browser");
2184+ application->setIconId("browser");
2185+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2186+
2187+ application = new FakeApplicationInfo("twitter-webapp");
2188+ application->setName("Twitter");
2189+ application->setScreenshotId("twitter");
2190+ application->setIconId("twitter");
2191+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2192+
2193+ application = new FakeApplicationInfo("map");
2194+ application->setName("Map");
2195+ application->setIconId("map");
2196+ application->setScreenshotId("map");
2197+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2198+
2199+ application = new FakeApplicationInfo("gmail-webapp");
2200+ application->setName("GMail");
2201+ application->setIconId("gmail");
2202+ application->setScreenshotId("gmail-webapp.svg");
2203+ application->setSupportedOrientations(Qt::PortraitOrientation
2204+ | Qt::LandscapeOrientation
2205+ | Qt::InvertedPortraitOrientation
2206+ | Qt::InvertedLandscapeOrientation);
2207+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2208+
2209+ application = new FakeApplicationInfo("music-app");
2210+ application->setName("Music");
2211+ application->setIconId("soundcloud");
2212+ application->setScreenshotId("music");
2213+ application->setSupportedOrientations(Qt::PortraitOrientation
2214+ | Qt::LandscapeOrientation
2215+ | Qt::InvertedPortraitOrientation
2216+ | Qt::InvertedLandscapeOrientation);
2217+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2218+
2219+ application = new FakeApplicationInfo("ubuntu-weather-app");
2220+ application->setName("Weather");
2221+ application->setIconId("weather");
2222+ application->setScreenshotId("ubuntu-weather-app.svg");
2223+ application->setSupportedOrientations(Qt::LandscapeOrientation
2224+ | Qt::InvertedLandscapeOrientation);
2225+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2226+
2227+ application = new FakeApplicationInfo("notes-app");
2228+ application->setName("Notepad");
2229+ application->setIconId("notepad");
2230+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2231+
2232+ application = new FakeApplicationInfo("calendar-app");
2233+ application->setName("Calendar");
2234+ application->setIconId("calendar");
2235+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2236+
2237+ application = new FakeApplicationInfo("evernote");
2238+ application->setName("Evernote");
2239+ application->setIconId("evernote");
2240+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2241+
2242+ application = new FakeApplicationInfo("pinterest");
2243+ application->setName("Pinterest");
2244+ application->setIconId("pinterest");
2245+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2246+
2247+ application = new FakeApplicationInfo("soundcloud");
2248+ application->setName("SoundCloud");
2249+ application->setIconId("soundcloud");
2250+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2251+
2252+ application = new FakeApplicationInfo("wikipedia");
2253+ application->setName("Wikipedia");
2254+ application->setIconId("wikipedia");
2255+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2256+
2257+ application = new FakeApplicationInfo("youtube");
2258+ application->setName("YouTube");
2259+ application->setIconId("youtube");
2260+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2261+
2262+ application = new FakeApplicationInfo("libreoffice");
2263+ application->setName("LibreOffice");
2264+ application->setIconId("libreoffice");
2265+ application->setScreenshotId("libreoffice");
2266+ application->setIsTouchApp(false);
2267+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2268+
2269+ application = new FakeApplicationInfo("primary-oriented-app");
2270+ application->setName("Primary Oriented");
2271+ application->setSupportedOrientations(Qt::PrimaryOrientation);
2272+ m_availableApplications.append(QSharedPointer<FakeApplicationInfo>(application));
2273+}
2274+
2275+bool FakeTaskController::appIdHasProcessId(const QString& appId, pid_t pid)
2276+{
2277+ if (m_runningClients.contains(appId)) {
2278+ return m_runningClients[appId]->pid() == pid;
2279+ } else {
2280+ return false;
2281+ }
2282+}
2283+
2284+bool FakeTaskController::stop(const QString& appId)
2285+{
2286+ QMutexLocker locker(&m_mutex);
2287+
2288+ DEBUG_MSG << "(" << appId << ")";
2289+
2290+ if (m_runningClients.contains(appId)) {
2291+ m_runningClients[appId]->quit();
2292+ return true;
2293+ } else {
2294+ return false;
2295+ }
2296+}
2297+
2298+bool FakeTaskController::start(const QString& appId, const QStringList& arguments)
2299+{
2300+ Q_UNUSED(arguments);
2301+ QMutexLocker locker(&m_mutex);
2302+
2303+ auto appInfo = qSharedPointerCast<FakeApplicationInfo>(getInfoForApp(appId));
2304+ if (!appInfo) {
2305+ return false;
2306+ }
2307+
2308+ auto *fakeMirClientLauncher = new FakeMirClientLauncher(appInfo, generatePid());
2309+ m_runningClients[appId] = fakeMirClientLauncher;
2310+
2311+ fakeMirClientLauncher->setManualWindowCreation(m_manualSurfaceCreation);
2312+
2313+ connect(fakeMirClientLauncher, &QThread::finished, this, [this, fakeMirClientLauncher]() {
2314+ QString appId = fakeMirClientLauncher->appInfo()->appId();
2315+ Q_ASSERT(m_runningClients.contains(appId));
2316+ m_runningClients.remove(appId);
2317+ if (fakeMirClientLauncher->killed()) {
2318+ Q_EMIT processFailed(appId, Error::APPLICATION_CRASHED);
2319+ }
2320+ Q_EMIT processStopped(appId);
2321+ delete fakeMirClientLauncher;
2322+ });
2323+
2324+ m_appIdLatestLaunch = appId;
2325+ fakeMirClientLauncher->launch();
2326+
2327+ // Do it separately, on the next event loop iteration, with a clean call stack.
2328+ QMetaObject::invokeMethod(this, "processStarting", Qt::QueuedConnection, Q_ARG(QString, appId));
2329+
2330+ return true;
2331+}
2332+
2333+bool FakeTaskController::suspend(const QString& appId)
2334+{
2335+ QTimer::singleShot(300, [this, appId] { Q_EMIT processSuspended(appId); });
2336+ return true;
2337+}
2338+
2339+bool FakeTaskController::resume(const QString& appId)
2340+{
2341+ Q_UNUSED(appId);
2342+ return true;
2343+}
2344+
2345+QSharedPointer<ApplicationInfo> FakeTaskController::getInfoForApp(const QString &appId) const
2346+{
2347+ for (int i = 0; i < m_availableApplications.count(); ++i) {
2348+ auto appInfo = m_availableApplications[i];
2349+ if (appInfo->appId() == appId) {
2350+ return appInfo;
2351+ }
2352+ }
2353+
2354+ return QSharedPointer<ApplicationInfo>();
2355+}
2356+
2357+QStringList FakeTaskController::availableApplications()
2358+{
2359+ QStringList result;
2360+ for (int i = 0; i < m_availableApplications.count(); ++i) {
2361+ result << m_availableApplications[i]->appId();
2362+ }
2363+ return result;
2364+}
2365+
2366+pid_t FakeTaskController::pidOf_impl(const miral::Application &application)
2367+{
2368+ QMutexLocker locker(&m_mutex);
2369+
2370+ const std::shared_ptr<mir::scene::Session> &session = application;
2371+
2372+ {
2373+ auto clientList = m_runningClients.values();
2374+ for (int i = 0; i < clientList.count(); ++i) {
2375+ auto *client = clientList[i];
2376+ if (client->session() == session) {
2377+ return client->pid();
2378+ }
2379+ }
2380+ }
2381+ {
2382+ auto promptList = m_runningPromptHelpers.values();
2383+ for (int i = 0; i < promptList.count(); ++i) {
2384+ auto *promptHelper = promptList[i];
2385+ if (promptHelper->sessionName() == session->name().c_str()) {
2386+ return promptHelper->pid();
2387+ }
2388+ auto *provider = promptHelper->provider();
2389+ if (provider && provider->sessionName() == session->name().c_str()) {
2390+ return provider->pid();
2391+ }
2392+ }
2393+ }
2394+
2395+ if (m_maliitLauncher && m_maliitLauncher->session() == session) {
2396+ return m_maliitLauncher->pid();
2397+ }
2398+
2399+ qFatal("FakeTaskController::pidOf_impl() - Did not find the requested session!");
2400+}
2401+
2402+pid_t FakeTaskController::generatePid()
2403+{
2404+ pid_t pid = m_nextPid;
2405+ m_nextPid = nextFreePid(nextPid(pid), pid);
2406+ return pid;
2407+}
2408+
2409+pid_t FakeTaskController::nextPid(pid_t pid) const
2410+{
2411+ if (pid == m_maxPid) {
2412+ return 1;
2413+ } else {
2414+ return pid + 1;
2415+ }
2416+}
2417+
2418+pid_t FakeTaskController::nextFreePid(pid_t candidatePid, const pid_t latestPid)
2419+{
2420+ pid_t firstCandidatePid = candidatePid;
2421+
2422+ while (isPidInUse(candidatePid) || candidatePid == latestPid) {
2423+ candidatePid = nextPid(candidatePid);
2424+
2425+ if (candidatePid == firstCandidatePid) {
2426+ qFatal("TopLevelWindowModel: run out of pids.");
2427+ }
2428+ }
2429+
2430+ return candidatePid;
2431+}
2432+
2433+bool FakeTaskController::isPidInUse(pid_t pid) const
2434+{
2435+ {
2436+ auto clientList = m_runningClients.values();
2437+ for (int i = 0; i < clientList.count(); ++i) {
2438+ auto *client = clientList[i];
2439+ if (client->pid() == pid) {
2440+ return true;
2441+ }
2442+ }
2443+ }
2444+ {
2445+ auto promptList = m_runningPromptHelpers.values();
2446+ for (int i = 0; i < promptList.count(); ++i) {
2447+ auto *prompt = promptList[i];
2448+ if (prompt->pid() == pid) {
2449+ return true;
2450+ }
2451+ }
2452+ }
2453+ return false;
2454+}
2455+
2456+void FakeTaskController::onAuthorizationForSessionRequested(const pid_t &, bool &authorized)
2457+{
2458+ // replace shell's own pid (because you know, we're running only internal clients) with a fake one
2459+ // from the session we most recently started (some assumptions going on, but should hold during tests).
2460+
2461+ if (m_runningClients.contains(m_appIdLatestLaunch)) {
2462+ Q_EMIT authorizationRequestedForSession(m_runningClients[m_appIdLatestLaunch]->pid(), authorized);
2463+ } else {
2464+ Q_ASSERT(m_maliitLauncher && m_maliitAppInfo->appId() == m_appIdLatestLaunch);
2465+ Q_EMIT authorizationRequestedForSession(m_maliitLauncher->pid(), authorized);
2466+ }
2467+}
2468+
2469+void FakeTaskController::createSurface(const QString &appId)
2470+{
2471+ QMutexLocker locker(&m_mutex);
2472+
2473+ DEBUG_MSG << "(" << appId << ")";
2474+ if (m_runningClients.contains(appId)) {
2475+ m_runningClients[appId]->createWindow();
2476+ } else {
2477+ qFatal("FakeTaskController::createSurface(%s) - no running client.", qPrintable(appId));
2478+ }
2479+}
2480+
2481+void FakeTaskController::killSurface(const QString & appId, unityapi::MirSurfaceInterface* surface)
2482+{
2483+ QMutexLocker locker(&m_mutex);
2484+ DEBUG_MSG << "("<< appId <<","<< surface <<")";
2485+
2486+ if (m_runningClients.contains(appId)) {
2487+ m_runningClients[appId]->killWindow(surface->persistentId());
2488+ }
2489+}
2490+
2491+void FakeTaskController::createPromptSurface(const QString &appId)
2492+{
2493+ QMutexLocker locker(&m_mutex);
2494+
2495+ if (!m_runningClients.contains(appId)) {
2496+ return;
2497+ }
2498+
2499+ m_appIdLatestPromptSurface = appId;
2500+ if (m_runningPromptHelpers.contains(appId)) {
2501+ // TODO
2502+ } else {
2503+ auto *promptHelperLauncher = new FakePromptHelperLauncher(generatePid(), this);
2504+
2505+ connect(promptHelperLauncher, &FakePromptHelperLauncher::promptProviderFdReady, this,
2506+ [this, promptHelperLauncher, appId](int fd) {
2507+ launchPromptProvider(promptHelperLauncher, fd);
2508+ });
2509+
2510+ connect(promptHelperLauncher, &QThread::finished, this,
2511+ [this, promptHelperLauncher, appId]() {
2512+ m_runningPromptHelpers.remove(appId);
2513+ promptHelperLauncher->deleteLater();
2514+ });
2515+
2516+ m_runningPromptHelpers[appId] = promptHelperLauncher;
2517+ promptHelperLauncher->launch();
2518+ }
2519+}
2520+
2521+void FakeTaskController::onPromptSessionStarting(const qtmir::PromptSession &promptSession)
2522+{
2523+ DEBUG_MSG << " - promptSession=" << promptSession.get();
2524+
2525+ Q_ASSERT(!m_appIdLatestPromptSurface.isEmpty());
2526+ Q_ASSERT(m_runningClients.contains(m_appIdLatestPromptSurface));
2527+
2528+ std::shared_ptr<mir::scene::Session> appSession = m_runningClients[m_appIdLatestPromptSurface]->session();
2529+ SessionInterface *qmlAppSession = findSession(appSession.get());
2530+ if (qmlAppSession) {
2531+ m_mirPromptToSessionHash[promptSession.get()] = qmlAppSession;
2532+ qmlAppSession->appendPromptSession(promptSession);
2533+ } else {
2534+ DEBUG_MSG << " - could not find app session for prompt session";
2535+ }
2536+}
2537+
2538+void FakeTaskController::launchPromptProvider(FakePromptHelperLauncher *promptHelperLauncher, int fd)
2539+{
2540+ auto *providerLauncher = new FakePromptProviderLauncher(generatePid(), fd);
2541+ promptHelperLauncher->setProvider(providerLauncher);
2542+ providerLauncher->launch();
2543+}
2544+
2545+void FakeTaskController::createInputMethodSurface()
2546+{
2547+ QMutexLocker locker(&m_mutex);
2548+
2549+ if (m_maliitLauncher) {
2550+ // already created
2551+ return;
2552+ }
2553+
2554+ m_maliitLauncher.reset(new FakeMaliitLauncher(m_maliitAppInfo, generatePid()));
2555+
2556+ connect(m_maliitLauncher.data(), &QThread::finished, this, [this]() {
2557+ delete m_maliitLauncher.take();
2558+ });
2559+
2560+ m_appIdLatestLaunch = m_maliitAppInfo->appId();
2561+ m_maliitLauncher->launch();
2562+}
2563+
2564+void FakeTaskController::stopInputMethod()
2565+{
2566+ QMutexLocker locker(&m_mutex);
2567+
2568+ if (m_maliitLauncher) {
2569+ m_maliitLauncher->quit();
2570+ }
2571+}
2572+
2573+bool FakeTaskController::isInputMethodRunning()
2574+{
2575+ return !m_maliitLauncher.isNull();
2576+}
2577+
2578+QSharedPointer<ProcInfo> FakeTaskController::procInfo()
2579+{
2580+ return m_procInfo;
2581+}
2582+
2583+std::unique_ptr<ProcInfo::CommandLine> FakeTaskController::commandLine(pid_t pid)
2584+{
2585+ if (m_maliitLauncher && m_maliitLauncher->pid() == pid) {
2586+ return std::unique_ptr<ProcInfo::CommandLine>(new ProcInfo::CommandLine{"maliit-server"});
2587+ } else {
2588+ return std::unique_ptr<ProcInfo::CommandLine>(new ProcInfo::CommandLine{"foo"});
2589+ }
2590+}
2591+
2592+void FakeTaskController::setSurfaceInputBounds(unityapi::MirSurfaceInterface *surfaceInterface, QRect rect)
2593+{
2594+ auto surface = static_cast<MirSurface*>(surfaceInterface);
2595+
2596+ DEBUG_MSG << "(" << surface << ", " << rect << ")";
2597+
2598+ pid_t pid = pidOf_impl(surface->session()->session());
2599+ FakeMirClientLauncher *clientLauncher = findClientWithPid(pid);
2600+ clientLauncher->setWindowInputBounds(surface->persistentId(), rect);
2601+}
2602+
2603+FakeMirClientLauncher *FakeTaskController::findClientWithPid(pid_t pid)
2604+{
2605+ QList<FakeMirClientLauncher*> clientList = m_runningClients.values();
2606+ for (int i = 0; i < clientList.size(); ++i) {
2607+ auto client = clientList[i];
2608+ if (client->pid() == pid) {
2609+ return client;
2610+ }
2611+ }
2612+
2613+ if (m_maliitLauncher && m_maliitLauncher->pid() == pid) {
2614+ return m_maliitLauncher.data();
2615+ }
2616+
2617+ return nullptr;
2618+}
2619+
2620+void FakeTaskController::setManualSurfaceCreation(bool value)
2621+{
2622+ if (value != m_manualSurfaceCreation) {
2623+ m_manualSurfaceCreation = value;
2624+ Q_EMIT manualSurfaceCreationChanged();
2625+ }
2626+}
2627+
2628+void FakeTaskController::killApplication(const QString &appId)
2629+{
2630+ QMutexLocker locker(&m_mutex);
2631+
2632+ DEBUG_MSG << "(" << appId << ")";
2633+
2634+ if (m_runningClients.contains(appId)) {
2635+ m_runningClients[appId]->kill();
2636+ }
2637+}
2638+
2639+bool FakeTaskController::isRunning(const QString &appId)
2640+{
2641+ return m_runningClients.contains(appId);
2642+}
2643+
2644+void FakeTaskController::killPrompts()
2645+{
2646+ auto promptList = m_runningPromptHelpers.values();
2647+ for (int i = 0; i < promptList.count(); ++i) {
2648+ auto *prompt = promptList[i];
2649+ if (prompt->provider()) {
2650+ prompt->provider()->quit();
2651+ }
2652+ }
2653+}
2654+
2655+bool FakeTaskController::promptsRunning()
2656+{
2657+ return !m_runningPromptHelpers.isEmpty();
2658+}
2659+
2660+void FakeTaskController::setSurfaceMinimumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
2661+{
2662+ if (m_runningClients.contains(appId)) {
2663+ m_runningClients[appId]->setWindowMinimumWidth(surface->persistentId(), value);
2664+ }
2665+}
2666+
2667+void FakeTaskController::setSurfaceMaximumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
2668+{
2669+ if (m_runningClients.contains(appId)) {
2670+ m_runningClients[appId]->setWindowMaximumWidth(surface->persistentId(), value);
2671+ }
2672+}
2673+
2674+void FakeTaskController::setSurfaceMinimumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
2675+{
2676+ if (m_runningClients.contains(appId)) {
2677+ m_runningClients[appId]->setWindowMinimumHeight(surface->persistentId(), value);
2678+ }
2679+}
2680+
2681+void FakeTaskController::setSurfaceMaximumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
2682+{
2683+ if (m_runningClients.contains(appId)) {
2684+ m_runningClients[appId]->setWindowMaximumHeight(surface->persistentId(), value);
2685+ }
2686+}
2687+
2688+void FakeTaskController::setSurfaceWidthIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
2689+{
2690+ if (m_runningClients.contains(appId)) {
2691+ m_runningClients[appId]->setWindowWidthIncrement(surface->persistentId(), value);
2692+ }
2693+}
2694+
2695+void FakeTaskController::setSurfaceHeightIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
2696+{
2697+ if (m_runningClients.contains(appId)) {
2698+ m_runningClients[appId]->setWindowHeightIncrement(surface->persistentId(), value);
2699+ }
2700+}
2701+
2702+const WindowAttributes *FakeTaskController::windowAttributes(const QString &appId, unity::shell::application::MirSurfaceInterface *surface)
2703+{
2704+ if (m_runningClients.contains(appId)) {
2705+ return m_runningClients[appId]->windowAttributes(surface->persistentId());
2706+ } else {
2707+ return nullptr;
2708+ }
2709+}
2710+
2711+const WindowAttributes *FakeTaskController::windowAttributes(unity::shell::application::MirSurfaceInterface *surface)
2712+{
2713+ const WindowAttributes *winAttribs = nullptr;
2714+
2715+ {
2716+ auto clientList = m_runningClients.values();
2717+ for (int i = 0; winAttribs == nullptr && i < clientList.count(); ++i) {
2718+ winAttribs = clientList[i]->windowAttributes(surface->persistentId());
2719+ }
2720+ }
2721+
2722+ if (winAttribs == nullptr && m_maliitLauncher) {
2723+ winAttribs = m_maliitLauncher->windowAttributes(surface->persistentId());
2724+ }
2725+
2726+ return winAttribs;
2727+}
2728+
2729+std::unique_ptr<ProcInfo::CommandLine> FakeProcInfo::commandLine(pid_t pid)
2730+{
2731+ return taskController->commandLine(pid);
2732+}
2733
2734=== added file 'src/modules/Unity/Application/fake/faketaskcontroller.h'
2735--- src/modules/Unity/Application/fake/faketaskcontroller.h 1970-01-01 00:00:00 +0000
2736+++ src/modules/Unity/Application/fake/faketaskcontroller.h 2017-02-14 10:17:32 +0000
2737@@ -0,0 +1,157 @@
2738+/*
2739+ * Copyright (C) 2017 Canonical, Ltd.
2740+ *
2741+ * This program is free software: you can redistribute it and/or modify it under
2742+ * the terms of the GNU Lesser General Public License version 3, as published by
2743+ * the Free Software Foundation.
2744+ *
2745+ * This program is distributed in the hope that it will be useful, but WITHOUT
2746+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2747+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2748+ * Lesser General Public License for more details.
2749+ *
2750+ * You should have received a copy of the GNU Lesser General Public License
2751+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2752+ *
2753+ */
2754+
2755+#ifndef QTMIR_FAKE_TASK_CONTROLLER_H
2756+#define QTMIR_FAKE_TASK_CONTROLLER_H
2757+
2758+#include "taskcontroller.h"
2759+#include "proc_info.h"
2760+
2761+#include "fakeapplicationinfo.h"
2762+
2763+#include <QMap>
2764+#include <QMutex>
2765+#include <QScopedPointer>
2766+#include <QSharedPointer>
2767+#include <QVector>
2768+
2769+namespace qtmir {
2770+
2771+class FakeMirClientLauncher;
2772+class FakePromptHelperLauncher;
2773+class WindowAttributes;
2774+
2775+class FakeTaskController;
2776+
2777+class FakeProcInfo : public ProcInfo {
2778+public:
2779+ std::unique_ptr<CommandLine> commandLine(pid_t pid) override;
2780+ FakeTaskController *taskController;
2781+};
2782+
2783+class FakeTaskController : public qtmir::TaskController
2784+{
2785+ Q_OBJECT
2786+public:
2787+ FakeTaskController();
2788+ ~FakeTaskController();
2789+
2790+ static FakeTaskController *instance() { return m_instance; }
2791+
2792+ bool appIdHasProcessId(const QString& appId, pid_t pid) override;
2793+
2794+ bool stop(const QString& appId) override;
2795+ bool start(const QString& appId, const QStringList& arguments) override;
2796+
2797+ bool suspend(const QString& appId) override;
2798+ bool resume(const QString& appId) override;
2799+
2800+ QSharedPointer<qtmir::ApplicationInfo> getInfoForApp(const QString &appId) const override;
2801+
2802+ QStringList availableApplications();
2803+ void createSurface(const QString &appId);
2804+ void killSurface(const QString &appId, unity::shell::application::MirSurfaceInterface*);
2805+ void createPromptSurface(const QString &appId);
2806+
2807+ void killApplication(const QString &appId);
2808+
2809+ void createInputMethodSurface();
2810+ void stopInputMethod();
2811+ bool isInputMethodRunning();
2812+ void setSurfaceInputBounds(unity::shell::application::MirSurfaceInterface*, QRect);
2813+
2814+ QSharedPointer<ProcInfo> procInfo();
2815+
2816+ std::unique_ptr<ProcInfo::CommandLine> commandLine(pid_t pid);
2817+
2818+ bool manualSurfaceCreation() const { return m_manualSurfaceCreation; };
2819+ void setManualSurfaceCreation(bool value);
2820+
2821+ bool isRunning(const QString &appId);
2822+
2823+ void killPrompts();
2824+ bool promptsRunning();
2825+
2826+ void setSurfaceMinimumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
2827+ void setSurfaceMaximumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
2828+ void setSurfaceMinimumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
2829+ void setSurfaceMaximumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
2830+ void setSurfaceWidthIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
2831+ void setSurfaceHeightIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
2832+
2833+ // returns the window attributes as seem on the client side
2834+ const WindowAttributes *windowAttributes(const QString &appId, unity::shell::application::MirSurfaceInterface*);
2835+ const WindowAttributes *windowAttributes(unity::shell::application::MirSurfaceInterface *surface);
2836+
2837+Q_SIGNALS:
2838+ void manualSurfaceCreationChanged();
2839+
2840+private Q_SLOTS:
2841+ void onAuthorizationForSessionRequested(const pid_t &pid, bool &authorized) override;
2842+ void onPromptSessionStarting(const PromptSession& promptSession) override;
2843+
2844+private:
2845+ // From PidFetcher
2846+ // NB: Can be called from either Qt gui or from some mir thread. Hence need for mutex.
2847+ pid_t pidOf_impl(const miral::Application &application) override;
2848+
2849+ FakeApplicationInfo *findAppInfo(const QString &appId);
2850+
2851+ void createAvailableApplications();
2852+
2853+ FakeMirClientLauncher *findClientWithPid(pid_t pid);
2854+
2855+ pid_t generatePid();
2856+ pid_t nextPid(pid_t pid) const;
2857+ pid_t nextFreePid(pid_t candidatePid, const pid_t latestPid);
2858+ bool isPidInUse(pid_t) const;
2859+
2860+ void launchPromptProvider(FakePromptHelperLauncher*, int fd);
2861+
2862+ // some arbitrary starting point and limit
2863+ pid_t m_nextPid{123};
2864+ static const pid_t m_maxPid{10000};
2865+
2866+
2867+ QVector< QSharedPointer<FakeApplicationInfo> > m_availableApplications;
2868+
2869+ // maps an appId to its running mir client
2870+ QMap<QString, FakeMirClientLauncher*> m_runningClients;
2871+ QString m_appIdLatestLaunch;
2872+
2873+ // maps an appId to its running prompt helper session
2874+ QMap<QString, FakePromptHelperLauncher*> m_runningPromptHelpers;
2875+
2876+ QString m_appIdLatestPromptSurface;
2877+
2878+ // Because of pidOf_impl()
2879+ QMutex m_mutex;
2880+
2881+ QSharedPointer<FakeApplicationInfo> m_maliitAppInfo;
2882+ QScopedPointer<FakeMirClientLauncher> m_maliitLauncher;
2883+
2884+ QSharedPointer<FakeProcInfo> m_procInfo;
2885+
2886+ static FakeTaskController *m_instance;
2887+
2888+ bool m_manualSurfaceCreation{false};
2889+};
2890+
2891+} // namespace qtmir
2892+
2893+#endif // QTMIR_FAKE_TASK_CONTROLLER_H
2894+
2895
2896=== added file 'src/modules/Unity/Application/fake/windowattributes.h'
2897--- src/modules/Unity/Application/fake/windowattributes.h 1970-01-01 00:00:00 +0000
2898+++ src/modules/Unity/Application/fake/windowattributes.h 2017-02-14 10:17:32 +0000
2899@@ -0,0 +1,47 @@
2900+/*
2901+ * Copyright (C) 2017 Canonical, Ltd.
2902+ *
2903+ * This program is free software: you can redistribute it and/or modify it under
2904+ * the terms of the GNU Lesser General Public License version 3, as published by
2905+ * the Free Software Foundation.
2906+ *
2907+ * This program is distributed in the hope that it will be useful, but WITHOUT
2908+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2909+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2910+ * Lesser General Public License for more details.
2911+ *
2912+ * You should have received a copy of the GNU Lesser General Public License
2913+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2914+ *
2915+ */
2916+
2917+#ifndef QTMIR_WINDOW_ATTRIBUTES
2918+#define QTMIR_WINDOW_ATTRIBUTES
2919+
2920+#include <mir_toolkit/mir_window.h>
2921+
2922+namespace qtmir {
2923+
2924+class WindowAttributes {
2925+public:
2926+ WindowAttributes() {}
2927+
2928+ WindowAttributes(MirWindow *mirWindow) {
2929+ visibility = mir_window_get_visibility(mirWindow);
2930+ focusState = mir_window_get_focus_state(mirWindow);
2931+ }
2932+
2933+ MirWindowVisibility visibility;
2934+ MirWindowFocusState focusState;
2935+
2936+ int touchPressCount{0};
2937+ int touchReleaseCount{0};
2938+ int mousePressCount{0};
2939+ int mouseReleaseCount{0};
2940+};
2941+
2942+} // namespace qtmir
2943+
2944+Q_DECLARE_METATYPE(qtmir::WindowAttributes)
2945+
2946+#endif // QTMIR_WINDOW_ATTRIBUTES
2947
2948=== added file 'src/modules/Unity/Application/mirtestsingleton.cpp'
2949--- src/modules/Unity/Application/mirtestsingleton.cpp 1970-01-01 00:00:00 +0000
2950+++ src/modules/Unity/Application/mirtestsingleton.cpp 2017-02-14 10:17:32 +0000
2951@@ -0,0 +1,211 @@
2952+/*
2953+ * Copyright (C) 2017 Canonical, Ltd.
2954+ *
2955+ * This program is free software: you can redistribute it and/or modify it under
2956+ * the terms of the GNU Lesser General Public License version 3, as published by
2957+ * the Free Software Foundation.
2958+ *
2959+ * This program is distributed in the hope that it will be useful, but WITHOUT
2960+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2961+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2962+ * Lesser General Public License for more details.
2963+ *
2964+ * You should have received a copy of the GNU Lesser General Public License
2965+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2966+ */
2967+
2968+#include "mirtestsingleton.h"
2969+
2970+#include "application.h"
2971+#include "faketaskcontroller.h"
2972+#include "mirsurface.h"
2973+#include "windowattributes.h"
2974+
2975+using namespace qtmir;
2976+
2977+MirTest *MirTest::m_instance = nullptr;
2978+
2979+MirTest::MirTest()
2980+{
2981+ Q_ASSERT(m_instance == nullptr);
2982+ m_instance = this;
2983+
2984+ connect(FakeTaskController::instance(), &FakeTaskController::manualSurfaceCreationChanged,
2985+ this, &MirTest::manualSurfaceCreationChanged);
2986+}
2987+
2988+MirTest::~MirTest()
2989+{
2990+ Q_ASSERT(m_instance != nullptr);
2991+ m_instance = nullptr;
2992+}
2993+
2994+void MirTest::createInputMethodSurface()
2995+{
2996+ FakeTaskController::instance()->createInputMethodSurface();
2997+}
2998+
2999+void MirTest::stopInputMethod()
3000+{
3001+ FakeTaskController::instance()->stopInputMethod();
3002+}
3003+
3004+bool MirTest::isInputMethodRunning()
3005+{
3006+ return FakeTaskController::instance()->isInputMethodRunning();
3007+}
3008+
3009+void MirTest::setSurfaceInputBounds(unity::shell::application::MirSurfaceInterface* surface, QRect rect)
3010+{
3011+ FakeTaskController::instance()->setSurfaceInputBounds(surface, rect);
3012+}
3013+
3014+void MirTest::createSurface(const QString &appId)
3015+{
3016+ FakeTaskController::instance()->createSurface(appId);
3017+}
3018+
3019+void MirTest::createPromptSurface(const QString &appId)
3020+{
3021+ FakeTaskController::instance()->createPromptSurface(appId);
3022+}
3023+
3024+bool MirTest::manualSurfaceCreation() const
3025+{
3026+ return FakeTaskController::instance()->manualSurfaceCreation();
3027+}
3028+
3029+void MirTest::setManualSurfaceCreation(bool value)
3030+{
3031+ FakeTaskController::instance()->setManualSurfaceCreation(value);
3032+}
3033+
3034+void MirTest::killSurface(const QString &appId, unity::shell::application::MirSurfaceInterface *surface)
3035+{
3036+ FakeTaskController::instance()->killSurface(appId, surface);
3037+}
3038+
3039+void MirTest::killApplication(const QString &appId)
3040+{
3041+ FakeTaskController::instance()->killApplication(appId);
3042+}
3043+
3044+auto MirTest::internalState(unity::shell::application::ApplicationInfoInterface *applicationInterface) -> ApplicationInternalState
3045+{
3046+ auto application = static_cast<Application*>(applicationInterface);
3047+ switch (application->internalState()) {
3048+ case Application::InternalState::Starting:
3049+ if (application->session() == nullptr) {
3050+ return StartingNoSession;
3051+ } else {
3052+ return StartingWithSession;
3053+ }
3054+ case Application::InternalState::Running:
3055+ return Running;
3056+ case Application::InternalState::RunningInBackground:
3057+ return RunningInBackground;
3058+ case Application::InternalState::SuspendingWaitSession:
3059+ return SuspendingWaitSession;
3060+ case Application::InternalState::SuspendingWaitProcess:
3061+ return SuspendingWaitProcess;
3062+ case Application::InternalState::Suspended:
3063+ return Suspended;
3064+ case Application::InternalState::Closing:
3065+ return Closing;
3066+ case Application::InternalState::StoppedResumable:
3067+ return StoppedResumable;
3068+ case Application::InternalState::Stopped:
3069+ return Stopped;
3070+ }
3071+ Q_UNREACHABLE();
3072+}
3073+
3074+QStringList MirTest::availableApplications()
3075+{
3076+ return FakeTaskController::instance()->availableApplications();
3077+}
3078+
3079+bool MirTest::isApplicationRunning(const QString &appId)
3080+{
3081+ return FakeTaskController::instance()->isRunning(appId);
3082+}
3083+
3084+void MirTest::killPrompts()
3085+{
3086+ FakeTaskController::instance()->killPrompts();
3087+}
3088+
3089+bool MirTest::promptsRunning()
3090+{
3091+ return FakeTaskController::instance()->promptsRunning();
3092+}
3093+
3094+void MirTest::setSurfaceMinimumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
3095+{
3096+ FakeTaskController::instance()->setSurfaceMinimumWidth(appId, surface, value);
3097+}
3098+
3099+void MirTest::setSurfaceMaximumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
3100+{
3101+ FakeTaskController::instance()->setSurfaceMaximumWidth(appId, surface, value);
3102+}
3103+
3104+void MirTest::setSurfaceMinimumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
3105+{
3106+ FakeTaskController::instance()->setSurfaceMinimumHeight(appId, surface, value);
3107+}
3108+
3109+void MirTest::setSurfaceMaximumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
3110+{
3111+ FakeTaskController::instance()->setSurfaceMaximumHeight(appId, surface, value);
3112+}
3113+
3114+void MirTest::setSurfaceWidthIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
3115+{
3116+ FakeTaskController::instance()->setSurfaceWidthIncrement(appId, surface, value);
3117+}
3118+
3119+void MirTest::setSurfaceHeightIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface *surface, int value)
3120+{
3121+ FakeTaskController::instance()->setSurfaceHeightIncrement(appId, surface, value);
3122+}
3123+
3124+bool MirTest::surfaceFocused(const QString &appId, unity::shell::application::MirSurfaceInterface *surface)
3125+{
3126+ const WindowAttributes *windowAttribs = FakeTaskController::instance()->windowAttributes(appId, surface);
3127+ Q_ASSERT(windowAttribs);
3128+ return windowAttribs->focusState == mir_window_focus_state_focused;
3129+}
3130+
3131+bool MirTest::surfaceExposed(const QString &appId, unity::shell::application::MirSurfaceInterface *surface)
3132+{
3133+ const WindowAttributes *windowAttribs = FakeTaskController::instance()->windowAttributes(appId, surface);
3134+ Q_ASSERT(windowAttribs);
3135+ return windowAttribs->visibility == mir_window_visibility_exposed;
3136+}
3137+
3138+bool MirTest::hasActiveFocus(unity::shell::application::MirSurfaceInterface *surfaceInterface)
3139+{
3140+ auto surface = static_cast<MirSurface*>(surfaceInterface);
3141+ return surface->activeFocus();
3142+}
3143+
3144+int MirTest::touchPressCount(unity::shell::application::MirSurfaceInterface *surface)
3145+{
3146+ return FakeTaskController::instance()->windowAttributes(surface)->touchPressCount;
3147+}
3148+
3149+int MirTest::touchReleaseCount(unity::shell::application::MirSurfaceInterface *surface)
3150+{
3151+ return FakeTaskController::instance()->windowAttributes(surface)->touchReleaseCount;
3152+}
3153+
3154+int MirTest::mousePressCount(unity::shell::application::MirSurfaceInterface *surface)
3155+{
3156+ return FakeTaskController::instance()->windowAttributes(surface)->mousePressCount;
3157+}
3158+
3159+int MirTest::mouseReleaseCount(unity::shell::application::MirSurfaceInterface *surface)
3160+{
3161+ return FakeTaskController::instance()->windowAttributes(surface)->mouseReleaseCount;
3162+}
3163
3164=== added file 'src/modules/Unity/Application/mirtestsingleton.h'
3165--- src/modules/Unity/Application/mirtestsingleton.h 1970-01-01 00:00:00 +0000
3166+++ src/modules/Unity/Application/mirtestsingleton.h 2017-02-14 10:17:32 +0000
3167@@ -0,0 +1,116 @@
3168+/*
3169+ * Copyright (C) 2017 Canonical, Ltd.
3170+ *
3171+ * This program is free software: you can redistribute it and/or modify it under
3172+ * the terms of the GNU Lesser General Public License version 3, as published by
3173+ * the Free Software Foundation.
3174+ *
3175+ * This program is distributed in the hope that it will be useful, but WITHOUT
3176+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3177+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3178+ * Lesser General Public License for more details.
3179+ *
3180+ * You should have received a copy of the GNU Lesser General Public License
3181+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3182+ */
3183+
3184+#ifndef QTMIR_MIRTEST_H
3185+#define QTMIR_MIRTEST_H
3186+
3187+#include <QObject>
3188+#include <QRect>
3189+
3190+namespace unity {
3191+ namespace shell {
3192+ namespace application {
3193+ class ApplicationInfoInterface;
3194+ class MirSurfaceInterface;
3195+ }
3196+ }
3197+}
3198+
3199+namespace qtmir {
3200+
3201+/*
3202+ Various tidbits useful for shell tests
3203+ */
3204+class MirTest : public QObject {
3205+ Q_OBJECT
3206+
3207+ Q_PROPERTY(bool manualSurfaceCreation READ manualSurfaceCreation WRITE setManualSurfaceCreation NOTIFY manualSurfaceCreationChanged)
3208+ Q_PROPERTY(QStringList availableApplications READ availableApplications NOTIFY availableApplicationsChanged)
3209+
3210+public:
3211+
3212+ enum ApplicationInternalState {
3213+ StartingNoSession,
3214+ StartingWithSession,
3215+ Running,
3216+ RunningInBackground,
3217+ SuspendingWaitSession,
3218+ SuspendingWaitProcess,
3219+ Suspended,
3220+ Closing,
3221+ StoppedResumable,
3222+ Stopped
3223+ };
3224+ Q_ENUM(ApplicationInternalState)
3225+
3226+
3227+ MirTest();
3228+ virtual ~MirTest();
3229+
3230+ static MirTest *instance() { return m_instance; }
3231+
3232+ Q_INVOKABLE void createInputMethodSurface();
3233+ Q_INVOKABLE void stopInputMethod();
3234+ Q_INVOKABLE bool isInputMethodRunning();
3235+
3236+ Q_INVOKABLE void setSurfaceInputBounds(unity::shell::application::MirSurfaceInterface*, QRect);
3237+
3238+ Q_INVOKABLE void createSurface(const QString &appId);
3239+
3240+ Q_INVOKABLE void createPromptSurface(const QString &appId);
3241+ Q_INVOKABLE void killPrompts();
3242+ Q_INVOKABLE bool promptsRunning();
3243+
3244+ bool manualSurfaceCreation() const;
3245+ void setManualSurfaceCreation(bool);
3246+
3247+ QStringList availableApplications();
3248+
3249+ Q_INVOKABLE void setSurfaceMinimumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
3250+ Q_INVOKABLE void setSurfaceMaximumWidth(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
3251+ Q_INVOKABLE void setSurfaceMinimumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
3252+ Q_INVOKABLE void setSurfaceMaximumHeight(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
3253+ Q_INVOKABLE void setSurfaceWidthIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
3254+ Q_INVOKABLE void setSurfaceHeightIncrement(const QString &appId, unity::shell::application::MirSurfaceInterface*, int value);
3255+
3256+ Q_INVOKABLE bool hasActiveFocus(unity::shell::application::MirSurfaceInterface*); // server-side information
3257+
3258+ Q_INVOKABLE bool surfaceFocused(const QString &appId, unity::shell::application::MirSurfaceInterface*);
3259+ Q_INVOKABLE bool surfaceExposed(const QString &appId, unity::shell::application::MirSurfaceInterface*);
3260+ Q_INVOKABLE int touchPressCount(unity::shell::application::MirSurfaceInterface*);
3261+ Q_INVOKABLE int touchReleaseCount(unity::shell::application::MirSurfaceInterface*);
3262+ Q_INVOKABLE int mousePressCount(unity::shell::application::MirSurfaceInterface*);
3263+ Q_INVOKABLE int mouseReleaseCount(unity::shell::application::MirSurfaceInterface*);
3264+
3265+ Q_INVOKABLE void killSurface(const QString &appId, unity::shell::application::MirSurfaceInterface*);
3266+ Q_INVOKABLE void killApplication(const QString &appId);
3267+
3268+ Q_INVOKABLE ApplicationInternalState internalState(unity::shell::application::ApplicationInfoInterface *application);
3269+
3270+ // Tells whether the application process (in this case, thread) is still running.
3271+ Q_INVOKABLE bool isApplicationRunning(const QString &appId);
3272+
3273+Q_SIGNALS:
3274+ void manualSurfaceCreationChanged();
3275+ void availableApplicationsChanged();
3276+
3277+private:
3278+ static MirTest *m_instance;
3279+};
3280+
3281+} // namespace qtmir
3282+
3283+#endif // QTMIR_MIRTEST_H
3284
3285=== modified file 'src/modules/Unity/Application/plugin.cpp'
3286--- src/modules/Unity/Application/plugin.cpp 2017-01-18 21:24:15 +0000
3287+++ src/modules/Unity/Application/plugin.cpp 2017-02-14 10:17:32 +0000
3288@@ -1,5 +1,5 @@
3289 /*
3290- * Copyright (C) 2013-2016 Canonical, Ltd.
3291+ * Copyright (C) 2013-2017 Canonical, Ltd.
3292 *
3293 * This program is free software: you can redistribute it and/or modify it under
3294 * the terms of the GNU Lesser General Public License version 3, as published by
3295@@ -23,6 +23,7 @@
3296 #include "mirsurfaceinterface.h"
3297 #include "mirsurfaceitem.h"
3298 #include "mirsurfacelistmodel.h"
3299+#include "mirtestsingleton.h"
3300 #include "windowmodel.h"
3301 #include "surfacemanager.h"
3302
3303@@ -49,6 +50,11 @@
3304 QObject* mirSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) {
3305 return qtmir::Mir::instance();
3306 }
3307+
3308+QObject* mirTestSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) {
3309+ qtmir::ApplicationManager::singleton(); // make sure ApplicationManager (and hence TaskController) is created first
3310+ return new MirTest();
3311+}
3312 } // anonymous namespace
3313
3314 class UnityApplicationPlugin : public QQmlExtensionPlugin {
3315@@ -82,6 +88,10 @@
3316 qmlRegisterType<qtmir::SurfaceManager>(uri, 0, 1, "SurfaceManager");
3317
3318 qmlRegisterType<qtmir::WindowModel>(uri, 0, 1, "WindowModel");
3319+
3320+ if (qgetenv("UNITY_TESTING") == "1") {
3321+ qmlRegisterSingletonType<qtmir::MirTest>(uri, 0, 1, "MirTest", mirTestSingleton);
3322+ }
3323 }
3324
3325 virtual void initializeEngine(QQmlEngine *engine, const char *uri)
3326
3327=== added directory 'src/modules/Unity/Application/resources'
3328=== added directory 'src/modules/Unity/Application/resources/icons'
3329=== added file 'src/modules/Unity/Application/resources/icons/browser@18.png'
3330Binary files src/modules/Unity/Application/resources/icons/browser@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/browser@18.png 2017-02-14 10:17:32 +0000 differ
3331=== added file 'src/modules/Unity/Application/resources/icons/calendar@18.png'
3332Binary files src/modules/Unity/Application/resources/icons/calendar@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/calendar@18.png 2017-02-14 10:17:32 +0000 differ
3333=== added file 'src/modules/Unity/Application/resources/icons/camera@18.png'
3334Binary files src/modules/Unity/Application/resources/icons/camera@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/camera@18.png 2017-02-14 10:17:32 +0000 differ
3335=== added file 'src/modules/Unity/Application/resources/icons/contacts-app@18.png'
3336Binary files src/modules/Unity/Application/resources/icons/contacts-app@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/contacts-app@18.png 2017-02-14 10:17:32 +0000 differ
3337=== added file 'src/modules/Unity/Application/resources/icons/dash@18.png'
3338Binary files src/modules/Unity/Application/resources/icons/dash@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/dash@18.png 2017-02-14 10:17:32 +0000 differ
3339=== added file 'src/modules/Unity/Application/resources/icons/dialer-app@18.png'
3340Binary files src/modules/Unity/Application/resources/icons/dialer-app@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/dialer-app@18.png 2017-02-14 10:17:32 +0000 differ
3341=== added file 'src/modules/Unity/Application/resources/icons/evernote@18.png'
3342Binary files src/modules/Unity/Application/resources/icons/evernote@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/evernote@18.png 2017-02-14 10:17:32 +0000 differ
3343=== added file 'src/modules/Unity/Application/resources/icons/facebook@18.png'
3344Binary files src/modules/Unity/Application/resources/icons/facebook@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/facebook@18.png 2017-02-14 10:17:32 +0000 differ
3345=== added file 'src/modules/Unity/Application/resources/icons/gallery@18.png'
3346Binary files src/modules/Unity/Application/resources/icons/gallery@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/gallery@18.png 2017-02-14 10:17:32 +0000 differ
3347=== added file 'src/modules/Unity/Application/resources/icons/gmail@18.png'
3348Binary files src/modules/Unity/Application/resources/icons/gmail@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/gmail@18.png 2017-02-14 10:17:32 +0000 differ
3349=== added file 'src/modules/Unity/Application/resources/icons/libreoffice@18.png'
3350Binary files src/modules/Unity/Application/resources/icons/libreoffice@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/libreoffice@18.png 2017-02-14 10:17:32 +0000 differ
3351=== added file 'src/modules/Unity/Application/resources/icons/map@18.png'
3352Binary files src/modules/Unity/Application/resources/icons/map@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/map@18.png 2017-02-14 10:17:32 +0000 differ
3353=== added file 'src/modules/Unity/Application/resources/icons/messages-app@18.png'
3354Binary files src/modules/Unity/Application/resources/icons/messages-app@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/messages-app@18.png 2017-02-14 10:17:32 +0000 differ
3355=== added file 'src/modules/Unity/Application/resources/icons/notepad@18.png'
3356Binary files src/modules/Unity/Application/resources/icons/notepad@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/notepad@18.png 2017-02-14 10:17:32 +0000 differ
3357=== added file 'src/modules/Unity/Application/resources/icons/pinterest@18.png'
3358Binary files src/modules/Unity/Application/resources/icons/pinterest@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/pinterest@18.png 2017-02-14 10:17:32 +0000 differ
3359=== added file 'src/modules/Unity/Application/resources/icons/soundcloud@18.png'
3360Binary files src/modules/Unity/Application/resources/icons/soundcloud@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/soundcloud@18.png 2017-02-14 10:17:32 +0000 differ
3361=== added file 'src/modules/Unity/Application/resources/icons/system-settings@18.png'
3362Binary files src/modules/Unity/Application/resources/icons/system-settings@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/system-settings@18.png 2017-02-14 10:17:32 +0000 differ
3363=== added file 'src/modules/Unity/Application/resources/icons/twitter@18.png'
3364Binary files src/modules/Unity/Application/resources/icons/twitter@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/twitter@18.png 2017-02-14 10:17:32 +0000 differ
3365=== added file 'src/modules/Unity/Application/resources/icons/weather@18.png'
3366Binary files src/modules/Unity/Application/resources/icons/weather@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/weather@18.png 2017-02-14 10:17:32 +0000 differ
3367=== added file 'src/modules/Unity/Application/resources/icons/wikipedia@18.png'
3368Binary files src/modules/Unity/Application/resources/icons/wikipedia@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/wikipedia@18.png 2017-02-14 10:17:32 +0000 differ
3369=== added file 'src/modules/Unity/Application/resources/icons/youtube@18.png'
3370Binary files src/modules/Unity/Application/resources/icons/youtube@18.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/icons/youtube@18.png 2017-02-14 10:17:32 +0000 differ
3371=== added directory 'src/modules/Unity/Application/resources/screenshots'
3372=== added file 'src/modules/Unity/Application/resources/screenshots/browser@12.png'
3373Binary files src/modules/Unity/Application/resources/screenshots/browser@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/browser@12.png 2017-02-14 10:17:32 +0000 differ
3374=== added file 'src/modules/Unity/Application/resources/screenshots/camera@12.png'
3375Binary files src/modules/Unity/Application/resources/screenshots/camera@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/camera@12.png 2017-02-14 10:17:32 +0000 differ
3376=== added file 'src/modules/Unity/Application/resources/screenshots/dialer@12.png'
3377Binary files src/modules/Unity/Application/resources/screenshots/dialer@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/dialer@12.png 2017-02-14 10:17:32 +0000 differ
3378=== added file 'src/modules/Unity/Application/resources/screenshots/facebook@12.png'
3379Binary files src/modules/Unity/Application/resources/screenshots/facebook@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/facebook@12.png 2017-02-14 10:17:32 +0000 differ
3380=== added file 'src/modules/Unity/Application/resources/screenshots/gallery@12.png'
3381Binary files src/modules/Unity/Application/resources/screenshots/gallery@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/gallery@12.png 2017-02-14 10:17:32 +0000 differ
3382=== added file 'src/modules/Unity/Application/resources/screenshots/gmail-webapp.svg'
3383--- src/modules/Unity/Application/resources/screenshots/gmail-webapp.svg 1970-01-01 00:00:00 +0000
3384+++ src/modules/Unity/Application/resources/screenshots/gmail-webapp.svg 2017-02-14 10:17:32 +0000
3385@@ -0,0 +1,343 @@
3386+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
3387+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3388+
3389+<svg
3390+ xmlns:dc="http://purl.org/dc/elements/1.1/"
3391+ xmlns:cc="http://creativecommons.org/ns#"
3392+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
3393+ xmlns:svg="http://www.w3.org/2000/svg"
3394+ xmlns="http://www.w3.org/2000/svg"
3395+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
3396+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
3397+ width="768"
3398+ height="1280"
3399+ id="svg2"
3400+ version="1.1"
3401+ inkscape:version="0.48.5 r10040"
3402+ sodipodi:docname="gmail-webapp.svg">
3403+ <defs
3404+ id="defs4" />
3405+ <sodipodi:namedview
3406+ id="base"
3407+ pagecolor="#ffffff"
3408+ bordercolor="#666666"
3409+ borderopacity="1.0"
3410+ inkscape:pageopacity="0.0"
3411+ inkscape:pageshadow="2"
3412+ inkscape:zoom="0.49497475"
3413+ inkscape:cx="117.33439"
3414+ inkscape:cy="668.80479"
3415+ inkscape:document-units="px"
3416+ inkscape:current-layer="layer1"
3417+ showgrid="false"
3418+ inkscape:window-width="1920"
3419+ inkscape:window-height="1056"
3420+ inkscape:window-x="0"
3421+ inkscape:window-y="24"
3422+ inkscape:window-maximized="1" />
3423+ <metadata
3424+ id="metadata7">
3425+ <rdf:RDF>
3426+ <cc:Work
3427+ rdf:about="">
3428+ <dc:format>image/svg+xml</dc:format>
3429+ <dc:type
3430+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
3431+ <dc:title></dc:title>
3432+ </cc:Work>
3433+ </rdf:RDF>
3434+ </metadata>
3435+ <g
3436+ inkscape:label="Layer 1"
3437+ inkscape:groupmode="layer"
3438+ id="layer1"
3439+ transform="translate(0,227.63782)">
3440+ <rect
3441+ style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3442+ id="rect2985"
3443+ width="769.73627"
3444+ height="1276.8328"
3445+ x="-2.0203052"
3446+ y="3.1671834"
3447+ transform="translate(0,-227.63782)" />
3448+ <rect
3449+ style="fill:#e6e6e6;fill-opacity:1;stroke:none;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3450+ id="rect3797"
3451+ width="773.77686"
3452+ height="129.29953"
3453+ x="-6.0609155"
3454+ y="-0.87342685"
3455+ transform="translate(0,-227.63782)" />
3456+ <text
3457+ xml:space="preserve"
3458+ style="font-size:72px;font-style:normal;font-weight:500;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium;font-stretch:normal;font-variant:normal"
3459+ x="20.203053"
3460+ y="-139.61781"
3461+ id="text3755"
3462+ sodipodi:linespacing="125%"><tspan
3463+ sodipodi:role="line"
3464+ id="tspan3757"
3465+ x="20.203053"
3466+ y="-139.61781">GMail</tspan></text>
3467+ <text
3468+ xml:space="preserve"
3469+ style="font-size:72px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3470+ x="14.142137"
3471+ y="-28.501045"
3472+ id="text3759"
3473+ sodipodi:linespacing="125%"><tspan
3474+ sodipodi:role="line"
3475+ id="tspan3761"
3476+ x="14.142137"
3477+ y="-28.501045">Inbox</tspan></text>
3478+ <path
3479+ sodipodi:type="arc"
3480+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3481+ id="path3765"
3482+ sodipodi:cx="107.07617"
3483+ sodipodi:cy="337.52768"
3484+ sodipodi:rx="64.649765"
3485+ sodipodi:ry="61.619305"
3486+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
3487+ transform="translate(-28.284271,-251.88148)" />
3488+ <path
3489+ style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
3490+ d="m 14.142136,237.52257 729.330134,0"
3491+ id="path3769"
3492+ inkscape:connector-curvature="0"
3493+ transform="translate(0,-227.63782)" />
3494+ <text
3495+ xml:space="preserve"
3496+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3497+ x="167.68533"
3498+ y="78.575127"
3499+ id="text3771"
3500+ sodipodi:linespacing="125%"><tspan
3501+ sodipodi:role="line"
3502+ id="tspan3773"
3503+ x="167.68533"
3504+ y="78.575127">Lorem ipsum</tspan></text>
3505+ <text
3506+ xml:space="preserve"
3507+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3508+ x="167.68532"
3509+ y="131.10306"
3510+ id="text3775"
3511+ sodipodi:linespacing="125%"><tspan
3512+ sodipodi:role="line"
3513+ id="tspan3777"
3514+ x="167.68532"
3515+ y="131.10306"
3516+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
3517+ <path
3518+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3519+ d="m 13.13199,165.44825 729.33013,0"
3520+ id="path3769-2"
3521+ inkscape:connector-curvature="0" />
3522+ <path
3523+ sodipodi:type="arc"
3524+ style="fill:#8eff58;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3525+ id="path3765-3"
3526+ sodipodi:cx="107.07617"
3527+ sodipodi:cy="337.52768"
3528+ sodipodi:rx="64.649765"
3529+ sodipodi:ry="61.619305"
3530+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
3531+ transform="translate(-26.263969,-93.102383)" />
3532+ <text
3533+ xml:space="preserve"
3534+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3535+ x="169.70564"
3536+ y="237.35422"
3537+ id="text3771-9"
3538+ sodipodi:linespacing="125%"><tspan
3539+ sodipodi:role="line"
3540+ id="tspan3773-7"
3541+ x="169.70564"
3542+ y="237.35422">Lorem ipsum</tspan></text>
3543+ <text
3544+ xml:space="preserve"
3545+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3546+ x="169.70561"
3547+ y="289.88217"
3548+ id="text3775-1"
3549+ sodipodi:linespacing="125%"><tspan
3550+ sodipodi:role="line"
3551+ id="tspan3777-1"
3552+ x="169.70561"
3553+ y="289.88217"
3554+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
3555+ <path
3556+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3557+ d="m 15.152292,324.22735 729.330128,0"
3558+ id="path3769-2-6"
3559+ inkscape:connector-curvature="0" />
3560+ <path
3561+ sodipodi:type="arc"
3562+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3563+ id="path3765-7"
3564+ sodipodi:cx="107.07617"
3565+ sodipodi:cy="337.52768"
3566+ sodipodi:rx="64.649765"
3567+ sodipodi:ry="61.619305"
3568+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
3569+ transform="translate(-28.284274,64.481414)" />
3570+ <text
3571+ xml:space="preserve"
3572+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3573+ x="167.68533"
3574+ y="394.93799"
3575+ id="text3771-92"
3576+ sodipodi:linespacing="125%"><tspan
3577+ sodipodi:role="line"
3578+ id="tspan3773-0"
3579+ x="167.68533"
3580+ y="394.93799">Lorem ipsum</tspan></text>
3581+ <text
3582+ xml:space="preserve"
3583+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3584+ x="167.68532"
3585+ y="447.46594"
3586+ id="text3775-9"
3587+ sodipodi:linespacing="125%"><tspan
3588+ sodipodi:role="line"
3589+ id="tspan3777-8"
3590+ x="167.68532"
3591+ y="447.46594"
3592+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
3593+ <path
3594+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3595+ d="m 13.131986,481.81114 729.330124,0"
3596+ id="path3769-2-3"
3597+ inkscape:connector-curvature="0" />
3598+ <path
3599+ sodipodi:type="arc"
3600+ style="fill:#8ea2ff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3601+ id="path3765-2"
3602+ sodipodi:cx="107.07617"
3603+ sodipodi:cy="337.52768"
3604+ sodipodi:rx="64.649765"
3605+ sodipodi:ry="61.619305"
3606+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
3607+ transform="translate(-26.263969,226.10582)" />
3608+ <text
3609+ xml:space="preserve"
3610+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3611+ x="169.70564"
3612+ y="556.56238"
3613+ id="text3771-1"
3614+ sodipodi:linespacing="125%"><tspan
3615+ sodipodi:role="line"
3616+ id="tspan3773-1"
3617+ x="169.70564"
3618+ y="556.56238">Lorem ipsum</tspan></text>
3619+ <text
3620+ xml:space="preserve"
3621+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3622+ x="169.70563"
3623+ y="609.09033"
3624+ id="text3775-8"
3625+ sodipodi:linespacing="125%"><tspan
3626+ sodipodi:role="line"
3627+ id="tspan3777-85"
3628+ x="169.70563"
3629+ y="609.09033"
3630+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
3631+ <path
3632+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3633+ d="m 15.152292,643.43555 729.330128,0"
3634+ id="path3769-2-1"
3635+ inkscape:connector-curvature="0" />
3636+ <path
3637+ sodipodi:type="arc"
3638+ style="fill:#8effff;fill-opacity:1;stroke:#000000;stroke-width:0.69999999;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3639+ id="path3765-8"
3640+ sodipodi:cx="107.07617"
3641+ sodipodi:cy="337.52768"
3642+ sodipodi:rx="64.649765"
3643+ sodipodi:ry="61.619305"
3644+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
3645+ transform="translate(-24.243664,391.77084)" />
3646+ <text
3647+ xml:space="preserve"
3648+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3649+ x="171.72594"
3650+ y="722.22742"
3651+ id="text3771-7"
3652+ sodipodi:linespacing="125%"><tspan
3653+ sodipodi:role="line"
3654+ id="tspan3773-6"
3655+ x="171.72594"
3656+ y="722.22742">Lorem ipsum</tspan></text>
3657+ <text
3658+ xml:space="preserve"
3659+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3660+ x="171.72592"
3661+ y="774.75537"
3662+ id="text3775-5"
3663+ sodipodi:linespacing="125%"><tspan
3664+ sodipodi:role="line"
3665+ id="tspan3777-2"
3666+ x="171.72592"
3667+ y="774.75537"
3668+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
3669+ <path
3670+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3671+ d="m 17.172597,809.10057 729.330133,0"
3672+ id="path3769-2-13"
3673+ inkscape:connector-curvature="0" />
3674+ <path
3675+ sodipodi:type="arc"
3676+ style="fill:#e44738;fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3677+ id="path3765-9"
3678+ sodipodi:cx="107.07617"
3679+ sodipodi:cy="337.52768"
3680+ sodipodi:rx="64.649765"
3681+ sodipodi:ry="61.619305"
3682+ d="m 171.72594,337.52768 a 64.649765,61.619305 0 1 1 -129.299533,0 64.649765,61.619305 0 1 1 129.299533,0 z"
3683+ transform="translate(-24.243664,557.43585)" />
3684+ <text
3685+ xml:space="preserve"
3686+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3687+ x="171.72594"
3688+ y="887.89246"
3689+ id="text3771-2"
3690+ sodipodi:linespacing="125%"><tspan
3691+ sodipodi:role="line"
3692+ id="tspan3773-8"
3693+ x="171.72594"
3694+ y="887.89246">Lorem ipsum</tspan></text>
3695+ <text
3696+ xml:space="preserve"
3697+ style="font-size:64px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Ubuntu;-inkscape-font-specification:Ubuntu"
3698+ x="171.72592"
3699+ y="940.42041"
3700+ id="text3775-3"
3701+ sodipodi:linespacing="125%"><tspan
3702+ sodipodi:role="line"
3703+ id="tspan3777-7"
3704+ x="171.72592"
3705+ y="940.42041"
3706+ style="font-size:48px;fill:#b3b3b3">bla bla bla bla bla bla bla ...</tspan></text>
3707+ <path
3708+ style="fill:none;stroke:#000000;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3709+ d="m 17.172597,974.76558 729.330123,0"
3710+ id="path3769-2-9"
3711+ inkscape:connector-curvature="0" />
3712+ <path
3713+ sodipodi:type="arc"
3714+ style="fill:none;stroke:#000000;stroke-width:8;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3715+ id="path3929"
3716+ sodipodi:cx="590.93921"
3717+ sodipodi:cy="-119.06127"
3718+ sodipodi:rx="33.335033"
3719+ sodipodi:ry="33.335033"
3720+ d="m 624.27424,-119.06127 a 33.335033,33.335033 0 1 1 -66.67006,0 33.335033,33.335033 0 1 1 66.67006,0 z"
3721+ transform="matrix(0.85096826,0,0,0.85096826,175.43863,-68.81729)" />
3722+ <path
3723+ style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:3.40387297;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
3724+ d="m 692.92249,-145.20602 20.63059,24.06901 12.03451,-13.75372 -20.63059,-17.19215"
3725+ id="path3931"
3726+ inkscape:connector-curvature="0" />
3727+ </g>
3728+</svg>
3729
3730=== added file 'src/modules/Unity/Application/resources/screenshots/libreoffice@12.png'
3731Binary files src/modules/Unity/Application/resources/screenshots/libreoffice@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/libreoffice@12.png 2017-02-14 10:17:32 +0000 differ
3732=== added file 'src/modules/Unity/Application/resources/screenshots/map@12.png'
3733Binary files src/modules/Unity/Application/resources/screenshots/map@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/map@12.png 2017-02-14 10:17:32 +0000 differ
3734=== added file 'src/modules/Unity/Application/resources/screenshots/music@12.png'
3735Binary files src/modules/Unity/Application/resources/screenshots/music@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/music@12.png 2017-02-14 10:17:32 +0000 differ
3736=== added file 'src/modules/Unity/Application/resources/screenshots/twitter@12.png'
3737Binary files src/modules/Unity/Application/resources/screenshots/twitter@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/twitter@12.png 2017-02-14 10:17:32 +0000 differ
3738=== added file 'src/modules/Unity/Application/resources/screenshots/ubuntu-weather-app.svg'
3739--- src/modules/Unity/Application/resources/screenshots/ubuntu-weather-app.svg 1970-01-01 00:00:00 +0000
3740+++ src/modules/Unity/Application/resources/screenshots/ubuntu-weather-app.svg 2017-02-14 10:17:32 +0000
3741@@ -0,0 +1,201 @@
3742+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
3743+<!-- Created with Inkscape (http://www.inkscape.org/) -->
3744+
3745+<svg
3746+ xmlns:dc="http://purl.org/dc/elements/1.1/"
3747+ xmlns:cc="http://creativecommons.org/ns#"
3748+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
3749+ xmlns:svg="http://www.w3.org/2000/svg"
3750+ xmlns="http://www.w3.org/2000/svg"
3751+ xmlns:xlink="http://www.w3.org/1999/xlink"
3752+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
3753+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
3754+ width="829"
3755+ height="480"
3756+ id="svg2740"
3757+ sodipodi:version="0.32"
3758+ inkscape:version="0.48.5 r10040"
3759+ version="1.0"
3760+ sodipodi:docname="ubuntu-weather-app.svg"
3761+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
3762+ <defs
3763+ id="defs2742">
3764+ <linearGradient
3765+ id="linearGradient3824">
3766+ <stop
3767+ style="stop-color:#e6e6e6;stop-opacity:1;"
3768+ offset="0"
3769+ id="stop3826" />
3770+ <stop
3771+ style="stop-color:#23abff;stop-opacity:1;"
3772+ offset="1"
3773+ id="stop3828" />
3774+ </linearGradient>
3775+ <linearGradient
3776+ inkscape:collect="always"
3777+ xlink:href="#linearGradient3824"
3778+ id="linearGradient3830"
3779+ x1="348.55862"
3780+ y1="343.23914"
3781+ x2="348.55862"
3782+ y2="-17.422215"
3783+ gradientUnits="userSpaceOnUse" />
3784+ </defs>
3785+ <sodipodi:namedview
3786+ id="base"
3787+ pagecolor="#ffffff"
3788+ bordercolor="#666666"
3789+ borderopacity="1.0"
3790+ gridtolerance="10000"
3791+ guidetolerance="10"
3792+ objecttolerance="10"
3793+ inkscape:pageopacity="0.0"
3794+ inkscape:pageshadow="2"
3795+ inkscape:zoom="0.82625984"
3796+ inkscape:cx="331.28234"
3797+ inkscape:cy="125.54212"
3798+ inkscape:document-units="px"
3799+ inkscape:current-layer="layer1"
3800+ showgrid="false"
3801+ inkscape:window-width="1145"
3802+ inkscape:window-height="847"
3803+ inkscape:window-x="268"
3804+ inkscape:window-y="63"
3805+ inkscape:window-maximized="0" />
3806+ <metadata
3807+ id="metadata2745">
3808+ <rdf:RDF>
3809+ <cc:Work
3810+ rdf:about="">
3811+ <dc:format>image/svg+xml</dc:format>
3812+ <dc:type
3813+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
3814+ </cc:Work>
3815+ </rdf:RDF>
3816+ </metadata>
3817+ <g
3818+ inkscape:label="Layer 1"
3819+ inkscape:groupmode="layer"
3820+ id="layer1"
3821+ transform="translate(-321.13452,-104.68346)">
3822+ <rect
3823+ style="fill:url(#linearGradient3830);fill-opacity:1;stroke:#000000;stroke-width:0.69999999000000002;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
3824+ id="rect2990"
3825+ width="832.66785"
3826+ height="480.47839"
3827+ x="-1.210273"
3828+ y="0.73188055"
3829+ transform="translate(321.13452,104.68346)" />
3830+ <path
3831+ d="m 310.66382,132.06057 c -1.82703,2.0443 -22.21039,-18.38308 -24.89334,-17.37811 -2.68295,1.00497 -6.73783,33.64192 -9.57527,34.01165 -2.83744,0.36973 -7.71546,-33.08881 -10.48084,-33.88783 -2.76539,-0.79903 -18.7793,19.34152 -21.17964,17.82892 -2.40034,-1.51259 5.69972,-26.09808 4.36486,-28.67057 -1.33487,-2.57249 -31.86305,-3.78032 -32.49182,-6.6501 -0.62876,-2.86978 27.66855,-10.399376 27.90248,-13.241764 0.23394,-2.842387 -18.99828,-18.90215 -17.89698,-21.393798 1.10131,-2.491647 26.85456,8.177399 28.59429,5.99159 1.73972,-2.185809 -3.39182,-29.683489 -0.75169,-30.507098 2.64013,-0.823608 18.50306,18.485327 21.32153,18.392972 2.81847,-0.09235 11.30312,-24.287685 14.18258,-23.722317 2.87947,0.565368 1.44329,25.667631 3.67226,27.34365 2.22897,1.676019 24.27161,-6.28288 25.90675,-4.004945 1.63514,2.277936 -12.63923,24.445025 -11.77955,27.07291 0.85968,2.627885 27.01183,5.1355 26.65457,7.852474 -0.35726,2.716973 -32.69913,3.875594 -34.04684,6.507203 -1.3477,2.631613 12.32368,32.410863 10.49665,34.455163 z"
3832+ id="path11949"
3833+ inkscape:flatsided="false"
3834+ inkscape:randomized="-0.092"
3835+ inkscape:rounded="0.1"
3836+ sodipodi:arg1="0.79570711"
3837+ sodipodi:arg2="1.144773"
3838+ sodipodi:cx="275.15002"
3839+ sodipodi:cy="88.090233"
3840+ sodipodi:r1="57.019234"
3841+ sodipodi:r2="32.818508"
3842+ sodipodi:sides="9"
3843+ sodipodi:type="star"
3844+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;marker:none;visibility:visible;display:inline;overflow:visible"
3845+ transform="matrix(2.527571,0,0,2.527571,85.214779,60.619097)" />
3846+ <path
3847+ d="m 194.51692,77.283737 c 0,20.659371 -17.86424,37.407103 -39.90091,37.407103 -22.03666,0 -39.90091,-16.747732 -39.90091,-37.407103 0,-20.659371 17.86425,-37.4071 39.90091,-37.4071 22.03667,0 39.90091,16.747729 39.90091,37.4071 z"
3848+ id="path11951"
3849+ sodipodi:cx="154.61601"
3850+ sodipodi:cy="77.283737"
3851+ sodipodi:rx="39.900909"
3852+ sodipodi:ry="37.407101"
3853+ sodipodi:type="arc"
3854+ style="fill:#f5ff12;fill-opacity:1;fill-rule:nonzero;stroke:#f5ff12;stroke-width:10.27388287;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
3855+ transform="matrix(2.4568175,0,0,2.1990879,404.91256,120.05076)" />
3856+ <path
3857+ transform="matrix(3.2663208,0,0,3.4016021,515.92101,-254.59331)"
3858+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:7.30059433;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
3859+ sodipodi:type="inkscape:offset"
3860+ inkscape:radius="0"
3861+ inkscape:original="M 110.53125 144.96875 C 100.76599 144.96875 92.384060 150.03292 88.281250 157.34375 C 86.642607 156.42892 84.835190 155.87500 82.906250 155.87500 C 77.073071 155.87500 72.219030 160.56753 70.718750 166.93750 C 68.157973 165.92533 65.180470 165.31250 62.000000 165.31250 C 52.458571 165.31250 44.718750 170.53296 44.718750 176.96875 C 44.718749 182.77816 51.066430 187.55402 59.312500 188.43750 C 58.942922 189.06456 58.656250 189.68551 58.656250 190.34375 C 58.656248 196.50568 75.538140 201.53125 96.343750 201.53125 C 117.14936 201.53125 134.03126 196.50568 134.03125 190.34375 C 134.03125 189.92916 133.77393 189.55916 133.62500 189.15625 C 134.53629 189.38287 135.43860 189.62500 136.43750 189.62500 C 142.00333 189.62501 146.53125 185.84318 146.53125 181.18750 C 146.53125 176.53182 142.00332 172.75000 136.43750 172.75000 C 135.75542 172.75000 135.17544 172.98560 134.53125 173.09375 C 135.09454 171.23218 135.46875 169.33268 135.46875 167.31250 C 135.46875 154.98864 124.31330 144.96875 110.53125 144.96875 z "
3862+ id="path11953"
3863+ d="m 110.53125,144.96875 c -9.76526,0 -18.14719,5.06417 -22.25,12.375 -1.638643,-0.91483 -3.44606,-1.46875 -5.375,-1.46875 -5.833179,0 -10.68722,4.69253 -12.1875,11.0625 -2.560777,-1.01217 -5.53828,-1.625 -8.71875,-1.625 -9.541429,0 -17.28125,5.22046 -17.28125,11.65625 -10e-7,5.80941 6.34768,10.58527 14.59375,11.46875 -0.369578,0.62706 -0.65625,1.24801 -0.65625,1.90625 -2e-6,6.16193 16.88189,11.1875 37.6875,11.1875 20.80561,0 37.68751,-5.02557 37.6875,-11.1875 0,-0.41459 -0.25732,-0.78459 -0.40625,-1.1875 0.91129,0.22662 1.8136,0.46875 2.8125,0.46875 5.56583,1e-5 10.09375,-3.78182 10.09375,-8.4375 0,-4.65568 -4.52793,-8.4375 -10.09375,-8.4375 -0.68208,0 -1.26206,0.2356 -1.90625,0.34375 0.56329,-1.86157 0.9375,-3.76107 0.9375,-5.78125 0,-12.32386 -11.15545,-22.34375 -24.9375,-22.34375 z" />
3864+ <path
3865+ inkscape:connector-curvature="0"
3866+ style="fill:#386195;fill-opacity:1;fill-rule:nonzero;stroke:#386195;stroke-width:24.07697487;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:0.5;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
3867+ id="path11963"
3868+ d="m 975.70833,305.75079 c -17.87875,0 -33.1897,9.10731 -40.70137,22.18913 -3.00012,-1.63696 -6.34124,-2.6392 -9.87286,-2.6392 -10.67967,0 -19.5639,8.39604 -22.31073,19.7943 -4.6884,-1.81117 -10.09942,-2.93251 -15.92242,-2.93251 -17.46892,0 -31.65122,9.35352 -31.65122,20.86955 0,10.39528 11.61748,18.9466 26.71485,20.52749 -0.67665,1.12204 -1.20995,2.24338 -1.20995,3.42121 0,11.02604 30.92124,19.98985 69.01327,19.98985 38.09195,0 69.0132,-8.96381 69.0132,-19.98985 0,-0.74183 -0.5017,-1.42952 -0.7743,-2.15046 1.6684,0.40556 3.3496,0.83088 5.1784,0.83088 10.1902,0 18.439,-6.7716 18.439,-15.10234 0,-8.33078 -8.2488,-15.05348 -18.439,-15.05348 -1.2488,0 -2.3535,0.39297 -3.5329,0.58647 1.0312,-3.33102 1.7422,-6.7466 1.7422,-10.36143 0,-22.05212 -20.4533,-39.97961 -45.68617,-39.97961 z" />
3869+ <path
3870+ inkscape:connector-curvature="0"
3871+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
3872+ sodipodi:nodetypes="cccc"
3873+ id="path11965"
3874+ d="m 717.80594,482.73655 c -32.27924,28.4432 5.62151,52.69793 20.03284,13.63938 l 11.08203,-36.22961 -31.11487,22.59023 z" />
3875+ <path
3876+ inkscape:connector-curvature="0"
3877+ style="fill:#6798e9;fill-opacity:1;fill-rule:nonzero;stroke:#6798e9;stroke-width:15.64964962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:3.20000005;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible"
3878+ sodipodi:nodetypes="cccc"
3879+ id="path11967"
3880+ d="m 799.47,485.76116 c -32.27925,28.4432 5.62151,52.69792 20.03284,13.63937 l 11.08202,-36.2296 -31.11486,22.59023 z" />
3881+ <text
3882+ xml:space="preserve"
3883+ style="font-size:32px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
3884+ x="68.985565"
3885+ y="92.712631"
3886+ id="text2992"
3887+ sodipodi:linespacing="125%"
3888+ transform="translate(321.13452,104.68346)"><tspan
3889+ sodipodi:role="line"
3890+ id="tspan2994"
3891+ x="68.985565"
3892+ y="92.712631" /></text>
3893+ <text
3894+ xml:space="preserve"
3895+ style="font-size:10px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
3896+ x="-202.1156"
3897+ y="155.64684"
3898+ id="text2996"
3899+ sodipodi:linespacing="125%"
3900+ transform="translate(321.13452,104.68346)"><tspan
3901+ sodipodi:role="line"
3902+ id="tspan2998"
3903+ x="-202.1156"
3904+ y="155.64684" /></text>
3905+ <text
3906+ xml:space="preserve"
3907+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
3908+ x="342.91943"
3909+ y="155.03653"
3910+ id="text3000"
3911+ sodipodi:linespacing="125%"><tspan
3912+ sodipodi:role="line"
3913+ id="tspan3002"
3914+ x="342.91943"
3915+ y="155.03653"
3916+ style="font-size:32px;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-family:Ubuntu;-inkscape-font-specification:Ubuntu Medium">Wheather App</tspan></text>
3917+ <text
3918+ xml:space="preserve"
3919+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ff0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
3920+ x="460.31592"
3921+ y="358.3624"
3922+ id="text3004"
3923+ sodipodi:linespacing="125%"><tspan
3924+ sodipodi:role="line"
3925+ id="tspan3006"
3926+ x="460.31592"
3927+ y="358.3624"
3928+ style="font-size:48px;fill:#ff0000;-inkscape-font-specification:Ubuntu;font-family:Ubuntu;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal">22C</tspan></text>
3929+ <text
3930+ xml:space="preserve"
3931+ style="font-size:24px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#0000ff;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
3932+ x="460.19751"
3933+ y="412.14325"
3934+ id="text3004-0"
3935+ sodipodi:linespacing="125%"><tspan
3936+ sodipodi:role="line"
3937+ id="tspan3006-4"
3938+ x="460.19751"
3939+ y="412.14325"
3940+ style="font-size:48px;fill:#0000ff;-inkscape-font-specification:Ubuntu Medium;font-family:Ubuntu;font-weight:500;font-style:normal;font-stretch:normal;font-variant:normal">14C</tspan></text>
3941+ </g>
3942+</svg>
3943
3944=== added file 'src/modules/Unity/Application/resources/screenshots/unity8-dash@12.png'
3945Binary files src/modules/Unity/Application/resources/screenshots/unity8-dash@12.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/unity8-dash@12.png 2017-02-14 10:17:32 +0000 differ
3946=== added file 'src/modules/Unity/Application/resources/screenshots/vkb_portrait.png'
3947Binary files src/modules/Unity/Application/resources/screenshots/vkb_portrait.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/screenshots/vkb_portrait.png 2017-02-14 10:17:32 +0000 differ
3948=== added file 'src/modules/Unity/Application/resources/surfaces.qrc'
3949--- src/modules/Unity/Application/resources/surfaces.qrc 1970-01-01 00:00:00 +0000
3950+++ src/modules/Unity/Application/resources/surfaces.qrc 2017-02-14 10:17:32 +0000
3951@@ -0,0 +1,39 @@
3952+<RCC>
3953+ <qresource prefix="/Unity/Application">
3954+ <file>vkb_portrait.png</file>
3955+ <file>screenshots/browser@12.png</file>
3956+ <file>screenshots/camera@12.png</file>
3957+ <file>screenshots/dialer@12.png</file>
3958+ <file>screenshots/facebook@12.png</file>
3959+ <file>screenshots/gallery@12.png</file>
3960+ <file>screenshots/gmail-webapp.svg</file>
3961+ <file>screenshots/map@12.png</file>
3962+ <file>screenshots/music@12.png</file>
3963+ <file>screenshots/twitter@12.png</file>
3964+ <file>screenshots/ubuntu-weather-app.svg</file>
3965+ <file>screenshots/unity8-dash@12.png</file>
3966+ <file>screenshots/vkb_portrait.png</file>
3967+ <file>screenshots/libreoffice@12.png</file>
3968+ <file>icons/browser@18.png</file>
3969+ <file>icons/calendar@18.png</file>
3970+ <file>icons/camera@18.png</file>
3971+ <file>icons/contacts-app@18.png</file>
3972+ <file>icons/dash@18.png</file>
3973+ <file>icons/dialer-app@18.png</file>
3974+ <file>icons/evernote@18.png</file>
3975+ <file>icons/facebook@18.png</file>
3976+ <file>icons/gallery@18.png</file>
3977+ <file>icons/gmail@18.png</file>
3978+ <file>icons/libreoffice@18.png</file>
3979+ <file>icons/map@18.png</file>
3980+ <file>icons/messages-app@18.png</file>
3981+ <file>icons/notepad@18.png</file>
3982+ <file>icons/pinterest@18.png</file>
3983+ <file>icons/soundcloud@18.png</file>
3984+ <file>icons/system-settings@18.png</file>
3985+ <file>icons/twitter@18.png</file>
3986+ <file>icons/weather@18.png</file>
3987+ <file>icons/wikipedia@18.png</file>
3988+ <file>icons/youtube@18.png</file>
3989+ </qresource>
3990+</RCC>
3991
3992=== added file 'src/modules/Unity/Application/resources/vkb_portrait.png'
3993Binary files src/modules/Unity/Application/resources/vkb_portrait.png 1970-01-01 00:00:00 +0000 and src/modules/Unity/Application/resources/vkb_portrait.png 2017-02-14 10:17:32 +0000 differ
3994=== modified file 'src/modules/Unity/Application/session.cpp'
3995--- src/modules/Unity/Application/session.cpp 2016-11-03 20:17:46 +0000
3996+++ src/modules/Unity/Application/session.cpp 2017-02-14 10:17:32 +0000
3997@@ -1,5 +1,5 @@
3998 /*
3999- * Copyright (C) 2014-2016 Canonical, Ltd.
4000+ * Copyright (C) 2014-2017 Canonical, Ltd.
4001 *
4002 * This program is free software: you can redistribute it and/or modify it under
4003 * the terms of the GNU Lesser General Public License version 3, as published by
4004@@ -215,6 +215,8 @@
4005 {
4006 DEBUG_MSG << "(surface=" << newSurface << ")";
4007
4008+ const bool focusedBefore = focused();
4009+
4010 connect(newSurface, &MirSurfaceInterface::stateChanged,
4011 this, &Session::updateFullscreenProperty);
4012
4013@@ -244,6 +246,11 @@
4014 setState(Running);
4015 }
4016
4017+ const bool focusedNow = focused();
4018+ if (focusedNow != focusedBefore) {
4019+ Q_EMIT focusedChanged(focused());
4020+ }
4021+
4022 updateFullscreenProperty();
4023 }
4024
4025@@ -389,6 +396,11 @@
4026 surface->setLive(false);
4027 }
4028
4029+ for (int i = 0; i < m_closingSurfaces.count(); ++i) {
4030+ auto surface = static_cast<MirSurfaceInterface*>(m_closingSurfaces[i]);
4031+ surface->setLive(false);
4032+ }
4033+
4034 deleteIfZombieAndEmpty();
4035 }
4036 }
4037
4038=== modified file 'src/modules/Unity/Application/session_interface.h'
4039--- src/modules/Unity/Application/session_interface.h 2016-11-03 20:17:46 +0000
4040+++ src/modules/Unity/Application/session_interface.h 2017-02-14 10:17:32 +0000
4041@@ -99,7 +99,7 @@
4042
4043 virtual pid_t pid() const = 0;
4044
4045- // For SessionManager use
4046+ // For TaskController use
4047
4048 virtual void addChildSession(SessionInterface* session) = 0;
4049 virtual void insertChildSession(uint index, SessionInterface* session) = 0;
4050
4051=== removed file 'src/modules/Unity/Application/sessionmanager.cpp'
4052--- src/modules/Unity/Application/sessionmanager.cpp 2016-11-03 20:17:46 +0000
4053+++ src/modules/Unity/Application/sessionmanager.cpp 1970-01-01 00:00:00 +0000
4054@@ -1,200 +0,0 @@
4055-/*
4056- * Copyright (C) 2014-2016 Canonical, Ltd.
4057- *
4058- * This program is free software: you can redistribute it and/or modify it under
4059- * the terms of the GNU Lesser General Public License version 3, as published by
4060- * the Free Software Foundation.
4061- *
4062- * This program is distributed in the hope that it will be useful, but WITHOUT
4063- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4064- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4065- * Lesser General Public License for more details.
4066- *
4067- * You should have received a copy of the GNU Lesser General Public License
4068- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4069- */
4070-
4071-// Qt
4072-#include <QGuiApplication>
4073-
4074-// local
4075-#include "application_manager.h"
4076-#include "debughelpers.h"
4077-#include "sessionmanager.h"
4078-
4079-// QPA mirserver
4080-#include "appnotifier.h"
4081-#include "logging.h"
4082-#include "nativeinterface.h"
4083-#include "promptsessionlistener.h"
4084-#include "promptsession.h"
4085-
4086-
4087-namespace qtmir {
4088-
4089-SessionManager *SessionManager::the_session_manager = nullptr;
4090-
4091-
4092-void connectToAppNotifier(SessionManager *manager, AppNotifier *appNotifier)
4093-{
4094- QObject::connect(appNotifier, &AppNotifier::appAdded,
4095- manager, &SessionManager::onSessionStarting);
4096- QObject::connect(appNotifier, &AppNotifier::appRemoved,
4097- manager, &SessionManager::onSessionStopping);
4098-}
4099-
4100-void connectToPromptSessionListener(SessionManager * manager, PromptSessionListener * listener)
4101-{
4102- QObject::connect(listener, &PromptSessionListener::promptSessionStarting,
4103- manager, &SessionManager::onPromptSessionStarting);
4104- QObject::connect(listener, &PromptSessionListener::promptSessionStopping,
4105- manager, &SessionManager::onPromptSessionStopping);
4106- QObject::connect(listener, &PromptSessionListener::promptProviderAdded,
4107- manager, &SessionManager::onPromptProviderAdded);
4108- QObject::connect(listener, &PromptSessionListener::promptProviderRemoved,
4109- manager, &SessionManager::onPromptProviderRemoved);
4110-}
4111-
4112-SessionManager* SessionManager::singleton()
4113-{
4114- if (!the_session_manager) {
4115-
4116- NativeInterface *nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());
4117-
4118- if (!nativeInterface) {
4119- qCritical("ERROR: Unity.Application QML plugin requires use of the 'mirserver' QPA plugin");
4120- QGuiApplication::quit();
4121- return nullptr;
4122- }
4123-
4124- auto appNotifier = static_cast<AppNotifier*>(nativeInterface->nativeResourceForIntegration("AppNotifier"));
4125- PromptSessionListener *promptSessionListener = static_cast<PromptSessionListener*>(nativeInterface->nativeResourceForIntegration("PromptSessionListener"));
4126-
4127- the_session_manager = new SessionManager(nativeInterface->thePromptSessionManager(), ApplicationManager::singleton());
4128-
4129- connectToAppNotifier(the_session_manager, appNotifier);
4130- connectToPromptSessionListener(the_session_manager, promptSessionListener);
4131- }
4132- return the_session_manager;
4133-}
4134-
4135-SessionManager::SessionManager(
4136- const std::shared_ptr<qtmir::PromptSessionManager>& promptSessionManager,
4137- ApplicationManager* applicationManager,
4138- QObject *parent)
4139- : SessionModel(parent)
4140- , m_promptSessionManager(promptSessionManager)
4141- , m_applicationManager(applicationManager)
4142-{
4143- qCDebug(QTMIR_SESSIONS) << "SessionManager::SessionManager - this=" << this;
4144- setObjectName(QStringLiteral("qtmir::SessionManager"));
4145-}
4146-
4147-SessionManager::~SessionManager()
4148-{
4149- qCDebug(QTMIR_SESSIONS) << "SessionManager::~SessionManager - this=" << this;
4150-}
4151-
4152-SessionInterface *SessionManager::findSession(const mir::scene::Session* session) const
4153-{
4154- if (!session) return nullptr;
4155-
4156- for (SessionInterface* child : list()) {
4157- if (child->session().get() == session)
4158- return child;
4159- }
4160- return nullptr;
4161-}
4162-
4163-void SessionManager::onSessionStarting(const miral::ApplicationInfo &appInfo)
4164-{
4165- qCDebug(QTMIR_SESSIONS) << "SessionManager::onSessionStarting - sessionName=" << appInfo.name().c_str();
4166-
4167- const auto &session = appInfo.application();
4168- Session* qmlSession = new Session(session, m_promptSessionManager);
4169- insert(0, qmlSession);
4170-
4171- Application* application = m_applicationManager->findApplicationWithSession(session);
4172- if (application && application->state() != Application::Running) {
4173- application->setSession(qmlSession);
4174- }
4175- // need to remove if we've destroyed outside
4176- connect(qmlSession, &Session::destroyed, this, [&](QObject *item) {
4177- auto sessionToRemove = static_cast<Session*>(item);
4178- remove(sessionToRemove);
4179- });
4180-
4181- Q_EMIT sessionStarting(qmlSession);
4182-}
4183-
4184-void SessionManager::onSessionStopping(const miral::ApplicationInfo &appInfo)
4185-{
4186- qCDebug(QTMIR_SESSIONS) << "SessionManager::onSessionStopping - sessionName=" << appInfo.name().c_str();
4187-
4188- SessionInterface* qmlSession = findSession(appInfo.application().get());
4189- if (!qmlSession) return;
4190-
4191- remove(qmlSession);
4192-
4193- qmlSession->setLive(false);
4194- Q_EMIT sessionStopping(qmlSession);
4195-}
4196-
4197-void SessionManager::onPromptSessionStarting(const qtmir::PromptSession &promptSession)
4198-{
4199- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptSessionStarting - promptSession=" << promptSession.get();
4200-
4201- std::shared_ptr<mir::scene::Session> appSession = m_promptSessionManager->applicationFor(promptSession);
4202- SessionInterface *qmlAppSession = findSession(appSession.get());
4203- if (qmlAppSession) {
4204- m_mirPromptToSessionHash[promptSession.get()] = qmlAppSession;
4205- qmlAppSession->appendPromptSession(promptSession);
4206- } else {
4207- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptSessionStarting - could not find app session for prompt session";
4208- }
4209-}
4210-
4211-void SessionManager::onPromptSessionStopping(const qtmir::PromptSession &promptSession)
4212-{
4213- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptSessionStopping - promptSession=" << promptSession.get();
4214-
4215- for (SessionInterface *qmlSession : this->list()) {
4216- qmlSession->removePromptSession(promptSession);
4217- }
4218- m_mirPromptToSessionHash.remove(promptSession.get());
4219-}
4220-
4221-void SessionManager::onPromptProviderAdded(const qtmir::PromptSession &promptSession,
4222- const std::shared_ptr<mir::scene::Session> &promptProvider)
4223-{
4224- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptProviderAdded - promptSession=" << promptSession.get() << " promptProvider=" << promptProvider.get();
4225-
4226- SessionInterface* qmlAppSession = m_mirPromptToSessionHash.value(promptSession.get(), nullptr);
4227- if (!qmlAppSession) {
4228- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptProviderAdded - could not find session item for app session";
4229- return;
4230- }
4231-
4232- SessionInterface* qmlPromptProvider = findSession(promptProvider.get());
4233- if (!qmlPromptProvider) {
4234- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptProviderAdded - could not find session item for provider session";
4235- return;
4236- }
4237-
4238- qmlAppSession->addChildSession(qmlPromptProvider);
4239-}
4240-
4241-void SessionManager::onPromptProviderRemoved(const qtmir::PromptSession &promptSession,
4242- const std::shared_ptr<mir::scene::Session> &promptProvider)
4243-{
4244- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptProviderRemoved - promptSession=" << promptSession.get() << " promptProvider=" << promptProvider.get();
4245-
4246- SessionInterface* qmlPromptProvider = findSession(promptProvider.get());
4247- if (!qmlPromptProvider) {
4248- qCDebug(QTMIR_SESSIONS) << "SessionManager::onPromptProviderAdded - could not find session item for provider session";
4249- return;
4250- }
4251- qmlPromptProvider->setLive(false);
4252-}
4253-
4254-} // namespace qtmir
4255
4256=== removed file 'src/modules/Unity/Application/sessionmanager.h'
4257--- src/modules/Unity/Application/sessionmanager.h 2016-11-03 20:17:46 +0000
4258+++ src/modules/Unity/Application/sessionmanager.h 1970-01-01 00:00:00 +0000
4259@@ -1,90 +0,0 @@
4260-/*
4261- * Copyright (C) 2014-2015 Canonical, Ltd.
4262- *
4263- * This program is free software: you can redistribute it and/or modify it under
4264- * the terms of the GNU Lesser General Public License version 3, as published by
4265- * the Free Software Foundation.
4266- *
4267- * This program is distributed in the hope that it will be useful, but WITHOUT
4268- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4269- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4270- * Lesser General Public License for more details.
4271- *
4272- * You should have received a copy of the GNU Lesser General Public License
4273- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4274- */
4275-
4276-#ifndef SESSIONMANAGER_H
4277-#define SESSIONMANAGER_H
4278-
4279-// std
4280-#include <memory>
4281-
4282-// Qt
4283-#include <QHash>
4284-#include <QSharedPointer>
4285-
4286-// Mir
4287-#include <mir_toolkit/common.h>
4288-
4289-// miral
4290-#include <miral/application_info.h>
4291-
4292-// local
4293-#include "session.h"
4294-#include "sessionmodel.h"
4295-
4296-namespace mir {
4297- namespace scene {
4298- class Session;
4299- class PromptSession;
4300- }
4301-}
4302-
4303-namespace qtmir {
4304-
4305-class Application;
4306-class ApplicationManager;
4307-
4308-class SessionManager : public SessionModel
4309-{
4310- Q_OBJECT
4311-
4312-public:
4313- explicit SessionManager(
4314- const std::shared_ptr<PromptSessionManager>& promptSessionManager,
4315- ApplicationManager* applicationManager,
4316- QObject *parent = 0
4317- );
4318- ~SessionManager();
4319-
4320- static SessionManager* singleton();
4321-
4322- SessionInterface *findSession(const mir::scene::Session* session) const;
4323-
4324-Q_SIGNALS:
4325- void sessionStarting(SessionInterface* session);
4326- void sessionStopping(SessionInterface* session);
4327-
4328-public Q_SLOTS:
4329- void onSessionStarting(const miral::ApplicationInfo &appInfo);
4330- void onSessionStopping(const miral::ApplicationInfo &appInfo);
4331-
4332- void onPromptSessionStarting(const PromptSession& promptSession);
4333- void onPromptSessionStopping(const PromptSession& promptSession);
4334- void onPromptProviderAdded(const qtmir::PromptSession &promptSession, const std::shared_ptr<mir::scene::Session> &);
4335- void onPromptProviderRemoved(const qtmir::PromptSession &promptSession, const std::shared_ptr<mir::scene::Session> &);
4336-
4337-protected:
4338-
4339-private:
4340- const std::shared_ptr<PromptSessionManager> m_promptSessionManager;
4341- ApplicationManager* m_applicationManager;
4342- static SessionManager *the_session_manager;
4343-
4344- QHash<const mir::scene::PromptSession *, SessionInterface *> m_mirPromptToSessionHash;
4345-};
4346-
4347-} // namespace qtmir
4348-
4349-#endif // SESSIONMANAGER_H
4350
4351=== modified file 'src/modules/Unity/Application/surfacemanager.cpp'
4352--- src/modules/Unity/Application/surfacemanager.cpp 2017-02-02 09:17:48 +0000
4353+++ src/modules/Unity/Application/surfacemanager.cpp 2017-02-14 10:17:32 +0000
4354@@ -1,5 +1,5 @@
4355 /*
4356- * Copyright (C) 2016 Canonical, Ltd.
4357+ * Copyright (C) 2016,2017 Canonical, Ltd.
4358 *
4359 * This program is free software: you can redistribute it and/or modify it under
4360 * the terms of the GNU Lesser General Public License version 3, as published by
4361@@ -17,7 +17,7 @@
4362 #include "surfacemanager.h"
4363
4364 #include "mirsurface.h"
4365-#include "sessionmanager.h"
4366+#include "application_manager.h"
4367
4368 // mirserver
4369 #include "nativeinterface.h"
4370@@ -54,8 +54,6 @@
4371
4372 auto windowModel = static_cast<WindowModelNotifier*>(nativeInterface->nativeResourceForIntegration("WindowModelNotifier"));
4373 connectToWindowModelNotifier(windowModel);
4374-
4375- m_sessionManager = SessionManager::singleton();
4376 }
4377
4378 void SurfaceManager::connectToWindowModelNotifier(WindowModelNotifier *notifier)
4379@@ -99,7 +97,7 @@
4380 }
4381
4382 auto mirSession = window.windowInfo.window().application();
4383- SessionInterface* session = m_sessionManager->findSession(mirSession.get());
4384+ SessionInterface* session = ApplicationManager::singleton()->findSession(mirSession.get());
4385
4386 MirSurface *parentSurface;
4387 {
4388@@ -122,6 +120,7 @@
4389
4390 void SurfaceManager::onWindowRemoved(const miral::WindowInfo &windowInfo)
4391 {
4392+ DEBUG_MSG << "()";
4393 MirSurface *surface = find(windowInfo);
4394 forgetMirSurface(windowInfo.window());
4395 surface->setLive(false);
4396
4397=== modified file 'src/modules/Unity/Application/surfacemanager.h'
4398--- src/modules/Unity/Application/surfacemanager.h 2017-02-02 09:17:48 +0000
4399+++ src/modules/Unity/Application/surfacemanager.h 2017-02-14 10:17:32 +0000
4400@@ -1,5 +1,5 @@
4401 /*
4402- * Copyright (C) 2016 Canonical, Ltd.
4403+ * Copyright (C) 2016,2017 Canonical, Ltd.
4404 *
4405 * This program is free software: you can redistribute it and/or modify it under
4406 * the terms of the GNU Lesser General Public License version 3, as published by
4407@@ -31,7 +31,6 @@
4408 namespace qtmir {
4409
4410 class MirSurface;
4411-class SessionManager;
4412 class WindowControllerInterface;
4413
4414 class SurfaceManager : public unity::shell::application::SurfaceManagerInterface
4415@@ -66,7 +65,6 @@
4416 QVector<MirSurface*> m_allSurfaces;
4417
4418 WindowControllerInterface *m_windowController;
4419- SessionManager* m_sessionManager;
4420 };
4421
4422 } // namespace qtmir
4423
4424=== added file 'src/modules/Unity/Application/taskcontroller.cpp'
4425--- src/modules/Unity/Application/taskcontroller.cpp 1970-01-01 00:00:00 +0000
4426+++ src/modules/Unity/Application/taskcontroller.cpp 2017-02-14 10:17:32 +0000
4427@@ -0,0 +1,196 @@
4428+/*
4429+ * Copyright (C) 2017 Canonical, Ltd.
4430+ *
4431+ * This program is free software: you can redistribute it and/or modify it under
4432+ * the terms of the GNU Lesser General Public License version 3, as published by
4433+ * the Free Software Foundation.
4434+ *
4435+ * This program is distributed in the hope that it will be useful, but WITHOUT
4436+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4437+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4438+ * Lesser General Public License for more details.
4439+ *
4440+ * You should have received a copy of the GNU Lesser General Public License
4441+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4442+ */
4443+
4444+// local
4445+#include "taskcontroller.h"
4446+#include "session.h"
4447+
4448+#include <QGuiApplication>
4449+
4450+// QPA mirserver
4451+#include "logging.h"
4452+#include "nativeinterface.h"
4453+#include "promptsessionlistener.h"
4454+#include "promptsession.h"
4455+#include <sessionauthorizer.h>
4456+
4457+// common
4458+#include "appnotifier.h"
4459+
4460+#define DEBUG_MSG qCDebug(QTMIR_SESSIONS).nospace() << "TaskController::" << __func__
4461+
4462+using namespace qtmir;
4463+
4464+void TaskController::connectToAppNotifier(AppNotifier *appNotifier)
4465+{
4466+ QObject::connect(appNotifier, &AppNotifier::appAdded,
4467+ this, &TaskController::onSessionStarting);
4468+ QObject::connect(appNotifier, &AppNotifier::appRemoved,
4469+ this, &TaskController::onSessionStopping);
4470+}
4471+
4472+void TaskController::connectToPromptSessionListener(PromptSessionListener * listener)
4473+{
4474+ QObject::connect(listener, &PromptSessionListener::promptSessionStarting,
4475+ this, &TaskController::onPromptSessionStarting);
4476+ QObject::connect(listener, &PromptSessionListener::promptSessionStopping,
4477+ this, &TaskController::onPromptSessionStopping);
4478+ QObject::connect(listener, &PromptSessionListener::promptProviderAdded,
4479+ this, &TaskController::onPromptProviderAdded);
4480+ QObject::connect(listener, &PromptSessionListener::promptProviderRemoved,
4481+ this, &TaskController::onPromptProviderRemoved);
4482+}
4483+
4484+TaskController::TaskController(QObject *parent)
4485+ : QObject(parent)
4486+{
4487+ Q_ASSERT(PidFetcher::m_instance == nullptr);
4488+ PidFetcher::m_instance = this;
4489+
4490+ NativeInterface *nativeInterface = dynamic_cast<NativeInterface*>(QGuiApplication::platformNativeInterface());
4491+
4492+ if (!nativeInterface) {
4493+ qFatal("ERROR: Unity.Application QML plugin requires use of the 'mirserver' QPA plugin");
4494+ }
4495+
4496+ m_promptSessionManager = nativeInterface->thePromptSessionManager();
4497+
4498+ auto appNotifier = static_cast<AppNotifier*>(nativeInterface->nativeResourceForIntegration("AppNotifier"));
4499+ connectToAppNotifier(appNotifier);
4500+
4501+ auto promptSessionListener = static_cast<PromptSessionListener*>(nativeInterface->nativeResourceForIntegration("PromptSessionListener"));
4502+ connectToPromptSessionListener(promptSessionListener);
4503+
4504+ auto sessionAuthorizer = static_cast<SessionAuthorizer*>(nativeInterface->nativeResourceForIntegration("SessionAuthorizer"));
4505+ QObject::connect(sessionAuthorizer, &SessionAuthorizer::requestAuthorizationForSession,
4506+ this, &TaskController::onAuthorizationForSessionRequested, Qt::BlockingQueuedConnection);
4507+}
4508+
4509+TaskController::TaskController(std::shared_ptr<PromptSessionManager> promptSessionManager, QObject *parent)
4510+ : QObject(parent)
4511+ , m_promptSessionManager(promptSessionManager)
4512+{
4513+ Q_ASSERT(PidFetcher::m_instance == nullptr);
4514+ PidFetcher::m_instance = this;
4515+}
4516+
4517+TaskController::~TaskController()
4518+{
4519+ Q_ASSERT(PidFetcher::m_instance != nullptr);
4520+ PidFetcher::m_instance = nullptr;
4521+}
4522+
4523+void TaskController::onSessionStarting(const miral::ApplicationInfo &appInfo)
4524+{
4525+ DEBUG_MSG << " - sessionName=" << appInfo.name().c_str();
4526+
4527+ const auto &session = appInfo.application();
4528+ Session* qmlSession = new Session(session, m_promptSessionManager);
4529+ m_sessionList.prepend(qmlSession);
4530+
4531+ // need to remove if we've destroyed outside
4532+ connect(qmlSession, &Session::destroyed, this, [&](QObject *item) {
4533+ auto sessionToRemove = static_cast<Session*>(item);
4534+ m_sessionList.removeAll(sessionToRemove);
4535+ });
4536+
4537+ Q_EMIT sessionStarting(qmlSession);
4538+}
4539+
4540+void TaskController::onSessionStopping(const miral::ApplicationInfo &appInfo)
4541+{
4542+ DEBUG_MSG << " - sessionName=" << appInfo.name().c_str();
4543+
4544+ SessionInterface* qmlSession = findSession(appInfo.application().get());
4545+ if (!qmlSession) return;
4546+
4547+ m_sessionList.removeAll(qmlSession);
4548+
4549+ qmlSession->setLive(false);
4550+}
4551+
4552+void TaskController::onPromptSessionStarting(const qtmir::PromptSession &promptSession)
4553+{
4554+ DEBUG_MSG << " - promptSession=" << promptSession.get();
4555+
4556+ std::shared_ptr<mir::scene::Session> appSession = m_promptSessionManager->applicationFor(promptSession);
4557+ SessionInterface *qmlAppSession = findSession(appSession.get());
4558+ if (qmlAppSession) {
4559+ m_mirPromptToSessionHash[promptSession.get()] = qmlAppSession;
4560+ qmlAppSession->appendPromptSession(promptSession);
4561+ } else {
4562+ DEBUG_MSG << " - could not find app session for prompt session";
4563+ }
4564+}
4565+
4566+void TaskController::onPromptSessionStopping(const qtmir::PromptSession &promptSession)
4567+{
4568+ DEBUG_MSG << " - promptSession=" << promptSession.get();
4569+
4570+ for (SessionInterface *qmlSession : m_sessionList) {
4571+ qmlSession->removePromptSession(promptSession);
4572+ }
4573+ m_mirPromptToSessionHash.remove(promptSession.get());
4574+}
4575+
4576+void TaskController::onPromptProviderAdded(const qtmir::PromptSession &promptSession,
4577+ const std::shared_ptr<mir::scene::Session> &promptProvider)
4578+{
4579+ DEBUG_MSG << " - promptSession=" << promptSession.get() << " promptProvider=" << promptProvider.get();
4580+
4581+ SessionInterface* qmlAppSession = m_mirPromptToSessionHash.value(promptSession.get(), nullptr);
4582+ if (!qmlAppSession) {
4583+ DEBUG_MSG << " - could not find session item for app session";
4584+ return;
4585+ }
4586+
4587+ SessionInterface* qmlPromptProvider = findSession(promptProvider.get());
4588+ if (!qmlPromptProvider) {
4589+ DEBUG_MSG << " - could not find session item for provider session";
4590+ return;
4591+ }
4592+
4593+ qmlAppSession->addChildSession(qmlPromptProvider);
4594+}
4595+
4596+void TaskController::onPromptProviderRemoved(const qtmir::PromptSession &promptSession,
4597+ const std::shared_ptr<mir::scene::Session> &promptProvider)
4598+{
4599+ DEBUG_MSG << " - promptSession=" << promptSession.get() << " promptProvider=" << promptProvider.get();
4600+
4601+ SessionInterface* qmlPromptProvider = findSession(promptProvider.get());
4602+ if (!qmlPromptProvider) {
4603+ DEBUG_MSG << " - could not find session item for provider session";
4604+ return;
4605+ }
4606+ qmlPromptProvider->setLive(false);
4607+}
4608+
4609+SessionInterface *TaskController::findSession(const mir::scene::Session* session) const
4610+{
4611+ if (!session) return nullptr;
4612+
4613+ Q_FOREACH(SessionInterface* child, m_sessionList) {
4614+ if (child->session().get() == session)
4615+ return child;
4616+ }
4617+ return nullptr;
4618+}
4619+
4620+void TaskController::onAuthorizationForSessionRequested(const pid_t &pid, bool &authorized)
4621+{
4622+ Q_EMIT authorizationRequestedForSession(pid, authorized);
4623+}
4624
4625=== modified file 'src/modules/Unity/Application/taskcontroller.h'
4626--- src/modules/Unity/Application/taskcontroller.h 2016-04-22 14:03:26 +0000
4627+++ src/modules/Unity/Application/taskcontroller.h 2017-02-14 10:17:32 +0000
4628@@ -1,5 +1,5 @@
4629 /*
4630- * Copyright (C) 2014-2015 Canonical, Ltd.
4631+ * Copyright (C) 2014-2017 Canonical, Ltd.
4632 *
4633 * This program is free software: you can redistribute it and/or modify it under
4634 * the terms of the GNU Lesser General Public License version 3, as published by
4635@@ -23,12 +23,39 @@
4636 #include <QString>
4637 #include <QStringList>
4638
4639+// miral
4640+#include <miral/application.h>
4641+#include <miral/application_info.h>
4642+
4643+// mirserver
4644+#include "promptsessionmanager.h"
4645+#include <pidfetcher.h>
4646+
4647+namespace mir {
4648+ namespace scene {
4649+ class Session;
4650+ class PromptSession;
4651+ }
4652+}
4653+
4654+namespace unity {
4655+ namespace shell {
4656+ namespace application {
4657+ class MirSurfaceInterface;
4658+ }
4659+ }
4660+}
4661+
4662+class PromptSessionListener;
4663+
4664 namespace qtmir
4665 {
4666
4667+class AppNotifier;
4668 class ApplicationInfo;
4669+class SessionInterface;
4670
4671-class TaskController : public QObject
4672+class TaskController : public QObject, PidFetcher
4673 {
4674 Q_OBJECT
4675
4676@@ -39,8 +66,7 @@
4677 APPLICATION_FAILED_TO_START
4678 };
4679
4680- TaskController(const TaskController&) = delete;
4681- virtual ~TaskController() = default;
4682+ virtual ~TaskController();
4683
4684 TaskController& operator=(const TaskController&) = delete;
4685
4686@@ -54,6 +80,14 @@
4687
4688 virtual QSharedPointer<qtmir::ApplicationInfo> getInfoForApp(const QString &appId) const = 0;
4689
4690+ // needed for injecting fake info when running a fake TaskController for testing
4691+ // While this class is not the best fit for them, it's good the have them all under the same interface
4692+private:
4693+ pid_t pidOf_impl(const miral::Application &application) override { return miral::pid_of(application); }
4694+public:
4695+
4696+ SessionInterface *findSession(const mir::scene::Session* session) const;
4697+
4698 Q_SIGNALS:
4699 void processStarting(const QString &appId);
4700 void applicationStarted(const QString &appId);
4701@@ -64,8 +98,32 @@
4702
4703 void processFailed(const QString &appId, TaskController::Error error);
4704
4705+ void authorizationRequestedForSession(const pid_t &pid, bool &authorized);
4706+
4707+ void sessionStarting(SessionInterface *session);
4708+
4709+public Q_SLOTS:
4710+ virtual void onSessionStarting(const miral::ApplicationInfo &appInfo);
4711+ virtual void onSessionStopping(const miral::ApplicationInfo &appInfo);
4712+
4713+ virtual void onPromptSessionStarting(const PromptSession& promptSession);
4714+ virtual void onPromptSessionStopping(const PromptSession& promptSession);
4715+ virtual void onPromptProviderAdded(const qtmir::PromptSession &promptSession, const std::shared_ptr<mir::scene::Session> &);
4716+ virtual void onPromptProviderRemoved(const qtmir::PromptSession &promptSession, const std::shared_ptr<mir::scene::Session> &);
4717+
4718+ virtual void onAuthorizationForSessionRequested(const pid_t &pid, bool &authorized);
4719+
4720 protected:
4721- TaskController() = default;
4722+ TaskController(QObject *parent = nullptr);
4723+ TaskController(std::shared_ptr<PromptSessionManager>, QObject *parent = nullptr);
4724+
4725+ void connectToAppNotifier(AppNotifier *);
4726+ void connectToPromptSessionListener(PromptSessionListener *);
4727+
4728+ std::shared_ptr<PromptSessionManager> m_promptSessionManager;
4729+
4730+ QHash<const mir::scene::PromptSession *, SessionInterface *> m_mirPromptToSessionHash;
4731+ QList<SessionInterface*> m_sessionList;
4732 };
4733
4734 } // namespace qtmir
4735
4736=== modified file 'src/platforms/mirserver/CMakeLists.txt'
4737--- src/platforms/mirserver/CMakeLists.txt 2017-02-02 09:36:07 +0000
4738+++ src/platforms/mirserver/CMakeLists.txt 2017-02-14 10:17:32 +0000
4739@@ -61,6 +61,7 @@
4740 shelluuid.cpp
4741 ubuntutheme.cpp
4742 clipboard.cpp
4743+ initialsurfacesizes.cpp
4744 openglcontextfactory.cpp openglcontextfactory.h
4745 initialsurfacesizes.cpp initialsurfacesizes.h
4746 mircursorimages.cpp
4747@@ -70,6 +71,7 @@
4748 promptsessionlistener.cpp
4749 mirserverstatuslistener.cpp
4750 screenscontroller.cpp
4751+ pidfetcher.cpp pidfetcher.h
4752 nativeinterface.cpp
4753 qtcompositor.cpp
4754 services.cpp
4755
4756=== removed file 'src/platforms/mirserver/argvHelper.h'
4757--- src/platforms/mirserver/argvHelper.h 2015-12-12 17:45:28 +0000
4758+++ src/platforms/mirserver/argvHelper.h 1970-01-01 00:00:00 +0000
4759@@ -1,52 +0,0 @@
4760-/*
4761- * Copyright (C) 2015 Canonical, Ltd.
4762- *
4763- * This program is free software: you can redistribute it and/or modify it under
4764- * the terms of the GNU Lesser General Public License version 3, as published by
4765- * the Free Software Foundation.
4766- *
4767- * This program is distributed in the hope that it will be useful, but WITHOUT
4768- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4769- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4770- * Lesser General Public License for more details.
4771- *
4772- * You should have received a copy of the GNU Lesser General Public License
4773- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4774- */
4775-
4776-#ifndef ARGVHELPER_P_H
4777-#define ARGVHELPER_P_H
4778-
4779-#include <cassert>
4780-
4781-namespace qtmir {
4782-
4783-// Function to edit argv to strip out unmatched entries of targetArray, so both arrays have identical contents
4784-// Note: Mir parses out arguments that it understands, but it also removes argv[0] (bug pad.lv/1511509)
4785-// so need to ensure argv[0] is the binary name as usual.
4786-void editArgvToMatch(int &argcToEdit, char** argvToEdit, int targetCount, const char* const targetArray[])
4787-{
4788- // Make copy of the argv array of pointers, as we will be editing the original
4789- const size_t arraySize = (argcToEdit + 1) * sizeof(char*);
4790- char** argvCopy = static_cast<char**>(malloc(arraySize));
4791- memcpy(argvCopy, argvToEdit, arraySize);
4792-
4793- int k=1; // index of argv we want to edit - note we'll leave argv[0] alone
4794- for (int i=0; i<targetCount; i++) { // taking each argument Mir did not parse out
4795- for (int j=1; j<argcToEdit; j++) { // find pointer to same argument in argvCopy (leave arg[0] out)
4796- if (strcmp(targetArray[i], argvCopy[j]) == 0) {
4797- argvToEdit[k] = const_cast<char*>(argvCopy[j]); // edit argv to position that argument to match argv2.
4798- k++;
4799- break;
4800- }
4801- }
4802- }
4803- assert(k == targetCount+1);
4804- argvToEdit[k] = nullptr;
4805- free(argvCopy);
4806- argcToEdit = targetCount+1; // now argv and targetArray should have list the same strings.
4807-}
4808-
4809-} // namespace qtmir
4810-
4811-#endif // ARGVHELPER_P_H
4812
4813=== modified file 'src/platforms/mirserver/cursor.cpp'
4814--- src/platforms/mirserver/cursor.cpp 2016-11-03 20:17:46 +0000
4815+++ src/platforms/mirserver/cursor.cpp 2017-02-14 10:17:32 +0000
4816@@ -1,5 +1,5 @@
4817 /*
4818- * Copyright (C) 2015 Canonical, Ltd.
4819+ * Copyright (C) 2015,2017 Canonical, Ltd.
4820 *
4821 * This program is free software: you can redistribute it and/or modify it under
4822 * the terms of the GNU Lesser General Public License version 3, as published by
4823@@ -98,7 +98,7 @@
4824 updateMousePointerCursorName();
4825 }
4826
4827-bool Cursor::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
4828+bool Cursor::handleMouseEvent(ulong timestamp, QPointF movement, QPointF position, Qt::MouseButtons buttons,
4829 Qt::KeyboardModifiers modifiers)
4830 {
4831 QMutexLocker locker(&m_mutex);
4832@@ -111,6 +111,7 @@
4833 bool ok = QMetaObject::invokeMethod(m_mousePointer, "handleMouseEvent", Qt::AutoConnection,
4834 Q_ARG(ulong, timestamp),
4835 Q_ARG(QPointF, movement),
4836+ Q_ARG(QPointF, position),
4837 Q_ARG(Qt::MouseButtons, buttons),
4838 Q_ARG(Qt::KeyboardModifiers, modifiers));
4839
4840@@ -136,7 +137,7 @@
4841 Q_ARG(Qt::KeyboardModifiers, modifiers));
4842
4843 if (!ok) {
4844- qCWarning(QTMIR_MIR_INPUT) << "Failed to invoke MousePointer::handleMouseEvent";
4845+ qCWarning(QTMIR_MIR_INPUT) << "Failed to invoke MousePointer::handleWheelEvent";
4846 }
4847
4848 return ok;
4849@@ -155,7 +156,7 @@
4850 movement.setX(pos.x() - mouseScenePos.x());
4851 movement.setY(pos.y() - mouseScenePos.y());
4852
4853- m_mousePointer->handleMouseEvent(0 /*timestamp*/, movement, Qt::NoButton, Qt::NoModifier);
4854+ m_mousePointer->handleMouseEvent(0 /*timestamp*/, movement, pos, Qt::NoButton, Qt::NoModifier);
4855 }
4856
4857 QPoint Cursor::pos() const
4858
4859=== modified file 'src/platforms/mirserver/cursor.h'
4860--- src/platforms/mirserver/cursor.h 2016-06-06 19:25:20 +0000
4861+++ src/platforms/mirserver/cursor.h 2017-02-14 10:17:32 +0000
4862@@ -1,5 +1,5 @@
4863 /*
4864- * Copyright (C) 2015 Canonical, Ltd.
4865+ * Copyright (C) 2015,2017 Canonical, Ltd.
4866 *
4867 * This program is free software: you can redistribute it and/or modify it under
4868 * the terms of the GNU Lesser General Public License version 3, as published by
4869@@ -33,7 +33,7 @@
4870 Cursor();
4871
4872 // Called form Mir input thread
4873- bool handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons,
4874+ bool handleMouseEvent(ulong timestamp, QPointF movement, QPointF position, Qt::MouseButtons buttons,
4875 Qt::KeyboardModifiers modifiers);
4876 bool handleWheelEvent(ulong timestamp, QPoint angleDelta, Qt::KeyboardModifiers mods);
4877
4878
4879=== modified file 'src/platforms/mirserver/logging.cpp'
4880--- src/platforms/mirserver/logging.cpp 2016-09-29 15:46:59 +0000
4881+++ src/platforms/mirserver/logging.cpp 2017-02-14 10:17:32 +0000
4882@@ -1,5 +1,5 @@
4883 /*
4884- * Copyright (C) 2016 Canonical, Ltd.
4885+ * Copyright (C) 2016,2017 Canonical, Ltd.
4886 *
4887 * This program is free software: you can redistribute it and/or modify it under
4888 * the terms of the GNU Lesser General Public License version 3, as published by
4889@@ -26,3 +26,4 @@
4890 Q_LOGGING_CATEGORY(QTMIR_SENSOR_MESSAGES, "qtmir.sensor")
4891 Q_LOGGING_CATEGORY(QTMIR_SCREENS, "qtmir.screens")
4892 Q_LOGGING_CATEGORY(QTMIR_DBUS, "qtmir.dbus", QtWarningMsg)
4893+Q_LOGGING_CATEGORY(QTMIR_FAKECLIENTS, "qtmir.fakeclients", QtDebugMsg)
4894
4895=== modified file 'src/platforms/mirserver/logging.h'
4896--- src/platforms/mirserver/logging.h 2016-09-29 15:46:59 +0000
4897+++ src/platforms/mirserver/logging.h 2017-02-14 10:17:32 +0000
4898@@ -1,5 +1,5 @@
4899 /*
4900- * Copyright (C) 2013-2014 Canonical, Ltd.
4901+ * Copyright (C) 2013-2017 Canonical, Ltd.
4902 *
4903 * This program is free software: you can redistribute it and/or modify it under
4904 * the terms of the GNU Lesser General Public License version 3, as published by
4905@@ -28,5 +28,6 @@
4906 Q_DECLARE_LOGGING_CATEGORY(QTMIR_CLIPBOARD)
4907 Q_DECLARE_LOGGING_CATEGORY(QTMIR_SCREENS)
4908 Q_DECLARE_LOGGING_CATEGORY(QTMIR_DBUS)
4909+Q_DECLARE_LOGGING_CATEGORY(QTMIR_FAKECLIENTS)
4910
4911 #endif // UBUNTU_APPLICATION_PLUGIN_LOGGING_H
4912
4913=== modified file 'src/platforms/mirserver/mirserverhooks.cpp'
4914--- src/platforms/mirserver/mirserverhooks.cpp 2017-01-03 20:59:50 +0000
4915+++ src/platforms/mirserver/mirserverhooks.cpp 2017-02-14 10:17:32 +0000
4916@@ -1,5 +1,5 @@
4917 /*
4918- * Copyright © 2016 Canonical Ltd.
4919+ * Copyright © 2016-2017 Canonical Ltd.
4920 *
4921 * This program is free software: you can redistribute it and/or modify it
4922 * under the terms of the GNU General Public License version 3,
4923@@ -77,6 +77,7 @@
4924 std::weak_ptr<mir::shell::DisplayConfigurationController> m_mirDisplayConfigurationController;
4925 std::weak_ptr<mir::scene::PromptSessionManager> m_mirPromptSessionManager;
4926 std::weak_ptr<mir::input::InputDeviceHub> m_inputDeviceHub;
4927+ mir::Server *m_server{nullptr};
4928 };
4929
4930 qtmir::MirServerHooks::MirServerHooks() :
4931@@ -109,6 +110,8 @@
4932 self->m_mirPromptSessionManager = server.the_prompt_session_manager();
4933 self->m_inputDeviceHub = server.the_input_device_hub();
4934 });
4935+
4936+ self->m_server = &server;
4937 }
4938
4939 PromptSessionListener *qtmir::MirServerHooks::promptSessionListener() const
4940@@ -143,6 +146,11 @@
4941 throw std::logic_error("No input device hub available. Server not running?");
4942 }
4943
4944+mir::Server *qtmir::MirServerHooks::server() const
4945+{
4946+ return self->m_server;
4947+}
4948+
4949 QSharedPointer<ScreensController> qtmir::MirServerHooks::createScreensController(QSharedPointer<ScreensModel> const &screensModel) const
4950 {
4951 return QSharedPointer<ScreensController>(
4952
4953=== modified file 'src/platforms/mirserver/mirserverhooks.h'
4954--- src/platforms/mirserver/mirserverhooks.h 2017-01-02 14:41:06 +0000
4955+++ src/platforms/mirserver/mirserverhooks.h 2017-02-14 10:17:32 +0000
4956@@ -1,5 +1,5 @@
4957 /*
4958- * Copyright © 2016 Canonical Ltd.
4959+ * Copyright © 2016-2017 Canonical Ltd.
4960 *
4961 * This program is free software: you can redistribute it and/or modify it
4962 * under the terms of the GNU General Public License version 3,
4963@@ -44,6 +44,7 @@
4964 std::shared_ptr<mir::scene::PromptSessionManager> thePromptSessionManager() const;
4965 std::shared_ptr<mir::graphics::Display> theMirDisplay() const;
4966 std::shared_ptr<mir::input::InputDeviceHub> theInputDeviceHub() const;
4967+ mir::Server *server() const;
4968
4969 QSharedPointer<ScreensController> createScreensController(QSharedPointer<ScreensModel> const &screensModel) const;
4970 void createInputDeviceObserver();
4971
4972=== modified file 'src/platforms/mirserver/mirserverintegration.cpp'
4973--- src/platforms/mirserver/mirserverintegration.cpp 2016-11-03 20:17:46 +0000
4974+++ src/platforms/mirserver/mirserverintegration.cpp 2017-02-14 10:17:32 +0000
4975@@ -50,11 +50,11 @@
4976 namespace mg = mir::graphics;
4977 using qtmir::Clipboard;
4978
4979-MirServerIntegration::MirServerIntegration(int &argc, char **argv)
4980+MirServerIntegration::MirServerIntegration()
4981 : m_accessibility(new QPlatformAccessibility())
4982 , m_fontDb(new QGenericUnixFontDatabase())
4983 , m_services(new Services)
4984- , m_mirServer(new QMirServer(argc, argv))
4985+ , m_mirServer(new QMirServer)
4986 , m_nativeInterface(nullptr)
4987 {
4988 // For access to sensors, qtmir uses qtubuntu-sensors. qtubuntu-sensors reads the
4989
4990=== modified file 'src/platforms/mirserver/mirserverintegration.h'
4991--- src/platforms/mirserver/mirserverintegration.h 2016-08-30 12:32:13 +0000
4992+++ src/platforms/mirserver/mirserverintegration.h 2017-02-14 10:17:32 +0000
4993@@ -27,7 +27,7 @@
4994 class MirServerIntegration : public QPlatformIntegration
4995 {
4996 public:
4997- MirServerIntegration(int &argc, char **argv);
4998+ MirServerIntegration();
4999 ~MirServerIntegration();
5000
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches