Merge lp:~unity-team/unity8/workspace-backend into lp:unity8

Proposed by Michael Zanetti
Status: Needs review
Proposed branch: lp:~unity-team/unity8/workspace-backend
Merge into: lp:unity8
Prerequisite: lp:~nick-dedekind/unity8/multi-monitor
Diff against target: 6781 lines (+4202/-755)
94 files modified
CMakeLists.txt (+5/-1)
debian/control (+6/-0)
plugins/WindowManager/CMakeLists.txt (+28/-7)
plugins/WindowManager/Screen.cpp (+316/-0)
plugins/WindowManager/Screen.h (+154/-0)
plugins/WindowManager/ScreenAttached.cpp (+164/-0)
plugins/WindowManager/ScreenAttached.h (+56/-0)
plugins/WindowManager/ScreenWindow.cpp (+45/-0)
plugins/WindowManager/ScreenWindow.h (+49/-0)
plugins/WindowManager/Screens.cpp (+235/-0)
plugins/WindowManager/Screens.h (+111/-0)
plugins/WindowManager/ScreensConfiguration.cpp (+94/-0)
plugins/WindowManager/ScreensConfiguration.h (+32/-0)
plugins/WindowManager/TopLevelWindowModel.cpp (+211/-79)
plugins/WindowManager/TopLevelWindowModel.h (+20/-24)
plugins/WindowManager/WindowManagerObjects.cpp (+47/-0)
plugins/WindowManager/WindowManagerObjects.h (+66/-0)
plugins/WindowManager/WindowManagerPlugin.cpp (+57/-2)
plugins/WindowManager/WindowManagerPlugin.h (+1/-0)
plugins/WindowManager/Workspace.cpp (+178/-0)
plugins/WindowManager/Workspace.h (+122/-0)
plugins/WindowManager/WorkspaceManager.cpp (+106/-0)
plugins/WindowManager/WorkspaceManager.h (+69/-0)
plugins/WindowManager/WorkspaceModel.cpp (+254/-0)
plugins/WindowManager/WorkspaceModel.h (+102/-0)
qml/ApplicationMenus/MenuItem.qml (+0/-2)
qml/ApplicationMenus/MenuPopup.qml (+0/-1)
qml/Components/VirtualTouchPad.qml (+1/-1)
qml/ErrorApplication.qml (+1/-1)
qml/OrientedShell.qml (+1/-2)
qml/Shell.qml (+10/-12)
qml/ShellApplication.qml (+12/-4)
qml/ShellScreen.qml (+1/-3)
src/CMakeLists.txt (+12/-2)
src/DisplayConfigurationStorage.cpp (+90/-0)
src/DisplayConfigurationStorage.h (+15/-0)
src/UnityApplication.cpp (+19/-13)
src/UnityApplication.h (+9/-7)
src/UnityCommandLineParser.cpp (+8/-0)
src/UnityCommandLineParser.h (+3/-0)
src/WindowManagementPolicy.cpp (+69/-0)
src/WindowManagementPolicy.h (+47/-0)
src/libunity8-private/CMakeLists.txt (+1/-0)
src/libunity8-private/wmpolicyinterface.cpp (+24/-0)
src/libunity8-private/wmpolicyinterface.h (+46/-0)
src/main.cpp (+4/-8)
tests/CMakeLists.txt (+1/-0)
tests/mocks/CMakeLists.txt (+1/-0)
tests/mocks/Unity/Application/CMakeLists.txt (+26/-1)
tests/mocks/Unity/Application/SurfaceManager.cpp (+117/-10)
tests/mocks/Unity/Application/SurfaceManager.h (+23/-3)
tests/mocks/Unity/Application/plugin.cpp (+7/-1)
tests/mocks/Unity/CMakeLists.txt (+0/-1)
tests/mocks/Unity/Screens/CMakeLists.txt (+0/-15)
tests/mocks/Unity/Screens/plugin.cpp (+0/-42)
tests/mocks/Unity/Screens/plugin.h (+0/-25)
tests/mocks/Unity/Screens/qmldir (+0/-2)
tests/mocks/Unity/Screens/screens.cpp (+0/-114)
tests/mocks/Unity/Screens/screens.h (+0/-162)
tests/mocks/Unity/Screens/screenwindow.cpp (+0/-35)
tests/mocks/Unity/Screens/screenwindow.h (+0/-42)
tests/mocks/WindowManager/CMakeLists.txt (+61/-0)
tests/mocks/WindowManager/MockScreenWindow.cpp (+34/-0)
tests/mocks/WindowManager/MockScreenWindow.h (+30/-0)
tests/mocks/WindowManager/MockScreens.cpp (+227/-0)
tests/mocks/WindowManager/MockScreens.h (+54/-0)
tests/mocks/WindowManager/MockScreensConfiguration.cpp (+47/-0)
tests/mocks/WindowManager/WindowManagementPolicy.cpp (+128/-0)
tests/mocks/WindowManager/WindowManagementPolicy.h (+75/-0)
tests/mocks/WindowManager/WindowManagerPlugin.cpp (+89/-0)
tests/mocks/WindowManager/WindowManagerPlugin.h (+33/-0)
tests/mocks/WindowManager/qmldir (+2/-0)
tests/plugins/WindowManager/CMakeLists.txt (+5/-1)
tests/plugins/WindowManager/UnityApplicationMocks.h (+25/-0)
tests/plugins/WindowManager/tst_TopLevelWindowModel.cpp (+20/-8)
tests/qmltests/ApplicationMenuDataLoader.qml (+3/-6)
tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+0/-2)
tests/qmltests/CMakeLists.txt (+1/-0)
tests/qmltests/Panel/tst_ActiveCallHint.qml (+0/-2)
tests/qmltests/Panel/tst_Panel.qml (+0/-2)
tests/qmltests/Stage/tst_ApplicationWindow.qml (+0/-2)
tests/qmltests/Stage/tst_DecoratedWindow.qml (+0/-2)
tests/qmltests/Stage/tst_DesktopStage.qml (+1/-7)
tests/qmltests/Stage/tst_PhoneStage.qml (+5/-7)
tests/qmltests/Stage/tst_SurfaceContainer.qml (+0/-2)
tests/qmltests/Stage/tst_TabletStage.qml (+43/-43)
tests/qmltests/Stage/tst_WindowDecoration.qml (+0/-2)
tests/qmltests/Tutorial/tst_Tutorial.qml (+3/-13)
tests/qmltests/tst_OrientedShell.qml (+2/-10)
tests/qmltests/tst_Shell.qml (+4/-16)
tests/qmltests/tst_ShellApplication.qml (+218/-0)
tests/qmltests/tst_ShellWithPin.qml (+1/-7)
tests/utils/modules/Unity/Test/StageTestCase.qml (+1/-1)
tests/utils/modules/Unity/Test/UnityTestCase.qml (+14/-0)
To merge this branch: bzr merge lp:~unity-team/unity8/workspace-backend
Reviewer Review Type Date Requested Status
Unity8 CI Bot continuous-integration Needs Fixing
Unity Team Pending
Review via email: mp+318090@code.launchpad.net

This proposal supersedes a proposal from 2017-02-23.

Commit message

WIP - just putting to Needs Review to get Jenkins' opinion

Description of the change

Prereq-archive: ppa:ci-train-ppa-service/2373

WIP

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

FAILED: Continuous integration, rev:2836
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3553/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4713
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2863
    FAILURE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2863/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4741
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4564
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4564/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4564
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4564/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4564
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4564/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4564
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4564/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4564
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4564/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4564
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4564/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2837. By Nick Dedekind

merged with parent

2838. By Nick Dedekind

fixed surfacelist in test

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

FAILED: Continuous integration, rev:2838
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3558/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4718
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2869
    FAILURE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2869/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4746
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4569
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4569/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4569
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4569/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4569
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4569/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4569
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4569/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4569
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4569/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4569
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4569/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2839. By Michael Zanetti

install libmockwindowmanagmentpolicy, needed for installed tests

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

FAILED: Continuous integration, rev:2839
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3563/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4724
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2875
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2875
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4752
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4575
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4575/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4575
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4575/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4575
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4575/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4575
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4575/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4575
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4575/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4575
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4575/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2840. By Michael Zanetti

add Windowmanager mock dir to LD_LIBRARY_PATH

2841. By Michael Zanetti

fix panel tests

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

FAILED: Continuous integration, rev:2840
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3571/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4732
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2884
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2884
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4760
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4583
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4583/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4583
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4583/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4583
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4583/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4583
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4583/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4583
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4583/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4583
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4583/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:2841
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3575/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4737
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2888
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2888
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4765
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4588
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4588/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4588
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4588/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4588
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4588/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4588
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4588/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4588
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4588/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4588
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4588/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2842. By Nick Dedekind

merged parent

2843. By Nick Dedekind

more test fixes

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

FAILED: Continuous integration, rev:2843
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3594/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4762
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2910
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2910
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4790
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4613
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4613/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4613
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4613/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4613
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4613/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4613
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4613/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4613
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4613/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4613
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4613/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2844. By Nick Dedekind

Mock fixes & test fixes

2845. By Nick Dedekind

fixed GlobalShortcut test

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

FAILED: Continuous integration, rev:2845
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3603/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4776
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2920
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2920
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4804
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4627
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4627/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4627
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4627/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4627
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4627/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4627
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4627/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4627
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4627/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4627
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4627/artifact/output/*zip*/output.zip

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

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

merged with trunk

2847. By Nick Dedekind

Added Mouse and KB controls

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

FAILED: Continuous integration, rev:2846
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3628/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4811
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2943
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2943
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4839
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4651
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4651/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4651
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4651/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4651
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4651/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4651
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4651/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4651
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4651/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4651
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4651/artifact/output/*zip*/output.zip

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

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

FAILED: Continuous integration, rev:2847
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3630/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4815
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2945
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2945
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4843
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4654
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4654/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4654
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4654/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4654
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4654/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4654
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4654/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4654
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4654/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4654
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4654/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2848. By Nick Dedekind

Dont allow proxy to be used as current workspace

2849. By Nick Dedekind

Fixed wm policy init

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

FAILED: Continuous integration, rev:2848
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3638/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4830
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2958
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2958
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4858
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4669
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4669/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4669
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4669/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4669
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4669/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4669
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4669/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4669
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4669/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4669
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4669/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2850. By Nick Dedekind

fixed sync end

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

display id

2852. By Nick Dedekind

ScreenConfig

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)
2853. By Nick Dedekind

display config storage

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

mock screen active

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

FAILED: Continuous integration, rev:2854
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3664/
Executed test runs:
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build/4865
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=xenial+overlay,testname=qmluitests.sh/2981
    UNSTABLE: https://unity8-jenkins.ubuntu.com/job/test-0-autopkgtest/label=amd64,release=zesty,testname=qmluitests.sh/2981
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4893
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4704
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4704/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4704
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4704/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4704
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4704/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4704
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4704/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4704
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4704/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4704
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4704/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)
2855. By Nick Dedekind

isSameAs

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

FAILED: Continuous integration, rev:2855
https://unity8-jenkins.ubuntu.com/job/lp-unity8-ci/3689/
Executed test runs:
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build/4899/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-0-fetch/4927
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4735
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=xenial+overlay/4735/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4735
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=amd64,release=zesty/4735/artifact/output/*zip*/output.zip
    FAILURE: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=xenial+overlay/4735/console
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4735
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=armhf,release=zesty/4735/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4735
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=xenial+overlay/4735/artifact/output/*zip*/output.zip
    SUCCESS: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4735
        deb: https://unity8-jenkins.ubuntu.com/job/build-2-binpkg/arch=i386,release=zesty/4735/artifact/output/*zip*/output.zip

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

review: Needs Fixing (continuous-integration)

Unmerged revisions

2855. By Nick Dedekind

isSameAs

2854. By Nick Dedekind

mock screen active

2853. By Nick Dedekind

display config storage

2852. By Nick Dedekind

ScreenConfig

2851. By Nick Dedekind

display id

2850. By Nick Dedekind

fixed sync end

2849. By Nick Dedekind

Fixed wm policy init

2848. By Nick Dedekind

Dont allow proxy to be used as current workspace

2847. By Nick Dedekind

Added Mouse and KB controls

2846. By Nick Dedekind

merged with trunk

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-03-28 21:45:31 +0000
3+++ CMakeLists.txt 2017-04-05 11:48:54 +0000
4@@ -72,6 +72,7 @@
5 find_package(Qt5Test 5.6 REQUIRED)
6
7 pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=27)
8+pkg_check_modules(QTMIRSERVER REQUIRED qtmirserver>=0.6.0)
9 pkg_check_modules(GEONAMES REQUIRED geonames>=0.2)
10 pkg_check_modules(GIO REQUIRED gio-2.0>=2.32)
11 pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32)
12@@ -84,6 +85,7 @@
13 libqtdbustest-1
14 libqtdbusmock-1
15 )
16+pkg_check_modules(MIRAL REQUIRED miral)
17
18 ### Check UbuntuGestures private headers. No pkg-config (.pc) file is provided for them
19 find_path(UBUNTUGESTUREPRIV
20@@ -118,7 +120,9 @@
21 message(FATAL_ERROR "Could not determine plugin import dir.")
22 endif()
23
24-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra")
25+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-permissive -pedantic -Wall -Wextra")
26+
27+set (CMAKE_CXX_STANDARD 14)
28
29 if ("${CMAKE_BUILD_TYPE}" STREQUAL "release" OR "${CMAKE_BUILD_TYPE}" STREQUAL "relwithdebinfo")
30 option(Werror "Treat warnings as errors" ON)
31
32=== modified file 'debian/control'
33--- debian/control 2017-04-05 11:48:53 +0000
34+++ debian/control 2017-04-05 11:48:54 +0000
35@@ -33,6 +33,7 @@
36 libqtdbustest1-dev,
37 libqt5svg5-dev,
38 libqt5xmlpatterns5-dev,
39+ libqtmirserver-dev (>= 0.6.0),
40 libsystemsettings-dev,
41 libubuntu-app-launch3-dev,
42 libubuntu-download-manager-common-dev,
43@@ -47,6 +48,7 @@
44 libxcb1-dev[!arm64 !armhf],
45 libxi-dev[!arm64 !armhf],
46 # End of X11 libs
47+ mirtest-dev,
48 pkg-config,
49 python3-all:any,
50 python3-setuptools,
51@@ -73,6 +75,9 @@
52 qtdeclarative5-ubuntu-settings-components (>= 0.11),
53 ttf-ubuntu-font-family,
54 xvfb,
55+# mirtest pkgconfig requires these, but doesn't have a deb dependency. Bug lp:1633537
56+ libboost-filesystem-dev,
57+ libboost-system-dev,
58 Standards-Version: 3.9.4
59 Homepage: http://launchpad.net/unity
60 # If you aren't a member of ~unity-team but need to upload
61@@ -134,6 +139,7 @@
62 qml-module-ubuntu-web,
63 qtdeclarative5-qtmir-plugin (>= 0.4.8),
64 qtdeclarative5-ubuntu-telephony0.1,
65+ qtmir-desktop (>= 0.6.0) | qtmir-android (>= 0.6.0),
66 ubuntu-system-settings (>= 0.4),
67 unity-launcher-impl-12,
68 unity8-common (= ${source:Version}),
69
70=== modified file 'plugins/WindowManager/CMakeLists.txt'
71--- plugins/WindowManager/CMakeLists.txt 2017-03-24 14:04:50 +0000
72+++ plugins/WindowManager/CMakeLists.txt 2017-04-05 11:48:54 +0000
73@@ -1,25 +1,46 @@
74+include_directories(
75+ SYSTEM
76+ ${QTMIRSERVER_INCLUDE_DIRS}
77+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
78+)
79+
80+include_directories(
81+ ${CMAKE_CURRENT_SOURCE_DIR}
82+ ${libunity8-private_SOURCE_DIR}
83+)
84+
85 set(WINDOWMANAGER_SRC
86 AvailableDesktopArea.cpp
87 TopLevelWindowModel.cpp
88 Window.cpp
89 WindowManagerPlugin.cpp
90 WindowMargins.cpp
91+ Screen.cpp
92+ ScreenAttached.cpp
93+ Screens.cpp
94+ ScreensConfiguration.cpp
95+ ScreenWindow.cpp
96+ WindowManagerObjects.cpp
97+ Workspace.cpp
98+ WorkspaceManager.cpp
99+ WorkspaceModel.cpp
100 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
101+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
102 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
103 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
104 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
105 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
106- )
107+)
108
109 add_definitions(-DWINDOWMANAGERQML_LIBRARY)
110
111-include_directories(
112- SYSTEM
113- ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
114-)
115-
116 add_library(windowmanager-qml SHARED ${WINDOWMANAGER_SRC})
117
118+target_link_libraries(windowmanager-qml
119+ ${QTMIRSERVER_LDFLAGS}
120+ unity8-private
121+)
122+
123 qt5_use_modules(windowmanager-qml Qml Quick Gui)
124
125-add_unity8_plugin(WindowManager 0.1 WindowManager TARGETS windowmanager-qml)
126+add_unity8_plugin(WindowManager 1.0 WindowManager TARGETS windowmanager-qml)
127
128=== added file 'plugins/WindowManager/Screen.cpp'
129--- plugins/WindowManager/Screen.cpp 1970-01-01 00:00:00 +0000
130+++ plugins/WindowManager/Screen.cpp 2017-04-05 11:48:54 +0000
131@@ -0,0 +1,316 @@
132+/*
133+ * Copyright (C) 2017 Canonical, Ltd.
134+ *
135+ * This program is free software: you can redistribute it and/or modify it under
136+ * the terms of the GNU Lesser General Public License version 3, as published by
137+ * the Free Software Foundation.
138+ *
139+ * This program is distributed in the hope that it will be useful, but WITHOUT
140+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
141+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
142+ * Lesser General Public License for more details.
143+ *
144+ * You should have received a copy of the GNU Lesser General Public License
145+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
146+ */
147+
148+#include "Screen.h"
149+#include "Screens.h"
150+#include "WorkspaceManager.h"
151+#include "Workspace.h"
152+
153+Screen::Screen(QObject *parent)
154+ : QObject(parent)
155+{
156+}
157+
158+void Screen::connectToScreen(qtmir::Screen *screen)
159+{
160+ m_wrapped = screen;
161+ connect(screen, &qtmir::Screen::usedChanged, this, &Screen::usedChanged);
162+ connect(screen, &qtmir::Screen::nameChanged, this, &Screen::nameChanged);
163+ connect(screen, &qtmir::Screen::outputTypeChanged, this, &Screen::outputTypeChanged);
164+ connect(screen, &qtmir::Screen::outputTypeChanged, this, &Screen::outputTypeNameChanged);
165+ connect(screen, &qtmir::Screen::scaleChanged, this, &Screen::scaleChanged);
166+ connect(screen, &qtmir::Screen::formFactorChanged, this, &Screen::formFactorChanged);
167+ connect(screen, &qtmir::Screen::physicalSizeChanged, this, &Screen::physicalSizeChanged);
168+ connect(screen, &qtmir::Screen::positionChanged, this, &Screen::positionChanged);
169+ connect(screen, &qtmir::Screen::activeChanged, this, &Screen::activeChanged);
170+ connect(screen, &qtmir::Screen::currentModeIndexChanged, this, &Screen::currentModeIndexChanged);
171+ connect(screen, &qtmir::Screen::availableModesChanged, this, &Screen::availableModesChanged);
172+}
173+
174+void Screen::connectToScreen(Screen *screen)
175+{
176+ connectToScreen(screen->wrapped());
177+ connect(screen, &Screen::currentWorkspaceChanged, this, &Screen::currentWorkspaceChanged);
178+}
179+
180+void Screen::setCurrentWorkspace2(Workspace *workspace)
181+{
182+ // Make sure we use the correct concrete class. Don't want to use a Proxy.
183+ workspace->setCurrentOn(this);
184+}
185+
186+bool Screen::used() const
187+{
188+ if (!m_wrapped) return false;
189+ return m_wrapped->used();
190+}
191+
192+QString Screen::name() const
193+{
194+ if (!m_wrapped) return QString();
195+ return m_wrapped->name();
196+}
197+
198+float Screen::scale() const
199+{
200+ if (!m_wrapped) return 1.0;
201+ return m_wrapped->scale();
202+}
203+
204+QSizeF Screen::physicalSize() const
205+{
206+ if (!m_wrapped) return QSizeF();
207+ return m_wrapped->physicalSize();
208+}
209+
210+qtmir::FormFactor Screen::formFactor() const
211+{
212+ if (!m_wrapped) return qtmir::FormFactorUnknown;
213+ return m_wrapped->formFactor();
214+}
215+
216+qtmir::OutputTypes Screen::outputType() const
217+{
218+ if (!m_wrapped) return qtmir::Unknown;
219+ return m_wrapped->outputType();
220+}
221+
222+MirPowerMode Screen::powerMode() const
223+{
224+ if (!m_wrapped) return mir_power_mode_on;
225+ return m_wrapped->powerMode();
226+}
227+
228+Qt::ScreenOrientation Screen::orientation() const
229+{
230+ if (!m_wrapped) return Qt::PrimaryOrientation;
231+ return m_wrapped->orientation();
232+}
233+
234+QPoint Screen::position() const
235+{
236+ if (!m_wrapped) return QPoint();
237+ return m_wrapped->position();
238+}
239+
240+QQmlListProperty<qtmir::ScreenMode> Screen::availableModes()
241+{
242+ if (!m_wrapped) return QQmlListProperty<qtmir::ScreenMode>();
243+ return m_wrapped->availableModes();
244+}
245+
246+uint Screen::currentModeIndex() const
247+{
248+ if (!m_wrapped) return -1;
249+ return m_wrapped->currentModeIndex();
250+}
251+
252+bool Screen::isActive() const
253+{
254+ if (!m_wrapped) return false;
255+ return m_wrapped->isActive();
256+}
257+
258+void Screen::activate()
259+{
260+ setActive(true);
261+}
262+
263+void Screen::setActive(bool active)
264+{
265+ if (!m_wrapped) return;
266+ m_wrapped->setActive(active);
267+}
268+
269+QScreen *Screen::qscreen() const
270+{
271+ if (!m_wrapped) return nullptr;
272+ return m_wrapped->qscreen();
273+}
274+
275+ScreenConfig *Screen::beginConfiguration() const
276+{
277+ if (!m_wrapped) return nullptr;
278+ return new ScreenConfig(m_wrapped->beginConfiguration());
279+}
280+
281+bool Screen::applyConfiguration(ScreenConfig *configuration)
282+{
283+ if (!m_wrapped) return false;
284+ return m_wrapped->applyConfiguration(configuration->m_config);
285+}
286+
287+QString Screen::outputTypeName() const
288+{
289+ switch (m_wrapped->outputType()) {
290+ case qtmir::Unknown:
291+ return tr("Unknown");
292+ case qtmir::VGA:
293+ return tr("VGA");
294+ case qtmir::DVII:
295+ case qtmir::DVID:
296+ case qtmir::DVIA:
297+ return tr("DVI");
298+ case qtmir::Composite:
299+ return tr("Composite");
300+ case qtmir::SVideo:
301+ return tr("S-Video");
302+ case qtmir::LVDS:
303+ case qtmir::NinePinDIN:
304+ case qtmir::EDP:
305+ return tr("Internal");
306+ case qtmir::Component:
307+ return tr("Component");
308+ case qtmir::DisplayPort:
309+ return tr("DisplayPort");
310+ case qtmir::HDMIA:
311+ case qtmir::HDMIB:
312+ return tr("HDMI");
313+ case qtmir::TV:
314+ return tr("TV");
315+ }
316+ return QString();
317+}
318+
319+bool Screen::isSameAs(Screen *screen) const
320+{
321+ if (!screen) return false;
322+ if (screen == this) return true;
323+ return wrapped() == screen->wrapped();
324+}
325+
326+void Screen::sync(Screen *proxy)
327+{
328+ if (!proxy) return;
329+ workspaces()->sync(proxy->workspaces());
330+}
331+
332+ConcreteScreen::ConcreteScreen(qtmir::Screen* wrapped)
333+ : m_workspaces(new WorkspaceModel)
334+{
335+ connectToScreen(wrapped);
336+
337+ // Connect the active workspace to activate the screen.
338+ connect(m_workspaces.data(), &WorkspaceModel::workspaceInserted, this, [this](int, Workspace* workspace) {
339+ connect(workspace, &Workspace::activeChanged, this, [this, workspace](bool active) {
340+ if (active) {
341+ setCurrentWorkspace(workspace);
342+ activate();
343+ }
344+ });
345+ if (workspace->isActive()) {
346+ activate();
347+ setCurrentWorkspace(workspace);
348+ }
349+ if (!m_currentWorspace) {
350+ setCurrentWorkspace(workspace);
351+ }
352+ });
353+ connect(m_workspaces.data(), &WorkspaceModel::workspaceRemoved, this, [this](Workspace* workspace) {
354+ disconnect(workspace, &Workspace::activeChanged, this, 0);
355+ if (workspace == m_currentWorspace) {
356+ resetCurrentWorkspace();
357+ }
358+ });
359+ connect(this, &ConcreteScreen::activeChanged, this, [this](bool active) {
360+ if (active && m_currentWorspace) {
361+ m_currentWorspace->activate();
362+ }
363+ });
364+}
365+
366+void ConcreteScreen::resetCurrentWorkspace()
367+{
368+ auto newCurrent = m_workspaces->rowCount() > 0 ? m_workspaces->get(0) : nullptr;
369+ if (m_currentWorspace != newCurrent) {
370+ m_currentWorspace = newCurrent;
371+ Q_EMIT currentWorkspaceChanged(newCurrent);
372+ }
373+}
374+
375+
376+WorkspaceModel *ConcreteScreen::workspaces() const
377+{
378+ return m_workspaces.data();
379+}
380+
381+Workspace *ConcreteScreen::currentWorkspace() const
382+{
383+ return m_currentWorspace.data();
384+}
385+
386+void ConcreteScreen::setCurrentWorkspace(Workspace *workspace)
387+{
388+ if (m_currentWorspace != workspace) {
389+ m_currentWorspace = workspace;
390+ Q_EMIT currentWorkspaceChanged(workspace);
391+ }
392+}
393+
394+ProxyScreen::ProxyScreen(Screen *const screen, ProxyScreens* screens)
395+ : m_workspaces(new ProxyWorkspaceModel(screen->workspaces(), this))
396+ , m_original(screen)
397+ , m_screens(screens)
398+{
399+ connectToScreen(screen);
400+
401+ auto updateCurrentWorkspaceFn = [this](Workspace* realWorkspace) {
402+ Q_FOREACH(Workspace* workspace, m_workspaces->list()) {
403+ auto p = qobject_cast<ProxyWorkspace*>(workspace);
404+ if (p && p->proxyObject() == realWorkspace) {
405+ if (m_currentWorspace != p) {
406+ m_currentWorspace = p;
407+ Q_EMIT currentWorkspaceChanged(p);
408+ }
409+ }
410+ }
411+ };
412+ connect(screen, &Screen::currentWorkspaceChanged, this, updateCurrentWorkspaceFn);
413+ updateCurrentWorkspaceFn(screen->currentWorkspace());
414+}
415+
416+WorkspaceModel *ProxyScreen::workspaces() const
417+{
418+ return m_workspaces.data();
419+}
420+
421+Workspace *ProxyScreen::currentWorkspace() const
422+{
423+ return m_currentWorspace.data();
424+}
425+
426+void ProxyScreen::setCurrentWorkspace(Workspace *workspace)
427+{
428+ auto p = qobject_cast<ProxyWorkspace*>(workspace);
429+ if (p) {
430+ m_original->setCurrentWorkspace(p->proxyObject());
431+ }
432+}
433+
434+bool ProxyScreen::isSyncing() const
435+{
436+ return m_screens->isSyncing();
437+}
438+
439+ScreenConfig::ScreenConfig(qtmir::ScreenConfiguration *config)
440+ : m_config(config)
441+{
442+}
443+
444+ScreenConfig::~ScreenConfig()
445+{
446+ delete m_config;
447+}
448
449=== added file 'plugins/WindowManager/Screen.h'
450--- plugins/WindowManager/Screen.h 1970-01-01 00:00:00 +0000
451+++ plugins/WindowManager/Screen.h 2017-04-05 11:48:54 +0000
452@@ -0,0 +1,154 @@
453+#ifndef SCREEN_H
454+#define SCREEN_H
455+
456+#include <qtmir/screen.h>
457+#include <QScopedPointer>
458+#include <QPointer>
459+
460+#include "WorkspaceModel.h"
461+
462+class ProxyScreen;
463+class ProxyScreens;
464+class ScreenConfig;
465+
466+class Screen: public QObject
467+{
468+ Q_OBJECT
469+
470+ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged)
471+
472+ Q_PROPERTY(bool used READ used NOTIFY usedChanged)
473+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
474+ Q_PROPERTY(qtmir::OutputTypes outputType READ outputType NOTIFY outputTypeChanged)
475+ Q_PROPERTY(float scale READ scale NOTIFY scaleChanged)
476+ Q_PROPERTY(qtmir::FormFactor formFactor READ formFactor NOTIFY formFactorChanged)
477+ Q_PROPERTY(MirPowerMode powerMode READ powerMode NOTIFY powerModeChanged)
478+ Q_PROPERTY(Qt::ScreenOrientation orientation READ orientation NOTIFY orientationChanged)
479+ Q_PROPERTY(QPoint position READ position NOTIFY positionChanged)
480+ Q_PROPERTY(uint currentModeIndex READ currentModeIndex NOTIFY currentModeIndexChanged)
481+ Q_PROPERTY(QQmlListProperty<qtmir::ScreenMode> availableModes READ availableModes NOTIFY availableModesChanged)
482+ Q_PROPERTY(QSizeF physicalSize READ physicalSize NOTIFY physicalSizeChanged)
483+ Q_PROPERTY(QString outputTypeName READ outputTypeName NOTIFY outputTypeChanged)
484+ Q_PROPERTY(WorkspaceModel* workspaces READ workspaces CONSTANT)
485+ Q_PROPERTY(Workspace* currentWorkspace READ currentWorkspace WRITE setCurrentWorkspace2 NOTIFY currentWorkspaceChanged)
486+public:
487+ bool used() const;
488+ QString name() const;
489+ float scale() const;
490+ QSizeF physicalSize() const;
491+ qtmir::FormFactor formFactor() const;
492+ qtmir::OutputTypes outputType() const;
493+ MirPowerMode powerMode() const;
494+ Qt::ScreenOrientation orientation() const;
495+ QPoint position() const;
496+ QQmlListProperty<qtmir::ScreenMode> availableModes();
497+ uint currentModeIndex() const;
498+ bool isActive() const;
499+ void setActive(bool active);
500+ QScreen* qscreen() const;
501+ QString outputTypeName() const;
502+
503+ Q_INVOKABLE bool isSameAs(Screen*) const;
504+
505+ Q_INVOKABLE ScreenConfig *beginConfiguration() const;
506+ Q_INVOKABLE bool applyConfiguration(ScreenConfig *configuration);
507+
508+ virtual WorkspaceModel* workspaces() const = 0;
509+ virtual Workspace *currentWorkspace() const = 0;
510+ virtual void setCurrentWorkspace(Workspace* workspace) = 0;
511+
512+ void sync(Screen* proxy);
513+
514+ qtmir::Screen* wrapped() const { return m_wrapped; }
515+
516+public Q_SLOTS:
517+ void activate();
518+
519+Q_SIGNALS:
520+ void usedChanged();
521+ void nameChanged();
522+ void outputTypeChanged();
523+ void outputTypeNameChanged();
524+ void scaleChanged();
525+ void formFactorChanged();
526+ void powerModeChanged();
527+ void orientationChanged();
528+ void positionChanged();
529+ void currentModeIndexChanged();
530+ void physicalSizeChanged();
531+ void availableModesChanged();
532+ void activeChanged(bool active);
533+ void currentWorkspaceChanged(Workspace*);
534+
535+protected:
536+ Screen(QObject* parent = 0);
537+
538+ void connectToScreen(qtmir::Screen* screen);
539+ void connectToScreen(Screen* screen);
540+
541+private:
542+ void setCurrentWorkspace2(Workspace* workspace);
543+
544+protected:
545+ QPointer<qtmir::Screen> m_wrapped;
546+};
547+
548+
549+class ConcreteScreen : public Screen
550+{
551+ Q_OBJECT
552+public:
553+ explicit ConcreteScreen(qtmir::Screen*const wrapped);
554+
555+ // From qtmir::Screen
556+ WorkspaceModel* workspaces() const override;
557+ Workspace *currentWorkspace() const override;
558+ void setCurrentWorkspace(Workspace* workspace) override;
559+
560+protected:
561+ void resetCurrentWorkspace();
562+
563+ const QScopedPointer<WorkspaceModel> m_workspaces;
564+ QPointer<Workspace> m_currentWorspace;
565+};
566+
567+class ProxyScreen : public Screen
568+{
569+ Q_OBJECT
570+public:
571+ explicit ProxyScreen(Screen*const screen, ProxyScreens* screens);
572+
573+ // From qtmir::Screen
574+ WorkspaceModel* workspaces() const override;
575+ Workspace *currentWorkspace() const override;
576+ void setCurrentWorkspace(Workspace* workspace) override;
577+
578+ Screen* proxyObject() const { return m_original.data(); }
579+
580+ bool isSyncing() const;
581+
582+private:
583+ const QScopedPointer<WorkspaceModel> m_workspaces;
584+ const QPointer<Screen> m_original;
585+ const ProxyScreens* m_screens;
586+ QPointer<Workspace> m_currentWorspace;
587+};
588+
589+class ScreenConfig: public QObject
590+{
591+ Q_OBJECT
592+ Q_PRIVATE_PROPERTY(m_config, bool valid MEMBER used CONSTANT)
593+ Q_PRIVATE_PROPERTY(m_config, bool used MEMBER used)
594+ Q_PRIVATE_PROPERTY(m_config, float scale MEMBER scale)
595+ Q_PRIVATE_PROPERTY(m_config, qtmir::FormFactor formFactor MEMBER formFactor)
596+ Q_PRIVATE_PROPERTY(m_config, uint currentModeIndex MEMBER currentModeIndex)
597+ Q_PRIVATE_PROPERTY(m_config, QPoint position MEMBER topLeft)
598+
599+public:
600+ ScreenConfig(qtmir::ScreenConfiguration*);
601+ ~ScreenConfig();
602+
603+ qtmir::ScreenConfiguration* m_config;
604+};
605+
606+#endif // SCREEN_H
607
608=== added file 'plugins/WindowManager/ScreenAttached.cpp'
609--- plugins/WindowManager/ScreenAttached.cpp 1970-01-01 00:00:00 +0000
610+++ plugins/WindowManager/ScreenAttached.cpp 2017-04-05 11:48:54 +0000
611@@ -0,0 +1,164 @@
612+/*
613+ * Copyright (C) 2017 Canonical, Ltd.
614+ *
615+ * This program is free software: you can redistribute it and/or modify it under
616+ * the terms of the GNU Lesser General Public License version 3, as published by
617+ * the Free Software Foundation.
618+ *
619+ * This program is distributed in the hope that it will be useful, but WITHOUT
620+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
621+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
622+ * Lesser General Public License for more details.
623+ *
624+ * You should have received a copy of the GNU Lesser General Public License
625+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
626+ */
627+
628+#include "ScreenAttached.h"
629+#include "ScreenWindow.h"
630+#include "Screens.h"
631+
632+#include <QQuickItem>
633+#include <QScreen>
634+
635+namespace
636+{
637+QQuickItem* itemForOwner(QObject* obj) {
638+ QObject* parent = obj;
639+ while(parent) {
640+ auto item = qobject_cast<QQuickItem*>(parent);
641+ if (item) return item;
642+ parent = parent->parent();
643+ }
644+ return nullptr;
645+}
646+} // namesapce
647+
648+ScreenAttached::ScreenAttached(QObject *owner)
649+ : Screen(owner)
650+ , m_window(nullptr)
651+{
652+ if (auto item = itemForOwner(owner)) {
653+ connect(item, &QQuickItem::windowChanged, this, &ScreenAttached::windowChanged);
654+ windowChanged(item->window());
655+ } else if (auto window = qobject_cast<QQuickWindow*>(owner)) {
656+ windowChanged(window);
657+ }
658+}
659+
660+WorkspaceModel *ScreenAttached::workspaces() const
661+{
662+ if (!m_screen) return nullptr;
663+ return m_screen->workspaces();
664+}
665+
666+Workspace *ScreenAttached::currentWorkspace() const
667+{
668+ if (!m_screen) return nullptr;
669+ return m_screen->currentWorkspace();
670+}
671+
672+void ScreenAttached::setCurrentWorkspace(Workspace *workspace)
673+{
674+ if (!m_screen) return;
675+ return m_screen->setCurrentWorkspace(workspace);
676+}
677+
678+void ScreenAttached::windowChanged(QQuickWindow *window)
679+{
680+ if (m_window) {
681+ disconnect(m_window, &QWindow::screenChanged, this, &ScreenAttached::screenChanged);
682+ }
683+
684+ m_window = window;
685+ auto screenWindow = qobject_cast<ScreenWindow*>(window);
686+
687+ if (screenWindow) {
688+ screenChanged2(screenWindow->screenWrapper());
689+ connect(screenWindow, &ScreenWindow::screenWrapperChanged, this, &ScreenAttached::screenChanged2);
690+ } else {
691+ screenChanged(window ? window->screen() : NULL);
692+ if (window) {
693+ connect(window, &QWindow::screenChanged, this, &ScreenAttached::screenChanged);
694+ }
695+ }
696+}
697+
698+void ScreenAttached::screenChanged(QScreen *qscreen)
699+{
700+ // Find a screen that matches.
701+ // Should only get here in mocks if we don't have a ScreenWindow
702+ Screen* screen{nullptr};
703+ Q_FOREACH(auto s, ConcreteScreens::self()->list()) {
704+ if (s->qscreen() == qscreen) {
705+ screen = s;
706+ }
707+ }
708+ screenChanged2(screen);
709+}
710+
711+void ScreenAttached::screenChanged2(Screen* screen)
712+{
713+ if (screen == m_screen) return;
714+
715+ Screen* oldScreen = m_screen;
716+ m_screen = screen;
717+
718+ if (oldScreen)
719+ oldScreen->disconnect(this);
720+
721+ if (!screen)
722+ return; //Don't bother emitting signals, because the new values are garbage anyways
723+
724+ if (!oldScreen || screen->isActive() != oldScreen->isActive())
725+ Q_EMIT activeChanged(screen->isActive());
726+ if (!oldScreen || screen->used() != oldScreen->used())
727+ Q_EMIT usedChanged();
728+ if (!oldScreen || screen->name() != oldScreen->name())
729+ Q_EMIT nameChanged();
730+ if (!oldScreen || screen->outputType() != oldScreen->outputType())
731+ Q_EMIT outputTypeChanged();
732+ if (!oldScreen || screen->scale() != oldScreen->scale())
733+ Q_EMIT scaleChanged();
734+ if (!oldScreen || screen->formFactor() != oldScreen->formFactor())
735+ Q_EMIT formFactorChanged();
736+ if (!oldScreen || screen->powerMode() != oldScreen->powerMode())
737+ Q_EMIT powerModeChanged();
738+ if (!oldScreen || screen->orientation() != oldScreen->orientation())
739+ Q_EMIT orientationChanged();
740+ if (!oldScreen || screen->position() != oldScreen->position())
741+ Q_EMIT positionChanged();
742+ if (!oldScreen || screen->currentModeIndex() != oldScreen->currentModeIndex())
743+ Q_EMIT currentModeIndexChanged();
744+ if (!oldScreen || screen->physicalSize() != oldScreen->physicalSize())
745+ Q_EMIT physicalSizeChanged();
746+ if (!oldScreen || screen->currentWorkspace() != oldScreen->currentWorkspace())
747+ Q_EMIT currentWorkspaceChanged(currentWorkspace());
748+
749+ if (oldScreen) {
750+ QVector<qtmir::ScreenMode*> oldModes;
751+ auto oldModesQmlList = oldScreen->availableModes();
752+ for (int i = 0; i < oldModesQmlList.count(&oldModesQmlList); i++) {
753+ oldModes << oldModesQmlList.at(&oldModesQmlList, i);
754+ }
755+
756+ QVector<qtmir::ScreenMode*> newModes;
757+ auto newModesQmlList = screen->availableModes();
758+ for (int i = 0; i < newModesQmlList.count(&newModesQmlList); i++) {
759+ newModes << newModesQmlList.at(&newModesQmlList, i);
760+ }
761+
762+ if (newModes != newModes) {
763+ Q_EMIT availableModesChanged();
764+ }
765+ } else {
766+ Q_EMIT availableModesChanged();
767+ }
768+
769+ connectToScreen(screen);
770+}
771+
772+ScreenAttached *WMScreen::qmlAttachedProperties(QObject *owner)
773+{
774+ return new ScreenAttached(owner);
775+}
776
777=== added file 'plugins/WindowManager/ScreenAttached.h'
778--- plugins/WindowManager/ScreenAttached.h 1970-01-01 00:00:00 +0000
779+++ plugins/WindowManager/ScreenAttached.h 2017-04-05 11:48:54 +0000
780@@ -0,0 +1,56 @@
781+/*
782+ * Copyright (C) 2017 Canonical, Ltd.
783+ *
784+ * This program is free software: you can redistribute it and/or modify it under
785+ * the terms of the GNU Lesser General Public License version 3, as published by
786+ * the Free Software Foundation.
787+ *
788+ * This program is distributed in the hope that it will be useful, but WITHOUT
789+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
790+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
791+ * Lesser General Public License for more details.
792+ *
793+ * You should have received a copy of the GNU Lesser General Public License
794+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
795+ */
796+
797+#ifndef SCREENATTACHED_H
798+#define SCREENATTACHED_H
799+
800+#include "Screen.h"
801+
802+#include <QQmlEngine>
803+#include <QtQml>
804+
805+class QQuickWindow;
806+
807+class ScreenAttached : public Screen
808+{
809+ Q_OBJECT
810+public:
811+ ScreenAttached(QObject* owner);
812+
813+ WorkspaceModel* workspaces() const override;
814+ Workspace *currentWorkspace() const override;
815+ void setCurrentWorkspace(Workspace* workspace) override;
816+
817+private Q_SLOTS:
818+ void windowChanged(QQuickWindow*);
819+ void screenChanged(QScreen*);
820+ void screenChanged2(Screen* screen);
821+
822+private:
823+ QPointer<Screen> m_screen;
824+ QQuickWindow* m_window;
825+};
826+
827+class WMScreen : public QObject
828+{
829+ Q_OBJECT
830+public:
831+ static ScreenAttached *qmlAttachedProperties(QObject *owner);
832+};
833+
834+QML_DECLARE_TYPEINFO(WMScreen, QML_HAS_ATTACHED_PROPERTIES)
835+
836+#endif // SCREENATTACHED_H
837
838=== added file 'plugins/WindowManager/ScreenWindow.cpp'
839--- plugins/WindowManager/ScreenWindow.cpp 1970-01-01 00:00:00 +0000
840+++ plugins/WindowManager/ScreenWindow.cpp 2017-04-05 11:48:54 +0000
841@@ -0,0 +1,45 @@
842+/*
843+ * Copyright (C) 2016-2017 Canonical, Ltd.
844+ *
845+ * This program is free software: you can redistribute it and/or modify it under
846+ * the terms of the GNU Lesser General Public License version 3, as published by
847+ * the Free Software Foundation.
848+ *
849+ * This program is distributed in the hope that it will be useful, but WITHOUT
850+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
851+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
852+ * Lesser General Public License for more details.
853+ *
854+ * You should have received a copy of the GNU Lesser General Public License
855+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
856+ */
857+
858+#include "ScreenWindow.h"
859+#include "Screen.h"
860+
861+// Qt
862+#include <QGuiApplication>
863+#include <QDebug>
864+
865+ScreenWindow::ScreenWindow(QQuickWindow *parent)
866+ : QQuickWindow(parent)
867+{
868+}
869+
870+ScreenWindow::~ScreenWindow()
871+{
872+}
873+
874+ConcreteScreen *ScreenWindow::screenWrapper() const
875+{
876+ return m_screen.data();
877+}
878+
879+void ScreenWindow::setScreenWrapper(ConcreteScreen *screen)
880+{
881+ if (m_screen != screen) {
882+ m_screen = screen;
883+ Q_EMIT screenWrapperChanged(screen);
884+ }
885+ QQuickWindow::setScreen(screen->qscreen());
886+}
887
888=== added file 'plugins/WindowManager/ScreenWindow.h'
889--- plugins/WindowManager/ScreenWindow.h 1970-01-01 00:00:00 +0000
890+++ plugins/WindowManager/ScreenWindow.h 2017-04-05 11:48:54 +0000
891@@ -0,0 +1,49 @@
892+/*
893+ * Copyright (C) 2016-2017 Canonical, Ltd.
894+ *
895+ * This program is free software: you can redistribute it and/or modify it under
896+ * the terms of the GNU Lesser General Public License version 3, as published by
897+ * the Free Software Foundation.
898+ *
899+ * This program is distributed in the hope that it will be useful, but WITHOUT
900+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
901+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
902+ * Lesser General Public License for more details.
903+ *
904+ * You should have received a copy of the GNU Lesser General Public License
905+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
906+ */
907+
908+#ifndef UNITY_SCREENWINDOW_H
909+#define UNITY_SCREENWINDOW_H
910+
911+#include <QQuickWindow>
912+#include <QPointer>
913+
914+#include "Screen.h"
915+
916+class ScreenAdapter;
917+
918+/*
919+ * ScreenWindow - wrapper of QQuickWindow to enable QML to specify destination screen.
920+**/
921+class ScreenWindow : public QQuickWindow
922+{
923+ Q_OBJECT
924+ Q_PROPERTY(ConcreteScreen *screen READ screenWrapper WRITE setScreenWrapper NOTIFY screenWrapperChanged)
925+ Q_PROPERTY(int winId READ winId CONSTANT)
926+public:
927+ explicit ScreenWindow(QQuickWindow *parent = 0);
928+ ~ScreenWindow();
929+
930+ ConcreteScreen *screenWrapper() const;
931+ void setScreenWrapper(ConcreteScreen *screen);
932+
933+Q_SIGNALS:
934+ void screenWrapperChanged(ConcreteScreen* screen);
935+
936+private:
937+ QPointer<ConcreteScreen> m_screen;
938+};
939+
940+#endif // UNITY_SCREENWINDOW_H
941
942=== added file 'plugins/WindowManager/Screens.cpp'
943--- plugins/WindowManager/Screens.cpp 1970-01-01 00:00:00 +0000
944+++ plugins/WindowManager/Screens.cpp 2017-04-05 11:48:54 +0000
945@@ -0,0 +1,235 @@
946+/*
947+ * Copyright (C) 2016-2017 Canonical, Ltd.
948+ *
949+ * This program is free software: you can redistribute it and/or modify it under
950+ * the terms of the GNU Lesser General Public License version 3, as published by
951+ * the Free Software Foundation.
952+ *
953+ * This program is distributed in the hope that it will be useful, but WITHOUT
954+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
955+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
956+ * Lesser General Public License for more details.
957+ *
958+ * You should have received a copy of the GNU Lesser General Public License
959+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
960+ */
961+
962+#include "Screens.h"
963+#include "ScreensConfiguration.h"
964+#include "Screen.h"
965+#include "WorkspaceManager.h"
966+
967+// qtmirserver
968+#include <qtmir/screens.h>
969+#include <QGuiApplication>
970+#include <QQmlEngine>
971+
972+// Qt
973+#include <QScreen>
974+#include <QWindow>
975+
976+ConcreteScreens* ConcreteScreens::m_self{nullptr};
977+
978+Screens::Screens(const QSharedPointer<qtmir::Screens>& model)
979+ : m_wrapped(model)
980+{
981+}
982+
983+Screens::~Screens()
984+{
985+ qDeleteAll(m_screens);
986+ m_screens.clear();
987+}
988+
989+QHash<int, QByteArray> Screens::roleNames() const
990+{
991+ QHash<int, QByteArray> roles;
992+ roles[ScreenRole] = "screen";
993+ return roles;
994+}
995+
996+QVariant Screens::data(const QModelIndex &index, int role) const
997+{
998+ if (!index.isValid() || index.row() >= m_screens.size()) {
999+ return QVariant();
1000+ }
1001+
1002+ switch(role) {
1003+ case ScreenRole:
1004+ return QVariant::fromValue(m_screens.at(index.row()));
1005+ } // switch
1006+
1007+ return QVariant();
1008+}
1009+
1010+int Screens::rowCount(const QModelIndex &) const
1011+{
1012+ return count();
1013+}
1014+
1015+int Screens::count() const
1016+{
1017+ return m_screens.size();
1018+}
1019+
1020+QVariant Screens::activeScreen() const
1021+{
1022+ for (int i = 0; i < m_screens.count(); i++) {
1023+ if (m_screens[i]->isActive()) return i;
1024+ }
1025+ return QVariant();
1026+}
1027+
1028+void Screens::activateScreen(const QVariant& vindex)
1029+{
1030+ bool ok = false;
1031+ int index = vindex.toInt(&ok);
1032+ if (!ok || index < 0 || m_screens.count() <= index) return;
1033+
1034+ auto screen = m_screens.at(index);
1035+ screen->setActive(true);
1036+}
1037+
1038+
1039+ConcreteScreens::ConcreteScreens(const QSharedPointer<qtmir::Screens> &model, ScreensConfiguration* config)
1040+ : Screens(model)
1041+ , m_config(config)
1042+{
1043+ m_self = this;
1044+ connect(m_wrapped.data(), &qtmir::Screens::screenAdded, this, &ConcreteScreens::onScreenAdded);
1045+ connect(m_wrapped.data(), &qtmir::Screens::screenRemoved, this, &ConcreteScreens::onScreenRemoved);
1046+ connect(m_wrapped.data(), &qtmir::Screens::activeScreenChanged, this, &ConcreteScreens::activeScreenChanged);
1047+
1048+ Q_FOREACH(qtmir::Screen* screen, m_wrapped->screens()) {
1049+ auto screenWrapper(new ConcreteScreen(screen));
1050+ m_config->load(screenWrapper);
1051+
1052+ QQmlEngine::setObjectOwnership(screenWrapper, QQmlEngine::CppOwnership);
1053+ m_screens.push_back(screenWrapper);
1054+ }
1055+}
1056+
1057+ConcreteScreens::~ConcreteScreens()
1058+{
1059+ Q_FOREACH(Screen* screen, m_screens) {
1060+ m_config->save(screen);
1061+ }
1062+ delete m_config;
1063+}
1064+
1065+ConcreteScreens *ConcreteScreens::self()
1066+{
1067+ return ConcreteScreens::m_self;
1068+}
1069+
1070+ProxyScreens *ConcreteScreens::createProxy()
1071+{
1072+ return new ProxyScreens(this);
1073+}
1074+
1075+void ConcreteScreens::sync(ProxyScreens *proxy)
1076+{
1077+ if (!proxy) return;
1078+ proxy->setSyncing(true);
1079+
1080+ const auto& proxyList = proxy->list();
1081+ for (int i = 0; i < m_screens.count() && i < proxyList.count(); ++i) {
1082+ m_screens[i]->sync(proxyList[i]);
1083+ }
1084+
1085+ proxy->setSyncing(false);
1086+}
1087+
1088+void ConcreteScreens::onScreenAdded(qtmir::Screen *added)
1089+{
1090+ Q_FOREACH(auto screenWrapper, m_screens) {
1091+ if (screenWrapper->wrapped() == added) return;
1092+ }
1093+
1094+ beginInsertRows(QModelIndex(), count(), count());
1095+ auto screenWrapper(new ConcreteScreen(added));
1096+ m_config->load(screenWrapper);
1097+
1098+ QQmlEngine::setObjectOwnership(screenWrapper, QQmlEngine::CppOwnership);
1099+ m_screens.push_back(screenWrapper);
1100+ endInsertRows();
1101+ Q_EMIT screenAdded(screenWrapper);
1102+ Q_EMIT countChanged();
1103+}
1104+
1105+void ConcreteScreens::onScreenRemoved(qtmir::Screen *removed)
1106+{
1107+ int index = 0;
1108+ QMutableVectorIterator<Screen*> iter(m_screens);
1109+ while(iter.hasNext()) {
1110+ auto screenWrapper = iter.next();
1111+ if (screenWrapper->wrapped() == removed) {
1112+ m_config->save(screenWrapper);
1113+
1114+ beginRemoveRows(QModelIndex(), index, index);
1115+ iter.remove();
1116+ endRemoveRows();
1117+
1118+ Q_EMIT screenRemoved(screenWrapper);
1119+ Q_EMIT countChanged();
1120+
1121+ screenWrapper->deleteLater();
1122+ break;
1123+ }
1124+ index++;
1125+ }
1126+}
1127+
1128+
1129+ProxyScreens::ProxyScreens(Screens * const screens)
1130+ : Screens(screens->m_wrapped)
1131+ , m_original(screens)
1132+ , m_syncing(false)
1133+{
1134+ connect(screens, &Screens::screenAdded, this, [this](Screen *added) {
1135+ Q_FOREACH(auto screen, m_screens) {
1136+ auto proxy = static_cast<ProxyScreen*>(screen);
1137+ if (proxy->proxyObject() == added) return;
1138+ }
1139+
1140+ beginInsertRows(QModelIndex(), count(), count());
1141+ auto screenWrapper(new ProxyScreen(added, this));
1142+ QQmlEngine::setObjectOwnership(screenWrapper, QQmlEngine::CppOwnership);
1143+ m_screens.push_back(screenWrapper);
1144+ endInsertRows();
1145+ Q_EMIT screenAdded(screenWrapper);
1146+ Q_EMIT countChanged();
1147+ });
1148+
1149+ connect(screens, &Screens::screenRemoved, this, [this](Screen *removed) {
1150+ int index = 0;
1151+ QMutableVectorIterator<Screen*> iter(m_screens);
1152+ while(iter.hasNext()) {
1153+ auto proxy = static_cast<ProxyScreen*>(iter.next());
1154+ if (proxy->proxyObject() == removed) {
1155+
1156+ beginRemoveRows(QModelIndex(), index, index);
1157+ iter.remove();
1158+ endRemoveRows();
1159+
1160+ Q_EMIT screenRemoved(proxy);
1161+ Q_EMIT countChanged();
1162+
1163+ delete proxy;
1164+ break;
1165+ }
1166+ index++;
1167+ }
1168+ });
1169+
1170+ Q_FOREACH(Screen* screen, screens->list()) {
1171+ auto screenWrapper(new ProxyScreen(screen, this));
1172+ QQmlEngine::setObjectOwnership(screenWrapper, QQmlEngine::CppOwnership);
1173+ m_screens.push_back(screenWrapper);
1174+ }
1175+}
1176+
1177+void ProxyScreens::setSyncing(bool syncing)
1178+{
1179+ m_syncing = syncing;
1180+}
1181
1182=== added file 'plugins/WindowManager/Screens.h'
1183--- plugins/WindowManager/Screens.h 1970-01-01 00:00:00 +0000
1184+++ plugins/WindowManager/Screens.h 2017-04-05 11:48:54 +0000
1185@@ -0,0 +1,111 @@
1186+/*
1187+ * Copyright (C) 2016 Canonical, Ltd.
1188+ *
1189+ * This program is free software: you can redistribute it and/or modify it under
1190+ * the terms of the GNU Lesser General Public License version 3, as published by
1191+ * the Free Software Foundation.
1192+ *
1193+ * This program is distributed in the hope that it will be useful, but WITHOUT
1194+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1195+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1196+ * Lesser General Public License for more details.
1197+ *
1198+ * You should have received a copy of the GNU Lesser General Public License
1199+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1200+ */
1201+
1202+#ifndef UNITY_SCREENS_H
1203+#define UNITY_SCREENS_H
1204+
1205+#include <QAbstractListModel>
1206+#include <QSharedPointer>
1207+#include <QPointer>
1208+
1209+namespace qtmir
1210+{
1211+class Screen;
1212+class Screens;
1213+}
1214+
1215+class Screen;
1216+class ProxyScreens;
1217+class ScreensConfiguration;
1218+
1219+class Screens : public QAbstractListModel
1220+{
1221+ Q_OBJECT
1222+ Q_PROPERTY(int count READ count NOTIFY countChanged)
1223+ Q_PROPERTY(QVariant activeScreen READ activeScreen WRITE activateScreen NOTIFY activeScreenChanged)
1224+
1225+public:
1226+ enum ItemRoles {
1227+ ScreenRole = Qt::UserRole + 1
1228+ };
1229+
1230+ virtual ~Screens();
1231+
1232+ /* QAbstractItemModel */
1233+ QHash<int, QByteArray> roleNames() const override;
1234+ QVariant data(const QModelIndex &index, int role = ScreenRole) const override;
1235+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
1236+
1237+ int count() const;
1238+ QVariant activeScreen() const;
1239+
1240+ const QVector<Screen*>& list() const { return m_screens; }
1241+
1242+public Q_SLOTS:
1243+ void activateScreen(const QVariant& index);
1244+
1245+Q_SIGNALS:
1246+ void countChanged();
1247+ void activeScreenChanged();
1248+
1249+ void screenAdded(Screen* screen);
1250+ void screenRemoved(Screen* screen);
1251+
1252+protected:
1253+ Screens(const QSharedPointer<qtmir::Screens>& model);
1254+
1255+ QVector<Screen*> m_screens;
1256+ const QSharedPointer<qtmir::Screens> m_wrapped;
1257+
1258+ friend class ProxyScreens;
1259+};
1260+
1261+class ConcreteScreens : public Screens
1262+{
1263+ Q_OBJECT
1264+public:
1265+ explicit ConcreteScreens(const QSharedPointer<qtmir::Screens>& model, ScreensConfiguration* config);
1266+ ~ConcreteScreens();
1267+
1268+ Q_INVOKABLE ProxyScreens *createProxy();
1269+ Q_INVOKABLE void sync(ProxyScreens *proxy);
1270+
1271+ static ConcreteScreens *self();
1272+
1273+protected Q_SLOTS:
1274+ void onScreenAdded(qtmir::Screen *screen);
1275+ void onScreenRemoved(qtmir::Screen *screen);
1276+
1277+private:
1278+ ScreensConfiguration* m_config;
1279+
1280+ static ConcreteScreens* m_self;
1281+};
1282+
1283+class ProxyScreens : public Screens
1284+{
1285+public:
1286+ explicit ProxyScreens(Screens*const screens);
1287+
1288+ void setSyncing(bool syncing);
1289+ bool isSyncing() const { return m_syncing; }
1290+
1291+private:
1292+ const QPointer<Screens> m_original;
1293+ bool m_syncing;
1294+};
1295+
1296+#endif // SCREENS_H
1297
1298=== added file 'plugins/WindowManager/ScreensConfiguration.cpp'
1299--- plugins/WindowManager/ScreensConfiguration.cpp 1970-01-01 00:00:00 +0000
1300+++ plugins/WindowManager/ScreensConfiguration.cpp 2017-04-05 11:48:54 +0000
1301@@ -0,0 +1,94 @@
1302+/*
1303+ * Copyright (C) 2017 Canonical, Ltd.
1304+ *
1305+ * This program is free software; you can redistribute it and/or modify
1306+ * it under the terms of the GNU General Public License as published by
1307+ * the Free Software Foundation; version 3.
1308+ *
1309+ * This program is distributed in the hope that it will be useful,
1310+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1311+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1312+ * GNU General Public License for more details.
1313+ *
1314+ * You should have received a copy of the GNU General Public License
1315+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1316+ */
1317+
1318+#include "ScreensConfiguration.h"
1319+#include "Screen.h"
1320+#include "Workspace.h"
1321+#include "WorkspaceManager.h"
1322+
1323+#include <QJsonObject>
1324+#include <QJsonDocument>
1325+#include <QJsonArray>
1326+#include <QFile>
1327+#include <QStandardPaths>
1328+
1329+namespace
1330+{
1331+QJsonArray jsonScreens;
1332+}
1333+
1334+ScreensConfiguration::ScreensConfiguration()
1335+{
1336+ const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
1337+ QFile f(dbPath + "workspaces");
1338+
1339+ if (f.open(QIODevice::ReadOnly)) {
1340+ QByteArray saveData = f.readAll();
1341+ QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
1342+ QJsonObject json(loadDoc.object());
1343+ jsonScreens = json["screens"].toArray();
1344+ }
1345+}
1346+
1347+ScreensConfiguration::~ScreensConfiguration()
1348+{
1349+ const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
1350+ QFile f(dbPath + "workspaces");
1351+ if (f.open(QIODevice::WriteOnly)) {
1352+ QJsonObject json;
1353+ json["screens"] = jsonScreens;
1354+ QJsonDocument saveDoc(json);
1355+ f.write(saveDoc.toJson());
1356+ }
1357+}
1358+
1359+void ScreensConfiguration::load(Screen *screen)
1360+{
1361+ int workspaces = 2;
1362+ for (auto iter = jsonScreens.begin(); iter != jsonScreens.end(); ++iter) {
1363+ QJsonObject jsonScreen = (*iter).toObject();
1364+ if (jsonScreen["name"] == screen->name()) {
1365+ QJsonValue jsonWorkspaces = jsonScreen["workspaces"];
1366+ workspaces = qMax(jsonWorkspaces.toInt(workspaces), 1);
1367+ break;
1368+ }
1369+ }
1370+
1371+ for (int i = 0; i < workspaces; i++) {
1372+ WorkspaceManager::instance()->createWorkspace()->assign(screen->workspaces());
1373+ }
1374+}
1375+
1376+void ScreensConfiguration::save(Screen *screen)
1377+{
1378+ QJsonObject newJsonScreen;
1379+ newJsonScreen["name"] = screen->name();
1380+ newJsonScreen["workspaces"] = qMax(screen->workspaces()->rowCount(), 1);
1381+
1382+ auto iter = jsonScreens.begin();
1383+ for (; iter != jsonScreens.end(); ++iter) {
1384+ QJsonObject jsonScreen = (*iter).toObject();
1385+ if (jsonScreen["name"] == screen->name()) {
1386+ break;
1387+ }
1388+ }
1389+
1390+ if (iter == jsonScreens.end()) {
1391+ jsonScreens.push_back(newJsonScreen);
1392+ } else {
1393+ *iter = newJsonScreen;
1394+ }
1395+}
1396
1397=== added file 'plugins/WindowManager/ScreensConfiguration.h'
1398--- plugins/WindowManager/ScreensConfiguration.h 1970-01-01 00:00:00 +0000
1399+++ plugins/WindowManager/ScreensConfiguration.h 2017-04-05 11:48:54 +0000
1400@@ -0,0 +1,32 @@
1401+/*
1402+ * Copyright (C) 2017 Canonical, Ltd.
1403+ *
1404+ * This program is free software; you can redistribute it and/or modify
1405+ * it under the terms of the GNU General Public License as published by
1406+ * the Free Software Foundation; version 3.
1407+ *
1408+ * This program is distributed in the hope that it will be useful,
1409+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1410+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1411+ * GNU General Public License for more details.
1412+ *
1413+ * You should have received a copy of the GNU General Public License
1414+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1415+ */
1416+
1417+#ifndef SCREENSCONFIGURATION_H
1418+#define SCREENSCONFIGURATION_H
1419+
1420+class Screen;
1421+
1422+class ScreensConfiguration
1423+{
1424+public:
1425+ explicit ScreensConfiguration();
1426+ ~ScreensConfiguration();
1427+
1428+ void load(Screen* screen);
1429+ void save(Screen* screen);
1430+};
1431+
1432+#endif // SCREENSCONFIGURATION_H
1433
1434=== modified file 'plugins/WindowManager/TopLevelWindowModel.cpp'
1435--- plugins/WindowManager/TopLevelWindowModel.cpp 2017-03-16 15:30:33 +0000
1436+++ plugins/WindowManager/TopLevelWindowModel.cpp 2017-04-05 11:48:54 +0000
1437@@ -15,6 +15,7 @@
1438 */
1439
1440 #include "TopLevelWindowModel.h"
1441+#include "WindowManagerObjects.h"
1442
1443 // unity-api
1444 #include <unity/shell/application/ApplicationInfoInterface.h>
1445@@ -24,11 +25,11 @@
1446 #include <unity/shell/application/SurfaceManagerInterface.h>
1447
1448 // Qt
1449-#include <QGuiApplication>
1450 #include <QDebug>
1451
1452 // local
1453 #include "Window.h"
1454+#include "Workspace.h"
1455
1456 Q_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL, "toplevelwindowmodel", QtInfoMsg)
1457
1458@@ -37,7 +38,19 @@
1459
1460 namespace unityapi = unity::shell::application;
1461
1462-TopLevelWindowModel::TopLevelWindowModel()
1463+TopLevelWindowModel::TopLevelWindowModel(Workspace* workspace)
1464+ : m_workspace(workspace)
1465+{
1466+ connect(WindowManagerObjects::instance(), &WindowManagerObjects::applicationManagerChanged,
1467+ this, &TopLevelWindowModel::setApplicationManager);
1468+ connect(WindowManagerObjects::instance(), &WindowManagerObjects::surfaceManagerChanged,
1469+ this, &TopLevelWindowModel::setSurfaceManager);
1470+
1471+ setApplicationManager(WindowManagerObjects::instance()->applicationManager());
1472+ setSurfaceManager(WindowManagerObjects::instance()->surfaceManager());
1473+}
1474+
1475+TopLevelWindowModel::~TopLevelWindowModel()
1476 {
1477 }
1478
1479@@ -55,7 +68,6 @@
1480 beginResetModel();
1481
1482 if (m_applicationManager) {
1483- m_windowModel.clear();
1484 disconnect(m_applicationManager, 0, this, 0);
1485 }
1486
1487@@ -64,26 +76,26 @@
1488 if (m_applicationManager) {
1489 connect(m_applicationManager, &QAbstractItemModel::rowsInserted,
1490 this, [this](const QModelIndex &/*parent*/, int first, int last) {
1491- for (int i = first; i <= last; ++i) {
1492- auto application = m_applicationManager->get(i);
1493- addApplication(application);
1494- }
1495- });
1496+ if (!m_workspace || !m_workspace->isActive())
1497+ return;
1498+
1499+ for (int i = first; i <= last; ++i) {
1500+ auto application = m_applicationManager->get(i);
1501+ addApplication(application);
1502+ }
1503+ });
1504
1505 connect(m_applicationManager, &QAbstractItemModel::rowsAboutToBeRemoved,
1506 this, [this](const QModelIndex &/*parent*/, int first, int last) {
1507- for (int i = first; i <= last; ++i) {
1508- auto application = m_applicationManager->get(i);
1509- removeApplication(application);
1510- }
1511- });
1512-
1513- for (int i = 0; i < m_applicationManager->rowCount(); ++i) {
1514- auto application = m_applicationManager->get(i);
1515- addApplication(application);
1516- }
1517+ for (int i = first; i <= last; ++i) {
1518+ auto application = m_applicationManager->get(i);
1519+ removeApplication(application);
1520+ }
1521+ });
1522 }
1523
1524+ refreshWindows();
1525+
1526 endResetModel();
1527 m_modelState = IdleState;
1528 }
1529@@ -96,6 +108,11 @@
1530
1531 DEBUG_MSG << "(" << surfaceManager << ")";
1532
1533+ Q_ASSERT(m_modelState == IdleState);
1534+ m_modelState = ResettingState;
1535+
1536+ beginResetModel();
1537+
1538 if (m_surfaceManager) {
1539 disconnect(m_surfaceManager, 0, this, 0);
1540 }
1541@@ -103,13 +120,16 @@
1542 m_surfaceManager = surfaceManager;
1543
1544 if (m_surfaceManager) {
1545- connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfaceCreated, this, &TopLevelWindowModel::onSurfaceCreated);
1546+ connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesAddedToWorkspace, this, &TopLevelWindowModel::onSurfacesAddedToWorkspace);
1547 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::surfacesRaised, this, &TopLevelWindowModel::onSurfacesRaised);
1548 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsStarted, this, &TopLevelWindowModel::onModificationsStarted);
1549 connect(m_surfaceManager, &unityapi::SurfaceManagerInterface::modificationsEnded, this, &TopLevelWindowModel::onModificationsEnded);
1550 }
1551
1552- Q_EMIT surfaceManagerChanged(m_surfaceManager);
1553+ refreshWindows();
1554+
1555+ endResetModel();
1556+ m_modelState = IdleState;
1557 }
1558
1559 void TopLevelWindowModel::addApplication(unityapi::ApplicationInfoInterface *application)
1560@@ -149,6 +169,7 @@
1561 Q_ASSERT(surface != nullptr);
1562
1563 connectSurface(surface);
1564+ m_allSurfaces.insert(surface);
1565
1566 bool filledPlaceholder = false;
1567 for (int i = 0; i < m_windowModel.count() && !filledPlaceholder; ++i) {
1568@@ -225,15 +246,14 @@
1569
1570 connect(window, &Window::focusedChanged, this, [this, window](bool focused) {
1571 if (window->surface()) {
1572- // Condense changes to the focused window
1573- // eg: Do focusedWindow=A to focusedWindow=B instead of
1574- // focusedWindow=A to focusedWindow=null to focusedWindow=B
1575 if (focused) {
1576- Q_ASSERT(m_newlyFocusedWindow == nullptr);
1577- m_focusedWindowChanged = true;
1578- m_newlyFocusedWindow = window;
1579+ setFocusedWindow(window);
1580+ m_focusedWindowCleared = false;
1581 } else if (m_focusedWindow == window) {
1582- m_focusedWindowChanged = true;
1583+ // Condense changes to the focused window
1584+ // eg: Do focusedWindow=A to focusedWindow=B instead of
1585+ // focusedWindow=A to focusedWindow=null to focusedWindow=B
1586+ m_focusedWindowCleared = true;
1587 } else {
1588 // don't clear the focused window if you were not there in the first place
1589 // happens when a filled window gets replaced with an empty one (no surface) as the focused window.
1590@@ -338,6 +358,7 @@
1591 auto window = m_windowModel[i].window;
1592 window->setSurface(nullptr);
1593 window->setFocused(false);
1594+ m_allSurfaces.remove(surface);
1595 INFO_MSG << " Removed surface from entry. After: " << toString();
1596 }
1597 }
1598@@ -353,44 +374,103 @@
1599 return qmlWindow;
1600 }
1601
1602-void TopLevelWindowModel::onSurfaceCreated(unityapi::MirSurfaceInterface *surface)
1603-{
1604- DEBUG_MSG << "(" << surface << ")";
1605-
1606- if (surface->parentSurface()) {
1607- // Wrap it in a Window so that we keep focusedWindow() up to date.
1608- Window *window = createWindow(surface);
1609- connect(surface, &QObject::destroyed, window, [=](){
1610+void TopLevelWindowModel::onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
1611+ const QVector<unity::shell::application::MirSurfaceInterface*> surfaces)
1612+{
1613+ if (!m_workspace || !m_applicationManager) return;
1614+ if (workspace != m_workspace->workspace()) {
1615+ removeSurfaces(surfaces);
1616+ return;
1617+ }
1618+
1619+ Q_FOREACH(auto surface, surfaces) {
1620+ if (m_allSurfaces.contains(surface)) continue;
1621+
1622+ if (surface->parentSurface()) {
1623+ // Wrap it in a Window so that we keep focusedWindow() up to date.
1624+ Window *window = createWindow(surface);
1625+ connect(surface, &QObject::destroyed, window, [=](){
1626+ window->setSurface(nullptr);
1627+ window->deleteLater();
1628+ });
1629+ } else {
1630+ if (surface->type() == Mir::InputMethodType) {
1631+ connectSurface(surface);
1632+ setInputMethodWindow(createWindow(surface));
1633+ } else {
1634+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
1635+ if (application) {
1636+ if (surface->state() == Mir::HiddenState) {
1637+ // Ignore it until it's finally shown
1638+ connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
1639+ Q_ASSERT(newState != Mir::HiddenState);
1640+ disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
1641+ prependSurface(surface, application);
1642+ });
1643+ } else {
1644+ prependSurface(surface, application);
1645+ }
1646+ } else {
1647+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
1648+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
1649+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
1650+ Window *promptWindow = createWindow(surface);
1651+ connect(surface, &QObject::destroyed, promptWindow, [=](){
1652+ promptWindow->setSurface(nullptr);
1653+ promptWindow->deleteLater();
1654+ });
1655+ }
1656+ }
1657+ }
1658+ }
1659+}
1660+
1661+void TopLevelWindowModel::removeSurfaces(const QVector<unity::shell::application::MirSurfaceInterface *> surfaces)
1662+{
1663+ int start = -1;
1664+ int end = -1;
1665+ for (auto iter = surfaces.constBegin(); iter != surfaces.constEnd();) {
1666+ auto surface = *iter;
1667+ iter++;
1668+
1669+ // Do removals in adjacent blocks.
1670+ start = end = indexOf(surface);
1671+ if (start == -1) {
1672+ // could be a child surface
1673+ m_allSurfaces.remove(surface);
1674+ continue;
1675+ }
1676+ while(iter != surfaces.constEnd()) {
1677+ int index = indexOf(*iter);
1678+ if (index != end+1) {
1679+ break;
1680+ }
1681+ end++;
1682+ iter++;
1683+ }
1684+
1685+ if (m_modelState == IdleState) {
1686+ beginRemoveRows(QModelIndex(), start, end);
1687+ m_modelState = RemovingState;
1688+ } else {
1689+ Q_ASSERT(m_modelState == ResettingState);
1690+ // No point in signaling anything if we're resetting the whole model
1691+ }
1692+
1693+ for (int index = start; index <= end; index++) {
1694+ auto window = m_windowModel[start].window;
1695 window->setSurface(nullptr);
1696- window->deleteLater();
1697- });
1698- } else {
1699- if (surface->type() == Mir::InputMethodType) {
1700- connectSurface(surface);
1701- setInputMethodWindow(createWindow(surface));
1702- } else {
1703- auto *application = m_applicationManager->findApplicationWithSurface(surface);
1704- if (application) {
1705- if (surface->state() == Mir::HiddenState) {
1706- // Ignore it until it's finally shown
1707- connect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, [=](Mir::State newState) {
1708- Q_ASSERT(newState != Mir::HiddenState);
1709- disconnect(surface, &unityapi::MirSurfaceInterface::stateChanged, this, 0);
1710- prependSurface(surface, application);
1711- });
1712- } else {
1713- prependSurface(surface, application);
1714- }
1715- } else {
1716- // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
1717- // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
1718- // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
1719- Window *promptWindow = createWindow(surface);
1720- connect(surface, &QObject::destroyed, promptWindow, [=](){
1721- promptWindow->setSurface(nullptr);
1722- promptWindow->deleteLater();
1723- });
1724- }
1725+ window->setFocused(false);
1726+
1727+ m_windowModel.removeAt(start);
1728+ m_allSurfaces.remove(surface);
1729+ }
1730+
1731+ if (m_modelState == RemovingState) {
1732+ endRemoveRows();
1733+ Q_EMIT countChanged();
1734+ Q_EMIT listChanged();
1735+ m_modelState = IdleState;
1736 }
1737 }
1738 }
1739@@ -417,12 +497,14 @@
1740 }
1741
1742 auto window = m_windowModel[index].window;
1743+ auto surface = window->surface();
1744
1745 if (!window->surface()) {
1746 window->setFocused(false);
1747 }
1748
1749 m_windowModel.removeAt(index);
1750+ m_allSurfaces.remove(surface);
1751
1752 if (m_modelState == RemovingState) {
1753 endRemoveRows();
1754@@ -433,6 +515,7 @@
1755
1756 if (m_focusedWindow == window) {
1757 setFocusedWindow(nullptr);
1758+ m_focusedWindowCleared = false;
1759 }
1760
1761 INFO_MSG << " after " << toString();
1762@@ -451,6 +534,15 @@
1763 void TopLevelWindowModel::removeInputMethodWindow()
1764 {
1765 if (m_inputMethodWindow) {
1766+ auto surface = m_inputMethodWindow->surface();
1767+ if (surface) {
1768+ m_allSurfaces.remove(surface);
1769+ }
1770+ if (m_focusedWindow == m_inputMethodWindow) {
1771+ setFocusedWindow(nullptr);
1772+ m_focusedWindowCleared = false;
1773+ }
1774+
1775 delete m_inputMethodWindow;
1776 m_inputMethodWindow = nullptr;
1777 Q_EMIT inputMethodSurfaceChanged(nullptr);
1778@@ -462,7 +554,7 @@
1779 DEBUG_MSG << "(" << surfaces << ")";
1780 const int raiseCount = surfaces.size();
1781 for (int i = 0; i < raiseCount; i++) {
1782- int fromIndex = findIndexOf(surfaces[i]);
1783+ int fromIndex = indexOf(surfaces[i]);
1784 if (fromIndex != -1) {
1785 move(fromIndex, 0);
1786 }
1787@@ -489,16 +581,6 @@
1788 }
1789 }
1790
1791-int TopLevelWindowModel::findIndexOf(const unityapi::MirSurfaceInterface *surface) const
1792-{
1793- for (int i=0; i<m_windowModel.count(); i++) {
1794- if (m_windowModel[i].window->surface() == surface) {
1795- return i;
1796- }
1797- }
1798- return -1;
1799-}
1800-
1801 int TopLevelWindowModel::generateId()
1802 {
1803 int id = m_nextId;
1804@@ -700,12 +782,11 @@
1805
1806 void TopLevelWindowModel::onModificationsEnded()
1807 {
1808- if (m_focusedWindowChanged) {
1809- setFocusedWindow(m_newlyFocusedWindow);
1810+ if (m_focusedWindowCleared) {
1811+ setFocusedWindow(nullptr);
1812 }
1813 // reset
1814- m_focusedWindowChanged = false;
1815- m_newlyFocusedWindow = nullptr;
1816+ m_focusedWindowCleared = false;
1817 }
1818
1819 void TopLevelWindowModel::activateTopMostWindowWithoutId(int forbiddenId)
1820@@ -719,3 +800,54 @@
1821 }
1822 }
1823 }
1824+
1825+void TopLevelWindowModel::refreshWindows()
1826+{
1827+ DEBUG_MSG << "()";
1828+ clear();
1829+
1830+ if (!m_workspace || !m_applicationManager || !m_surfaceManager) return;
1831+
1832+ m_surfaceManager->forEachSurfaceInWorkspace(m_workspace->workspace(), [this](unity::shell::application::MirSurfaceInterface* surface) {
1833+ if (surface->parentSurface()) {
1834+ // Wrap it in a Window so that we keep focusedWindow() up to date.
1835+ Window *window = createWindow(surface);
1836+ connect(surface, &QObject::destroyed, window, [=](){
1837+ window->setSurface(nullptr);
1838+ window->deleteLater();
1839+ });
1840+ } else {
1841+ if (surface->type() == Mir::InputMethodType) {
1842+ setInputMethodWindow(createWindow(surface));
1843+ } else {
1844+ auto *application = m_applicationManager->findApplicationWithSurface(surface);
1845+ if (application) {
1846+ prependSurface(surface, application);
1847+ } else {
1848+ // Must be a prompt session. No need to do add it as a prompt surface is not top-level.
1849+ // It will show up in the ApplicationInfoInterface::promptSurfaceList of some application.
1850+ // Still wrap it in a Window though, so that we keep focusedWindow() up to date.
1851+ Window *promptWindow = createWindow(surface);
1852+ connect(surface, &QObject::destroyed, promptWindow, [=](){
1853+ promptWindow->setSurface(nullptr);
1854+ promptWindow->deleteLater();
1855+ });
1856+ }
1857+ }
1858+ }
1859+ });
1860+}
1861+
1862+void TopLevelWindowModel::clear()
1863+{
1864+ DEBUG_MSG << "()";
1865+
1866+ while(m_windowModel.count() > 0) {
1867+ ModelEntry entry = m_windowModel.takeAt(0);
1868+ disconnect(entry.window, 0, this, 0);
1869+ delete entry.window;
1870+ }
1871+ m_allSurfaces.clear();
1872+ setFocusedWindow(nullptr);
1873+ m_focusedWindowCleared = false;
1874+}
1875
1876=== modified file 'plugins/WindowManager/TopLevelWindowModel.h'
1877--- plugins/WindowManager/TopLevelWindowModel.h 2017-03-16 15:30:33 +0000
1878+++ plugins/WindowManager/TopLevelWindowModel.h 2017-04-05 11:48:54 +0000
1879@@ -20,11 +20,16 @@
1880 #include <QAbstractListModel>
1881 #include <QLoggingCategory>
1882
1883+#include <memory>
1884+
1885 #include "WindowManagerGlobal.h"
1886
1887 Q_DECLARE_LOGGING_CATEGORY(TOPLEVELWINDOWMODEL)
1888
1889 class Window;
1890+class Workspace;
1891+
1892+namespace miral { class Workspace; }
1893
1894 namespace unity {
1895 namespace shell {
1896@@ -72,16 +77,6 @@
1897 */
1898 Q_PROPERTY(Window* focusedWindow READ focusedWindow NOTIFY focusedWindowChanged)
1899
1900- Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager
1901- READ surfaceManager
1902- WRITE setSurfaceManager
1903- NOTIFY surfaceManagerChanged)
1904-
1905- Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager
1906- READ applicationManager
1907- WRITE setApplicationManager
1908- NOTIFY applicationManagerChanged)
1909-
1910 /**
1911 The id to be used on the next entry created
1912 Useful for tests
1913@@ -100,7 +95,8 @@
1914 ApplicationRole = Qt::UserRole + 1,
1915 };
1916
1917- TopLevelWindowModel();
1918+ TopLevelWindowModel(Workspace* workspace);
1919+ ~TopLevelWindowModel();
1920
1921 // From QAbstractItemModel
1922 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
1923@@ -111,17 +107,11 @@
1924 return roleNames;
1925 }
1926
1927- // Own API
1928+ // Own API;
1929
1930 unity::shell::application::MirSurfaceInterface* inputMethodSurface() const;
1931 Window* focusedWindow() const;
1932
1933- unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; }
1934- void setApplicationManager(unity::shell::application::ApplicationManagerInterface*);
1935-
1936- unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; }
1937- void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*);
1938-
1939 int nextId() const { return m_nextId; }
1940
1941 public:
1942@@ -164,12 +154,13 @@
1943 */
1944 Q_INVOKABLE void raiseId(int id);
1945
1946+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface*);
1947+ void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*);
1948+
1949 Q_SIGNALS:
1950 void countChanged();
1951 void inputMethodSurfaceChanged(unity::shell::application::MirSurfaceInterface* inputMethodSurface);
1952 void focusedWindowChanged(Window *focusedWindow);
1953- void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*);
1954- void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*);
1955
1956 /**
1957 * @brief Emitted when the list changes
1958@@ -181,7 +172,8 @@
1959 void nextIdChanged();
1960
1961 private Q_SLOTS:
1962- void onSurfaceCreated(unity::shell::application::MirSurfaceInterface *surface);
1963+ void onSurfacesAddedToWorkspace(const std::shared_ptr<miral::Workspace>& workspace,
1964+ const QVector<unity::shell::application::MirSurfaceInterface*> surfaces);
1965 void onSurfacesRaised(const QVector<unity::shell::application::MirSurfaceInterface*> &surfaces);
1966
1967 void onModificationsStarted();
1968@@ -198,9 +190,9 @@
1969 void setInputMethodWindow(Window *window);
1970 void setFocusedWindow(Window *window);
1971 void removeInputMethodWindow();
1972- int findIndexOf(const unity::shell::application::MirSurfaceInterface *surface) const;
1973 void deleteAt(int index);
1974 void removeAt(int index);
1975+ void removeSurfaces(const QVector<unity::shell::application::MirSurfaceInterface *> surfaces);
1976
1977 void addApplication(unity::shell::application::ApplicationInfoInterface *application);
1978 void removeApplication(unity::shell::application::ApplicationInfoInterface *application);
1979@@ -223,6 +215,8 @@
1980 void activateEmptyWindow(Window *window);
1981
1982 void activateTopMostWindowWithoutId(int forbiddenId);
1983+ void refreshWindows();
1984+ void clear();
1985
1986 Window *createWindow(unity::shell::application::MirSurfaceInterface *surface);
1987
1988@@ -239,6 +233,9 @@
1989 QVector<ModelEntry> m_windowModel;
1990 Window* m_inputMethodWindow{nullptr};
1991 Window* m_focusedWindow{nullptr};
1992+ Workspace* m_workspace{nullptr};
1993+ // track all the surfaces we've been told about.
1994+ QSet<unity::shell::application::MirSurfaceInterface*> m_allSurfaces;
1995
1996 int m_nextId{1};
1997 // Just something big enough that we don't risk running out of unused id numbers.
1998@@ -259,8 +256,7 @@
1999 ModelState m_modelState{IdleState};
2000
2001 // Valid between modificationsStarted and modificationsEnded
2002- bool m_focusedWindowChanged{false};
2003- Window *m_newlyFocusedWindow{nullptr};
2004+ bool m_focusedWindowCleared{false};
2005 };
2006
2007 #endif // TOPLEVELWINDOWMODEL_H
2008
2009=== added file 'plugins/WindowManager/WindowManagerObjects.cpp'
2010--- plugins/WindowManager/WindowManagerObjects.cpp 1970-01-01 00:00:00 +0000
2011+++ plugins/WindowManager/WindowManagerObjects.cpp 2017-04-05 11:48:54 +0000
2012@@ -0,0 +1,47 @@
2013+/*
2014+ * Copyright (C) 2017 Canonical, Ltd.
2015+ *
2016+ * This program is free software; you can redistribute it and/or modify
2017+ * it under the terms of the GNU General Public License as published by
2018+ * the Free Software Foundation; version 3.
2019+ *
2020+ * This program is distributed in the hope that it will be useful,
2021+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2022+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2023+ * GNU General Public License for more details.
2024+ *
2025+ * You should have received a copy of the GNU General Public License
2026+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2027+ */
2028+
2029+#include "WindowManagerObjects.h"
2030+
2031+WindowManagerObjects::WindowManagerObjects(QObject *parent)
2032+ : QObject(parent)
2033+ , m_surfaceManager(nullptr)
2034+ , m_applicationManager(nullptr)
2035+{
2036+}
2037+
2038+WindowManagerObjects *WindowManagerObjects::instance()
2039+{
2040+ static WindowManagerObjects* objects(new WindowManagerObjects());
2041+ return objects;
2042+}
2043+
2044+
2045+void WindowManagerObjects::setSurfaceManager(unity::shell::application::SurfaceManagerInterface *surfaceManager)
2046+{
2047+ if (m_surfaceManager == surfaceManager) return;
2048+
2049+ m_surfaceManager = surfaceManager;
2050+ Q_EMIT surfaceManagerChanged(surfaceManager);
2051+}
2052+
2053+void WindowManagerObjects::setApplicationManager(unity::shell::application::ApplicationManagerInterface *applicationManager)
2054+{
2055+ if (m_applicationManager == applicationManager) return;
2056+
2057+ m_applicationManager = applicationManager;
2058+ Q_EMIT applicationManagerChanged(applicationManager);
2059+}
2060
2061=== added file 'plugins/WindowManager/WindowManagerObjects.h'
2062--- plugins/WindowManager/WindowManagerObjects.h 1970-01-01 00:00:00 +0000
2063+++ plugins/WindowManager/WindowManagerObjects.h 2017-04-05 11:48:54 +0000
2064@@ -0,0 +1,66 @@
2065+/*
2066+ * Copyright (C) 2017 Canonical, Ltd.
2067+ *
2068+ * This program is free software; you can redistribute it and/or modify
2069+ * it under the terms of the GNU General Public License as published by
2070+ * the Free Software Foundation; version 3.
2071+ *
2072+ * This program is distributed in the hope that it will be useful,
2073+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2074+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2075+ * GNU General Public License for more details.
2076+ *
2077+ * You should have received a copy of the GNU General Public License
2078+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2079+ */
2080+
2081+#ifndef WINDOWMANAGEROBJECTS_H
2082+#define WINDOWMANAGEROBJECTS_H
2083+
2084+#include <QObject>
2085+
2086+#include "WindowManagerGlobal.h"
2087+
2088+namespace unity {
2089+ namespace shell {
2090+ namespace application {
2091+ class SurfaceManagerInterface;
2092+ class ApplicationManagerInterface;
2093+ }
2094+ }
2095+}
2096+
2097+class WINDOWMANAGERQML_EXPORT WindowManagerObjects : public QObject
2098+{
2099+ Q_OBJECT
2100+
2101+ Q_PROPERTY(unity::shell::application::SurfaceManagerInterface* surfaceManager
2102+ READ surfaceManager
2103+ WRITE setSurfaceManager
2104+ NOTIFY surfaceManagerChanged)
2105+
2106+ Q_PROPERTY(unity::shell::application::ApplicationManagerInterface* applicationManager
2107+ READ applicationManager
2108+ WRITE setApplicationManager
2109+ NOTIFY applicationManagerChanged)
2110+public:
2111+ explicit WindowManagerObjects(QObject *parent = 0);
2112+
2113+ static WindowManagerObjects *instance();
2114+
2115+ unity::shell::application::SurfaceManagerInterface *surfaceManager() const { return m_surfaceManager; }
2116+ void setSurfaceManager(unity::shell::application::SurfaceManagerInterface*);
2117+
2118+ unity::shell::application::ApplicationManagerInterface *applicationManager() const { return m_applicationManager; }
2119+ void setApplicationManager(unity::shell::application::ApplicationManagerInterface*);
2120+
2121+Q_SIGNALS:
2122+ void surfaceManagerChanged(unity::shell::application::SurfaceManagerInterface*);
2123+ void applicationManagerChanged(unity::shell::application::ApplicationManagerInterface*);
2124+
2125+private:
2126+ unity::shell::application::SurfaceManagerInterface* m_surfaceManager;
2127+ unity::shell::application::ApplicationManagerInterface* m_applicationManager;
2128+};
2129+
2130+#endif // WINDOWMANAGEROBJECTS_H
2131
2132=== modified file 'plugins/WindowManager/WindowManagerPlugin.cpp'
2133--- plugins/WindowManager/WindowManagerPlugin.cpp 2017-03-24 14:04:50 +0000
2134+++ plugins/WindowManager/WindowManagerPlugin.cpp 2017-04-05 11:48:54 +0000
2135@@ -17,19 +17,74 @@
2136 #include "WindowManagerPlugin.h"
2137
2138 #include "AvailableDesktopArea.h"
2139+#include "Screen.h"
2140+#include "ScreenAttached.h"
2141+#include "Screens.h"
2142+#include "ScreensConfiguration.h"
2143+#include "ScreenWindow.h"
2144 #include "TopLevelWindowModel.h"
2145 #include "Window.h"
2146+#include "WindowManagerObjects.h"
2147 #include "WindowMargins.h"
2148+#include "WorkspaceManager.h"
2149+#include "Workspace.h"
2150+#include "WorkspaceModel.h"
2151
2152 #include <QtQml>
2153+#include <qtmir/qtmir.h>
2154+
2155+namespace {
2156+
2157+static const QString notInstantiatable = QStringLiteral("Not instantiatable");
2158+
2159+static QObject *workspace_manager(QQmlEngine *engine, QJSEngine *scriptEngine)
2160+{
2161+ Q_UNUSED(engine)
2162+ Q_UNUSED(scriptEngine)
2163+ return WorkspaceManager::instance();
2164+}
2165+QObject* screensSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
2166+ Q_UNUSED(engine);
2167+ Q_UNUSED(scriptEngine);
2168+ return ConcreteScreens::self();
2169+}
2170+QObject* objectsSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
2171+ Q_UNUSED(engine);
2172+ Q_UNUSED(scriptEngine);
2173+ return WindowManagerObjects::instance();
2174+}
2175+
2176+} // namspace
2177
2178 void WindowManagerPlugin::registerTypes(const char *uri)
2179 {
2180 qmlRegisterType<AvailableDesktopArea>(uri, 1, 0, "AvailableDesktopArea");
2181- qmlRegisterType<TopLevelWindowModel>(uri, 1, 0, "TopLevelWindowModel");
2182 qmlRegisterType<WindowMargins>(uri, 1, 0, "WindowMargins");
2183+ qmlRegisterSingletonType<WorkspaceManager>(uri, 1, 0, "WorkspaceManager", workspace_manager);
2184+ qmlRegisterSingletonType<ConcreteScreens>(uri, 1, 0, "Screens", screensSingleton);
2185+ qmlRegisterUncreatableType<qtmir::ScreenMode>(uri, 1, 0, "ScreenMode", notInstantiatable);
2186+ qmlRegisterSingletonType<WindowManagerObjects>(uri, 1, 0, "WindowManagerObjects", objectsSingleton);
2187+
2188+ qRegisterMetaType<ConcreteScreen*>("ConcreteScreen*");
2189+ qRegisterMetaType<ProxyScreens*>("ProxyScreens*");
2190+ qRegisterMetaType<Workspace*>("Workspace*");
2191+ qRegisterMetaType<TopLevelWindowModel*>("TopLevelWindowModel*");
2192+ qRegisterMetaType<ScreenConfig*>("ScreenConfig*");
2193+ qRegisterMetaType<WorkspaceModel*>("WorkspaceModel*");
2194
2195 qRegisterMetaType<Window*>("Window*");
2196-
2197 qRegisterMetaType<QAbstractListModel*>("QAbstractListModel*");
2198+
2199+ qmlRegisterType<ScreenWindow>(uri, 1, 0, "ScreenWindow");
2200+ qmlRegisterRevision<QWindow,1>(uri, 1, 0);
2201+
2202+ qmlRegisterUncreatableType<WMScreen>(uri, 1, 0, "WMScreen", notInstantiatable);
2203+}
2204+
2205+void WindowManagerPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
2206+{
2207+ QQmlExtensionPlugin::initializeEngine(engine, uri);
2208+
2209+ // Create Screens
2210+ new ConcreteScreens(qtmir::get_screen_model(), new ScreensConfiguration());
2211 }
2212
2213=== modified file 'plugins/WindowManager/WindowManagerPlugin.h'
2214--- plugins/WindowManager/WindowManagerPlugin.h 2016-04-04 13:37:49 +0000
2215+++ plugins/WindowManager/WindowManagerPlugin.h 2017-04-05 11:48:54 +0000
2216@@ -27,6 +27,7 @@
2217
2218 public:
2219 void registerTypes(const char *uri) override;
2220+ void initializeEngine(QQmlEngine *engine, const char *uri) override;
2221 };
2222
2223 #endif // WINDOWMANAGER_PLUGIN_H
2224
2225=== added file 'plugins/WindowManager/Workspace.cpp'
2226--- plugins/WindowManager/Workspace.cpp 1970-01-01 00:00:00 +0000
2227+++ plugins/WindowManager/Workspace.cpp 2017-04-05 11:48:54 +0000
2228@@ -0,0 +1,178 @@
2229+/*
2230+ * Copyright (C) 2017 Canonical, Ltd.
2231+ *
2232+ * This program is free software; you can redistribute it and/or modify
2233+ * it under the terms of the GNU General Public License as published by
2234+ * the Free Software Foundation; version 3.
2235+ *
2236+ * This program is distributed in the hope that it will be useful,
2237+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2238+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2239+ * GNU General Public License for more details.
2240+ *
2241+ * You should have received a copy of the GNU General Public License
2242+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2243+ */
2244+
2245+#include "Workspace.h"
2246+#include "WorkspaceModel.h"
2247+#include "WorkspaceManager.h"
2248+#include "TopLevelWindowModel.h"
2249+#include "Screen.h"
2250+
2251+#include "wmpolicyinterface.h"
2252+
2253+int nextWorkspace = 0;
2254+
2255+Workspace::Workspace(QObject *parent)
2256+ : QObject(parent)
2257+ , m_workspace(WMPolicyInterface::instance()->createWorkspace())
2258+ , m_model(nullptr)
2259+{
2260+ setObjectName((QString("Wks%1").arg(nextWorkspace++)));
2261+}
2262+
2263+Workspace::Workspace(const Workspace &other)
2264+ : QObject(nullptr)
2265+ , m_workspace(other.m_workspace)
2266+ , m_model(nullptr)
2267+{
2268+ setObjectName(other.objectName());
2269+
2270+ connect(&other, &Workspace::activeChanged, this, &Workspace::activeChanged);
2271+}
2272+
2273+Workspace::~Workspace()
2274+{
2275+ if (m_model) {
2276+ m_model->remove(this);
2277+ }
2278+}
2279+
2280+void Workspace::assign(WorkspaceModel *model, const QVariant& vIndex)
2281+{
2282+ if (m_model == model) return;
2283+
2284+ if (m_model) {
2285+ disconnect(m_model, 0, this, 0);
2286+ m_model->remove(this);
2287+ }
2288+
2289+ m_model = model;
2290+
2291+ if (model) {
2292+ int index = m_model->rowCount();
2293+ if (vIndex.isValid() && vIndex.canConvert(QVariant::Int)) {
2294+ index = vIndex.toInt();
2295+ }
2296+ m_model->insert(index, this);
2297+
2298+ connect(m_model, &QObject::destroyed, this, [this]() {
2299+ m_model = nullptr;
2300+ Q_EMIT unassigned();
2301+ });
2302+ Q_EMIT assigned();
2303+ } else {
2304+ Q_EMIT unassigned();
2305+ }
2306+}
2307+
2308+void Workspace::unassign()
2309+{
2310+ assign(nullptr);
2311+}
2312+
2313+bool Workspace::isAssigned() const
2314+{
2315+ return m_model != nullptr;
2316+}
2317+
2318+bool Workspace::isSameAs(Workspace *wks) const
2319+{
2320+ if (!wks) return false;
2321+ if (wks == this) return true;
2322+ return wks->workspace() == workspace();
2323+}
2324+
2325+
2326+ConcreteWorkspace::ConcreteWorkspace(QObject *parent)
2327+ : Workspace(parent)
2328+ , m_active(false)
2329+ , m_windowModel(new TopLevelWindowModel(this))
2330+{
2331+ connect(WorkspaceManager::instance(), &WorkspaceManager::activeWorkspaceChanged, this, [this](Workspace* activeWorkspace) {
2332+ bool newActive = activeWorkspace == this;
2333+ if (newActive != m_active) {
2334+ m_active = newActive;
2335+ Q_EMIT activeChanged(m_active);
2336+
2337+ if (m_active) {
2338+ WMPolicyInterface::instance()->setActiveWorkspace(m_workspace);
2339+ }
2340+ }
2341+ });
2342+}
2343+
2344+ConcreteWorkspace::~ConcreteWorkspace()
2345+{
2346+ WorkspaceManager::instance()->destroyWorkspace(this);
2347+ WMPolicyInterface::instance()->releaseWorkspace(m_workspace);
2348+}
2349+
2350+TopLevelWindowModel *ConcreteWorkspace::windowModel() const
2351+{
2352+ return m_windowModel.data();
2353+}
2354+
2355+void ConcreteWorkspace::activate()
2356+{
2357+ WorkspaceManager::instance()->setActiveWorkspace(this);
2358+}
2359+
2360+void ConcreteWorkspace::setCurrentOn(Screen *screen)
2361+{
2362+ if (screen) {
2363+ screen->setCurrentWorkspace(this);
2364+ }
2365+}
2366+
2367+
2368+ProxyWorkspace::ProxyWorkspace(Workspace * const workspace)
2369+ : Workspace(*workspace)
2370+ , m_original(workspace)
2371+{
2372+}
2373+
2374+void ProxyWorkspace::assign(WorkspaceModel *model, const QVariant &index)
2375+{
2376+ Workspace::assign(model, index);
2377+}
2378+
2379+void ProxyWorkspace::unassign()
2380+{
2381+ Workspace::unassign();
2382+}
2383+
2384+bool ProxyWorkspace::isActive() const
2385+{
2386+ return m_original ? m_original->isActive() : false;
2387+}
2388+
2389+TopLevelWindowModel *ProxyWorkspace::windowModel() const
2390+{
2391+ return m_original ? m_original->windowModel() : nullptr;
2392+}
2393+
2394+void ProxyWorkspace::activate()
2395+{
2396+ if (m_original) {
2397+ m_original->activate();
2398+ }
2399+}
2400+
2401+void ProxyWorkspace::setCurrentOn(Screen *screen)
2402+{
2403+ if (screen && m_original) {
2404+ screen->setCurrentWorkspace(m_original);
2405+ }
2406+}
2407
2408=== added file 'plugins/WindowManager/Workspace.h'
2409--- plugins/WindowManager/Workspace.h 1970-01-01 00:00:00 +0000
2410+++ plugins/WindowManager/Workspace.h 2017-04-05 11:48:54 +0000
2411@@ -0,0 +1,122 @@
2412+/*
2413+ * Copyright (C) 2017 Canonical, Ltd.
2414+ *
2415+ * This program is free software; you can redistribute it and/or modify
2416+ * it under the terms of the GNU General Public License as published by
2417+ * the Free Software Foundation; version 3.
2418+ *
2419+ * This program is distributed in the hope that it will be useful,
2420+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2421+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2422+ * GNU General Public License for more details.
2423+ *
2424+ * You should have received a copy of the GNU General Public License
2425+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2426+ */
2427+
2428+#ifndef WINDOWMANAGER_WORKSPACE_H
2429+#define WINDOWMANAGER_WORKSPACE_H
2430+
2431+#include <QObject>
2432+#include <QVariant>
2433+#include <QPointer>
2434+#include <QSharedPointer>
2435+
2436+#include <memory>
2437+#include <functional>
2438+
2439+#include "WindowManagerGlobal.h"
2440+
2441+class WorkspaceModel;
2442+class TopLevelWindowModel;
2443+class Screen;
2444+
2445+namespace miral { class Workspace; }
2446+
2447+namespace unity {
2448+ namespace shell {
2449+ namespace application {
2450+ class MirSurfaceInterface;
2451+ }
2452+ }
2453+}
2454+
2455+class WINDOWMANAGERQML_EXPORT Workspace : public QObject
2456+{
2457+ Q_OBJECT
2458+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
2459+ Q_PROPERTY(TopLevelWindowModel* windowModel READ windowModel CONSTANT)
2460+public:
2461+ virtual ~Workspace();
2462+
2463+ virtual void assign(WorkspaceModel* model, const QVariant& index = QVariant());
2464+ virtual void unassign();
2465+
2466+ virtual bool isActive() const = 0;
2467+ virtual TopLevelWindowModel *windowModel() const = 0;
2468+ virtual void setCurrentOn(Screen*) = 0;
2469+
2470+ std::shared_ptr<miral::Workspace> workspace() const { return m_workspace; }
2471+ bool isAssigned() const;
2472+ Q_INVOKABLE bool isSameAs(Workspace*) const;
2473+
2474+public Q_SLOTS:
2475+ virtual void activate() = 0;
2476+
2477+Q_SIGNALS:
2478+ void assigned();
2479+ void unassigned();
2480+
2481+ void activeChanged(bool);
2482+
2483+protected:
2484+ Workspace(QObject *parent = nullptr);
2485+ Workspace(Workspace const& other);
2486+
2487+ std::shared_ptr<miral::Workspace> m_workspace;
2488+ WorkspaceModel* m_model;
2489+};
2490+
2491+class WINDOWMANAGERQML_EXPORT ConcreteWorkspace : public Workspace
2492+{
2493+public:
2494+ ~ConcreteWorkspace();
2495+
2496+ bool isActive() const override { return m_active; }
2497+ TopLevelWindowModel *windowModel() const override;
2498+ void activate() override;
2499+ void setCurrentOn(Screen*) override;
2500+
2501+private:
2502+ explicit ConcreteWorkspace(QObject *parent = nullptr);
2503+
2504+ bool m_active;
2505+ const QScopedPointer<TopLevelWindowModel> m_windowModel;
2506+
2507+ friend class WorkspaceManager;
2508+};
2509+
2510+class ProxyWorkspace : public Workspace
2511+{
2512+ Q_OBJECT
2513+public:
2514+ explicit ProxyWorkspace(Workspace*const workspace);
2515+ ~ProxyWorkspace() = default;
2516+
2517+ Q_INVOKABLE void assign(WorkspaceModel* model, const QVariant& index = QVariant()) override;
2518+
2519+ bool isActive() const override;
2520+ TopLevelWindowModel *windowModel() const override;
2521+ void activate() override;
2522+ void setCurrentOn(Screen*) override;
2523+
2524+ Workspace* proxyObject() const { return m_original.data(); }
2525+
2526+public Q_SLOTS:
2527+ void unassign() override;
2528+
2529+private:
2530+ const QPointer<Workspace> m_original;
2531+};
2532+
2533+#endif // WINDOWMANAGER_WORKSPACE_H
2534
2535=== added file 'plugins/WindowManager/WorkspaceManager.cpp'
2536--- plugins/WindowManager/WorkspaceManager.cpp 1970-01-01 00:00:00 +0000
2537+++ plugins/WindowManager/WorkspaceManager.cpp 2017-04-05 11:48:54 +0000
2538@@ -0,0 +1,106 @@
2539+/*
2540+ * Copyright (C) 2017 Canonical, Ltd.
2541+ *
2542+ * This program is free software; you can redistribute it and/or modify
2543+ * it under the terms of the GNU General Public License as published by
2544+ * the Free Software Foundation; version 3.
2545+ *
2546+ * This program is distributed in the hope that it will be useful,
2547+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2548+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2549+ * GNU General Public License for more details.
2550+ *
2551+ * You should have received a copy of the GNU General Public License
2552+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2553+ */
2554+
2555+#include "WorkspaceManager.h"
2556+#include "Workspace.h"
2557+#include "TopLevelWindowModel.h"
2558+#include "WindowManagerObjects.h"
2559+#include <unity/shell/application/SurfaceManagerInterface.h>
2560+
2561+// Qt
2562+#include <QGuiApplication>
2563+#include <QScreen>
2564+#include <QQmlEngine>
2565+
2566+WorkspaceManager *WorkspaceManager::instance()
2567+{
2568+ static WorkspaceManager* workspaceManager(new WorkspaceManager());
2569+ return workspaceManager;
2570+}
2571+
2572+WorkspaceManager::WorkspaceManager()
2573+ : m_activeWorkspace(nullptr)
2574+{
2575+}
2576+
2577+Workspace *WorkspaceManager::createWorkspace()
2578+{
2579+ auto workspace = new ConcreteWorkspace(this);
2580+ QQmlEngine::setObjectOwnership(workspace, QQmlEngine::CppOwnership);
2581+ m_allWorkspaces.insert(workspace);
2582+
2583+ if (m_allWorkspaces.count() == 0 && m_activeWorkspace) {
2584+ setActiveWorkspace(nullptr);
2585+ } else if (m_allWorkspaces.count() == 1) {
2586+ setActiveWorkspace(workspace);
2587+ }
2588+
2589+ return workspace;
2590+}
2591+
2592+void WorkspaceManager::destroyWorkspace(Workspace *workspace)
2593+{
2594+ if (!workspace) return;
2595+
2596+ if (workspace->isAssigned()) {
2597+ workspace->unassign();
2598+ }
2599+ m_allWorkspaces.remove(workspace);
2600+
2601+ if (m_activeWorkspace == workspace) {
2602+ setActiveWorkspace(m_allWorkspaces.count() ? *m_allWorkspaces.begin() : nullptr);
2603+ }
2604+ if (m_activeWorkspace) {
2605+ moveWorkspaceContentToWorkspace(m_activeWorkspace, workspace);
2606+ }
2607+
2608+ disconnect(workspace, 0, this, 0);
2609+}
2610+
2611+void WorkspaceManager::moveSurfaceToWorkspace(unity::shell::application::MirSurfaceInterface *surface, Workspace *workspace)
2612+{
2613+ auto surfaceManager = WindowManagerObjects::instance()->surfaceManager();
2614+ if (surfaceManager) {
2615+ surfaceManager->moveSurfaceToWorkspace(surface, workspace->workspace());
2616+ }
2617+}
2618+
2619+void WorkspaceManager::moveWorkspaceContentToWorkspace(Workspace *to, Workspace *from)
2620+{
2621+ auto surfaceManager = WindowManagerObjects::instance()->surfaceManager();
2622+ if (surfaceManager) {
2623+ surfaceManager->moveWorkspaceContentToWorkspace(to->workspace(), from->workspace());
2624+ }
2625+}
2626+
2627+Workspace *WorkspaceManager::activeWorkspace() const
2628+{
2629+ return m_activeWorkspace;
2630+}
2631+
2632+void WorkspaceManager::setActiveWorkspace(Workspace *workspace)
2633+{
2634+ if (workspace != m_activeWorkspace) {
2635+ m_activeWorkspace = workspace;
2636+ Q_EMIT activeWorkspaceChanged(workspace);
2637+ }
2638+}
2639+
2640+void WorkspaceManager::setActiveWorkspace2(Workspace *workspace)
2641+{
2642+ if (!workspace) return;
2643+ workspace->activate();
2644+}
2645
2646=== added file 'plugins/WindowManager/WorkspaceManager.h'
2647--- plugins/WindowManager/WorkspaceManager.h 1970-01-01 00:00:00 +0000
2648+++ plugins/WindowManager/WorkspaceManager.h 2017-04-05 11:48:54 +0000
2649@@ -0,0 +1,69 @@
2650+/*
2651+ * Copyright (C) 2017 Canonical, Ltd.
2652+ *
2653+ * This program is free software; you can redistribute it and/or modify
2654+ * it under the terms of the GNU General Public License as published by
2655+ * the Free Software Foundation; version 3.
2656+ *
2657+ * This program is distributed in the hope that it will be useful,
2658+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2659+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2660+ * GNU General Public License for more details.
2661+ *
2662+ * You should have received a copy of the GNU General Public License
2663+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2664+ */
2665+
2666+#ifndef WORKSPACEMANAGER_H
2667+#define WORKSPACEMANAGER_H
2668+
2669+#include <QQmlListProperty>
2670+
2671+#include "WindowManagerGlobal.h"
2672+#include "WorkspaceModel.h"
2673+
2674+class Workspace;
2675+
2676+namespace unity {
2677+ namespace shell {
2678+ namespace application {
2679+ class MirSurfaceInterface;
2680+ class SurfaceManagerInterface;
2681+ }
2682+ }
2683+}
2684+
2685+class WINDOWMANAGERQML_EXPORT WorkspaceManager : public QObject
2686+{
2687+ Q_OBJECT
2688+ Q_PROPERTY(Workspace* activeWorkspace READ activeWorkspace WRITE setActiveWorkspace2 NOTIFY activeWorkspaceChanged)
2689+
2690+public:
2691+ WorkspaceManager();
2692+ static WorkspaceManager* instance();
2693+
2694+ Workspace* activeWorkspace() const;
2695+ void setActiveWorkspace(Workspace* workspace);
2696+
2697+ Workspace* createWorkspace();
2698+ void destroyWorkspace(Workspace* workspace);
2699+
2700+ void destroyFloatingWorkspaces();
2701+
2702+ Q_INVOKABLE void moveSurfaceToWorkspace(unity::shell::application::MirSurfaceInterface* surface,
2703+ Workspace* workspace);
2704+
2705+ Q_INVOKABLE void moveWorkspaceContentToWorkspace(Workspace* to, Workspace* from);
2706+
2707+Q_SIGNALS:
2708+ void activeWorkspaceChanged(Workspace*);
2709+
2710+private:
2711+ void setActiveWorkspace2(Workspace* workspace);
2712+
2713+ QSet<Workspace*> m_allWorkspaces;
2714+ Workspace* m_activeWorkspace;
2715+ unity::shell::application::SurfaceManagerInterface* m_surfaceManager;
2716+};
2717+
2718+#endif // WORKSPACEMANAGER_H
2719
2720=== added file 'plugins/WindowManager/WorkspaceModel.cpp'
2721--- plugins/WindowManager/WorkspaceModel.cpp 1970-01-01 00:00:00 +0000
2722+++ plugins/WindowManager/WorkspaceModel.cpp 2017-04-05 11:48:54 +0000
2723@@ -0,0 +1,254 @@
2724+/*
2725+ * Copyright (C) 2017 Canonical, Ltd.
2726+ *
2727+ * This program is free software; you can redistribute it and/or modify
2728+ * it under the terms of the GNU General Public License as published by
2729+ * the Free Software Foundation; version 3.
2730+ *
2731+ * This program is distributed in the hope that it will be useful,
2732+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2733+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2734+ * GNU General Public License for more details.
2735+ *
2736+ * You should have received a copy of the GNU General Public License
2737+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2738+ */
2739+
2740+#include "WorkspaceModel.h"
2741+#include "WorkspaceManager.h"
2742+#include "Workspace.h"
2743+#include "Screen.h"
2744+
2745+#include <QQmlEngine>
2746+
2747+Q_LOGGING_CATEGORY(WORKSPACES, "Workspaces", QtInfoMsg)
2748+
2749+#define DEBUG_MSG qCDebug(WORKSPACES).nospace().noquote() << __func__
2750+#define INFO_MSG qCInfo(WORKSPACES).nospace().noquote() << __func__
2751+
2752+WorkspaceModel::WorkspaceModel(QObject *parent)
2753+ : QAbstractListModel(parent)
2754+{
2755+}
2756+
2757+WorkspaceModel::~WorkspaceModel()
2758+{
2759+ qDeleteAll(m_workspaces.toList()); // make a copy so the list doesnt edit itself during delete.
2760+ m_workspaces.clear();
2761+}
2762+
2763+void WorkspaceModel::append(Workspace *workspace)
2764+{
2765+ insert(m_workspaces.count(), workspace);
2766+}
2767+
2768+void WorkspaceModel::insert(int index, Workspace *workspace)
2769+{
2770+ beginInsertRows(QModelIndex(), index, index);
2771+
2772+ m_workspaces.insert(index, workspace);
2773+
2774+ endInsertRows();
2775+
2776+ Q_EMIT workspaceInserted(index, workspace);
2777+ Q_EMIT countChanged();
2778+}
2779+
2780+void WorkspaceModel::remove(Workspace *workspace)
2781+{
2782+ int index = m_workspaces.indexOf(workspace);
2783+ if (index < 0) return;
2784+
2785+ beginRemoveRows(QModelIndex(), index, index);
2786+
2787+ m_workspaces.removeAt(index);
2788+ insertUnassigned(workspace);
2789+
2790+ endRemoveRows();
2791+
2792+ Q_EMIT workspaceRemoved(workspace);
2793+ Q_EMIT countChanged();
2794+}
2795+
2796+void WorkspaceModel::move(int from, int to)
2797+{
2798+ if (from == to) return;
2799+ DEBUG_MSG << " from=" << from << " to=" << to;
2800+
2801+ if (from >= 0 && from < m_workspaces.size() && to >= 0 && to < m_workspaces.size()) {
2802+ QModelIndex parent;
2803+
2804+ beginMoveRows(parent, from, from, parent, to + (to > from ? 1 : 0));
2805+#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
2806+ const auto &window = m_windowModel.takeAt(from);
2807+ m_workspaces.insert(to, window);
2808+#else
2809+ m_workspaces.move(from, to);
2810+#endif
2811+ endMoveRows();
2812+
2813+ Q_EMIT workspaceMoved(from, to);
2814+ }
2815+}
2816+
2817+int WorkspaceModel::indexOf(Workspace *workspace) const
2818+{
2819+ return m_workspaces.indexOf(workspace);
2820+}
2821+
2822+Workspace *WorkspaceModel::get(int index) const
2823+{
2824+ if (index < 0 || index >= rowCount()) return nullptr;
2825+ return m_workspaces.at(index);
2826+}
2827+
2828+int WorkspaceModel::rowCount(const QModelIndex &) const
2829+{
2830+ return m_workspaces.count();
2831+}
2832+
2833+QVariant WorkspaceModel::data(const QModelIndex &index, int role) const
2834+{
2835+ if (index.row() < 0 || index.row() >= m_workspaces.size())
2836+ return QVariant();
2837+
2838+ if (role == WorkspaceRole) {
2839+ Workspace *workspace = m_workspaces.at(index.row());
2840+ return QVariant::fromValue(workspace);
2841+ } else {
2842+ return QVariant();
2843+ }
2844+}
2845+
2846+void WorkspaceModel::sync(WorkspaceModel *proxy)
2847+{
2848+ if (!proxy) return;
2849+ const auto& proxyList = proxy->list();
2850+
2851+ // check for removals
2852+ int removedIndexWhichWasActive = -1;
2853+ QVector<Workspace*> dpCpy(this->list());
2854+ Q_FOREACH(auto workspace, dpCpy) {
2855+
2856+ bool found = false;
2857+ Q_FOREACH(auto p, proxyList) {
2858+ auto workspaceProxy = qobject_cast<ProxyWorkspace*>(p);
2859+ if (workspaceProxy->proxyObject() == workspace) {
2860+ found = true;
2861+ break;
2862+ }
2863+ }
2864+ if (!found) {
2865+ if (workspace->isActive()) {
2866+ removedIndexWhichWasActive = indexOf(workspace);
2867+ }
2868+ workspace->unassign();
2869+ }
2870+ }
2871+
2872+ // existing
2873+ QSet<Workspace*> newWorkspaces;
2874+ for (int i = 0; i < proxyList.count(); i++) {
2875+ auto workspaceProxy = qobject_cast<ProxyWorkspace*>(proxyList[i]);
2876+ auto workspace = workspaceProxy->proxyObject();
2877+
2878+ int oldIndex = this->indexOf(workspace);
2879+
2880+ if (oldIndex < 0) {
2881+ workspace->assign(this, QVariant(i));
2882+ } else if (oldIndex != i) {
2883+ this->move(oldIndex, i);
2884+ }
2885+ newWorkspaces.insert(workspace);
2886+ }
2887+
2888+ // Make sure we have at least one workspace in the model.
2889+ if (rowCount() == 0) {
2890+ Workspace* workspace = WorkspaceManager::instance()->createWorkspace();
2891+ workspace->assign(this);
2892+ (new ProxyWorkspace(workspace))->assign(proxy);
2893+ }
2894+
2895+ if (removedIndexWhichWasActive != -1) {
2896+ int newActiveIndex = qMin(removedIndexWhichWasActive, this->rowCount()-1);
2897+ Workspace* newActiveWorkspace = newActiveIndex >= 0 ? this->get(newActiveIndex) : nullptr;
2898+
2899+ WorkspaceManager::instance()->setActiveWorkspace(newActiveWorkspace);
2900+ }
2901+
2902+ proxy->finishSync();
2903+ finishSync();
2904+}
2905+
2906+void WorkspaceModel::finishSync()
2907+{
2908+ QSet<Workspace*> dpCpy(m_unassignedWorkspaces);
2909+ Q_FOREACH(auto workspace, dpCpy) {
2910+ delete workspace;
2911+ }
2912+ m_unassignedWorkspaces.clear();
2913+}
2914+
2915+void WorkspaceModel::insertUnassigned(Workspace *workspace)
2916+{
2917+ m_unassignedWorkspaces.insert(workspace);
2918+ connect(workspace, &Workspace::assigned, this, [=]() {
2919+ m_unassignedWorkspaces.remove(workspace);
2920+ disconnect(workspace, &Workspace::assigned, this, 0);
2921+ });
2922+ connect(workspace, &QObject::destroyed, this, [=]() {
2923+ m_unassignedWorkspaces.remove(workspace);
2924+ });
2925+}
2926+
2927+
2928+ProxyWorkspaceModel::ProxyWorkspaceModel(WorkspaceModel * const model, ProxyScreen* screen)
2929+ : m_original(model)
2930+ , m_screen(screen)
2931+{
2932+ Q_FOREACH(auto workspace, model->list()) {
2933+ auto proxy = new ProxyWorkspace(workspace);
2934+ QQmlEngine::setObjectOwnership(proxy, QQmlEngine::CppOwnership);
2935+ proxy->assign(this);
2936+ }
2937+ connect(m_original, &WorkspaceModel::workspaceInserted, this, [this](int index, Workspace* inserted) {
2938+ if (isSyncing()) return;
2939+
2940+ (new ProxyWorkspace(inserted))->assign(this, index);
2941+ });
2942+ connect(m_original, &WorkspaceModel::workspaceRemoved, this, [this](Workspace* removed) {
2943+ if (isSyncing()) return;
2944+
2945+ for (int i = 0; i < rowCount(); i++) {
2946+ auto workspaceProxy = qobject_cast<ProxyWorkspace*>(get(i));
2947+ auto w = workspaceProxy->proxyObject();
2948+ if (w == removed) {
2949+ remove(workspaceProxy);
2950+ break;
2951+ }
2952+ }
2953+ });
2954+ connect(m_original, &WorkspaceModel::workspaceMoved, this, [this](int from, int to) {
2955+ if (isSyncing()) return;
2956+
2957+ move(from, to);
2958+ });
2959+}
2960+
2961+void ProxyWorkspaceModel::move(int from, int to)
2962+{
2963+ WorkspaceModel::move(from, to);
2964+}
2965+
2966+bool ProxyWorkspaceModel::isSyncing() const
2967+{
2968+ return m_screen->isSyncing();
2969+}
2970+
2971+void ProxyWorkspaceModel::addWorkspace()
2972+{
2973+ auto newWorkspace = WorkspaceManager::instance()->createWorkspace();
2974+ m_original->insertUnassigned(newWorkspace);
2975+
2976+ (new ProxyWorkspace(newWorkspace))->assign(this);
2977+}
2978
2979=== added file 'plugins/WindowManager/WorkspaceModel.h'
2980--- plugins/WindowManager/WorkspaceModel.h 1970-01-01 00:00:00 +0000
2981+++ plugins/WindowManager/WorkspaceModel.h 2017-04-05 11:48:54 +0000
2982@@ -0,0 +1,102 @@
2983+/*
2984+ * Copyright (C) 2017 Canonical, Ltd.
2985+ *
2986+ * This program is free software; you can redistribute it and/or modify
2987+ * it under the terms of the GNU General Public License as published by
2988+ * the Free Software Foundation; version 3.
2989+ *
2990+ * This program is distributed in the hope that it will be useful,
2991+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2992+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2993+ * GNU General Public License for more details.
2994+ *
2995+ * You should have received a copy of the GNU General Public License
2996+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2997+ */
2998+
2999+#ifndef WORKSPACEMODEL_H
3000+#define WORKSPACEMODEL_H
3001+
3002+#include <QAbstractListModel>
3003+#include <QLoggingCategory>
3004+#include <QPointer>
3005+
3006+Q_DECLARE_LOGGING_CATEGORY(WORKSPACES)
3007+
3008+class Workspace;
3009+class ProxyWorkspaceModel;
3010+class ProxyScreen;
3011+
3012+class WorkspaceModel : public QAbstractListModel
3013+{
3014+ Q_OBJECT
3015+ Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
3016+public:
3017+ /**
3018+ * @brief The Roles supported by the model
3019+ *
3020+ * WorkspaceRole - A workspace.
3021+ */
3022+ enum Roles {
3023+ WorkspaceRole = Qt::UserRole
3024+ };
3025+
3026+ explicit WorkspaceModel(QObject *parent = 0);
3027+ ~WorkspaceModel();
3028+
3029+ void append(Workspace *workspace);
3030+ void insert(int index, Workspace *workspace);
3031+ void remove(Workspace* workspace);
3032+ virtual void move(int from, int to);
3033+
3034+ int indexOf(Workspace *workspace) const;
3035+ Workspace* get(int index) const;
3036+
3037+ // From QAbstractItemModel
3038+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
3039+ QVariant data(const QModelIndex& index, int role) const override;
3040+ QHash<int, QByteArray> roleNames() const override {
3041+ QHash<int, QByteArray> roleNames { {WorkspaceRole, "workspace"} };
3042+ return roleNames;
3043+ }
3044+
3045+ const QVector<Workspace*>& list() const { return m_workspaces; }
3046+
3047+ void sync(WorkspaceModel* proxy);
3048+ void finishSync();
3049+
3050+Q_SIGNALS:
3051+ void countChanged();
3052+
3053+ void workspaceInserted(int index, Workspace *workspace);
3054+ void workspaceRemoved(Workspace *workspace);
3055+ void workspaceMoved(int from, int to);
3056+
3057+protected:
3058+ void insertUnassigned(Workspace* workspace);
3059+
3060+ QVector<Workspace*> m_workspaces;
3061+ QSet<Workspace*> m_unassignedWorkspaces;
3062+
3063+ friend class ProxyWorkspaceModel;
3064+};
3065+
3066+class ProxyWorkspaceModel : public WorkspaceModel
3067+{
3068+ Q_OBJECT
3069+public:
3070+ explicit ProxyWorkspaceModel(WorkspaceModel*const model, ProxyScreen* screen);
3071+
3072+ Q_INVOKABLE void move(int from, int to) override;
3073+
3074+ bool isSyncing() const;
3075+
3076+public Q_SLOTS:
3077+ void addWorkspace();
3078+
3079+protected:
3080+ const QPointer<WorkspaceModel> m_original;
3081+ const ProxyScreen* m_screen;
3082+};
3083+
3084+#endif // WORKSPACEMODEL_H
3085
3086=== modified file 'qml/ApplicationMenus/MenuItem.qml'
3087--- qml/ApplicationMenus/MenuItem.qml 2017-04-05 11:48:53 +0000
3088+++ qml/ApplicationMenus/MenuItem.qml 2017-02-23 13:19:41 +0000
3089@@ -17,7 +17,6 @@
3090 import QtQuick 2.4
3091 import QtQuick.Layouts 1.1
3092 import Ubuntu.Components 1.3
3093-import "../Components/PanelState"
3094
3095 ActionItem {
3096 id: root
3097@@ -25,7 +24,6 @@
3098 implicitWidth: requiredWidth
3099
3100 property var menuData: undefined
3101- property PanelState panelState
3102
3103 readonly property real requiredWidth: {
3104 var val = 0;
3105
3106=== modified file 'qml/ApplicationMenus/MenuPopup.qml'
3107--- qml/ApplicationMenus/MenuPopup.qml 2017-04-05 11:48:53 +0000
3108+++ qml/ApplicationMenus/MenuPopup.qml 2017-04-05 11:48:54 +0000
3109@@ -294,7 +294,6 @@
3110 // Parent will be loader
3111 id: menuItem
3112 menuData: parent.__menuData
3113- panelState: root.panelState
3114 objectName: parent.objectName + "-actionItem"
3115
3116 width: MathUtils.clamp(implicitWidth, d.__minimumWidth, d.__maximumWidth)
3117
3118=== modified file 'qml/Components/VirtualTouchPad.qml'
3119--- qml/Components/VirtualTouchPad.qml 2017-04-05 11:48:53 +0000
3120+++ qml/Components/VirtualTouchPad.qml 2017-04-05 11:48:54 +0000
3121@@ -18,7 +18,7 @@
3122 import QtQuick.Layouts 1.1
3123 import Ubuntu.Components 1.3
3124 import Qt.labs.settings 1.0
3125-import Unity.Screens 0.1
3126+import WindowManager 1.0
3127 import UInput 0.1
3128 import "../Components"
3129
3130
3131=== modified file 'qml/ErrorApplication.qml'
3132--- qml/ErrorApplication.qml 2017-04-05 11:48:53 +0000
3133+++ qml/ErrorApplication.qml 2017-04-05 11:48:54 +0000
3134@@ -17,7 +17,7 @@
3135 import QtQuick 2.4
3136 import QtQuick.Window 2.2
3137 import Ubuntu.Components 1.3
3138-import Unity.Screens 0.1
3139+import WindowManager 1.0
3140
3141 Instantiator {
3142 id: root
3143
3144=== modified file 'qml/OrientedShell.qml'
3145--- qml/OrientedShell.qml 2017-04-05 11:48:53 +0000
3146+++ qml/OrientedShell.qml 2017-04-05 11:48:54 +0000
3147@@ -18,7 +18,7 @@
3148 import QtQuick.Window 2.2
3149 import Unity.InputInfo 0.1
3150 import Unity.Session 0.1
3151-import Unity.Screens 0.1
3152+import WindowManager 1.0
3153 import Utils 0.1
3154 import GSettings 1.0
3155 import "Components"
3156@@ -34,7 +34,6 @@
3157
3158 property alias deviceConfiguration: _deviceConfiguration
3159 property alias orientations: d.orientations
3160- property alias surfaceManager: shell.surfaceManager
3161
3162 onWidthChanged: calculateUsageMode();
3163
3164
3165=== modified file 'qml/Shell.qml'
3166--- qml/Shell.qml 2017-04-05 11:48:53 +0000
3167+++ qml/Shell.qml 2017-04-05 11:48:54 +0000
3168@@ -53,7 +53,6 @@
3169 theme.name: "Ubuntu.Components.Themes.SuruDark"
3170
3171 // to be set from outside
3172- property alias surfaceManager: topLevelSurfaceList.surfaceManager
3173 property int orientationAngle: 0
3174 property int orientation
3175 property Orientations orientations
3176@@ -98,6 +97,11 @@
3177
3178 readonly property var mainApp: stage.mainApp
3179
3180+ readonly property var topLevelSurfaceList: {
3181+ if (!WMScreen.currentWorkspace) return null;
3182+ return WMScreen.currentWorkspace.windowModel
3183+ }
3184+
3185 onMainAppChanged: {
3186 if (mainApp) {
3187 _onMainAppChanged(mainApp.appId);
3188@@ -280,12 +284,6 @@
3189 width: parent.width
3190 height: parent.height
3191
3192- TopLevelWindowModel {
3193- id: topLevelSurfaceList
3194- objectName: "topLevelSurfaceList"
3195- applicationManager: ApplicationManager // it's a singleton
3196- }
3197-
3198 Stage {
3199 id: stage
3200 objectName: "stage"
3201@@ -296,7 +294,7 @@
3202 background: wallpaperResolver.background
3203
3204 applicationManager: ApplicationManager
3205- topLevelSurfaceList: topLevelSurfaceList
3206+ topLevelSurfaceList: shell.topLevelSurfaceList
3207 inputMethodRect: inputMethod.visibleRect
3208 rightEdgePushProgress: rightEdgeBarrier.progress
3209 availableDesktopArea: availableDesktopAreaItem
3210@@ -366,7 +364,7 @@
3211 InputMethod {
3212 id: inputMethod
3213 objectName: "inputMethod"
3214- surface: topLevelSurfaceList.inputMethodSurface
3215+ surface: shell.topLevelSurfaceList.inputMethodSurface
3216 anchors {
3217 fill: parent
3218 topMargin: panel.panelHeight
3219@@ -553,8 +551,8 @@
3220 && !stage.spreadShown
3221 }
3222
3223- readonly property bool focusedSurfaceIsFullscreen: topLevelSurfaceList.focusedWindow
3224- ? topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
3225+ readonly property bool focusedSurfaceIsFullscreen: shell.topLevelSurfaceList.focusedWindow
3226+ ? shell.topLevelSurfaceList.focusedWindow.state === Mir.FullscreenState
3227 : false
3228 fullscreenMode: (focusedSurfaceIsFullscreen && !LightDMService.greeter.active && launcher.progress == 0 && !stage.spreadShown)
3229 || greeter.hasLockedApp
3230@@ -890,7 +888,7 @@
3231
3232 // non-visual objects
3233 KeymapSwitcher {
3234- focusedSurface: topLevelSurfaceList.focusedWindow ? topLevelSurfaceList.focusedWindow.surface : null
3235+ focusedSurface: shell.topLevelSurfaceList.focusedWindow ? shell.topLevelSurfaceList.focusedWindow.surface : null
3236 }
3237 BrightnessControl {}
3238
3239
3240=== modified file 'qml/ShellApplication.qml'
3241--- qml/ShellApplication.qml 2017-04-05 11:48:53 +0000
3242+++ qml/ShellApplication.qml 2017-04-05 11:48:54 +0000
3243@@ -16,22 +16,19 @@
3244
3245 import QtQuick 2.4
3246 import QtQuick.Window 2.2
3247-import Unity.Screens 0.1
3248+import WindowManager 1.0
3249 import Unity.Application 0.1
3250
3251 Instantiator {
3252 id: root
3253 model: Screens
3254
3255- property QtObject surfaceMan: SurfaceManager {}
3256-
3257 ShellScreen {
3258 id: window
3259 objectName: "screen"+index
3260 screen: model.screen
3261 visibility: applicationArguments.hasFullscreen ? Window.FullScreen : Window.Windowed
3262 flags: applicationArguments.hasFrameless ? Qt.FramelessWindowHint : 0
3263- surfaceManager: surfaceMan
3264
3265 Binding {
3266 when: applicationArguments.hasGeometry
3267@@ -49,4 +46,15 @@
3268 Component.onCompleted: screen.active = primary
3269 primary: index == 0
3270 }
3271+
3272+ property var windowManagerSurfaceManagerBinding: Binding {
3273+ target: WindowManagerObjects
3274+ property: "surfaceManager"
3275+ value: SurfaceManager
3276+ }
3277+ property var windowManagerApplicationManagerBinding: Binding {
3278+ target: WindowManagerObjects
3279+ property: "applicationManager"
3280+ value: ApplicationManager
3281+ }
3282 }
3283
3284=== modified file 'qml/ShellScreen.qml'
3285--- qml/ShellScreen.qml 2017-04-05 11:48:53 +0000
3286+++ qml/ShellScreen.qml 2017-04-05 11:48:54 +0000
3287@@ -16,7 +16,7 @@
3288
3289 import QtQuick 2.4
3290 import Ubuntu.Components 1.3
3291-import Unity.Screens 0.1
3292+import WindowManager 1.0
3293 import Cursor 1.1
3294 import "Components"
3295
3296@@ -26,7 +26,6 @@
3297 color: "black"
3298 title: "Unity8 Shell"
3299 property bool primary: false
3300- property QtObject surfaceManager: null
3301
3302 DeviceConfiguration {
3303 id: deviceConfiguration
3304@@ -51,7 +50,6 @@
3305 OrientedShell {
3306 implicitWidth: screenWindow.width
3307 implicitHeight: screenWindow.height
3308- surfaceManager: screenWindow.surfaceManager
3309
3310 deviceConfiguration {
3311 name: Screens.count > 1 ? "desktop" : applicationArguments.deviceName
3312
3313=== modified file 'src/CMakeLists.txt'
3314--- src/CMakeLists.txt 2017-04-05 11:48:53 +0000
3315+++ src/CMakeLists.txt 2017-04-05 11:48:54 +0000
3316@@ -9,6 +9,7 @@
3317 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
3318 ${GSETTINGS_QT_INCLUDE_DIRS}
3319 ${CONNECTIVITY_INCLUDE_DIRS}
3320+ ${QTMIRSERVER_INCLUDE_DIRS}
3321 )
3322
3323 include_directories(
3324@@ -29,10 +30,12 @@
3325 ApplicationArguments.cpp
3326 main.cpp
3327 CachingNetworkManagerFactory.cpp
3328- ShellApplication.cpp
3329+ DisplayConfigurationStorage.cpp
3330+ UnityApplication.cpp
3331 UnityCommandLineParser.cpp
3332 UnixSignalHandler.cpp
3333 DebuggingController.cpp
3334+ WindowManagementPolicy.cpp
3335 ${QML_FILES} # This is to make qml and image files appear in the IDE's project tree
3336 )
3337
3338@@ -47,7 +50,14 @@
3339 if (NOT "${ANDROID_PROPERTIES_INCLUDE_DIRS}" STREQUAL "")
3340 set_target_properties(${SHELL_APP} PROPERTIES INCLUDE_DIRECTORIES ${ANDROID_PROPERTIES_INCLUDE_DIRS})
3341 endif()
3342-target_link_libraries(${SHELL_APP} ${ANDROID_PROPERTIES_LDFLAGS} ${GSETTINGS_QT_LDFLAGS} UbuntuGestures connectivity-qt1 unity8-private)
3343+target_link_libraries(${SHELL_APP}
3344+ ${ANDROID_PROPERTIES_LDFLAGS}
3345+ ${GSETTINGS_QT_LDFLAGS}
3346+ ${QTMIRSERVER_LDFLAGS}
3347+ UbuntuGestures
3348+ connectivity-qt1
3349+ unity8-private
3350+)
3351
3352 if (ENABLE_TOUCH_EMULATION)
3353 target_link_libraries(${SHELL_APP} ${MOUSETOUCHADAPTOR_LIBS_LDFLAGS})
3354
3355=== added file 'src/DisplayConfigurationStorage.cpp'
3356--- src/DisplayConfigurationStorage.cpp 1970-01-01 00:00:00 +0000
3357+++ src/DisplayConfigurationStorage.cpp 2017-04-05 11:48:54 +0000
3358@@ -0,0 +1,90 @@
3359+#include "DisplayConfigurationStorage.h"
3360+
3361+#include <QFile>
3362+#include <QStandardPaths>
3363+#include <QJsonObject>
3364+#include <QJsonDocument>
3365+
3366+namespace {
3367+
3368+inline QString stringFromEdid(const miral::Edid& edid)
3369+{
3370+ QString str;
3371+ str += QString::fromStdString(edid.vendor);
3372+ str += QString("%1%2").arg(edid.product_code).arg(edid.serial_number);
3373+
3374+ for (int i = 0; i < 4; i++) {
3375+ str += QString::fromStdString(edid.descriptors[i].string_value());
3376+ }
3377+ return str;
3378+}
3379+
3380+}
3381+
3382+DisplayConfigurationStorage::DisplayConfigurationStorage()
3383+{
3384+}
3385+
3386+void DisplayConfigurationStorage::save(const miral::DisplayId &displayId, const miral::DisplayConfigurationOptions &options)
3387+{
3388+ const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
3389+ QFile f(dbPath + stringFromEdid(displayId.edid) + ".edid");
3390+
3391+ QJsonObject json;
3392+ if (options.used.is_set()) json.insert("used", options.used.value());
3393+ if (options.clone_output_index.is_set()) json.insert("clone_output_index", static_cast<int>(options.clone_output_index.value()));
3394+ if (options.mode.is_set()) {
3395+ auto const& mode = options.mode.value();
3396+
3397+ QString sz(QString("%1x%2").arg(mode.size.width.as_int()).arg(mode.size.height.as_int()));
3398+ QJsonObject jsonMode({
3399+ {"size", sz},
3400+ {"refresh_rate", mode.refresh_rate }
3401+ });
3402+ json.insert("mode", jsonMode);
3403+ }
3404+ if (options.orientation.is_set()) json.insert("orientation", static_cast<int>(options.orientation.value()));
3405+ if (options.form_factor.is_set()) json.insert("form_factor", static_cast<int>(options.form_factor.value()));
3406+ if (options.scale.is_set()) json.insert("scale", options.scale.value());
3407+
3408+ if (f.open(QIODevice::WriteOnly)) {
3409+ QJsonDocument saveDoc(json);
3410+ f.write(saveDoc.toJson());
3411+ }
3412+}
3413+
3414+bool DisplayConfigurationStorage::load(const miral::DisplayId &displayId, miral::DisplayConfigurationOptions &options) const
3415+{
3416+ const QString dbPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/unity8/");
3417+ QFile f(dbPath + stringFromEdid(displayId.edid) + ".edid");
3418+
3419+ if (f.open(QIODevice::ReadOnly)) {
3420+ QByteArray saveData = f.readAll();
3421+ QJsonDocument loadDoc(QJsonDocument::fromJson(saveData));
3422+
3423+ QJsonObject json(loadDoc.object());
3424+ if (json.contains("used")) options.used = json["used"].toBool();
3425+ if (json.contains("clone_output_index")) options.clone_output_index = json["clone_output_index"].toInt();
3426+ if (json.contains("mode")) {
3427+ QJsonObject jsonMode = json["mode"].toObject();
3428+
3429+ if (jsonMode.contains("size") && jsonMode.contains("refresh_rate")) {
3430+ QString sz(jsonMode["size"].toString());
3431+ QStringList geo = sz.split("x", QString::SkipEmptyParts);
3432+ if (geo.count() == 2) {
3433+ miral::DisplayConfigurationOptions::DisplayMode mode;
3434+ mode.size = mir::geometry::Size(geo[0].toInt(), geo[1].toInt());
3435+ mode.refresh_rate = jsonMode["refresh_rate"].toDouble();
3436+ options.mode = mode;
3437+ }
3438+ }
3439+ }
3440+ if (json.contains("orientation")) options.orientation = static_cast<MirOrientation>(json["orientation"].toInt());
3441+ if (json.contains("form_factor")) options.form_factor = static_cast<MirFormFactor>(json["form_factor"].toInt());
3442+ if (json.contains("scale")) options.scale = json["form_factor"].toDouble();
3443+
3444+ return true;
3445+ }
3446+
3447+ return false;
3448+}
3449
3450=== added file 'src/DisplayConfigurationStorage.h'
3451--- src/DisplayConfigurationStorage.h 1970-01-01 00:00:00 +0000
3452+++ src/DisplayConfigurationStorage.h 2017-04-05 11:48:54 +0000
3453@@ -0,0 +1,15 @@
3454+#ifndef UNITY_DISPLAYCONFIGURATIONSTORAGE_H
3455+#define UNITY_DISPLAYCONFIGURATIONSTORAGE_H
3456+
3457+#include <qtmir/miral/display_configuration_storage.h>
3458+
3459+class DisplayConfigurationStorage : public miral::DisplayConfigurationStorage
3460+{
3461+public:
3462+ DisplayConfigurationStorage();
3463+
3464+ void save(const miral::DisplayId& displayId, const miral::DisplayConfigurationOptions& options) override;
3465+ bool load(const miral::DisplayId& displayId, miral::DisplayConfigurationOptions& options) const override;
3466+};
3467+
3468+#endif // UNITY_DISPLAYCONFIGURATIONSTORAGE_H
3469
3470=== renamed file 'src/ShellApplication.cpp' => 'src/UnityApplication.cpp'
3471--- src/ShellApplication.cpp 2017-04-05 11:48:53 +0000
3472+++ src/UnityApplication.cpp 2017-04-05 11:48:54 +0000
3473@@ -14,7 +14,7 @@
3474 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3475 */
3476
3477-#include "ShellApplication.h"
3478+#include "UnityApplication.h"
3479
3480 // Qt
3481 #include <QLibrary>
3482@@ -30,20 +30,30 @@
3483 // libandroid-properties
3484 #include <hybris/properties/properties.h>
3485
3486+// qtmir
3487+#include <qtmir/displayconfigurationstorage.h>
3488+
3489 // local
3490 #include <paths.h>
3491 #include "CachingNetworkManagerFactory.h"
3492 #include "UnityCommandLineParser.h"
3493 #include "DebuggingController.h"
3494-
3495-ShellApplication::ShellApplication(int & argc, char ** argv, bool isMirServer)
3496- : QGuiApplication(argc, argv)
3497+#include "WindowManagementPolicy.h"
3498+#include "DisplayConfigurationStorage.h"
3499+
3500+#include <QDebug>
3501+
3502+
3503+
3504+UnityApplication::UnityApplication(int & argc, char ** argv)
3505+ : qtmir::MirServerApplication(argc, argv, { qtmir::SetWindowManagementPolicy<WindowManagementPolicy>(),
3506+ qtmir::SetDisplayConfigurationStorage<DisplayConfigurationStorage>() })
3507 , m_qmlArgs(this)
3508 {
3509 setApplicationName(QStringLiteral("unity8"));
3510 setOrganizationName(QStringLiteral("Canonical"));
3511
3512- setupQmlEngine(isMirServer);
3513+ setupQmlEngine();
3514
3515 if (m_qmlArgs.deviceName().isEmpty()) {
3516 char buffer[200];
3517@@ -85,8 +95,7 @@
3518 m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("applicationArguments"), &m_qmlArgs);
3519 m_qmlEngine->rootContext()->setContextProperty("DebuggingController", new DebuggingController(this));
3520
3521- auto component(new QQmlComponent(m_qmlEngine,
3522- QUrl::fromLocalFile(::qmlDirectory() + "/ShellApplication.qml")));
3523+ auto component(new QQmlComponent(m_qmlEngine, m_qmlArgs.qmlfie()));
3524 component->create();
3525 if (component->status() == QQmlComponent::Error) {
3526 m_qmlEngine->rootContext()->setContextProperty(QStringLiteral("errorString"), component->errorString());
3527@@ -112,12 +121,12 @@
3528 }
3529 }
3530
3531-ShellApplication::~ShellApplication()
3532+UnityApplication::~UnityApplication()
3533 {
3534 destroyResources();
3535 }
3536
3537-void ShellApplication::destroyResources()
3538+void UnityApplication::destroyResources()
3539 {
3540 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
3541 delete m_mouseTouchAdaptor;
3542@@ -128,16 +137,13 @@
3543 m_qmlEngine = nullptr;
3544 }
3545
3546-void ShellApplication::setupQmlEngine(bool isMirServer)
3547+void UnityApplication::setupQmlEngine()
3548 {
3549 m_qmlEngine = new QQmlEngine(this);
3550
3551 m_qmlEngine->setBaseUrl(QUrl::fromLocalFile(::qmlDirectory()));
3552
3553 prependImportPaths(m_qmlEngine, ::overrideImportPaths());
3554- if (!isMirServer) {
3555- prependImportPaths(m_qmlEngine, ::nonMirImportPaths());
3556- }
3557 appendImportPaths(m_qmlEngine, ::fallbackImportPaths());
3558
3559 m_qmlEngine->setNetworkAccessManagerFactory(new CachingNetworkManagerFactory);
3560
3561=== renamed file 'src/ShellApplication.h' => 'src/UnityApplication.h'
3562--- src/ShellApplication.h 2017-04-05 11:48:53 +0000
3563+++ src/UnityApplication.h 2017-04-05 11:48:54 +0000
3564@@ -14,8 +14,8 @@
3565 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3566 */
3567
3568-#ifndef SHELLAPPLICATION_H
3569-#define SHELLAPPLICATION_H
3570+#ifndef UNITYAPPLICATION_H
3571+#define UNITYAPPLICATION_H
3572
3573 #include <QGuiApplication>
3574 #include <QQmlApplicationEngine>
3575@@ -28,17 +28,19 @@
3576 #include "MouseTouchAdaptor.h"
3577 #endif
3578
3579-class ShellApplication : public QGuiApplication
3580+#include <qtmir/mirserverapplication.h>
3581+
3582+class UnityApplication : public qtmir::MirServerApplication
3583 {
3584 Q_OBJECT
3585 public:
3586- ShellApplication(int & argc, char ** argv, bool isMirServer);
3587- virtual ~ShellApplication();
3588+ UnityApplication(int & argc, char ** argv);
3589+ virtual ~UnityApplication();
3590
3591 void destroyResources();
3592
3593 private:
3594- void setupQmlEngine(bool isMirServer);
3595+ void setupQmlEngine();
3596 ApplicationArguments m_qmlArgs;
3597
3598 #ifdef UNITY8_ENABLE_TOUCH_EMULATION
3599@@ -48,4 +50,4 @@
3600 QQmlEngine *m_qmlEngine{nullptr};
3601 };
3602
3603-#endif // SHELLAPPLICATION_H
3604+#endif // UNITYAPPLICATION_H
3605
3606=== modified file 'src/UnityCommandLineParser.cpp'
3607--- src/UnityCommandLineParser.cpp 2015-12-16 13:58:39 +0000
3608+++ src/UnityCommandLineParser.cpp 2017-04-05 11:48:54 +0000
3609@@ -15,6 +15,7 @@
3610 */
3611
3612 #include "UnityCommandLineParser.h"
3613+#include <paths.h>
3614
3615 #include <QDebug>
3616
3617@@ -60,6 +61,11 @@
3618 QStringLiteral("mode"), QStringLiteral("full-greeter"));
3619 parser.addOption(modeOption);
3620
3621+ QCommandLineOption qmlfileOption(QStringLiteral("qmlfile"),
3622+ QStringLiteral("The base qml file to load"),
3623+ QStringLiteral("qmlfile"), ::qmlDirectory() + "/ShellApplication.qml");
3624+ parser.addOption(qmlfileOption);
3625+
3626 // Treat args with single dashes the same as arguments with two dashes
3627 // Ex: -fullscreen == --fullscreen
3628 parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions);
3629@@ -85,6 +91,8 @@
3630 m_hasFullscreen = parser.isSet(fullscreenOption);
3631 m_deviceName = parser.value(devicenameOption);
3632 resolveMode(parser, modeOption);
3633+
3634+ m_qmlfile = parser.value(qmlfileOption);
3635 }
3636
3637 int UnityCommandLineParser::parsePixelsValue(const QString &str)
3638
3639=== modified file 'src/UnityCommandLineParser.h'
3640--- src/UnityCommandLineParser.h 2017-04-05 11:48:53 +0000
3641+++ src/UnityCommandLineParser.h 2017-04-05 11:48:54 +0000
3642@@ -37,6 +37,8 @@
3643 QString deviceName() const { return m_deviceName; }
3644 QString mode() const { return m_mode; }
3645
3646+ QString qmlfie() const { return m_qmlfile; }
3647+
3648 protected:
3649 int parsePixelsValue(const QString &str);
3650 static float getenvFloat(const char* name, float defaultValue);
3651@@ -55,6 +57,7 @@
3652 bool m_hasFullscreen;
3653 QString m_deviceName;
3654 QString m_mode;
3655+ QString m_qmlfile;
3656 };
3657
3658 #endif // UNITY_COMMAND_LINE_PARSER_H
3659
3660=== added file 'src/WindowManagementPolicy.cpp'
3661--- src/WindowManagementPolicy.cpp 1970-01-01 00:00:00 +0000
3662+++ src/WindowManagementPolicy.cpp 2017-04-05 11:48:54 +0000
3663@@ -0,0 +1,69 @@
3664+/*
3665+ * Copyright (C) 2017 Canonical, Ltd.
3666+ *
3667+ * This program is free software; you can redistribute it and/or modify
3668+ * it under the terms of the GNU General Public License as published by
3669+ * the Free Software Foundation; version 3.
3670+ *
3671+ * This program is distributed in the hope that it will be useful,
3672+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3673+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3674+ * GNU General Public License for more details.
3675+ *
3676+ * You should have received a copy of the GNU General Public License
3677+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3678+ */
3679+
3680+#include "WindowManagementPolicy.h"
3681+
3682+WindowManagementPolicy::WindowManagementPolicy(const miral::WindowManagerTools &tools, std::shared_ptr<qtmir::WindowManagementPolicyPrivate> dd)
3683+ : qtmir::WindowManagementPolicy(tools, dd)
3684+ , m_dummyWorkspace(this->tools.create_workspace())
3685+{
3686+ wmPolicyInterface = this;
3687+
3688+ // we must always have a active workspace.
3689+ m_activeWorkspace = m_dummyWorkspace;
3690+}
3691+
3692+void WindowManagementPolicy::advise_new_window(miral::WindowInfo const& window_info)
3693+{
3694+ qtmir::WindowManagementPolicy::advise_new_window(window_info);
3695+
3696+ auto const parent = window_info.parent();
3697+
3698+ auto activeWorkspace = m_activeWorkspace.lock();
3699+ if (!parent && activeWorkspace) {
3700+ tools.add_tree_to_workspace(window_info.window(), activeWorkspace);
3701+ }
3702+}
3703+
3704+std::shared_ptr<miral::Workspace> WindowManagementPolicy::createWorkspace()
3705+{
3706+ auto workspace = tools.create_workspace();
3707+ m_workspaces.insert(workspace);
3708+
3709+ if (m_activeWorkspace.lock() == m_dummyWorkspace) {
3710+ tools.move_workspace_content_to_workspace(workspace, m_dummyWorkspace);
3711+ m_activeWorkspace = workspace;
3712+ }
3713+ return workspace;
3714+}
3715+
3716+void WindowManagementPolicy::releaseWorkspace(const std::shared_ptr<miral::Workspace> &workspace)
3717+{
3718+ auto iter = m_workspaces.find(workspace);
3719+ if (iter != m_workspaces.end()) m_workspaces.erase(iter);
3720+
3721+ if (m_workspaces.size() == 0) {
3722+ m_activeWorkspace = m_dummyWorkspace;
3723+ tools.move_workspace_content_to_workspace(m_dummyWorkspace, workspace);
3724+ }
3725+}
3726+
3727+void WindowManagementPolicy::setActiveWorkspace(const std::shared_ptr<miral::Workspace> &workspace)
3728+{
3729+ if (m_activeWorkspace.lock() == workspace)
3730+ return;
3731+ m_activeWorkspace = workspace ? workspace : m_dummyWorkspace;
3732+}
3733
3734=== added file 'src/WindowManagementPolicy.h'
3735--- src/WindowManagementPolicy.h 1970-01-01 00:00:00 +0000
3736+++ src/WindowManagementPolicy.h 2017-04-05 11:48:54 +0000
3737@@ -0,0 +1,47 @@
3738+/*
3739+ * Copyright (C) 2017 Canonical, Ltd.
3740+ *
3741+ * This program is free software; you can redistribute it and/or modify
3742+ * it under the terms of the GNU General Public License as published by
3743+ * the Free Software Foundation; version 3.
3744+ *
3745+ * This program is distributed in the hope that it will be useful,
3746+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3747+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3748+ * GNU General Public License for more details.
3749+ *
3750+ * You should have received a copy of the GNU General Public License
3751+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3752+ */
3753+
3754+#ifndef UNITY_WINDOWMANAGEMENTPOLICY_H
3755+#define UNITY_WINDOWMANAGEMENTPOLICY_H
3756+
3757+#include <qtmir/windowmanagementpolicy.h>
3758+#include "wmpolicyinterface.h"
3759+
3760+#include <unordered_set>
3761+
3762+class Q_DECL_EXPORT WindowManagementPolicy : public qtmir::WindowManagementPolicy,
3763+ public WMPolicyInterface
3764+{
3765+public:
3766+ WindowManagementPolicy(const miral::WindowManagerTools &tools, std::shared_ptr<qtmir::WindowManagementPolicyPrivate> dd);
3767+
3768+ void advise_new_window(miral::WindowInfo const& window_info) override;
3769+
3770+ // From WMPolicyInterface
3771+ std::shared_ptr<miral::Workspace> createWorkspace() override;
3772+
3773+ void releaseWorkspace(const std::shared_ptr<miral::Workspace> &workspace) override;
3774+
3775+ void setActiveWorkspace(const std::shared_ptr<miral::Workspace> &workspace) override;
3776+
3777+private:
3778+ std::weak_ptr<miral::Workspace> m_activeWorkspace;
3779+
3780+ std::unordered_set<std::shared_ptr<miral::Workspace>> m_workspaces;
3781+ const std::shared_ptr<miral::Workspace> m_dummyWorkspace;
3782+};
3783+
3784+#endif // UNITY_WINDOWMANAGEMENTPOLICY_H
3785
3786=== modified file 'src/libunity8-private/CMakeLists.txt'
3787--- src/libunity8-private/CMakeLists.txt 2014-10-09 14:53:00 +0000
3788+++ src/libunity8-private/CMakeLists.txt 2017-04-05 11:48:54 +0000
3789@@ -8,6 +8,7 @@
3790 abstractdbusservicemonitor.cpp
3791 unitydbusobject.cpp
3792 unitydbusvirtualobject.cpp
3793+ wmpolicyinterface.cpp
3794 )
3795
3796 add_library(${LIB_NAME} SHARED
3797
3798=== added file 'src/libunity8-private/wmpolicyinterface.cpp'
3799--- src/libunity8-private/wmpolicyinterface.cpp 1970-01-01 00:00:00 +0000
3800+++ src/libunity8-private/wmpolicyinterface.cpp 2017-04-05 11:48:54 +0000
3801@@ -0,0 +1,24 @@
3802+/*
3803+ * Copyright (C) 2017 Canonical, Ltd.
3804+ *
3805+ * This program is free software; you can redistribute it and/or modify
3806+ * it under the terms of the GNU General Public License as published by
3807+ * the Free Software Foundation; version 3.
3808+ *
3809+ * This program is distributed in the hope that it will be useful,
3810+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3811+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3812+ * GNU General Public License for more details.
3813+ *
3814+ * You should have received a copy of the GNU General Public License
3815+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3816+ */
3817+
3818+#include "wmpolicyinterface.h"
3819+
3820+WMPolicyInterface* wmPolicyInterface = nullptr;
3821+
3822+WMPolicyInterface *WMPolicyInterface::instance()
3823+{
3824+ return wmPolicyInterface;
3825+}
3826
3827=== added file 'src/libunity8-private/wmpolicyinterface.h'
3828--- src/libunity8-private/wmpolicyinterface.h 1970-01-01 00:00:00 +0000
3829+++ src/libunity8-private/wmpolicyinterface.h 2017-04-05 11:48:54 +0000
3830@@ -0,0 +1,46 @@
3831+/*
3832+ * Copyright (C) 2017 Canonical, Ltd.
3833+ *
3834+ * This program is free software; you can redistribute it and/or modify
3835+ * it under the terms of the GNU General Public License as published by
3836+ * the Free Software Foundation; version 3.
3837+ *
3838+ * This program is distributed in the hope that it will be useful,
3839+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3840+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3841+ * GNU General Public License for more details.
3842+ *
3843+ * You should have received a copy of the GNU General Public License
3844+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3845+ */
3846+
3847+#ifndef WMPOLICYINTERFACE_H
3848+#define WMPOLICYINTERFACE_H
3849+
3850+#include <memory>
3851+#include <functional>
3852+
3853+#include <qglobal.h>
3854+
3855+namespace miral {
3856+class Workspace;
3857+class Window;
3858+}
3859+
3860+class Q_DECL_EXPORT WMPolicyInterface
3861+{
3862+public:
3863+ virtual ~WMPolicyInterface() {}
3864+
3865+ static WMPolicyInterface *instance();
3866+
3867+ virtual std::shared_ptr<miral::Workspace> createWorkspace() = 0;
3868+
3869+ virtual void releaseWorkspace(const std::shared_ptr<miral::Workspace> &workspace) = 0;
3870+
3871+ virtual void setActiveWorkspace(const std::shared_ptr<miral::Workspace> &workspace) = 0;
3872+};
3873+
3874+extern Q_DECL_EXPORT WMPolicyInterface* wmPolicyInterface;
3875+
3876+#endif // WMPOLICYINTERFACE_H
3877
3878=== modified file 'src/main.cpp'
3879--- src/main.cpp 2016-11-28 13:46:55 +0000
3880+++ src/main.cpp 2017-04-05 11:48:54 +0000
3881@@ -15,9 +15,10 @@
3882 */
3883
3884 // local
3885-#include "ShellApplication.h"
3886+#include "UnityApplication.h"
3887 #include "qmldebuggerutils.h"
3888 #include "UnixSignalHandler.h"
3889+#include <paths.h>
3890
3891 #include <QTranslator>
3892 #include <QLibraryInfo>
3893@@ -27,17 +28,12 @@
3894 {
3895 qSetMessagePattern("[%{time yyyy-MM-dd:hh:mm:ss.zzz}] %{if-category}%{category}: %{endif}%{message}");
3896
3897- bool isMirServer = qgetenv("QT_QPA_PLATFORM") == "mirserver";
3898- if (!isMirServer && qgetenv("QT_QPA_PLATFORM") == "ubuntumirclient") {
3899- setenv("QT_QPA_PLATFORM", "mirserver", 1 /* overwrite */);
3900- isMirServer = true;
3901- }
3902-
3903 if (enableQmlDebugger(argc, argv)) {
3904 QQmlDebuggingEnabler qQmlEnableDebuggingHelper(true);
3905 }
3906
3907- ShellApplication *application = new ShellApplication(argc, (char**)argv, isMirServer);
3908+ auto *application = new UnityApplication(argc,
3909+ (char**)argv);
3910
3911 UnixSignalHandler signalHandler([]{
3912 QGuiApplication::exit(0);
3913
3914=== modified file 'tests/CMakeLists.txt'
3915--- tests/CMakeLists.txt 2017-03-15 18:57:52 +0000
3916+++ tests/CMakeLists.txt 2017-04-05 11:48:54 +0000
3917@@ -47,6 +47,7 @@
3918 list(APPEND ld_paths
3919 ${UNITY_MOCKPATH}/liblightdm
3920 ${UNITY_MOCKPATH}/libusermetrics
3921+ ${UNITY_MOCKPATH}/WindowManager
3922 )
3923
3924 string(REPLACE ";" ":" ld_library_path "${ld_paths}")
3925
3926=== modified file 'tests/mocks/CMakeLists.txt'
3927--- tests/mocks/CMakeLists.txt 2017-01-18 00:25:13 +0000
3928+++ tests/mocks/CMakeLists.txt 2017-04-05 11:48:54 +0000
3929@@ -43,6 +43,7 @@
3930 add_subdirectory(Ubuntu)
3931 add_subdirectory(Unity)
3932 add_subdirectory(QtMultimedia)
3933+add_subdirectory(WindowManager)
3934 add_subdirectory(Wizard)
3935 add_subdirectory(Utils)
3936 add_subdirectory(UInput)
3937
3938=== modified file 'tests/mocks/Unity/Application/CMakeLists.txt'
3939--- tests/mocks/Unity/Application/CMakeLists.txt 2016-12-20 15:50:41 +0000
3940+++ tests/mocks/Unity/Application/CMakeLists.txt 2017-04-05 11:48:54 +0000
3941@@ -1,3 +1,12 @@
3942+pkg_check_modules(MIRTEST mirtest>=0.26 REQUIRED)
3943+
3944+include_directories(
3945+ SYSTEM
3946+ ${MIRTEST_INCLUDE_DIRS}
3947+ ${MIRAL_INCLUDE_DIRS}
3948+ ${libunity8-private_SOURCE_DIR}
3949+)
3950+
3951 set(FakeUnityApplicationQml_SOURCES
3952 plugin.cpp
3953 ApplicationInfo.cpp
3954@@ -19,11 +28,27 @@
3955 resources/surfaces.qrc
3956 )
3957
3958-add_library(FakeUnityApplicationQml MODULE ${FakeUnityApplicationQml_SOURCES})
3959+add_library(FakeUnityApplicationQml MODULE
3960+ ${FakeUnityApplicationQml_SOURCES}
3961+)
3962+
3963+#add_dependencies(FakeUnityApplicationQml windowmanagementpolicy)
3964+
3965+target_link_libraries(FakeUnityApplicationQml
3966+ ${MIRTEST_LDFLAGS}
3967+ ${MIRAL_LDFLAGS}
3968+ mockwindowmanagmentpolicy
3969+)
3970
3971 add_library(NonMirUnityApplicationQml MODULE ${FakeUnityApplicationQml_SOURCES})
3972 set_target_properties(NonMirUnityApplicationQml PROPERTIES OUTPUT_NAME FakeUnityApplicationQml)
3973
3974+target_link_libraries(NonMirUnityApplicationQml
3975+ ${MIRTEST_LDFLAGS}
3976+ ${MIRAL_LDFLAGS}
3977+ mockwindowmanagmentpolicy
3978+)
3979+
3980 qt5_use_modules(FakeUnityApplicationQml Core Quick DBus)
3981 qt5_use_modules(NonMirUnityApplicationQml Core Quick DBus)
3982
3983
3984=== modified file 'tests/mocks/Unity/Application/SurfaceManager.cpp'
3985--- tests/mocks/Unity/Application/SurfaceManager.cpp 2017-04-05 11:48:53 +0000
3986+++ tests/mocks/Unity/Application/SurfaceManager.cpp 2017-04-05 11:48:54 +0000
3987@@ -18,8 +18,10 @@
3988
3989 #include "ApplicationInfo.h"
3990 #include "VirtualKeyboard.h"
3991+#include "../../WindowManager/WindowManagementPolicy.h"
3992
3993 #include <paths.h>
3994+#include <mirtest/mir/test/doubles/stub_surface.h>
3995
3996 #define SURFACEMANAGER_DEBUG 1
3997
3998@@ -31,6 +33,12 @@
3999
4000 namespace unityapi = unity::shell::application;
4001
4002+uint qHash(const WindowWrapper &key, uint)
4003+{
4004+ std::shared_ptr<mir::scene::Surface> surface = key.window;
4005+ return (quintptr)surface.get();
4006+}
4007+
4008 SurfaceManager *SurfaceManager::m_instance = nullptr;
4009
4010 SurfaceManager *SurfaceManager::instance()
4011@@ -44,20 +52,63 @@
4012
4013 Q_ASSERT(m_instance == nullptr);
4014 m_instance = this;
4015+
4016+ connect(WindowManagementPolicy::instance(), &WindowManagementPolicy::windowAdded,
4017+ this, [this](const miral::Window& window) {
4018+ Q_EMIT surfaceCreated(surfaceFor(window));
4019+ });
4020+
4021+ connect(WindowManagementPolicy::instance(), &WindowManagementPolicy::windowsAddedToWorkspace,
4022+ this, [this](const std::shared_ptr<miral::Workspace> &workspace, const std::vector<miral::Window> &windows) {
4023+ Q_EMIT surfacesAddedToWorkspace(workspace, surfacesFor(windows));
4024+ });
4025+
4026+ connect(WindowManagementPolicy::instance(), &WindowManagementPolicy::windowsAboutToBeRemovedFromWorkspace,
4027+ this, [this](const std::shared_ptr<miral::Workspace> &workspace, const std::vector<miral::Window> &windows) {
4028+ Q_EMIT surfacesAboutToBeRemovedFromWorkspace(workspace, surfacesFor(windows));
4029+ });
4030 }
4031
4032 SurfaceManager::~SurfaceManager()
4033 {
4034 DEBUG_MSG("");
4035
4036- if (m_virtualKeyboard) {
4037- m_virtualKeyboard->setLive(false);
4038- }
4039+ releaseInputMethodSurface();
4040
4041 Q_ASSERT(m_instance == this);
4042 m_instance = nullptr;
4043 }
4044
4045+MirSurfaceInterface *SurfaceManager::surfaceFor(const miral::Window& window) const
4046+{
4047+ auto iter = m_windowToSurface.find({window});
4048+ if (iter != m_windowToSurface.end()) {
4049+ return *iter;
4050+ }
4051+ return nullptr;
4052+}
4053+
4054+QVector<MirSurfaceInterface*> SurfaceManager::surfacesFor(const std::vector<miral::Window> &windows) const
4055+{
4056+ QVector<unityapi::MirSurfaceInterface*> surfaces;
4057+ for (size_t i = 0; i < windows.size(); i++) {
4058+ auto mirSurface = surfaceFor(windows[i]);
4059+ if (mirSurface) {
4060+ surfaces.push_back(mirSurface);
4061+ }
4062+ }
4063+ return surfaces;
4064+}
4065+
4066+miral::Window SurfaceManager::windowFor(MirSurfaceInterface *surface) const
4067+{
4068+ auto iter = m_surfaceToWindow.find(surface);
4069+ if (iter != m_surfaceToWindow.end()) {
4070+ return iter->window;
4071+ }
4072+ return miral::Window();
4073+}
4074+
4075 MirSurface *SurfaceManager::createSurface(const QString& name,
4076 Mir::Type type,
4077 Mir::State state,
4078@@ -75,7 +126,12 @@
4079
4080 void SurfaceManager::registerSurface(MirSurface *surface)
4081 {
4082+ auto fakeSurface = std::make_shared<mir::test::doubles::StubSurface>();
4083+ WindowWrapper window{miral::Window(nullptr, fakeSurface), fakeSurface};
4084+
4085 m_surfaces.prepend(surface);
4086+ m_windowToSurface.insert(window, surface);
4087+ m_surfaceToWindow.insert(surface, window);
4088
4089 if (!surface->parentSurface()) {
4090 surface->setMinimumWidth(m_newSurfaceMinimumWidth);
4091@@ -90,16 +146,24 @@
4092 this->onStateRequested(surface, state);
4093 });
4094
4095- const QString persistentId = surface->persistentId();
4096 connect(surface, &QObject::destroyed, this, [=]() {
4097- this->onSurfaceDestroyed(surface, persistentId);
4098+ auto iter = m_surfaceToWindow.find(surface);
4099+ if (iter != m_surfaceToWindow.end()) {
4100+ WindowWrapper key = m_surfaceToWindow.value(surface);
4101+ WindowManagementPolicy::instance()->removeWindow(key.window);
4102+ this->onSurfaceDestroyed(surface);
4103+ }
4104 });
4105-
4106 }
4107
4108 void SurfaceManager::notifySurfaceCreated(unityapi::MirSurfaceInterface *surface)
4109 {
4110- Q_EMIT surfaceCreated(surface);
4111+ if (Q_UNLIKELY(!m_surfaceToWindow.contains(surface))) {
4112+ Q_EMIT surfaceCreated(surface);
4113+ return;
4114+ }
4115+
4116+ WindowManagementPolicy::instance()->addWindow(m_surfaceToWindow[surface].window);
4117 }
4118
4119 void SurfaceManager::setNewSurfaceMinimumWidth(int value)
4120@@ -209,6 +273,32 @@
4121 DEBUG_MSG("("<<surface<<") ended");
4122 }
4123
4124+void SurfaceManager::forEachSurfaceInWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
4125+ const std::function<void(unity::shell::application::MirSurfaceInterface*)> &callback)
4126+{
4127+ WindowManagementPolicy::instance()->forEachWindowInWorkspace(workspace, [&](const miral::Window &window) {
4128+ auto surface = surfaceFor(window);
4129+ if (surface) {
4130+ callback(surface);
4131+ }
4132+ });
4133+}
4134+
4135+void SurfaceManager::moveSurfaceToWorkspace(unity::shell::application::MirSurfaceInterface* surface,
4136+ const std::shared_ptr<miral::Workspace> &workspace)
4137+{
4138+ auto window = windowFor(surface);
4139+ if (window) {
4140+ WindowManagementPolicy::instance()->moveWindowToWorkspace(window, workspace);
4141+ }
4142+}
4143+
4144+void SurfaceManager::moveWorkspaceContentToWorkspace(const std::shared_ptr<miral::Workspace> &to,
4145+ const std::shared_ptr<miral::Workspace> &from)
4146+{
4147+ WindowManagementPolicy::instance()->moveWorkspaceContentToWorkspace(to, from);
4148+}
4149+
4150 void SurfaceManager::onStateRequested(MirSurface *surface, Mir::State state)
4151 {
4152 DEBUG_MSG("("<<surface<<","<<state<<") started");
4153@@ -230,9 +320,17 @@
4154 DEBUG_MSG("("<<surface<<","<<state<<") ended");
4155 }
4156
4157-void SurfaceManager::onSurfaceDestroyed(MirSurface *surface, const QString& persistentId)
4158+void SurfaceManager::onSurfaceDestroyed(MirSurface *surface)
4159 {
4160 m_surfaces.removeAll(surface);
4161+
4162+ auto iter = m_surfaceToWindow.find(surface);
4163+ if (iter != m_surfaceToWindow.end()) {
4164+ WindowWrapper key = iter.value();
4165+ m_windowToSurface.remove(key);
4166+ m_surfaceToWindow.erase(iter);
4167+ }
4168+
4169 if (m_focusedSurface == surface) {
4170 m_focusedSurface = nullptr;
4171
4172@@ -244,7 +342,7 @@
4173 m_underModification = false;
4174 Q_EMIT modificationsEnded();
4175 }
4176- Q_EMIT surfaceDestroyed(persistentId);
4177+ Q_EMIT surfaceRemoved(surface);
4178 }
4179
4180 void SurfaceManager::focusFirstAvailableSurface()
4181@@ -276,6 +374,15 @@
4182 if (!m_virtualKeyboard) {
4183 m_virtualKeyboard = new VirtualKeyboard;
4184 registerSurface(m_virtualKeyboard);
4185- Q_EMIT surfaceCreated(m_virtualKeyboard);
4186+
4187+ WindowManagementPolicy::instance()->addWindow(m_surfaceToWindow[m_virtualKeyboard].window);
4188+ }
4189+}
4190+
4191+void SurfaceManager::releaseInputMethodSurface()
4192+{
4193+ if (m_virtualKeyboard) {
4194+ m_virtualKeyboard->setLive(false);
4195+ m_virtualKeyboard = nullptr;
4196 }
4197 }
4198
4199=== modified file 'tests/mocks/Unity/Application/SurfaceManager.h'
4200--- tests/mocks/Unity/Application/SurfaceManager.h 2017-01-26 11:10:01 +0000
4201+++ tests/mocks/Unity/Application/SurfaceManager.h 2017-04-05 11:48:54 +0000
4202@@ -20,12 +20,20 @@
4203 #include <QObject>
4204
4205 #include <unity/shell/application/SurfaceManagerInterface.h>
4206+#include <miral/window.h>
4207
4208 #include "MirSurface.h"
4209 #include "VirtualKeyboard.h"
4210
4211 class ApplicationInfo;
4212
4213+struct WindowWrapper {
4214+ miral::Window window;
4215+ std::shared_ptr<mir::scene::Surface> session{nullptr}; // Keeps the window surface alive.
4216+ bool operator==(const WindowWrapper& other) const { return window==other.window; }
4217+};
4218+uint qHash(const WindowWrapper &key, uint seed = 0);
4219+
4220 class SurfaceManager : public unity::shell::application::SurfaceManagerInterface
4221 {
4222 Q_OBJECT
4223@@ -46,6 +54,13 @@
4224 void raise(unity::shell::application::MirSurfaceInterface *surface) override;
4225 void activate(unity::shell::application::MirSurfaceInterface *surface) override;
4226
4227+ void forEachSurfaceInWorkspace(const std::shared_ptr<miral::Workspace> &workspace,
4228+ const std::function<void(unity::shell::application::MirSurfaceInterface*)> &callback) override;
4229+ void moveSurfaceToWorkspace(unity::shell::application::MirSurfaceInterface* surface,
4230+ const std::shared_ptr<miral::Workspace> &workspace) override;
4231+ void moveWorkspaceContentToWorkspace(const std::shared_ptr<miral::Workspace> &to,
4232+ const std::shared_ptr<miral::Workspace> &from) override;
4233+
4234 Q_INVOKABLE MirSurface* createSurface(const QString& name,
4235 Mir::Type type,
4236 Mir::State state,
4237@@ -76,10 +91,9 @@
4238
4239 public Q_SLOTS:
4240 void createInputMethodSurface();
4241+ void releaseInputMethodSurface();
4242
4243 Q_SIGNALS:
4244- void surfaceDestroyed(const QString& persistentSurfaceId);
4245-
4246 void newSurfaceMinimumWidthChanged(int value);
4247 void newSurfaceMaximumWidthChanged(int value);
4248 void newSurfaceMinimumHeightChanged(int value);
4249@@ -89,12 +103,15 @@
4250
4251 private Q_SLOTS:
4252 void onStateRequested(MirSurface *surface, Mir::State state);
4253- void onSurfaceDestroyed(MirSurface *surface, const QString& persistentId);
4254+ void onSurfaceDestroyed(MirSurface *surface);
4255
4256 private:
4257 void doRaise(unity::shell::application::MirSurfaceInterface *surface);
4258 void focusFirstAvailableSurface();
4259 void registerSurface(MirSurface *surface);
4260+ unity::shell::application::MirSurfaceInterface* surfaceFor(const miral::Window &window) const;
4261+ QVector<unity::shell::application::MirSurfaceInterface*> surfacesFor(const std::vector<miral::Window> &windows) const;
4262+ miral::Window windowFor(unity::shell::application::MirSurfaceInterface* surface) const;
4263
4264 static SurfaceManager *m_instance;
4265
4266@@ -110,6 +127,9 @@
4267
4268 QList<MirSurface*> m_surfaces;
4269
4270+ QHash<WindowWrapper, unity::shell::application::MirSurfaceInterface*> m_windowToSurface;
4271+ QHash<unity::shell::application::MirSurfaceInterface*, WindowWrapper> m_surfaceToWindow;
4272+
4273 VirtualKeyboard *m_virtualKeyboard{nullptr};
4274 };
4275
4276
4277=== modified file 'tests/mocks/Unity/Application/plugin.cpp'
4278--- tests/mocks/Unity/Application/plugin.cpp 2016-12-01 11:38:26 +0000
4279+++ tests/mocks/Unity/Application/plugin.cpp 2017-04-05 11:48:54 +0000
4280@@ -38,6 +38,12 @@
4281 {
4282 return new MirMock;
4283 }
4284+
4285+QObject* surfaceManagerSingleton(QQmlEngine*, QJSEngine*)
4286+{
4287+ return new SurfaceManager;
4288+}
4289+
4290 } // anonymous namespace
4291
4292 void FakeUnityApplicationQmlPlugin::registerTypes(const char *uri)
4293@@ -59,7 +65,7 @@
4294
4295 qmlRegisterSingletonType<ApplicationManager>(uri, 0, 1, "ApplicationManager", applicationManagerSingleton);
4296 qmlRegisterSingletonType<MirMock>(uri, 0, 1, "Mir", mirSingleton);
4297- qmlRegisterType<SurfaceManager>(uri, 0, 1, "SurfaceManager");
4298+ qmlRegisterSingletonType<SurfaceManager>(uri, 0, 1, "SurfaceManager", surfaceManagerSingleton);
4299 }
4300
4301 void FakeUnityApplicationQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
4302
4303=== modified file 'tests/mocks/Unity/CMakeLists.txt'
4304--- tests/mocks/Unity/CMakeLists.txt 2017-01-31 13:35:50 +0000
4305+++ tests/mocks/Unity/CMakeLists.txt 2017-04-05 11:48:54 +0000
4306@@ -6,7 +6,6 @@
4307 add_subdirectory(Launcher)
4308 add_subdirectory(Notifications)
4309 add_subdirectory(DashCommunicator)
4310-add_subdirectory(Screens)
4311 add_subdirectory(Platform)
4312
4313 pkg_search_module(GOBJECT gobject-2.0 REQUIRED)
4314
4315=== removed directory 'tests/mocks/Unity/Screens'
4316=== removed file 'tests/mocks/Unity/Screens/CMakeLists.txt'
4317--- tests/mocks/Unity/Screens/CMakeLists.txt 2017-04-05 11:48:53 +0000
4318+++ tests/mocks/Unity/Screens/CMakeLists.txt 1970-01-01 00:00:00 +0000
4319@@ -1,15 +0,0 @@
4320-include_directories(
4321- ${CMAKE_CURRENT_SOURCE_DIR}
4322-)
4323-
4324-set(MockScreens_SOURCES
4325- plugin.cpp
4326- screens.cpp
4327- screenwindow.cpp
4328-)
4329-
4330-add_library(MockScreensPlugin MODULE ${MockScreens_SOURCES})
4331-
4332-qt5_use_modules(MockScreensPlugin Gui Qml Quick)
4333-
4334-add_unity8_mock(Unity.Screens 0.1 Unity/Screens PREFIX mocks TARGETS MockScreensPlugin)
4335
4336=== removed file 'tests/mocks/Unity/Screens/plugin.cpp'
4337--- tests/mocks/Unity/Screens/plugin.cpp 2017-04-05 11:48:53 +0000
4338+++ tests/mocks/Unity/Screens/plugin.cpp 1970-01-01 00:00:00 +0000
4339@@ -1,42 +0,0 @@
4340-/*
4341- * Copyright (C) 2015 Canonical, Ltd.
4342- *
4343- * This program is free software: you can redistribute it and/or modify it under
4344- * the terms of the GNU Lesser General Public License version 3, as published by
4345- * the Free Software Foundation.
4346- *
4347- * This program is distributed in the hope that it will be useful, but WITHOUT
4348- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4349- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4350- * Lesser General Public License for more details.
4351- *
4352- * You should have received a copy of the GNU Lesser General Public License
4353- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4354- */
4355-
4356-#include "plugin.h"
4357-#include "screens.h"
4358-#include "screenwindow.h"
4359-
4360-#include <QScreen>
4361-
4362-namespace {
4363-QObject* screensSingleton(QQmlEngine* engine, QJSEngine* scriptEngine) {
4364- Q_UNUSED(engine);
4365- Q_UNUSED(scriptEngine);
4366- return new Screens();
4367-}
4368-}
4369-
4370-void UnityScreensPlugin::registerTypes(const char* uri)
4371-{
4372- Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Screens"));
4373-
4374- qRegisterMetaType<QScreen*>("QScreen*");
4375- qRegisterMetaType<ScreenMode*>("ScreenMode*");
4376- qmlRegisterUncreatableType<ScreenMode>(uri, 0, 1, "ScreenMode", "ScreenMode is not creatable.");
4377-
4378- qmlRegisterSingletonType<Screens>(uri, 0, 1, "Screens", screensSingleton);
4379- qmlRegisterType<ScreenWindow>(uri, 0, 1, "ScreenWindow");
4380- qmlRegisterRevision<QWindow,1>(uri, 0, 1);
4381-}
4382
4383=== removed file 'tests/mocks/Unity/Screens/plugin.h'
4384--- tests/mocks/Unity/Screens/plugin.h 2016-04-28 12:17:38 +0000
4385+++ tests/mocks/Unity/Screens/plugin.h 1970-01-01 00:00:00 +0000
4386@@ -1,25 +0,0 @@
4387-/*
4388- * Copyright (C) 2015 Canonical, Ltd.
4389- *
4390- * This program is free software: you can redistribute it and/or modify it under
4391- * the terms of the GNU Lesser General Public License version 3, as published by
4392- * the Free Software Foundation.
4393- *
4394- * This program is distributed in the hope that it will be useful, but WITHOUT
4395- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4396- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4397- * Lesser General Public License for more details.
4398- *
4399- * You should have received a copy of the GNU Lesser General Public License
4400- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4401- */
4402-
4403-#include <QQmlExtensionPlugin>
4404-#include <QtQml/qqml.h>
4405-
4406-class UnityScreensPlugin : public QQmlExtensionPlugin {
4407- Q_OBJECT
4408- Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
4409-public:
4410- void registerTypes(const char* uri) override;
4411-};
4412
4413=== removed file 'tests/mocks/Unity/Screens/qmldir'
4414--- tests/mocks/Unity/Screens/qmldir 2015-12-02 13:23:45 +0000
4415+++ tests/mocks/Unity/Screens/qmldir 1970-01-01 00:00:00 +0000
4416@@ -1,2 +0,0 @@
4417-module Unity.Screens
4418-plugin MockScreensPlugin
4419
4420=== removed file 'tests/mocks/Unity/Screens/screens.cpp'
4421--- tests/mocks/Unity/Screens/screens.cpp 2017-04-05 11:48:53 +0000
4422+++ tests/mocks/Unity/Screens/screens.cpp 1970-01-01 00:00:00 +0000
4423@@ -1,114 +0,0 @@
4424-/*
4425- * Copyright (C) 2015 Canonical, Ltd.
4426- *
4427- * This program is free software: you can redistribute it and/or modify it under
4428- * the terms of the GNU Lesser General Public License version 3, as published by
4429- * the Free Software Foundation.
4430- *
4431- * This program is distributed in the hope that it will be useful, but WITHOUT
4432- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4433- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4434- * Lesser General Public License for more details.
4435- *
4436- * You should have received a copy of the GNU Lesser General Public License
4437- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4438- */
4439-
4440-#include "screens.h"
4441-
4442-// Qt
4443-#include <QGuiApplication>
4444-#include <QDebug>
4445-
4446-Screens::Screens(QObject *parent) :
4447- QAbstractListModel(parent)
4448-{
4449- bool ok = false;
4450- int screenCount = qEnvironmentVariableIntValue("UNITY_MOCK_SCREEN_COUNT", &ok);
4451- if (!ok) screenCount = 1;
4452- QPoint lastPoint(0,0);
4453- for (int i = 0; i < screenCount; ++i) {
4454- auto screen = new Screen();
4455- screen->m_active = i == 0;
4456- screen->m_name = QString("Monitor %1").arg(i);
4457- screen->m_position = QPoint(lastPoint.x(), lastPoint.y());
4458- screen->m_sizes.append(new ScreenMode(50, QSize(640,480)));
4459- screen->m_sizes.append(new ScreenMode(60, QSize(1280,1024)));
4460- screen->m_sizes.append(new ScreenMode(60, QSize(1440,900)));
4461- screen->m_sizes.append(new ScreenMode(60, QSize(1920,1080)));
4462- screen->m_currentModeIndex = 3;
4463- screen->m_physicalSize = QSize(300,200);
4464- m_screenList.append(screen);
4465-
4466- lastPoint.rx() += screen->m_sizes[screen->m_currentModeIndex]->size.width();
4467- }
4468-}
4469-
4470-Screens::~Screens() noexcept
4471-{
4472- qDeleteAll(m_screenList);
4473- m_screenList.clear();
4474-}
4475-
4476-QHash<int, QByteArray> Screens::roleNames() const
4477-{
4478- QHash<int, QByteArray> roles;
4479- roles[ScreenRole] = "screen";
4480- return roles;
4481-}
4482-
4483-QVariant Screens::data(const QModelIndex &index, int role) const
4484-{
4485- if (!index.isValid() || index.row() >= m_screenList.size()) {
4486- return QVariant();
4487- }
4488-
4489- switch(role) {
4490- case ScreenRole:
4491- return QVariant::fromValue(m_screenList.at(index.row()));
4492- }
4493-
4494- return QVariant();
4495-}
4496-
4497-int Screens::rowCount(const QModelIndex &) const
4498-{
4499- return count();
4500-}
4501-
4502-int Screens::count() const
4503-{
4504- return m_screenList.size();
4505-}
4506-
4507-void Screens::activateScreen(int)
4508-{
4509- qWarning("Not Implemented");
4510-}
4511-
4512-Screen::Screen(QObject* parent)
4513- : QObject(parent)
4514-{
4515-}
4516-
4517-Screen::~Screen()
4518-{
4519- qDeleteAll(m_sizes);
4520- m_sizes.clear();
4521-}
4522-
4523-QQmlListProperty<ScreenMode> Screen::availableModes()
4524-{
4525- return QQmlListProperty<ScreenMode>(this, m_sizes);
4526-}
4527-
4528-Screen *Screen::beginConfiguration()
4529-{
4530- qWarning("Not Implemented");
4531- return nullptr;
4532-}
4533-
4534-void Screen::applyConfiguration()
4535-{
4536- qWarning("Not Implemented");
4537-}
4538
4539=== removed file 'tests/mocks/Unity/Screens/screens.h'
4540--- tests/mocks/Unity/Screens/screens.h 2017-04-05 11:48:53 +0000
4541+++ tests/mocks/Unity/Screens/screens.h 1970-01-01 00:00:00 +0000
4542@@ -1,162 +0,0 @@
4543-/*
4544- * Copyright (C) 2015 Canonical, Ltd.
4545- *
4546- * This program is free software: you can redistribute it and/or modify it under
4547- * the terms of the GNU Lesser General Public License version 3, as published by
4548- * the Free Software Foundation.
4549- *
4550- * This program is distributed in the hope that it will be useful, but WITHOUT
4551- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4552- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4553- * Lesser General Public License for more details.
4554- *
4555- * You should have received a copy of the GNU Lesser General Public License
4556- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4557- */
4558-
4559-#ifndef SCREENS_H
4560-#define SCREENS_H
4561-
4562-#include <QAbstractListModel>
4563-#include <QScreen>
4564-#include <QQmlListProperty>
4565-
4566-class Screen;
4567-
4568-class Screens : public QAbstractListModel
4569-{
4570- Q_OBJECT
4571-
4572- Q_PROPERTY(int count READ count NOTIFY countChanged)
4573-
4574-public:
4575- enum ItemRoles {
4576- ScreenRole = Qt::UserRole + 1,
4577- OutputTypeRole,
4578- EnabledRole,
4579- NameRole,
4580- ScaleRole,
4581- FormFactorRole,
4582- GeometryRole,
4583- SizesRole
4584- };
4585-
4586- enum OutputTypes {
4587- Unknown,
4588- VGA,
4589- DVII,
4590- DVID,
4591- DVIA,
4592- Composite,
4593- SVideo,
4594- LVDS,
4595- Component,
4596- NinePinDIN,
4597- DisplayPort,
4598- HDMIA,
4599- HDMIB,
4600- TV,
4601- EDP
4602- };
4603- Q_ENUM(OutputTypes)
4604-
4605- enum FormFactor {
4606- FormFactorUnknown,
4607- FormFactorPhone,
4608- FormFactorTablet,
4609- FormFactorMonitor,
4610- FormFactorTV,
4611- FormFactorProjector,
4612- };
4613-
4614- explicit Screens(QObject *parent = 0);
4615- virtual ~Screens() noexcept;
4616-
4617- /* QAbstractItemModel */
4618- QHash<int, QByteArray> roleNames() const override;
4619- QVariant data(const QModelIndex &index, int role = ScreenRole) const override;
4620- int rowCount(const QModelIndex &parent = QModelIndex()) const override;
4621-
4622- int count() const;
4623-
4624-public Q_SLOTS:
4625- void activateScreen(int index);
4626-
4627-Q_SIGNALS:
4628- void countChanged();
4629- void screenAdded(QScreen *screen);
4630- void screenRemoved(QScreen *screen);
4631-
4632-private:
4633- QList<Screen *> m_screenList;
4634-};
4635-
4636-class ScreenMode : public QObject
4637-{
4638- Q_OBJECT
4639- Q_PROPERTY(qreal refreshRate MEMBER refreshRate CONSTANT)
4640- Q_PROPERTY(QSize size MEMBER size CONSTANT)
4641-public:
4642- ScreenMode() {}
4643- ScreenMode(qreal refreshRate, QSize size):refreshRate(refreshRate),size(size) {}
4644- ScreenMode(const ScreenMode& other)
4645- : QObject(nullptr),
4646- refreshRate{other.refreshRate},size{other.size}
4647- {}
4648-
4649- qreal refreshRate;
4650- QSize size;
4651-};
4652-
4653-class Screen : public QObject
4654-{
4655- Q_OBJECT
4656-
4657- Q_PROPERTY(bool active MEMBER m_active NOTIFY activeChanged)
4658-
4659- Q_PROPERTY(bool used MEMBER m_used NOTIFY usedChanged)
4660- Q_PROPERTY(QString name MEMBER m_name NOTIFY nameChanged)
4661- Q_PROPERTY(Screens::OutputTypes outputType MEMBER m_outputType NOTIFY outputTypeChanged)
4662- Q_PROPERTY(float scale MEMBER m_scale NOTIFY scaleChanged)
4663- Q_PROPERTY(Screens::FormFactor formFactor MEMBER m_formFactor NOTIFY formFactorChanged)
4664- Q_PROPERTY(QPoint position MEMBER m_position NOTIFY positionChanged)
4665- Q_PROPERTY(uint currentModeIndex MEMBER m_currentModeIndex NOTIFY currentModeIndexChanged)
4666- Q_PROPERTY(QQmlListProperty<ScreenMode> availableModes READ availableModes NOTIFY availableModesChanged)
4667- Q_PROPERTY(QSizeF physicalSize MEMBER m_physicalSize NOTIFY physicalSizeChanged)
4668-public:
4669- Screen(QObject* parent = 0);
4670- ~Screen();
4671-
4672- QQmlListProperty<ScreenMode> availableModes();
4673-
4674- Q_INVOKABLE Screen* beginConfiguration();
4675- Q_INVOKABLE void applyConfiguration();
4676-
4677-Q_SIGNALS:
4678- void activeChanged();
4679- void usedChanged();
4680- void nameChanged();
4681- void outputTypeChanged();
4682- void scaleChanged();
4683- void formFactorChanged();
4684- void positionChanged();
4685- void currentModeIndexChanged();
4686- void availableModesChanged();
4687- void physicalSizeChanged();
4688-
4689-public:
4690- bool m_active{false};
4691- bool m_used{true};
4692- QString m_name;
4693- Screens::OutputTypes m_outputType{Screens::Unknown};
4694- float m_scale{1.0};
4695- Screens::FormFactor m_formFactor{Screens::FormFactorMonitor};
4696- QPoint m_position;
4697- uint m_currentModeIndex{0};
4698- QList<ScreenMode*> m_sizes;
4699- QSizeF m_physicalSize;
4700-};
4701-
4702-Q_DECLARE_METATYPE(ScreenMode)
4703-
4704-#endif // SCREENS_H
4705
4706=== removed file 'tests/mocks/Unity/Screens/screenwindow.cpp'
4707--- tests/mocks/Unity/Screens/screenwindow.cpp 2017-04-05 11:48:53 +0000
4708+++ tests/mocks/Unity/Screens/screenwindow.cpp 1970-01-01 00:00:00 +0000
4709@@ -1,35 +0,0 @@
4710-/*
4711- * Copyright (C) 2016 Canonical, Ltd.
4712- *
4713- * This program is free software: you can redistribute it and/or modify it under
4714- * the terms of the GNU Lesser General Public License version 3, as published by
4715- * the Free Software Foundation.
4716- *
4717- * This program is distributed in the hope that it will be useful, but WITHOUT
4718- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4719- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4720- * Lesser General Public License for more details.
4721- *
4722- * You should have received a copy of the GNU Lesser General Public License
4723- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4724- */
4725-
4726-#include "screenwindow.h"
4727-
4728-ScreenWindow::ScreenWindow(QWindow *parent)
4729- : QQuickWindow(parent)
4730-{
4731-}
4732-
4733-Screen *ScreenWindow::screenWrapper() const
4734-{
4735- return m_screen.data();
4736-}
4737-
4738-void ScreenWindow::setScreenWrapper(Screen *screen)
4739-{
4740- if (m_screen != screen) {
4741- m_screen = screen;
4742- Q_EMIT screenWrapperChanged();
4743- }
4744-}
4745
4746=== removed file 'tests/mocks/Unity/Screens/screenwindow.h'
4747--- tests/mocks/Unity/Screens/screenwindow.h 2017-04-05 11:48:53 +0000
4748+++ tests/mocks/Unity/Screens/screenwindow.h 1970-01-01 00:00:00 +0000
4749@@ -1,42 +0,0 @@
4750-/*
4751- * Copyright (C) 2016 Canonical, Ltd.
4752- *
4753- * This program is free software: you can redistribute it and/or modify it under
4754- * the terms of the GNU Lesser General Public License version 3, as published by
4755- * the Free Software Foundation.
4756- *
4757- * This program is distributed in the hope that it will be useful, but WITHOUT
4758- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4759- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4760- * Lesser General Public License for more details.
4761- *
4762- * You should have received a copy of the GNU Lesser General Public License
4763- * along with this program. If not, see <http://www.gnu.org/licenses/>.
4764- */
4765-
4766-#ifndef SCREENWINDOW_H
4767-#define SCREENWINDOW_H
4768-
4769-#include <QQuickWindow>
4770-#include <QPointer>
4771-
4772-#include "screens.h"
4773-
4774-class ScreenWindow : public QQuickWindow
4775-{
4776- Q_OBJECT
4777- Q_PROPERTY(Screen *screen READ screenWrapper WRITE setScreenWrapper NOTIFY screenWrapperChanged)
4778-public:
4779- ScreenWindow(QWindow *parent = 0);
4780-
4781- Screen *screenWrapper() const;
4782- void setScreenWrapper(Screen *screen);
4783-
4784-Q_SIGNALS:
4785- void screenWrapperChanged();
4786-
4787-private:
4788- QPointer<Screen> m_screen;
4789-};
4790-
4791-#endif // SCREENWINDOW_H
4792
4793=== added directory 'tests/mocks/WindowManager'
4794=== added file 'tests/mocks/WindowManager/CMakeLists.txt'
4795--- tests/mocks/WindowManager/CMakeLists.txt 1970-01-01 00:00:00 +0000
4796+++ tests/mocks/WindowManager/CMakeLists.txt 2017-04-05 11:48:54 +0000
4797@@ -0,0 +1,61 @@
4798+include_directories(
4799+ SYSTEM
4800+ ${QTMIRSERVER_INCLUDE_DIRS}
4801+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
4802+)
4803+
4804+include_directories(
4805+ ${CMAKE_CURRENT_SOURCE_DIR}
4806+ ${CMAKE_CURRENT_BINARY_DIR}
4807+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager
4808+ ${libunity8-private_SOURCE_DIR}
4809+)
4810+
4811+add_library(mockwindowmanagmentpolicy SHARED
4812+ WindowManagementPolicy.cpp
4813+)
4814+target_link_libraries(mockwindowmanagmentpolicy
4815+ ${MIRAL_LDFLAGS}
4816+ unity8-private
4817+)
4818+qt5_use_modules(mockwindowmanagmentpolicy Core)
4819+
4820+install(TARGETS mockwindowmanagmentpolicy
4821+ DESTINATION ${SHELL_INSTALL_QML}/mocks/WindowManager
4822+ )
4823+
4824+set(WINDOWMANAGER_SRC
4825+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/AvailableDesktopArea.cpp
4826+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Screen.cpp
4827+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/ScreenAttached.cpp
4828+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Screens.cpp
4829+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/ScreenWindow.cpp
4830+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/TopLevelWindowModel.cpp
4831+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Window.cpp
4832+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/WindowMargins.cpp
4833+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/WindowManagerObjects.cpp
4834+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/WorkspaceManager.cpp
4835+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/WorkspaceModel.cpp
4836+ ${CMAKE_SOURCE_DIR}/plugins/WindowManager/Workspace.cpp
4837+ MockScreens.cpp
4838+ MockScreenWindow.cpp
4839+ MockScreensConfiguration.cpp
4840+ WindowManagerPlugin.cpp
4841+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h
4842+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h
4843+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
4844+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h
4845+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceListInterface.h
4846+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/SurfaceManagerInterface.h
4847+)
4848+
4849+add_library(MockWindowManager-qml SHARED ${WINDOWMANAGER_SRC})
4850+
4851+target_link_libraries(MockWindowManager-qml
4852+ ${QTMIRSERVER_LDFLAGS}
4853+ mockwindowmanagmentpolicy
4854+)
4855+
4856+qt5_use_modules(MockWindowManager-qml Qml Quick Gui)
4857+
4858+add_unity8_mock(WindowManager 1.0 WindowManager TARGETS MockWindowManager-qml)
4859
4860=== added file 'tests/mocks/WindowManager/MockScreenWindow.cpp'
4861--- tests/mocks/WindowManager/MockScreenWindow.cpp 1970-01-01 00:00:00 +0000
4862+++ tests/mocks/WindowManager/MockScreenWindow.cpp 2017-04-05 11:48:54 +0000
4863@@ -0,0 +1,34 @@
4864+/*
4865+ * Copyright (C) 2016-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+ * the Free Software Foundation.
4870+ *
4871+ * This program is distributed in the hope that it will be useful, but WITHOUT
4872+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4873+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4874+ * Lesser General Public License for more details.
4875+ *
4876+ * You should have received a copy of the GNU Lesser General Public License
4877+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4878+ */
4879+
4880+#include "MockScreenWindow.h"
4881+#include "MockScreens.h"
4882+
4883+// Qt
4884+#include <QGuiApplication>
4885+#include <QDebug>
4886+
4887+MockScreenWindow::MockScreenWindow(QQuickWindow *parent)
4888+ : ScreenWindow(parent)
4889+{
4890+ connect(this, &ScreenWindow::screenWrapperChanged, this, [this]() {
4891+ MockScreens::instance()->connectWindow(this);
4892+ });
4893+}
4894+
4895+MockScreenWindow::~MockScreenWindow()
4896+{
4897+}
4898
4899=== added file 'tests/mocks/WindowManager/MockScreenWindow.h'
4900--- tests/mocks/WindowManager/MockScreenWindow.h 1970-01-01 00:00:00 +0000
4901+++ tests/mocks/WindowManager/MockScreenWindow.h 2017-04-05 11:48:54 +0000
4902@@ -0,0 +1,30 @@
4903+/*
4904+ * Copyright (C) 2016-2017 Canonical, Ltd.
4905+ *
4906+ * This program is free software: you can redistribute it and/or modify it under
4907+ * the terms of the GNU Lesser General Public License version 3, as published by
4908+ * the Free Software Foundation.
4909+ *
4910+ * This program is distributed in the hope that it will be useful, but WITHOUT
4911+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4912+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4913+ * Lesser General Public License for more details.
4914+ *
4915+ * You should have received a copy of the GNU Lesser General Public License
4916+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4917+ */
4918+
4919+#ifndef MOCK_SCREENWINDOW_H
4920+#define MOCK_SCREENWINDOW_H
4921+
4922+#include "ScreenWindow.h"
4923+
4924+class MockScreenWindow : public ScreenWindow
4925+{
4926+ Q_OBJECT
4927+public:
4928+ explicit MockScreenWindow(QQuickWindow *parent = 0);
4929+ ~MockScreenWindow();
4930+};
4931+
4932+#endif // MOCK_SCREENWINDOW_H
4933
4934=== added file 'tests/mocks/WindowManager/MockScreens.cpp'
4935--- tests/mocks/WindowManager/MockScreens.cpp 1970-01-01 00:00:00 +0000
4936+++ tests/mocks/WindowManager/MockScreens.cpp 2017-04-05 11:48:54 +0000
4937@@ -0,0 +1,227 @@
4938+/*
4939+ * Copyright (C) 2016-2017 Canonical, Ltd.
4940+ *
4941+ * This program is free software: you can redistribute it and/or modify it under
4942+ * the terms of the GNU Lesser General Public License version 3, as published by
4943+ * the Free Software Foundation.
4944+ *
4945+ * This program is distributed in the hope that it will be useful, but WITHOUT
4946+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4947+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4948+ * Lesser General Public License for more details.
4949+ *
4950+ * You should have received a copy of the GNU Lesser General Public License
4951+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4952+ */
4953+
4954+#include "MockScreens.h"
4955+#include "ScreenWindow.h"
4956+
4957+// qtmirserver
4958+#include <qtmir/screen.h>
4959+
4960+#include <QGuiApplication>
4961+#include <QWindow>
4962+#include <QWeakPointer>
4963+
4964+namespace {
4965+
4966+QWeakPointer<MockScreens> m_screens;
4967+
4968+class MockScreen : public qtmir::Screen
4969+{
4970+ Q_OBJECT
4971+public:
4972+ MockScreen()
4973+ {
4974+ m_sizes.append(new qtmir::ScreenMode(50, QSize(640,480)));
4975+ m_sizes.append(new qtmir::ScreenMode(60, QSize(1280,1024)));
4976+ m_sizes.append(new qtmir::ScreenMode(60, QSize(1440,900)));
4977+ m_sizes.append(new qtmir::ScreenMode(60, QSize(1920,1080)));
4978+ m_physicalSize = QSize(800,568);
4979+ }
4980+ ~MockScreen() {
4981+ qDeleteAll(m_sizes);
4982+ m_sizes.clear();
4983+ }
4984+
4985+ void connectToWindow(QWindow* w)
4986+ {
4987+ if (m_connectedWindow == w) return;
4988+
4989+ if (m_connectedWindow) {
4990+ disconnect(m_connectedWindow.data());
4991+ m_sizes.takeFirst()->deleteLater();
4992+ }
4993+
4994+ m_connectedWindow = w;
4995+
4996+ if (w) {
4997+ connect(w, &QWindow::heightChanged, this, [this](int height) {
4998+ if (height == 0 || height == m_sizes.first()->size.rheight()) {
4999+ return;
5000+ }
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches