Merge lp:~gerboland/qtmir/multimonitor into lp:qtmir

Proposed by Gerry Boland
Status: Superseded
Proposed branch: lp:~gerboland/qtmir/multimonitor
Merge into: lp:qtmir
Diff against target: 4409 lines (+2661/-540)
66 files modified
CMakeLists.txt (+1/-1)
debian/control (+2/-2)
demos/qml-demo-shell/ResizeArea.qml (+128/-0)
demos/qml-demo-shell/Shell.qml (+19/-1)
demos/qml-demo-shell/TitleBar.qml (+4/-1)
demos/qml-demo-shell/Window.qml (+4/-81)
demos/qml-demo-shell/qml-demo-shell.qml (+19/-0)
src/modules/Unity/Application/CMakeLists.txt (+0/-1)
src/modules/Unity/Application/mirbuffersgtexture.cpp (+5/-0)
src/modules/Unity/Application/mirbuffersgtexture.h (+1/-0)
src/modules/Unity/Application/mirsurface.cpp (+3/-1)
src/modules/Unity/Application/plugin.cpp (+8/-1)
src/modules/Unity/CMakeLists.txt (+1/-0)
src/modules/Unity/Screens/CMakeLists.txt (+24/-0)
src/modules/Unity/Screens/plugin.cpp (+41/-0)
src/modules/Unity/Screens/qmldir (+2/-0)
src/modules/Unity/Screens/screens.cpp (+107/-0)
src/modules/Unity/Screens/screens.h (+82/-0)
src/platforms/mirserver/CMakeLists.txt (+10/-3)
src/platforms/mirserver/cursor.cpp (+152/-0)
src/platforms/mirserver/cursor.h (+66/-0)
src/platforms/mirserver/display.cpp (+0/-44)
src/platforms/mirserver/display.h (+0/-37)
src/platforms/mirserver/logging.h (+1/-0)
src/platforms/mirserver/miropenglcontext.cpp (+25/-10)
src/platforms/mirserver/miropenglcontext.h (+1/-0)
src/platforms/mirserver/mirserver.cpp (+38/-4)
src/platforms/mirserver/mirserver.h (+8/-2)
src/platforms/mirserver/mirserverintegration.cpp (+51/-40)
src/platforms/mirserver/mirserverintegration.h (+4/-7)
src/platforms/mirserver/mirsingleton.cpp (+33/-0)
src/platforms/mirserver/mirsingleton.h (+46/-0)
src/platforms/mirserver/offscreensurface.cpp (+61/-0)
src/platforms/mirserver/offscreensurface.h (+43/-0)
src/platforms/mirserver/qmirserver.cpp (+12/-2)
src/platforms/mirserver/qmirserver.h (+3/-0)
src/platforms/mirserver/qmirserver_p.h (+2/-0)
src/platforms/mirserver/qtcompositor.cpp (+9/-34)
src/platforms/mirserver/qtcompositor.h (+13/-5)
src/platforms/mirserver/qteventfeeder.cpp (+117/-90)
src/platforms/mirserver/qteventfeeder.h (+13/-9)
src/platforms/mirserver/screen.cpp (+103/-7)
src/platforms/mirserver/screen.h (+36/-4)
src/platforms/mirserver/screencontroller.cpp (+258/-0)
src/platforms/mirserver/screencontroller.h (+96/-0)
src/platforms/mirserver/screenwindow.cpp (+71/-80)
src/platforms/mirserver/screenwindow.h (+13/-23)
src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp (+44/-0)
src/platforms/mirserver/tileddisplayconfigurationpolicy.h (+35/-0)
tests/common/fake_displayconfigurationoutput.h (+73/-0)
tests/common/gmock_fixes.h (+124/-0)
tests/common/mock_display.h (+53/-0)
tests/common/mock_display_buffer.h (+43/-0)
tests/common/mock_display_configuration.h (+35/-0)
tests/common/mock_main_loop.h (+53/-0)
tests/mirserver/CMakeLists.txt (+1/-0)
tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h (+16/-9)
tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp (+27/-16)
tests/mirserver/Screen/CMakeLists.txt (+1/-0)
tests/mirserver/Screen/screen_test.cpp (+38/-24)
tests/mirserver/ScreenController/CMakeLists.txt (+28/-0)
tests/mirserver/ScreenController/screencontroller_test.cpp (+189/-0)
tests/mirserver/ScreenController/stub_display.h (+96/-0)
tests/mirserver/ScreenController/stub_screen.h (+31/-0)
tests/mirserver/ScreenController/testable_screencontroller.h (+37/-0)
tests/modules/common/qtmir_test.h (+1/-1)
To merge this branch: bzr merge lp:~gerboland/qtmir/multimonitor
Reviewer Review Type Date Requested Status
Daniel d'Andrada (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+269906@code.launchpad.net

This proposal supersedes a proposal from 2015-07-01.

This proposal has been superseded by a proposal from 2015-09-30.

Commit message

Initial multimonitor support - react correctly to Mir DisplayConfiguration changes.

On Mir DisplayConfiguration changes, QtMir now correctly:
1. blocks Mir until it has stopped all renderers and has their GL contexts released
2. reads the new DisplayConfiguration, matches any existing ScreenWindows to new DisplayBuffers should they change (as Mir may destroy and create it on us)
3. restarts all renderers

This also solves shutdown crash issues due to raciness of mir destroying the GL context backing the shell's QWindow before its renderer had stopped.

Add Unity.Screens qml module to advertise current screen state to QML.

Description of the change

 * Are there any related MPs required for this MP to build/function as expected? Please list.
N
 * Did you perform an exploratory manual test run of your code change and any related functionality?
Y
 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

To post a comment you must log in.
Revision history for this message
Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

The QML demo is pretty lame, but I'm holding on until your demo work in the mirSurface is approved.

Also there are FIXMEs related to input, which I would prefer to address later. This MR is big enough.

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

Gah, input on second display not working

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal
Download full text (8.2 KiB)

src/modules/Unity/Screens/screens.h:

"""
public Q_SLOTS:
    void onScreenAdded(QScreen *screen);
    void onScreenRemoved(QScreen *screen);
"""

Shouldn't they be private?

--------------------------------

src/platforms/mirserver/miropenglcontext.h

"""
    EGLDisplay m_eglDisplay;
    EGLContext m_eglContext;
"""

You store them as member variables but they are still only used in the constructor.

----------------------------------

In MirServerIntegration::createPlatformWindow:

"""
    qDebug() << "createPlatformWindow" << window;
"""

A leftover.

"""
    // If Screen was not specified, just grab an unused one, if available
"""

I don't get the first "if". This method doesn't seem to handle the case where a screen *is* specified. It doesn't even seem possible.

"""
    if (!screens) {
        qDebug() << "Screens are not initialized, unable to create a new QWindow/ScreenWindow";
        return nullptr;
    }
    [...]
    if (!screen) {
        qDebug() << "No available Screens to create a new QWindow/ScreenWindow for";
        return nullptr;
    }
"""

I think these should be turned into criticals (and also use the logging category API).

"""
    qDebug() << "New" << window << "with geom" << window->geometry()
             << "is backed by a" << screen << "with geometry" << screen->geometry();
"""

You might want to keep this, but using the logging category API.

--------------------------

In MirServerIntegration::initialize()

"""
        qDebug() << "ScreenController not initialized";
"""

More qDebug. Maybe this should be a qFatal?

-----------------------------

In src/platforms/mirserver/offscreensurface.cpp

"""
#include <QDebug>
"""

You don't need that.

----------------------------

In src/platforms/mirserver/qmirserver.h:

"""
    QWeakPointer<ScreenController> screenController() const;
"""

Why return a weak pointer if all users of it do "screenController().lock()"?. So why not make it return a shared pointer already (making for cleaner code)?

------------------------------

QtEventFeeder::QtEventFeeder

I think it would be cleaner to have two constructors instead:

QtEventFeeder::QtEventFeeder(QSharedPointer<ScreenController> screenController)
    : QtEventFeeder(new QtWindowSystem(screenController))
{
}

QtEventFeeder::QtEventFeeder(QtEventFeeder::QtWindowSystemInterface *windowSystem)
{
- if (windowSystem) {
- mQtWindowSystem = windowSystem;
- } else {
- mQtWindowSystem = new QtWindowSystem;
- }
}

And remove the "= nullptr" from the signature of the second one.

--------------------------

"""
void QtEventFeeder::dispatch(MirEvent const& event)
{
- auto type = mir_event_get_type(&event);
- if (type != mir_event_type_input)
- return;
"""

Why remove it?

----------------------

"""
void QtEventFeeder::sendActiveTouchRelease(ulong timestamp, int id)
{
    [...]
+ mQtWindowSystem->handleTouchEvent(mQtWindowSystem->focusedWindow(), timestamp, mTouchDevice, touchPoints);
}
"""

You have to pass the window chosen in QtEventFeeder::dispatchTouch to validateTouches() as well, so that the function above send the event to the correct window.

----------------------------

Please update...

Read more...

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

As for testing: When I connected my monitor to the micro hdmi port of my yoga 2 laptop, qtmir detected the new display and reacted accordingly. So I saw the unity logo on a green background in the external monitor.

But when I disconnected it, nothing happened on qtmir side. Connected back again and that was when I got the screen disconnected info in qtmir, it seems. Restarted the qml mirserver and now it wouldn't detect the external monitor at all.

Rebooted the laptop.

Run the mirserver again. Connected the external monitor. All fine. Disconnected and reconnected and boom. Laptop shuts itself down.

Despite the somewhat bad results, since this is just the first step in the multi-monitor story I'm still ok with having it merged.

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

I'll be addressing your code comments soon.

On your testing, please watch the console output carefully. ScreenController::update() prints the list of displays each time Mir notifies it something changed. When you unplug, does this list get updated correctly?

Also, can you have the shell animating on both screens before you unplug?

Can you compare with mir_proving_server too?

I'm just trying to figure out if it is a QtMir bug, or a Mir one.
Thanks for the review!

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

I think I've followed all the recommendations you've made. I've only a couple of replies:

> """
> // If Screen was not specified, just grab an unused one, if available
> """
>
> I don't get the first "if". This method doesn't seem to handle the case where
> a screen *is* specified. It doesn't even seem possible.

You're right, it was terribly phrased. Have updated it to make sense now

> qDebug() << "Screens are not initialized, unable to create a new
> QWindow/ScreenWindow";
<snip>
> qDebug() << "No available Screens to create a new QWindow/ScreenWindow
>
> I think these should be turned into criticals (and also use the logging
> category API).

I did make them critical, but I always want those errors to print so didn't make them part of a category.

> In src/platforms/mirserver/qmirserver.h:
>
> """
> QWeakPointer<ScreenController> screenController() const;
> """
>
> Why return a weak pointer if all users of it do "screenController().lock()"?.
> So why not make it return a shared pointer already (making for cleaner code)?

Because the QMirServer has ownership of the ScreenController, and manages its lifetime. A ScreenController will only exist after the mir server is started, and must be deleted before mir shuts down. I can't have API users keeping it around longer than it should.

This may be exposing an API problem which can be resolved, but I didn't see any obvious solution. So this will have to do.

> src/platforms/mirserver/screencontroller.cpp
> """
> auto displayConfig = display->configuration();
> """
>
> I think you're abusing the use of "auto" here. I would prefer to know the
> class of this object.

You really want to know is it std::unique_ptr<mg::DisplayConfiguration> ? I don't, the variable name is enough for me.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

On 06/08/15 08:49, Gerry Boland wrote:
>> qDebug() << "Screens are not initialized, unable to create a new
>> > QWindow/ScreenWindow";
> <snip>
>> > qDebug() << "No available Screens to create a new QWindow/ScreenWindow
>> >
>> > I think these should be turned into criticals (and also use the logging
>> > category API).
> I did make them critical, but I always want those errors to print so didn't make them part of a category.
>

This doesn't stop you from using logging categories. You can configure
logging categories to print by severity. Eg: only from warnings and
upwards, or only criticals.

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

> On 06/08/15 08:49, Gerry Boland wrote:
> > I did make them critical, but I always want those errors to print so didn't
> make them part of a category.
> >
>
> This doesn't stop you from using logging categories. You can configure
> logging categories to print by severity. Eg: only from warnings and
> upwards, or only criticals.

I know that. But IMO this error should always print if that codepath hit, as it's an error path which I see no reason to be filter-able.

Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

"""
qtmir (0.4.6) vivid; urgency=medium

"""

nitpick: Shouldn't it be UNRELEASED instead of vivid?

Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

Good clean up! Only a couple of minor issues left:

----------------------

Please update copyright year of src/platforms/mirserver/miropenglcontext.cpp

---------------------

src/platforms/mirserver/screencontroller.cpp

It still has some qDebug() messages and commented-out code.

"""
        if (window && window->window()) { qDebug() << "HIDE" << window;
"""

"""
            //window->setVisible(false);
"""

NB: the stuff above appears in two separate locations in the file

----------------------------------

In tests/mirserver/ScreenController/screencontroller_test.cpp

"""
    ASSERT_EQ(screenController->screens().count(), 1);
"""

Google test format for assertions is as follows: ASSERT_EQ(expected, actual)

But in this file you're doing the other way around, which will make for confusing messages in case of failure.

Same goes for EXPECT_EQ() and friends.

review: Needs Fixing
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

Ah, and the qml-demo-shell changes, naturally. You probably gonna rebase this branch on top of lp:~dandrader/qtmir/mousePointer or lp:~dandrader/qtmir/mirSurface, right?

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

Ways to test:
1. on N4/N7, install the packages and reboot. Plug in external display with slimport cable - you should see part of unity8 on the external display. (unity8 crashed before)

2. on N4/N7/desktop, stop lightdm, and run the qml-demo-shell demo at root (QT_QPA_PLATFORM=mirserver qmlscene qml-demo-shell.qml). Try plugging/unplugging displays - you should see independent shells on each connected display.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

Code looks ok.

Tested on a N7 with unity8 and it works. Connecting an external monitor shows the cloned image there (albeit cut off) and disconnecting it doesn't crash unity8.

qml-demo-shell on the N7 freezes. Don't know if it's related to this MP.

Also could not test on my laptop as its flimsy micro-HDMI port seems to be broken, or preferably, my "HDMI to micro-HDMI" adaptor. :(

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

Testing this as a nested server, I have found
https://bugs.launchpad.net/mir/+bug/1491816
which needs the Mir team to investigate.

Which means this is unreliable to test as a nested server while adding/removing monitors and trying to draw on them all.

So the only things you can reliably test are
1. phone/tablet - testing a nested QtMir server (i.e. unity8) does not crash on plug/unplug of external monitor
2. desktop - running QtMir as host server - here plug/unplug of monitor will succeed.

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

Have resolved the issue I reported in the Mir bug above. I was using an API I wasn't supposed to. Now nested servers work reliably.

So in testing, the only situation where QtMir will not work reliably is:
- android, host server
mainly because of the buffer post thing.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote :

tests/mirserver/ScreenController/stub_display.h needs an "#include <chrono>"

Revision history for this message
Michael Terry (mterry) wrote :

Using just this branch on top of rc-proposed, I don't see any changes when I plug in an external monitor. Nothing crashes, but nothing changes either. And I don't see any obvious screen-list dumps in the log, like you mentioned I might see.

So I'm guessing I need another branch?

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

ScreenController test fails.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~gerboland/qtmir/multimonitor updated
381. By Gerry Boland

Merge trunk

382. By Gerry Boland

OpenGlContext - remove attempt at clean doneCurrent call, it just breaks things

383. By Gerry Boland

Disable any GL or Scenegraph persistence, ensures all GL resources are released on display config change

384. By Gerry Boland

Add reassuring debug message

385. By Gerry Boland

On scenegraph re-creation, was possible for MirBufferSGTexture to be used in the renderer without a texture being assigned. Prevent that

386. By Gerry Boland

Fix test fail

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

I declare this is ready for a final review pass. There are issues remaining, which appear to be USC/Mir-based:
https://bugs.launchpad.net/mir/+bug/1496069
But I'd prefer to land this and then work on those after.

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

Ways to test:
1. desktop - as root user:
    systemctl stop lightdm
    export QT_QPA_PLATFORM=mirserver
    qmlscene qml-demo-shell.qml
and then plug/unplug external monitor. You should see the simple shell appear on all screens.

2. phone, with unity8 up
    plug/unplug monitor, verify shell does not crash.
Depending on USC's mood, you might see unity8 on the external display, or you might not. Touch input unreliable, due to USC we think.

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

Code still looks ok and tests pass again.

Proceeding with manual tests now...

review: Approve (code & tests)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

> 2. phone, with unity8 up
> plug/unplug monitor, verify shell does not crash.
> Depending on USC's mood, you might see unity8 on the external display, or you
> might not. Touch input unreliable, due to USC we think.

Tried that on my Nexus 7.
When I plug in the external monitor, the UI on the device flashes for a split second. Then I unplug the monitor (I think it flashes again, not sure now). But then it doesn't to input anymore and eventually crashes. :(

Revision history for this message
Daniel d'Andrada (dandrader) :
review: Needs Fixing
lp:~gerboland/qtmir/multimonitor updated
387. By Gerry Boland

Merge trunk

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

> > 2. phone, with unity8 up
> > plug/unplug monitor, verify shell does not crash.
> > Depending on USC's mood, you might see unity8 on the external display, or
> you
> > might not. Touch input unreliable, due to USC we think.
>
> Tried that on my Nexus 7.
> When I plug in the external monitor, the UI on the device flashes for a split
> second. Then I unplug the monitor (I think it flashes again, not sure now).
> But then it doesn't to input anymore and eventually crashes. :(

Something to watch carefully is if unity8 crashes alone, or USC crashes which brings unity8 down with it. There are some input handling bugs in Mir/USC with multimonitor which are things we need the Mir team to fix.

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

https://bugs.launchpad.net/mir/+bug/1496069 contains the issue in Mir you may be experiencing

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

Tested with a fresh image (no multimonitor branch).

Plugging in an external monitor makes unity8 restart, and so does unplugging it.

Therefore this branch doesn't make matters worse. It just makes it crash in a different way.

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

But it still feels odd to land a branch that doesn't get us anywhere... As it still crashes, I can't be confident that it's indeed doing the right thing...

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

Running unity8 + qtmir/multimonitor on a wily laptop (tried with both mir 0.16 and mir 0.17), once I connect an external monitor it crashes.

Should it work? or is the crash expected?

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

No crashes are expected. I've just tested on Wily, and I can't reproduce a crash.

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

Not getting a crash anymore... Don't know what happened.

Since mousePointer is gonna land before this branch, I've it rebased on top of mousePointer here: lp:~dandrader/qtmir/mousePointer-multimonitor. Should make your rebase painless.

review: Approve
lp:~gerboland/qtmir/multimonitor updated
388. By Gerry Boland

Merge Daniel's kind work lp:~dandrader/qtmir/mousePointer-multimonitor

389. By Gerry Boland

Merge mousePointer again

390. By Gerry Boland

Fix conflicts but have ScreenController test failing

391. By Gerry Boland

Fix failing test, RenderTarget not derived from NativeDisplayBuffer

Unmerged revisions

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 2015-09-17 20:17:17 +0000
3+++ CMakeLists.txt 2015-09-30 13:40:22 +0000
4@@ -75,7 +75,7 @@
5 pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
6 pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
7 pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
8-pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=8)
9+pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=9)
10
11 include_directories(${APPLICATION_API_INCLUDE_DIRS})
12
13
14=== modified file 'debian/control'
15--- debian/control 2015-09-25 12:11:08 +0000
16+++ debian/control 2015-09-30 13:40:22 +0000
17@@ -23,7 +23,7 @@
18 libubuntu-app-launch2-dev,
19 libubuntu-application-api-dev (>= 2.1.0),
20 libudev-dev,
21- libunity-api-dev (>= 7.100),
22+ libunity-api-dev (>= 7.101),
23 liburl-dispatcher1-dev,
24 libxkbcommon-dev,
25 libxrender-dev,
26@@ -93,7 +93,7 @@
27 Conflicts: libqtmir,
28 libunity-mir1,
29 Provides: unity-application-impl,
30- unity-application-impl-8,
31+ unity-application-impl-9,
32 Description: Qt plugin for Unity specific Mir APIs
33 QtMir provides Qt/QML bindings for Mir features that are exposed through the
34 qtmir-desktop or qtmir-android QPA plugin such as Application management
35
36=== added file 'demos/qml-demo-shell/ResizeArea.qml'
37--- demos/qml-demo-shell/ResizeArea.qml 1970-01-01 00:00:00 +0000
38+++ demos/qml-demo-shell/ResizeArea.qml 2015-09-30 13:40:22 +0000
39@@ -0,0 +1,128 @@
40+import QtQuick 2.4
41+import Unity.Application 0.1
42+
43+MouseArea {
44+ id: root
45+
46+ // to be set from outside
47+ property Item target
48+ property real borderThickness
49+
50+ property bool leftBorder: false
51+ property bool rightBorder: false
52+ property bool topBorder: false
53+ property bool bottomBorder: false
54+
55+ property bool dragging: false
56+ property real startX
57+ property real startY
58+ property real startWidth
59+ property real startHeight
60+
61+ hoverEnabled: true
62+
63+ property string cursorName: {
64+ if (containsMouse || pressed) {
65+ if (leftBorder && !topBorder && !bottomBorder) {
66+ return "left_side";
67+ } else if (rightBorder && !topBorder && !bottomBorder) {
68+ return "right_side";
69+ } else if (topBorder && !leftBorder && !rightBorder) {
70+ return "top_side";
71+ } else if (bottomBorder && !leftBorder && !rightBorder) {
72+ return "bottom_side";
73+ } else if (leftBorder && topBorder) {
74+ return "top_left_corner";
75+ } else if (leftBorder && bottomBorder) {
76+ return "bottom_left_corner";
77+ } else if (rightBorder && topBorder) {
78+ return "top_right_corner";
79+ } else if (rightBorder && bottomBorder) {
80+ return "bottom_right_corner";
81+ } else {
82+ return "";
83+ }
84+ } else {
85+ return "";
86+ }
87+ }
88+ onCursorNameChanged: {
89+ Mir.cursorName = cursorName;
90+ }
91+
92+ function updateBorders() {
93+ leftBorder = mouseX <= borderThickness;
94+ rightBorder = mouseX >= width - borderThickness;
95+ topBorder = mouseY <= borderThickness;
96+ bottomBorder = mouseY >= height - borderThickness;
97+ }
98+
99+ onPressedChanged: {
100+ if (pressed) {
101+ var pos = mapToItem(target.parent, mouseX, mouseY);
102+ startX = pos.x;
103+ startY = pos.y;
104+ startWidth = target.width;
105+ startHeight = target.height;
106+ dragging = true;
107+ } else {
108+ dragging = false;
109+ if (containsMouse) {
110+ updateBorders();
111+ }
112+ }
113+ }
114+
115+ onEntered: {
116+ if (!pressed) {
117+ updateBorders();
118+ }
119+ }
120+
121+ onPositionChanged: {
122+ if (!pressed) {
123+ updateBorders();
124+ }
125+
126+ if (!dragging) {
127+ return;
128+ }
129+
130+ var pos = mapToItem(target.parent, mouse.x, mouse.y);
131+
132+ if (leftBorder) {
133+ if (startX + startWidth - pos.x > target.minWidth) {
134+ target.x = pos.x;
135+ target.width = startX + startWidth - target.x;
136+ startX = target.x;
137+ startWidth = target.width;
138+ }
139+
140+ } else if (rightBorder) {
141+ var deltaX = pos.x - startX;
142+ if (startWidth + deltaX >= target.minWidth) {
143+ target.width = startWidth + deltaX;
144+ } else {
145+ target.width = target.minWidth;
146+ }
147+ }
148+
149+ if (topBorder) {
150+ if (startY + startHeight - pos.y > target.minHeight) {
151+ target.y = pos.y;
152+ target.height = startY + startHeight - target.y;
153+ startY = target.y;
154+ startHeight = target.height;
155+ }
156+
157+ } else if (bottomBorder) {
158+ var deltaY = pos.y - startY;
159+ if (startHeight + deltaY >= target.minHeight) {
160+ target.height = startHeight + deltaY;
161+ } else {
162+ target.height = target.minHeight;
163+ }
164+ }
165+ }
166+}
167+
168
169=== renamed file 'demos/qml-demo-shell/qml-demo-shell.qml' => 'demos/qml-demo-shell/Shell.qml'
170--- demos/qml-demo-shell/qml-demo-shell.qml 2015-08-31 09:51:28 +0000
171+++ demos/qml-demo-shell/Shell.qml 2015-09-30 13:40:22 +0000
172@@ -1,4 +1,4 @@
173-import QtQuick 2.0
174+import QtQuick 2.4
175 import Unity.Application 0.1
176
177 Rectangle {
178@@ -88,6 +88,7 @@
179 }
180
181 Rectangle {
182+ id: resizeButton
183 width: 90
184 height: 40
185 color: "blue"
186@@ -103,6 +104,23 @@
187 }
188 }
189
190+ Rectangle {
191+ width: 40
192+ height: 40
193+ color: "green"
194+ anchors { right: resizeButton.left; bottom: parent.bottom }
195+ Text {
196+ anchors.centerIn: parent
197+ text: "⟳"
198+ color: "white"
199+ font.pixelSize: 35
200+ }
201+ MouseArea {
202+ anchors.fill: parent
203+ onClicked: { root.rotation += 180; }
204+ }
205+ }
206+
207 Component {
208 id: windowStretchComponent
209 Window {
210
211=== modified file 'demos/qml-demo-shell/TitleBar.qml'
212--- demos/qml-demo-shell/TitleBar.qml 2015-08-24 12:43:01 +0000
213+++ demos/qml-demo-shell/TitleBar.qml 2015-09-30 13:40:22 +0000
214@@ -1,4 +1,5 @@
215-import QtQuick 2.0
216+import QtQuick 2.4
217+import Unity.Application 0.1
218
219 Rectangle {
220 id: root
221@@ -21,8 +22,10 @@
222 distanceX = pos.x;
223 distanceY = pos.y;
224 dragging = true;
225+ Mir.cursorName = "grabbing";
226 } else {
227 dragging = false;
228+ Mir.cursorName = "";
229 }
230 }
231 onMouseXChanged: {
232
233=== modified file 'demos/qml-demo-shell/Window.qml'
234--- demos/qml-demo-shell/Window.qml 2015-08-31 09:51:28 +0000
235+++ demos/qml-demo-shell/Window.qml 2015-09-30 13:40:22 +0000
236@@ -58,87 +58,10 @@
237 }
238 ]
239
240-
241- MouseArea {
242- anchors.fill: parent
243-
244- property real startX
245- property real startY
246- property real startWidth
247- property real startHeight
248- property bool leftBorder
249- property bool rightBorder
250- property bool topBorder
251- property bool bottomBorder
252- property bool dragging
253- onPressedChanged: {
254- if (pressed) {
255- var pos = mapToItem(root.parent, mouseX, mouseY);
256- startX = pos.x;
257- startY = pos.y;
258- startWidth = width;
259- startHeight = height;
260- leftBorder = mouseX > 0 && mouseX < root.borderThickness;
261- rightBorder = mouseX > (root.width - root.borderThickness) && mouseX < root.width;
262- topBorder = mouseY > 0 && mouseY < root.borderThickness;
263- bottomBorder = mouseY > (root.height - root.borderThickness) && mouseY < root.height;
264- dragging = true;
265- } else {
266- dragging = false;
267- }
268- }
269-
270- onMouseXChanged: {
271- if (!pressed || !dragging) {
272- return;
273- }
274-
275- var pos = mapToItem(root.parent, mouseX, mouseY);
276-
277- if (leftBorder) {
278-
279- if (startX + startWidth - pos.x > root.minWidth) {
280- root.x = pos.x;
281- root.width = startX + startWidth - root.x;
282- startX = root.x;
283- startWidth = root.width;
284- }
285-
286- } else if (rightBorder) {
287- var deltaX = pos.x - startX;
288- if (startWidth + deltaX >= root.minWidth) {
289- root.width = startWidth + deltaX;
290- } else {
291- root.width = root.minWidth;
292- }
293- }
294- }
295-
296- onMouseYChanged: {
297- if (!pressed || !dragging) {
298- return;
299- }
300-
301- var pos = mapToItem(root.parent, mouseX, mouseY);
302-
303- if (topBorder) {
304-
305- if (startY + startHeight - pos.y > root.minHeight) {
306- root.y = pos.y;
307- root.height = startY + startHeight - root.y;
308- startY = root.y;
309- startHeight = root.height;
310- }
311-
312- } else if (bottomBorder) {
313- var deltaY = pos.y - startY;
314- if (startHeight + deltaY >= root.minHeight) {
315- root.height = startHeight + deltaY;
316- } else {
317- root.height = root.minHeight;
318- }
319- }
320- }
321+ ResizeArea {
322+ anchors.fill: root
323+ borderThickness: root.borderThickness
324+ target: root
325 }
326
327 TitleBar {
328
329=== added file 'demos/qml-demo-shell/qml-demo-shell.qml'
330--- demos/qml-demo-shell/qml-demo-shell.qml 1970-01-01 00:00:00 +0000
331+++ demos/qml-demo-shell/qml-demo-shell.qml 2015-09-30 13:40:22 +0000
332@@ -0,0 +1,19 @@
333+import QtQuick 2.3
334+import QtQuick.Window 2.2 as QQW
335+import Unity.Screens 0.1
336+
337+Instantiator {
338+ id: root
339+
340+ property var screens: Screens{}
341+
342+ model: screens
343+ QQW.Window {
344+ id: window
345+ visible: true
346+ Shell{ anchors.fill: parent }
347+ Component.onCompleted: {
348+ print("HEY", screen, screen.geometry, outputType, Screens.HDMIA)
349+ }
350+ }
351+}
352
353=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
354--- src/modules/Unity/Application/CMakeLists.txt 2015-09-17 16:17:17 +0000
355+++ src/modules/Unity/Application/CMakeLists.txt 2015-09-30 13:40:22 +0000
356@@ -92,4 +92,3 @@
357 # install
358 add_qml_plugin(Unity.Application 0.1 Unity/Application TARGETS unityapplicationplugin)
359 install(FILES com.canonical.qtmir.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
360-
361
362=== modified file 'src/modules/Unity/Application/mirbuffersgtexture.cpp'
363--- src/modules/Unity/Application/mirbuffersgtexture.cpp 2015-09-17 16:46:37 +0000
364+++ src/modules/Unity/Application/mirbuffersgtexture.cpp 2015-09-30 13:40:22 +0000
365@@ -59,6 +59,11 @@
366 m_width = size.width.as_int();
367 }
368
369+bool MirBufferSGTexture::hasBuffer() const
370+{
371+ return !!m_mirBuffer;
372+}
373+
374 int MirBufferSGTexture::textureId() const
375 {
376 return m_textureId;
377
378=== modified file 'src/modules/Unity/Application/mirbuffersgtexture.h'
379--- src/modules/Unity/Application/mirbuffersgtexture.h 2015-08-31 09:51:28 +0000
380+++ src/modules/Unity/Application/mirbuffersgtexture.h 2015-09-30 13:40:22 +0000
381@@ -34,6 +34,7 @@
382
383 void setBuffer(std::shared_ptr<mir::graphics::Buffer> buffer);
384 void freeBuffer();
385+ bool hasBuffer() const;
386
387 int textureId() const override;
388 QSize textureSize() const override;
389
390=== modified file 'src/modules/Unity/Application/mirsurface.cpp'
391--- src/modules/Unity/Application/mirsurface.cpp 2015-09-18 16:33:06 +0000
392+++ src/modules/Unity/Application/mirsurface.cpp 2015-09-30 13:40:22 +0000
393@@ -323,7 +323,9 @@
394 const void* const userId = (void*)123;
395 auto renderables = m_surface->generate_renderables(userId);
396
397- if (m_surface->buffers_ready_for_compositor(userId) > 0 && renderables.size() > 0) {
398+ if (renderables.size() > 0 &&
399+ (m_surface->buffers_ready_for_compositor(userId) > 0 || !texture->hasBuffer())
400+ ) {
401 // Avoid holding two buffers for the compositor at the same time. Thus free the current
402 // before acquiring the next
403 texture->freeBuffer();
404
405=== modified file 'src/modules/Unity/Application/plugin.cpp'
406--- src/modules/Unity/Application/plugin.cpp 2015-08-31 09:51:28 +0000
407+++ src/modules/Unity/Application/plugin.cpp 2015-09-30 13:40:22 +0000
408@@ -27,6 +27,9 @@
409 #include "sessionmanager.h"
410 #include "ubuntukeyboardinfo.h"
411
412+// platforms/mirserver
413+#include <mirsingleton.h>
414+
415 // qtmir
416 #include "logging.h"
417
418@@ -64,6 +67,10 @@
419 }
420 return UbuntuKeyboardInfo::instance();
421 }
422+
423+QObject* mirSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) {
424+ return qtmir::Mir::instance();
425+}
426 } // anonymous namespace
427
428 class UnityApplicationPlugin : public QQmlExtensionPlugin {
429@@ -102,7 +109,7 @@
430 uri, 0, 1, "Session", "Session can't be instantiated from QML");
431 qmlRegisterSingletonType<qtmir::UbuntuKeyboardInfo>(
432 uri, 0, 1, "UbuntuKeyboardInfo", ubuntuKeyboardInfoSingleton);
433- qmlRegisterUncreatableType<Mir>(uri, 0, 1, "Mir", "Mir provides enum values, it can't be instantiated");
434+ qmlRegisterSingletonType<qtmir::Mir>(uri, 0, 1, "Mir", mirSingleton);
435 }
436
437 virtual void initializeEngine(QQmlEngine *engine, const char *uri)
438
439=== modified file 'src/modules/Unity/CMakeLists.txt'
440--- src/modules/Unity/CMakeLists.txt 2014-09-22 18:06:58 +0000
441+++ src/modules/Unity/CMakeLists.txt 2015-09-30 13:40:22 +0000
442@@ -1,1 +1,2 @@
443 add_subdirectory(Application)
444+add_subdirectory(Screens)
445
446=== added directory 'src/modules/Unity/Screens'
447=== added file 'src/modules/Unity/Screens/CMakeLists.txt'
448--- src/modules/Unity/Screens/CMakeLists.txt 1970-01-01 00:00:00 +0000
449+++ src/modules/Unity/Screens/CMakeLists.txt 2015-09-30 13:40:22 +0000
450@@ -0,0 +1,24 @@
451+include_directories(
452+ ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
453+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
454+ ${MIRSERVER_INCLUDE_DIRS}
455+ )
456+
457+set(SCREENSPLUGIN_SRC
458+ plugin.cpp
459+ screens.cpp
460+ )
461+
462+add_library(unityscreensplugin SHARED
463+ ${SCREENSPLUGIN_SRC}
464+)
465+
466+target_link_libraries(
467+ unityscreensplugin
468+
469+ Qt5::Gui
470+ Qt5::Qml
471+)
472+
473+# install
474+add_qml_plugin(Unity.Screens 0.1 Unity/Screens TARGETS unityscreensplugin)
475
476=== added file 'src/modules/Unity/Screens/plugin.cpp'
477--- src/modules/Unity/Screens/plugin.cpp 1970-01-01 00:00:00 +0000
478+++ src/modules/Unity/Screens/plugin.cpp 2015-09-30 13:40:22 +0000
479@@ -0,0 +1,41 @@
480+/*
481+ * Copyright (C) 2015 Canonical, Ltd.
482+ *
483+ * This program is free software: you can redistribute it and/or modify it under
484+ * the terms of the GNU Lesser General Public License version 3, as published by
485+ * the Free Software Foundation.
486+ *
487+ * This program is distributed in the hope that it will be useful, but WITHOUT
488+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
489+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
490+ * Lesser General Public License for more details.
491+ *
492+ * You should have received a copy of the GNU Lesser General Public License
493+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
494+ */
495+
496+// Qt
497+#include <QQmlExtensionPlugin>
498+#include <QtQml/qqml.h>
499+#include <QScreen>
500+
501+// local
502+#include "screens.h"
503+
504+using namespace qtmir;
505+
506+class UnityScreensPlugin : public QQmlExtensionPlugin {
507+ Q_OBJECT
508+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
509+
510+ virtual void registerTypes(const char* uri)
511+ {
512+ Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Screens"));
513+
514+ qRegisterMetaType<QScreen*>("QScreen*");
515+
516+ qmlRegisterType<qtmir::Screens>(uri, 0, 1, "Screens");
517+ }
518+};
519+
520+#include "plugin.moc"
521
522=== added file 'src/modules/Unity/Screens/qmldir'
523--- src/modules/Unity/Screens/qmldir 1970-01-01 00:00:00 +0000
524+++ src/modules/Unity/Screens/qmldir 2015-09-30 13:40:22 +0000
525@@ -0,0 +1,2 @@
526+module Unity.Screens
527+plugin unityscreensplugin
528
529=== added file 'src/modules/Unity/Screens/screens.cpp'
530--- src/modules/Unity/Screens/screens.cpp 1970-01-01 00:00:00 +0000
531+++ src/modules/Unity/Screens/screens.cpp 2015-09-30 13:40:22 +0000
532@@ -0,0 +1,107 @@
533+/*
534+ * Copyright (C) 2015 Canonical, Ltd.
535+ *
536+ * This program is free software: you can redistribute it and/or modify it under
537+ * the terms of the GNU Lesser General Public License version 3, as published by
538+ * the Free Software Foundation.
539+ *
540+ * This program is distributed in the hope that it will be useful, but WITHOUT
541+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
542+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
543+ * Lesser General Public License for more details.
544+ *
545+ * You should have received a copy of the GNU Lesser General Public License
546+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
547+ */
548+
549+#include "screens.h"
550+
551+// mirserver
552+#include "screen.h"
553+
554+// Qt
555+#include <QGuiApplication>
556+#include <QScreen>
557+
558+Q_DECLARE_METATYPE(QScreen*)
559+
560+namespace qtmir {
561+
562+Screens::Screens(QObject *parent) :
563+ QAbstractListModel(parent)
564+{
565+ auto app = static_cast<QGuiApplication *>(QGuiApplication::instance());
566+ if (!app) {
567+ return;
568+ }
569+ connect(app, &QGuiApplication::screenAdded, this, &Screens::onScreenAdded);
570+ connect(app, &QGuiApplication::screenRemoved, this, &Screens::onScreenRemoved);
571+
572+ m_screenList = QGuiApplication::screens();
573+}
574+
575+QHash<int, QByteArray> Screens::roleNames() const
576+{
577+ QHash<int, QByteArray> roles;
578+ roles[ScreenRole] = "screen";
579+ roles[OutputTypeRole] = "outputType";
580+ return roles;
581+}
582+
583+QVariant Screens::data(const QModelIndex &index, int role) const
584+{
585+ if (!index.isValid() || index.row() >= m_screenList.size()) {
586+ return QVariant();
587+ }
588+
589+ switch(role) {
590+ case ScreenRole:
591+ return QVariant::fromValue(m_screenList.at(index.row()));
592+ case OutputTypeRole:
593+ auto screen = static_cast<Screen*>(m_screenList.at(index.row())->handle());
594+ if (screen) {
595+ return QVariant(static_cast<OutputTypes>(screen->outputType())); //FIXME: cheeky
596+ } else
597+ return OutputTypes::Unknown;
598+ }
599+
600+ return QVariant();
601+}
602+
603+int Screens::rowCount(const QModelIndex &) const
604+{
605+ return count();
606+}
607+
608+int Screens::count() const
609+{
610+ return m_screenList.size();
611+}
612+
613+void Screens::onScreenAdded(QScreen *screen)
614+{
615+ if (m_screenList.contains(screen))
616+ return;
617+
618+ beginInsertRows(QModelIndex(), count(), count());
619+ m_screenList.push_back(screen);
620+ endInsertRows();
621+ Q_EMIT screenAdded(screen);
622+ Q_EMIT countChanged();
623+}
624+
625+void Screens::onScreenRemoved(QScreen *screen)
626+{
627+ int index = m_screenList.indexOf(screen);
628+ if (index < 0)
629+ return;
630+
631+ beginRemoveRows(QModelIndex(), index, index);
632+ m_screenList.removeAt(index);
633+ endRemoveRows();
634+ Q_EMIT screenRemoved(screen);
635+ Q_EMIT countChanged();
636+}
637+
638+
639+} // namespace qtmir
640
641=== added file 'src/modules/Unity/Screens/screens.h'
642--- src/modules/Unity/Screens/screens.h 1970-01-01 00:00:00 +0000
643+++ src/modules/Unity/Screens/screens.h 2015-09-30 13:40:22 +0000
644@@ -0,0 +1,82 @@
645+/*
646+ * Copyright (C) 2015 Canonical, Ltd.
647+ *
648+ * This program is free software: you can redistribute it and/or modify it under
649+ * the terms of the GNU Lesser General Public License version 3, as published by
650+ * the Free Software Foundation.
651+ *
652+ * This program is distributed in the hope that it will be useful, but WITHOUT
653+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
654+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
655+ * Lesser General Public License for more details.
656+ *
657+ * You should have received a copy of the GNU Lesser General Public License
658+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
659+ */
660+
661+#ifndef SCREENS_H
662+#define SCREENS_H
663+
664+#include <QAbstractListModel>
665+
666+class QScreen;
667+
668+namespace qtmir {
669+
670+class Screens : public QAbstractListModel
671+{
672+ Q_OBJECT
673+ Q_ENUMS(OutputTypes)
674+
675+ Q_PROPERTY(int count READ count NOTIFY countChanged)
676+
677+public:
678+ enum ItemRoles {
679+ ScreenRole = Qt::UserRole + 1,
680+ OutputTypeRole
681+ };
682+
683+ enum OutputTypes {
684+ Unknown,
685+ VGA,
686+ DVII,
687+ DVID,
688+ DVIA,
689+ Composite,
690+ SVideo,
691+ LVDS,
692+ Component,
693+ NinePinDIN,
694+ DisplayPort,
695+ HDMIA,
696+ HDMIB,
697+ TV,
698+ EDP
699+ };
700+
701+ explicit Screens(QObject *parent = 0);
702+ virtual ~Screens() noexcept = default;
703+
704+ /* QAbstractItemModel */
705+ QHash<int, QByteArray> roleNames() const override;
706+ QVariant data(const QModelIndex &index, int role = ScreenRole) const override;
707+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
708+
709+ int count() const;
710+
711+Q_SIGNALS:
712+ void countChanged();
713+ void screenAdded(QScreen *screen);
714+ void screenRemoved(QScreen *screen);
715+
716+private Q_SLOTS:
717+ void onScreenAdded(QScreen *screen);
718+ void onScreenRemoved(QScreen *screen);
719+
720+private:
721+ QList<QScreen *> m_screenList;
722+};
723+
724+} // namespace qtmir
725+
726+#endif // SCREENS_H
727
728=== modified file 'src/platforms/mirserver/CMakeLists.txt'
729--- src/platforms/mirserver/CMakeLists.txt 2015-08-11 19:25:04 +0000
730+++ src/platforms/mirserver/CMakeLists.txt 2015-09-30 13:40:22 +0000
731@@ -30,6 +30,7 @@
732 ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS}
733 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
734 ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS}
735+ ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
736
737 ${APPLICATION_API_INCLUDE_DIRS}
738 )
739@@ -41,7 +42,9 @@
740
741 set(MIRSERVER_QPA_PLUGIN_SRC
742 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
743+ cursor.cpp
744 mirwindowmanager.cpp
745+ mirsingleton.cpp
746 qteventfeeder.cpp
747 plugin.cpp
748 qmirserver.cpp
749@@ -52,17 +55,21 @@
750 promptsessionlistener.cpp
751 mirserver.cpp
752 mirserverstatuslistener.cpp
753- display.cpp
754 screen.cpp
755- displaywindow.cpp
756+ screencontroller.cpp
757+ screenwindow.cpp
758 mirserverintegration.cpp
759 miropenglcontext.cpp
760 nativeinterface.cpp
761+ offscreensurface.cpp
762 qtcompositor.cpp
763 services.cpp
764 ubuntutheme.cpp
765 clipboard.cpp
766+ tileddisplayconfigurationpolicy.cpp
767 tracepoints.c
768+# We need to run moc on these headers
769+ ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
770 )
771
772 add_library(qpa-mirserver SHARED
773@@ -82,7 +89,7 @@
774 ${QT5PLATFORM_SUPPORT_LDFLAGS}
775 # TODO Qt5Platform support LDFLAGS dont provide actual required ldflags...
776 # I found these were needed...perhaps there is some way to query qmake/qconfig?
777- -lfreetype
778+ -lfreetype
779 ${GIO_LDFLAGS}
780 ${FONTCONFIG_LDFLAGS}
781
782
783=== added file 'src/platforms/mirserver/cursor.cpp'
784--- src/platforms/mirserver/cursor.cpp 1970-01-01 00:00:00 +0000
785+++ src/platforms/mirserver/cursor.cpp 2015-09-30 13:40:22 +0000
786@@ -0,0 +1,152 @@
787+/*
788+ * Copyright (C) 2015 Canonical, Ltd.
789+ *
790+ * This program is free software: you can redistribute it and/or modify it under
791+ * the terms of the GNU Lesser General Public License version 3, as published by
792+ * the Free Software Foundation.
793+ *
794+ * This program is distributed in the hope that it will be useful, but WITHOUT
795+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
796+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
797+ * Lesser General Public License for more details.
798+ *
799+ * You should have received a copy of the GNU Lesser General Public License
800+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
801+ *
802+ */
803+
804+#include "cursor.h"
805+#include "logging.h"
806+
807+#include "mirsingleton.h"
808+
809+// Unity API
810+#include <unity/shell/application/MirMousePointerInterface.h>
811+
812+using namespace qtmir;
813+
814+Cursor::Cursor()
815+{
816+ m_shapeToCursorName[Qt::ArrowCursor] = "left_ptr";
817+ m_shapeToCursorName[Qt::UpArrowCursor] = "up_arrow";
818+ m_shapeToCursorName[Qt::CrossCursor] = "cross";
819+ m_shapeToCursorName[Qt::WaitCursor] = "watch";
820+ m_shapeToCursorName[Qt::IBeamCursor] = "xterm";
821+ m_shapeToCursorName[Qt::SizeVerCursor] = "size_ver";
822+ m_shapeToCursorName[Qt::SizeHorCursor] = "size_hor";
823+ m_shapeToCursorName[Qt::SizeBDiagCursor] = "size_bdiag";
824+ m_shapeToCursorName[Qt::SizeFDiagCursor] = "size_fdiag";
825+ m_shapeToCursorName[Qt::SizeAllCursor] = "size_all";
826+ m_shapeToCursorName[Qt::BlankCursor] = "blank";
827+ m_shapeToCursorName[Qt::SplitVCursor] = "split_v";
828+ m_shapeToCursorName[Qt::SplitHCursor] = "split_h";
829+ m_shapeToCursorName[Qt::PointingHandCursor] = "pointing_hand";
830+ m_shapeToCursorName[Qt::ForbiddenCursor] = "forbidden";
831+ m_shapeToCursorName[Qt::WhatsThisCursor] = "whats_this";
832+ m_shapeToCursorName[Qt::BusyCursor] = "left_ptr_watch";
833+ m_shapeToCursorName[Qt::OpenHandCursor] = "openhand";
834+ m_shapeToCursorName[Qt::ClosedHandCursor] = "closedhand";
835+ m_shapeToCursorName[Qt::DragCopyCursor] = "copy";
836+ m_shapeToCursorName[Qt::DragMoveCursor] = "move";
837+ m_shapeToCursorName[Qt::DragLinkCursor] = "link";
838+
839+ connect(Mir::instance(), &Mir::cursorNameChanged, this, &Cursor::setMirCursorName);
840+}
841+
842+void Cursor::changeCursor(QCursor *windowCursor, QWindow * /*window*/)
843+{
844+ if (m_mousePointer.isNull()) {
845+ return;
846+ }
847+
848+ if (windowCursor) {
849+ m_qtCursorName = m_shapeToCursorName.value(windowCursor->shape(), QString("left_ptr"));
850+ } else {
851+ m_qtCursorName.clear();
852+ }
853+
854+ updateMousePointerCursorName();
855+}
856+
857+void Cursor::setMirCursorName(const QString &mirCursorName)
858+{
859+ m_mirCursorName = mirCursorName;
860+ updateMousePointerCursorName();
861+}
862+
863+void Cursor::setMousePointer(MirMousePointerInterface *mousePointer)
864+{
865+ QMutexLocker locker(&m_mutex);
866+
867+ if (mousePointer && !m_mousePointer.isNull()) {
868+ qFatal("QPA mirserver: Only one MousePointer per screen is allowed!");
869+ }
870+
871+ m_mousePointer = mousePointer;
872+ updateMousePointerCursorName();
873+}
874+
875+bool Cursor::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButton buttons,
876+ Qt::KeyboardModifiers modifiers)
877+{
878+ QMutexLocker locker(&m_mutex);
879+
880+ if (!m_mousePointer || !m_mousePointer->isVisible()) {
881+ return false;
882+ }
883+
884+ // Must not be called directly as we're most likely not in Qt's GUI (main) thread.
885+ bool ok = QMetaObject::invokeMethod(m_mousePointer, "handleMouseEvent", Qt::AutoConnection,
886+ Q_ARG(ulong, timestamp),
887+ Q_ARG(QPointF, movement),
888+ Q_ARG(Qt::MouseButton, buttons),
889+ Q_ARG(Qt::KeyboardModifiers, modifiers));
890+
891+ if (!ok) {
892+ qCWarning(QTMIR_MIR_INPUT) << "Failed to invoke MousePointer::handleMouseEvent";
893+ }
894+
895+ return ok;
896+}
897+
898+void Cursor::setPos(const QPoint &pos)
899+{
900+ if (!m_mousePointer) {
901+ QPlatformCursor::setPos(pos);
902+ return;
903+ }
904+
905+ QPointF movement;
906+ QPointF mouseScenePos = m_mousePointer->mapToItem(nullptr, QPointF(0, 0));
907+
908+ movement.setX(pos.x() - mouseScenePos.x());
909+ movement.setY(pos.y() - mouseScenePos.y());
910+
911+ m_mousePointer->handleMouseEvent(0 /*timestamp*/, movement, Qt::NoButton, Qt::NoModifier);
912+}
913+
914+QPoint Cursor::pos() const
915+{
916+ if (m_mousePointer) {
917+ return m_mousePointer->mapToItem(nullptr, QPointF(0, 0)).toPoint();
918+ } else {
919+ return QPlatformCursor::pos();
920+ }
921+}
922+
923+void Cursor::updateMousePointerCursorName()
924+{
925+ if (!m_mousePointer) {
926+ return;
927+ }
928+
929+ if (m_mirCursorName.isEmpty()) {
930+ if (m_qtCursorName.isEmpty()) {
931+ m_mousePointer->setCursorName("left_ptr");
932+ } else {
933+ m_mousePointer->setCursorName(m_qtCursorName);
934+ }
935+ } else {
936+ m_mousePointer->setCursorName(m_mirCursorName);
937+ }
938+}
939
940=== added file 'src/platforms/mirserver/cursor.h'
941--- src/platforms/mirserver/cursor.h 1970-01-01 00:00:00 +0000
942+++ src/platforms/mirserver/cursor.h 2015-09-30 13:40:22 +0000
943@@ -0,0 +1,66 @@
944+/*
945+ * Copyright (C) 2015 Canonical, Ltd.
946+ *
947+ * This program is free software: you can redistribute it and/or modify it under
948+ * the terms of the GNU Lesser General Public License version 3, as published by
949+ * the Free Software Foundation.
950+ *
951+ * This program is distributed in the hope that it will be useful, but WITHOUT
952+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
953+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
954+ * Lesser General Public License for more details.
955+ *
956+ * You should have received a copy of the GNU Lesser General Public License
957+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
958+ *
959+ */
960+
961+#ifndef QTMIR_CURSOR_H
962+#define QTMIR_CURSOR_H
963+
964+#include <QMutex>
965+#include <QPointer>
966+
967+// Unity API
968+#include <unity/shell/application/MirPlatformCursor.h>
969+
970+namespace qtmir {
971+
972+class Cursor : public MirPlatformCursor
973+{
974+public:
975+ Cursor();
976+
977+ // Called form Mir input thread
978+ bool handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButton buttons,
979+ Qt::KeyboardModifiers modifiers);
980+
981+ ////
982+ // MirPlatformCursor
983+
984+ // Called from Qt's GUI thread
985+ void setMousePointer(MirMousePointerInterface *mousePointer) override;
986+
987+ ////
988+ // QPlatformCursor
989+
990+ void changeCursor(QCursor *windowCursor, QWindow *window) override;
991+
992+ void setPos(const QPoint &pos) override;
993+ QPoint pos() const override;
994+
995+private Q_SLOTS:
996+ void setMirCursorName(const QString &mirCursorName);
997+
998+private:
999+ void updateMousePointerCursorName();
1000+ QMutex m_mutex;
1001+ QPointer<MirMousePointerInterface> m_mousePointer;
1002+ QMap<int,QString> m_shapeToCursorName;
1003+ QString m_qtCursorName;
1004+ QString m_mirCursorName;
1005+};
1006+
1007+} // namespace qtmir
1008+
1009+#endif // QTMIR_CURSOR_H
1010
1011=== removed file 'src/platforms/mirserver/display.cpp'
1012--- src/platforms/mirserver/display.cpp 2015-08-11 12:08:32 +0000
1013+++ src/platforms/mirserver/display.cpp 1970-01-01 00:00:00 +0000
1014@@ -1,44 +0,0 @@
1015-/*
1016- * Copyright (C) 2013-2015 Canonical, Ltd.
1017- *
1018- * This program is free software: you can redistribute it and/or modify it under
1019- * the terms of the GNU Lesser General Public License version 3, as published by
1020- * the Free Software Foundation.
1021- *
1022- * This program is distributed in the hope that it will be useful, but WITHOUT
1023- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1024- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1025- * Lesser General Public License for more details.
1026- *
1027- * You should have received a copy of the GNU Lesser General Public License
1028- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1029- */
1030-
1031-#include "display.h"
1032-
1033-#include "screen.h"
1034-#include "mirserver.h"
1035-
1036-#include <mir/graphics/display.h>
1037-#include <mir/graphics/display_configuration.h>
1038-
1039-namespace mg = mir::graphics;
1040-
1041-// TODO: Listen for display changes and update the list accordingly
1042-
1043-Display::Display(const std::shared_ptr<mir::graphics::DisplayConfiguration> &displayConfig)
1044-{
1045- displayConfig->for_each_output([this](mg::DisplayConfigurationOutput const& output) {
1046- if (output.used) {
1047- auto screen = new Screen(output);
1048- m_screens.push_back(screen);
1049- }
1050- });
1051-}
1052-
1053-Display::~Display()
1054-{
1055- for (auto screen : m_screens)
1056- delete screen;
1057- m_screens.clear();
1058-}
1059
1060=== removed file 'src/platforms/mirserver/display.h'
1061--- src/platforms/mirserver/display.h 2015-08-11 12:08:32 +0000
1062+++ src/platforms/mirserver/display.h 1970-01-01 00:00:00 +0000
1063@@ -1,37 +0,0 @@
1064-/*
1065- * Copyright (C) 2013-2015 Canonical, Ltd.
1066- *
1067- * This program is free software: you can redistribute it and/or modify it under
1068- * the terms of the GNU Lesser General Public License version 3, as published by
1069- * the Free Software Foundation.
1070- *
1071- * This program is distributed in the hope that it will be useful, but WITHOUT
1072- * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1073- * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1074- * Lesser General Public License for more details.
1075- *
1076- * You should have received a copy of the GNU Lesser General Public License
1077- * along with this program. If not, see <http://www.gnu.org/licenses/>.
1078- */
1079-
1080-#ifndef DISPLAY_H
1081-#define DISPLAY_H
1082-
1083-#include <qpa/qplatformscreen.h>
1084-#include <memory>
1085-
1086-namespace mir { namespace graphics { class DisplayConfiguration; }}
1087-
1088-class Display
1089-{
1090-public:
1091- Display(const std::shared_ptr<mir::graphics::DisplayConfiguration> &displayConfig);
1092- virtual ~Display();
1093-
1094- QList<QPlatformScreen *> screens() const { return m_screens; }
1095-
1096-private:
1097- QList<QPlatformScreen *> m_screens;
1098-};
1099-
1100-#endif // DISPLAY_H
1101
1102=== modified file 'src/platforms/mirserver/logging.h'
1103--- src/platforms/mirserver/logging.h 2014-10-01 18:42:26 +0000
1104+++ src/platforms/mirserver/logging.h 2015-09-30 13:40:22 +0000
1105@@ -25,5 +25,6 @@
1106 Q_DECLARE_LOGGING_CATEGORY(QTMIR_SENSOR_MESSAGES)
1107 Q_DECLARE_LOGGING_CATEGORY(QTMIR_MIR_INPUT)
1108 Q_DECLARE_LOGGING_CATEGORY(QTMIR_CLIPBOARD)
1109+Q_DECLARE_LOGGING_CATEGORY(QTMIR_SCREENS)
1110
1111 #endif // UBUNTU_APPLICATION_PLUGIN_LOGGING_H
1112
1113=== modified file 'src/platforms/mirserver/miropenglcontext.cpp'
1114--- src/platforms/mirserver/miropenglcontext.cpp 2015-08-11 12:08:32 +0000
1115+++ src/platforms/mirserver/miropenglcontext.cpp 2015-09-30 13:40:22 +0000
1116@@ -16,12 +16,14 @@
1117
1118 #include "miropenglcontext.h"
1119
1120-#include "displaywindow.h"
1121+#include "offscreensurface.h"
1122+#include "mirglconfig.h"
1123 #include "mirserver.h"
1124-#include "mirglconfig.h"
1125+#include "screenwindow.h"
1126
1127 #include <QDebug>
1128
1129+#include <QOpenGLFramebufferObject>
1130 #include <QSurfaceFormat>
1131 #include <QtPlatformSupport/private/qeglconvenience_p.h>
1132
1133@@ -38,7 +40,7 @@
1134 : m_logger(new QOpenGLDebugLogger(this))
1135 #endif
1136 {
1137- std::shared_ptr<mir::graphics::Display> display = server->the_display();
1138+ auto display = server->the_display();
1139
1140 // create a temporary GL context to fetch the EGL display and config, so Qt can determine the surface format
1141 std::unique_ptr<mir::graphics::GLContext> mirContext = display->create_gl_context();
1142@@ -106,17 +108,30 @@
1143
1144 void MirOpenGLContext::swapBuffers(QPlatformSurface *surface)
1145 {
1146- // ultimately calls Mir's DisplayBuffer::post_update()
1147- DisplayWindow *displayBuffer = static_cast<DisplayWindow*>(surface);
1148- displayBuffer->swapBuffers(); //blocks for vsync
1149+ if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
1150+ // NOOP
1151+ } else {
1152+ // ultimately calls Mir's DisplayBuffer::post_update()
1153+ ScreenWindow *screenWindow = static_cast<ScreenWindow*>(surface);
1154+ screenWindow->swapBuffers(); //blocks for vsync
1155+ }
1156 }
1157
1158 bool MirOpenGLContext::makeCurrent(QPlatformSurface *surface)
1159 {
1160+ if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
1161+ auto offscreen = static_cast<OffscreenSurface *>(surface);
1162+ if (!offscreen->buffer()) {
1163+ auto buffer = new QOpenGLFramebufferObject(surface->surface()->size());
1164+ offscreen->setBuffer(buffer);
1165+ }
1166+ return offscreen->buffer()->bind();
1167+ }
1168+
1169 // ultimately calls Mir's DisplayBuffer::make_current()
1170- DisplayWindow *displayBuffer = static_cast<DisplayWindow*>(surface);
1171- if (displayBuffer) {
1172- displayBuffer->makeCurrent();
1173+ ScreenWindow *screenWindow = static_cast<ScreenWindow*>(surface);
1174+ if (screenWindow) {
1175+ screenWindow->makeCurrent();
1176
1177 #ifndef QT_NO_DEBUG
1178 if (!m_logger->isLogging() && m_logger->initialize()) {
1179@@ -133,7 +148,7 @@
1180
1181 void MirOpenGLContext::doneCurrent()
1182 {
1183- // could call Mir's DisplayBuffer::release_current(), but for what DisplayBuffer?
1184+ // FIXME: create a temporary GL context just to release? Would be better to get existing one.
1185 }
1186
1187 QFunctionPointer MirOpenGLContext::getProcAddress(const QByteArray &procName)
1188
1189=== modified file 'src/platforms/mirserver/miropenglcontext.h'
1190--- src/platforms/mirserver/miropenglcontext.h 2015-08-11 12:08:32 +0000
1191+++ src/platforms/mirserver/miropenglcontext.h 2015-09-30 13:40:22 +0000
1192@@ -23,6 +23,7 @@
1193 #include <QOpenGLDebugLogger>
1194 #endif
1195
1196+
1197 class MirServer;
1198
1199 class MirOpenGLContext : public QObject, public QPlatformOpenGLContext
1200
1201=== modified file 'src/platforms/mirserver/mirserver.cpp'
1202--- src/platforms/mirserver/mirserver.cpp 2015-08-11 12:08:32 +0000
1203+++ src/platforms/mirserver/mirserver.cpp 2015-09-30 13:40:22 +0000
1204@@ -23,15 +23,25 @@
1205 #include "mirglconfig.h"
1206 #include "mirserverstatuslistener.h"
1207 #include "promptsessionlistener.h"
1208+#include "screencontroller.h"
1209 #include "sessionlistener.h"
1210 #include "sessionauthorizer.h"
1211 #include "qtcompositor.h"
1212 #include "qteventfeeder.h"
1213+#include "tileddisplayconfigurationpolicy.h"
1214 #include "logging.h"
1215
1216+// std
1217+#include <memory>
1218+
1219 // egl
1220+#define MESA_EGL_NO_X11_HEADERS
1221 #include <EGL/egl.h>
1222
1223+// mir
1224+#include <mir/graphics/cursor.h>
1225+
1226+namespace mg = mir::graphics;
1227 namespace mo = mir::options;
1228 namespace msh = mir::shell;
1229 namespace ms = mir::scene;
1230@@ -45,8 +55,10 @@
1231
1232 Q_LOGGING_CATEGORY(QTMIR_MIR_MESSAGES, "qtmir.mir")
1233
1234-MirServer::MirServer(int argc, char const* argv[], QObject* parent)
1235+MirServer::MirServer(int argc, char const* argv[],
1236+ const QSharedPointer<ScreenController> &screenController, QObject* parent)
1237 : QObject(parent)
1238+ , m_screenController(screenController)
1239 {
1240 set_command_line_handler(&ignore_unparsed_arguments);
1241 set_command_line(argc, argv);
1242@@ -71,9 +83,9 @@
1243 return std::make_shared<QtCompositor>();
1244 });
1245
1246- override_the_input_dispatcher([]
1247+ override_the_input_dispatcher([&screenController]
1248 {
1249- return std::make_shared<QtEventFeeder>();
1250+ return std::make_shared<QtEventFeeder>(screenController);
1251 });
1252
1253 override_the_gl_config([]
1254@@ -92,17 +104,39 @@
1255 return std::make_shared<MirWindowManager>(the_shell_display_layout());
1256 });
1257
1258- set_terminator([&](int)
1259+ wrap_display_configuration_policy(
1260+ [](const std::shared_ptr<mg::DisplayConfigurationPolicy> &wrapped)
1261+ -> std::shared_ptr<mg::DisplayConfigurationPolicy>
1262+ {
1263+ return std::make_shared<TiledDisplayConfigurationPolicy>(wrapped);
1264+ });
1265+
1266+ set_terminator([](int)
1267 {
1268 qDebug() << "Signal caught by Mir, stopping Mir server..";
1269 QCoreApplication::quit();
1270 });
1271
1272+ add_init_callback([this, &screenController] {
1273+ screenController->init(the_display(), the_compositor());
1274+ });
1275+
1276 apply_settings();
1277
1278+ // We will draw our own cursor.
1279+ add_init_callback([this](){ the_cursor()->hide(); });
1280+
1281 qCDebug(QTMIR_MIR_MESSAGES) << "MirServer created";
1282 }
1283
1284+// Override default implementation to ensure we terminate the ScreenController first.
1285+// Code path followed when Qt tries to shutdown the server.
1286+void MirServer::stop()
1287+{
1288+ m_screenController->terminate();
1289+ mir::Server::stop();
1290+}
1291+
1292
1293 /************************************ Shell side ************************************/
1294
1295
1296=== modified file 'src/platforms/mirserver/mirserver.h'
1297--- src/platforms/mirserver/mirserver.h 2015-08-11 12:08:32 +0000
1298+++ src/platforms/mirserver/mirserver.h 2015-09-30 13:40:22 +0000
1299@@ -18,6 +18,7 @@
1300 #define MIRSERVER_H
1301
1302 #include <QObject>
1303+#include <QSharedPointer>
1304 #include <mir/server.h>
1305
1306 class QtEventFeeder;
1307@@ -25,6 +26,7 @@
1308 class SessionAuthorizer;
1309 using MirShell = mir::shell::Shell;
1310 class PromptSessionListener;
1311+class ScreenController;
1312
1313 // We use virtual inheritance of mir::Server to facilitate derived classes (e.g. testing)
1314 // calling initialization functions before MirServer is constructed.
1315@@ -38,12 +40,12 @@
1316 Q_PROPERTY(PromptSessionListener* promptSessionListener READ promptSessionListener CONSTANT)
1317
1318 public:
1319- MirServer(int argc, char const* argv[], QObject* parent = 0);
1320+ MirServer(int argc, char const* argv[], const QSharedPointer<ScreenController> &, QObject* parent = 0);
1321 ~MirServer() = default;
1322
1323 /* mir specific */
1324 using mir::Server::run;
1325- using mir::Server::stop;
1326+ using mir::Server::the_compositor;
1327 using mir::Server::the_display;
1328 using mir::Server::the_gl_config;
1329 using mir::Server::the_main_loop;
1330@@ -52,6 +54,8 @@
1331 using mir::Server::the_session_authorizer;
1332 using mir::Server::the_session_listener;
1333
1334+ void stop();
1335+
1336 /* qt specific */
1337 // getters
1338 SessionAuthorizer *sessionAuthorizer();
1339@@ -60,7 +64,9 @@
1340 MirShell *shell();
1341
1342 private:
1343+ std::weak_ptr<MirShell> m_shell;
1344 std::shared_ptr<QtEventFeeder> m_qtEventFeeder;
1345+ const QSharedPointer<ScreenController> m_screenController;
1346 };
1347
1348 #endif // MIRSERVER_H
1349
1350=== modified file 'src/platforms/mirserver/mirserverintegration.cpp'
1351--- src/platforms/mirserver/mirserverintegration.cpp 2015-08-11 12:08:32 +0000
1352+++ src/platforms/mirserver/mirserverintegration.cpp 2015-09-30 13:40:22 +0000
1353@@ -26,7 +26,8 @@
1354 #include <qpa/qplatforminputcontextfactory_p.h>
1355 #include <qpa/qwindowsysteminterface.h>
1356
1357-#include <QCoreApplication>
1358+#include <QGuiApplication>
1359+#include <QStringList>
1360 #include <QOpenGLContext>
1361 #include <QDebug>
1362
1363@@ -36,13 +37,16 @@
1364
1365 // local
1366 #include "clipboard.h"
1367-#include "display.h"
1368-#include "displaywindow.h"
1369 #include "miropenglcontext.h"
1370 #include "nativeinterface.h"
1371+#include "offscreensurface.h"
1372 #include "qmirserver.h"
1373+#include "screen.h"
1374+#include "screencontroller.h"
1375+#include "screenwindow.h"
1376 #include "services.h"
1377 #include "ubuntutheme.h"
1378+#include "logging.h"
1379
1380 namespace mg = mir::graphics;
1381 using qtmir::Clipboard;
1382@@ -52,7 +56,6 @@
1383 , m_fontDb(new QGenericUnixFontDatabase())
1384 , m_services(new Services)
1385 , m_mirServer(new QMirServer(QCoreApplication::arguments()))
1386- , m_display(nullptr)
1387 , m_nativeInterface(nullptr)
1388 , m_clipboard(new Clipboard)
1389 {
1390@@ -72,12 +75,14 @@
1391 QCoreApplication::instance(), &QCoreApplication::quit);
1392
1393 m_inputContext = QPlatformInputContextFactory::create();
1394+
1395+ // Default Qt behaviour doesn't match a shell's intentions, so customize:
1396+ qGuiApp->setQuitOnLastWindowClosed(false);
1397 }
1398
1399 MirServerIntegration::~MirServerIntegration()
1400 {
1401 delete m_nativeInterface;
1402- delete m_display;
1403 }
1404
1405 bool MirServerIntegration::hasCapability(QPlatformIntegration::Capability cap) const
1406@@ -87,7 +92,7 @@
1407 case OpenGL: return true;
1408 case ThreadedOpenGL: return true;
1409 case BufferQueueingOpenGL: return true;
1410- case MultipleWindows: return false; // multi-monitor support
1411+ case MultipleWindows: return true; // multi-monitor support
1412 case WindowManagement: return false; // platform has no WM, as this implements the WM!
1413 case NonFullScreenWindows: return false;
1414 default: return QPlatformIntegration::hasCapability(cap);
1415@@ -98,44 +103,38 @@
1416 {
1417 QWindowSystemInterface::flushWindowSystemEvents();
1418
1419- DisplayWindow* displayWindow = nullptr;
1420-
1421- auto const mirServer = m_mirServer->mirServer().lock();
1422- mg::DisplayBuffer* first_buffer{nullptr};
1423- mg::DisplaySyncGroup* first_group{nullptr};
1424- if (mirServer) {
1425- mirServer->the_display()->for_each_display_sync_group([&](mg::DisplaySyncGroup &group) {
1426- if (!first_group) {
1427- first_group = &group;
1428- }
1429- group.for_each_display_buffer([&](mg::DisplayBuffer &buffer) {
1430- if (!first_buffer) {
1431- first_buffer = &buffer;
1432- }
1433- });
1434- });
1435- }
1436-
1437- // FIXME(gerry) this will go very bad for >1 display buffer
1438- if (first_group && first_buffer)
1439- displayWindow = new DisplayWindow(window, first_group, first_buffer);
1440-
1441- if (!displayWindow)
1442- return nullptr;
1443-
1444- //displayWindow->requestActivateWindow();
1445- return displayWindow;
1446+ // FIXME: QWindow can be created specifying a destination QScreen. For now we
1447+ // will ignore it and just associate any unused Screen, if available.
1448+ auto screens = m_mirServer->screenController().lock();
1449+ if (!screens) {
1450+ qCritical("Screens are not initialized, unable to create a new QWindow/ScreenWindow");
1451+ return nullptr;
1452+ }
1453+ Screen *screen = screens->getUnusedScreen();
1454+ if (!screen) {
1455+ qCritical("No available Screens to create a new QWindow/ScreenWindow for");
1456+ return nullptr;
1457+ }
1458+ QScreen *qscreen = screen->screen();
1459+ window->setScreen(qscreen);
1460+
1461+ auto platformWindow = new ScreenWindow(window);
1462+ if (screens->compositing()) {
1463+ platformWindow->setExposed(true);
1464+ }
1465+
1466+ qCDebug(QTMIR_SCREENS) << "New" << window << "with geom" << window->geometry()
1467+ << "is backed by a" << screen << "with geometry" << screen->geometry();
1468+ return platformWindow;
1469 }
1470
1471-QPlatformBackingStore *MirServerIntegration::createPlatformBackingStore(QWindow *window) const
1472+QPlatformBackingStore *MirServerIntegration::createPlatformBackingStore(QWindow */*window*/) const
1473 {
1474- qDebug() << "createPlatformBackingStore" << window;
1475 return nullptr;
1476 }
1477
1478 QPlatformOpenGLContext *MirServerIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
1479 {
1480- qDebug() << "createPlatformOpenGLContext" << context;
1481 return new MirOpenGLContext(m_mirServer->mirServer(), context->format());
1482 }
1483
1484@@ -151,12 +150,18 @@
1485 exit(2);
1486 }
1487
1488- m_display = new Display(m_mirServer->mirServer().data()->the_display()->configuration());
1489+ auto screens = m_mirServer->screenController().lock();
1490+ if (!screens) {
1491+ qFatal("ScreenController not initialized");
1492+ }
1493+ QObject::connect(screens.data(), &ScreenController::screenAdded,
1494+ [this](Screen *screen) { this->screenAdded(screen); });
1495+ Q_FOREACH(auto screen, screens->screens()) {
1496+ screenAdded(screen);
1497+ }
1498+
1499 m_nativeInterface = new NativeInterface(m_mirServer->mirServer());
1500
1501- for (QPlatformScreen *screen : m_display->screens())
1502- screenAdded(screen);
1503-
1504 m_clipboard->setupDBusService();
1505 }
1506
1507@@ -195,3 +200,9 @@
1508 {
1509 return m_clipboard.data();
1510 }
1511+
1512+QPlatformOffscreenSurface *MirServerIntegration::createPlatformOffscreenSurface(
1513+ QOffscreenSurface *surface) const
1514+{
1515+ return new OffscreenSurface(surface);
1516+}
1517
1518=== modified file 'src/platforms/mirserver/mirserverintegration.h'
1519--- src/platforms/mirserver/mirserverintegration.h 2015-08-11 12:08:32 +0000
1520+++ src/platforms/mirserver/mirserverintegration.h 2015-09-30 13:40:22 +0000
1521@@ -19,13 +19,9 @@
1522
1523 // qt
1524 #include <qpa/qplatformintegration.h>
1525-
1526-// local
1527-#include "mirserver.h"
1528-
1529-class Display;
1530+#include <QScopedPointer>
1531+
1532 class NativeInterface;
1533-class MirServer;
1534 class QMirServer;
1535
1536 namespace qtmir {
1537@@ -60,6 +56,8 @@
1538
1539 QPlatformNativeInterface *nativeInterface() const override;
1540
1541+ QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
1542+
1543 private:
1544 QScopedPointer<QPlatformAccessibility> m_accessibility;
1545 QScopedPointer<QPlatformFontDatabase> m_fontDb;
1546@@ -67,7 +65,6 @@
1547
1548 QScopedPointer<QMirServer> m_mirServer;
1549
1550- Display *m_display;
1551 NativeInterface *m_nativeInterface;
1552 QPlatformInputContext* m_inputContext;
1553 QScopedPointer<qtmir::Clipboard> m_clipboard;
1554
1555=== added file 'src/platforms/mirserver/mirsingleton.cpp'
1556--- src/platforms/mirserver/mirsingleton.cpp 1970-01-01 00:00:00 +0000
1557+++ src/platforms/mirserver/mirsingleton.cpp 2015-09-30 13:40:22 +0000
1558@@ -0,0 +1,33 @@
1559+#include "mirsingleton.h"
1560+
1561+qtmir::Mir *qtmir::Mir::m_instance = nullptr;
1562+
1563+qtmir::Mir::Mir()
1564+{
1565+}
1566+
1567+qtmir::Mir::~Mir()
1568+{
1569+ m_instance = nullptr;
1570+}
1571+
1572+qtmir::Mir *qtmir::Mir::instance()
1573+{
1574+ if (!m_instance) {
1575+ m_instance = new qtmir::Mir;
1576+ }
1577+ return m_instance;
1578+}
1579+
1580+void qtmir::Mir::setCursorName(const QString &cursorName)
1581+{
1582+ if (m_cursorName != cursorName) {
1583+ m_cursorName = cursorName;
1584+ Q_EMIT cursorNameChanged(m_cursorName);
1585+ }
1586+}
1587+
1588+QString qtmir::Mir::cursorName() const
1589+{
1590+ return m_cursorName;
1591+}
1592
1593=== added file 'src/platforms/mirserver/mirsingleton.h'
1594--- src/platforms/mirserver/mirsingleton.h 1970-01-01 00:00:00 +0000
1595+++ src/platforms/mirserver/mirsingleton.h 2015-09-30 13:40:22 +0000
1596@@ -0,0 +1,46 @@
1597+/*
1598+ * Copyright (C) 2015 Canonical, Ltd.
1599+ *
1600+ * This program is free software: you can redistribute it and/or modify it under
1601+ * the terms of the GNU Lesser General Public License version 3, as published by
1602+ * the Free Software Foundation.
1603+ *
1604+ * This program is distributed in the hope that it will be useful, but WITHOUT
1605+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1606+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1607+ * Lesser General Public License for more details.
1608+ *
1609+ * You should have received a copy of the GNU Lesser General Public License
1610+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1611+ */
1612+
1613+#ifndef QTMIR_MIRSINGLETON_H
1614+#define QTMIR_MIRSINGLETON_H
1615+
1616+// unity-api
1617+#include <unity/shell/application/Mir.h>
1618+
1619+namespace qtmir {
1620+
1621+class Mir : public ::Mir
1622+{
1623+ Q_OBJECT
1624+public:
1625+ virtual ~Mir();
1626+
1627+ static Mir *instance();
1628+
1629+ void setCursorName(const QString &cursorName) override;
1630+ QString cursorName() const override;
1631+
1632+private:
1633+ Mir();
1634+ Q_DISABLE_COPY(Mir)
1635+
1636+ QString m_cursorName;
1637+ static qtmir::Mir *m_instance;
1638+};
1639+
1640+} // namespace qtmir
1641+
1642+#endif // QTMIR_MIRSINGLETON_H
1643
1644=== added file 'src/platforms/mirserver/offscreensurface.cpp'
1645--- src/platforms/mirserver/offscreensurface.cpp 1970-01-01 00:00:00 +0000
1646+++ src/platforms/mirserver/offscreensurface.cpp 2015-09-30 13:40:22 +0000
1647@@ -0,0 +1,61 @@
1648+/*
1649+ * Copyright (C) 2015 Canonical, Ltd.
1650+ *
1651+ * This program is free software: you can redistribute it and/or modify it under
1652+ * the terms of the GNU Lesser General Public License version 3, as published by
1653+ * the Free Software Foundation.
1654+ *
1655+ * This program is distributed in the hope that it will be useful, but WITHOUT
1656+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1657+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1658+ * Lesser General Public License for more details.
1659+ *
1660+ * You should have received a copy of the GNU Lesser General Public License
1661+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1662+ */
1663+
1664+#include "offscreensurface.h"
1665+
1666+#include "mirserver.h"
1667+
1668+// Mir
1669+#include <mir/graphics/display.h>
1670+#include <mir/graphics/gl_context.h>
1671+
1672+//Qt
1673+#include <QOffscreenSurface>
1674+#include <QOpenGLFramebufferObject>
1675+#include <QSurfaceFormat>
1676+#include <QtPlatformSupport/private/qeglconvenience_p.h>
1677+
1678+namespace mg = mir::graphics;
1679+
1680+OffscreenSurface::OffscreenSurface(QOffscreenSurface *offscreenSurface)
1681+ : QPlatformOffscreenSurface(offscreenSurface)
1682+ , m_buffer(nullptr)
1683+ , m_format(offscreenSurface->requestedFormat())
1684+{
1685+}
1686+
1687+QSurfaceFormat OffscreenSurface::format() const
1688+{
1689+ return m_format;
1690+}
1691+
1692+bool OffscreenSurface::isValid() const
1693+{
1694+ if (m_buffer) {
1695+ return m_buffer->isValid();
1696+ }
1697+ return false;
1698+}
1699+
1700+QOpenGLFramebufferObject* OffscreenSurface::buffer() const
1701+{
1702+ return m_buffer;
1703+}
1704+
1705+void OffscreenSurface::setBuffer(QOpenGLFramebufferObject *buffer)
1706+{
1707+ m_buffer = buffer;
1708+}
1709
1710=== added file 'src/platforms/mirserver/offscreensurface.h'
1711--- src/platforms/mirserver/offscreensurface.h 1970-01-01 00:00:00 +0000
1712+++ src/platforms/mirserver/offscreensurface.h 2015-09-30 13:40:22 +0000
1713@@ -0,0 +1,43 @@
1714+/*
1715+ * Copyright (C) 2015 Canonical, Ltd.
1716+ *
1717+ * This program is free software: you can redistribute it and/or modify it under
1718+ * the terms of the GNU Lesser General Public License version 3, as published by
1719+ * the Free Software Foundation.
1720+ *
1721+ * This program is distributed in the hope that it will be useful, but WITHOUT
1722+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
1723+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1724+ * Lesser General Public License for more details.
1725+ *
1726+ * You should have received a copy of the GNU Lesser General Public License
1727+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1728+ */
1729+
1730+#ifndef OFFSCREENSURFACE_H
1731+#define OFFSCREENSURFACE_H
1732+
1733+#include <qpa/qplatformoffscreensurface.h>
1734+#include <QSurfaceFormat>
1735+#include <QSharedPointer>
1736+
1737+class MirServer;
1738+class QOpenGLFramebufferObject;
1739+
1740+class OffscreenSurface : public QPlatformOffscreenSurface
1741+{
1742+public:
1743+ OffscreenSurface(QOffscreenSurface *offscreenSurface);
1744+
1745+ QSurfaceFormat format() const override;
1746+ bool isValid() const override;
1747+
1748+ QOpenGLFramebufferObject* buffer() const;
1749+ void setBuffer(QOpenGLFramebufferObject *buffer);
1750+
1751+private:
1752+ QOpenGLFramebufferObject *m_buffer;
1753+ QSurfaceFormat m_format;
1754+};
1755+
1756+#endif // OFFSCREENSURFACE_H
1757
1758=== modified file 'src/platforms/mirserver/qmirserver.cpp'
1759--- src/platforms/mirserver/qmirserver.cpp 2015-05-19 15:36:17 +0000
1760+++ src/platforms/mirserver/qmirserver.cpp 2015-09-30 13:40:22 +0000
1761@@ -23,7 +23,8 @@
1762 #include "mirserver.h"
1763 #include "qmirserver.h"
1764 #include "qmirserver_p.h"
1765-
1766+#include "screencontroller.h"
1767+#include "screen.h"
1768
1769 QMirServer::QMirServer(const QStringList &arguments, QObject *parent)
1770 : QObject(parent)
1771@@ -40,7 +41,9 @@
1772 }
1773 argv[argc] = '\0';
1774
1775- d->server = QSharedPointer<MirServer>(new MirServer(argc, const_cast<const char**>(argv)));
1776+ d->screenController = QSharedPointer<ScreenController>(new ScreenController());
1777+
1778+ d->server = QSharedPointer<MirServer>(new MirServer(argc, const_cast<const char**>(argv), d->screenController));
1779
1780 d->serverThread = new MirServerThread(d->server);
1781
1782@@ -63,6 +66,7 @@
1783 qCritical() << "ERROR: QMirServer - Mir failed to start";
1784 return false;
1785 }
1786+ d->screenController->update();
1787
1788 Q_EMIT started();
1789 return true;
1790@@ -93,3 +97,9 @@
1791 Q_D(const QMirServer);
1792 return d->server.toWeakRef();
1793 }
1794+
1795+QWeakPointer<ScreenController> QMirServer::screenController() const
1796+{
1797+ Q_D(const QMirServer);
1798+ return d->screenController;
1799+}
1800
1801=== modified file 'src/platforms/mirserver/qmirserver.h'
1802--- src/platforms/mirserver/qmirserver.h 2015-05-18 20:39:09 +0000
1803+++ src/platforms/mirserver/qmirserver.h 2015-09-30 13:40:22 +0000
1804@@ -23,6 +23,7 @@
1805
1806 class QMirServerPrivate;
1807 class MirServer;
1808+class ScreenController;
1809
1810 class QMirServer: public QObject
1811 {
1812@@ -38,6 +39,8 @@
1813
1814 QWeakPointer<MirServer> mirServer() const;
1815
1816+ QWeakPointer<ScreenController> screenController() const;
1817+
1818 Q_SIGNALS:
1819 void started();
1820 void stopped();
1821
1822=== modified file 'src/platforms/mirserver/qmirserver_p.h'
1823--- src/platforms/mirserver/qmirserver_p.h 2015-05-18 18:30:33 +0000
1824+++ src/platforms/mirserver/qmirserver_p.h 2015-09-30 13:40:22 +0000
1825@@ -27,6 +27,7 @@
1826
1827 // local
1828 #include "mirserver.h"
1829+#include "screencontroller.h"
1830
1831 class QMirServer;
1832 class MirServerThread;
1833@@ -34,6 +35,7 @@
1834 struct QMirServerPrivate
1835 {
1836 QSharedPointer<MirServer> server;
1837+ QSharedPointer<ScreenController> screenController;
1838 MirServerThread *serverThread;
1839 };
1840
1841
1842=== modified file 'src/platforms/mirserver/qtcompositor.cpp'
1843--- src/platforms/mirserver/qtcompositor.cpp 2015-08-11 12:08:32 +0000
1844+++ src/platforms/mirserver/qtcompositor.cpp 2015-09-30 13:40:22 +0000
1845@@ -15,44 +15,19 @@
1846 */
1847
1848 #include "qtcompositor.h"
1849-#include "displaywindow.h"
1850-
1851-#include <QGuiApplication>
1852-#include <QWindow>
1853-
1854-#include <QDebug>
1855-
1856-QtCompositor::QtCompositor()
1857-{
1858-
1859-}
1860-
1861+#include "logging.h"
1862+
1863+// Lives in a Mir thread
1864 void QtCompositor::start()
1865 {
1866- // (Re)Start Qt's render thread by setting all its windows to exposed
1867- setAllWindowsExposed(true);
1868+ qCDebug(QTMIR_SCREENS) << "QtCompositor::start";
1869+
1870+ Q_EMIT starting(); // blocks
1871 }
1872
1873 void QtCompositor::stop()
1874 {
1875- // Stop Qt's render threads by setting all its windows it obscured
1876- setAllWindowsExposed(false);
1877-}
1878-
1879-void QtCompositor::setAllWindowsExposed(const bool exposed)
1880-{
1881- qDebug() << "QtCompositor::setAllWindowsExposed" << exposed;
1882- QList<QWindow *> windowList = QGuiApplication::allWindows();
1883-
1884- // manipulate Qt object's indirectly via posted events as we're not in Qt's GUI thread
1885- auto iterator = windowList.constBegin();
1886- while (iterator != windowList.constEnd()) {
1887- QWindow *window = *iterator;
1888- DisplayWindow *displayWindow = static_cast<DisplayWindow*>(window->handle());
1889- if (displayWindow) {
1890- QCoreApplication::postEvent(displayWindow,
1891- new QEvent( (exposed) ? QEvent::Show : QEvent::Hide));
1892- }
1893- iterator++;
1894- }
1895+ qCDebug(QTMIR_SCREENS) << "QtCompositor::stop";
1896+
1897+ Q_EMIT stopping(); // blocks
1898 }
1899
1900=== modified file 'src/platforms/mirserver/qtcompositor.h'
1901--- src/platforms/mirserver/qtcompositor.h 2015-08-11 12:08:32 +0000
1902+++ src/platforms/mirserver/qtcompositor.h 2015-09-30 13:40:22 +0000
1903@@ -17,18 +17,26 @@
1904 #ifndef QTCOMPOSITOR_H
1905 #define QTCOMPOSITOR_H
1906
1907-#include "mir/compositor/compositor.h"
1908-
1909-class QtCompositor : public mir::compositor::Compositor
1910+#include <mir/compositor/compositor.h>
1911+
1912+// Qt
1913+#include <QObject>
1914+
1915+class QtCompositor : public QObject, public mir::compositor::Compositor
1916 {
1917+ Q_OBJECT
1918 public:
1919- QtCompositor();
1920+ QtCompositor() = default;
1921+ virtual ~QtCompositor() noexcept = default;
1922
1923 void start();
1924 void stop();
1925
1926+Q_SIGNALS:
1927+ void starting();
1928+ void stopping();
1929+
1930 private:
1931- void setAllWindowsExposed(const bool exposed);
1932 };
1933
1934 #endif // QTCOMPOSITOR_H
1935
1936=== modified file 'src/platforms/mirserver/qteventfeeder.cpp'
1937--- src/platforms/mirserver/qteventfeeder.cpp 2015-08-27 16:10:20 +0000
1938+++ src/platforms/mirserver/qteventfeeder.cpp 2015-09-30 13:40:22 +0000
1939@@ -15,7 +15,10 @@
1940 */
1941
1942 #include "qteventfeeder.h"
1943+#include "cursor.h"
1944 #include "logging.h"
1945+#include "screen.h" // NEEDED?
1946+#include "screencontroller.h"
1947
1948 #include <qpa/qplatforminputcontext.h>
1949 #include <qpa/qplatformintegration.h>
1950@@ -365,20 +368,29 @@
1951
1952 namespace {
1953
1954-class QtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {
1955-
1956- bool hasTargetWindow() override
1957- {
1958- if (mTopLevelWindow.isNull() && !QGuiApplication::topLevelWindows().isEmpty()) {
1959- mTopLevelWindow = QGuiApplication::topLevelWindows().first();
1960- }
1961- return !mTopLevelWindow.isNull();
1962- }
1963-
1964- QRect targetWindowGeometry() override
1965- {
1966- Q_ASSERT(!mTopLevelWindow.isNull());
1967- return mTopLevelWindow->geometry();
1968+class QtWindowSystem : public QtEventFeeder::QtWindowSystemInterface
1969+{
1970+public:
1971+ QtWindowSystem()
1972+ {
1973+ // because we're using QMetaObject::invoke with arguments of those types
1974+ qRegisterMetaType<Qt::KeyboardModifiers>("Qt::KeyboardModifiers");
1975+ qRegisterMetaType<Qt::MouseButton>("Qt::MouseButton");
1976+ }
1977+
1978+ void setScreenController(const QSharedPointer<ScreenController> &sc) override
1979+ {
1980+ m_screenController = sc;
1981+ }
1982+
1983+ virtual QWindow* focusedWindow() override
1984+ {
1985+ return QGuiApplication::focusWindow();
1986+ }
1987+
1988+ QWindow* getWindowForTouchPoint(const QPoint &point) override //FIXME: not efficient, not updating focused window
1989+ {
1990+ return m_screenController->getWindowForPoint(point);
1991 }
1992
1993 void registerTouchDevice(QTouchDevice *device) override
1994@@ -386,47 +398,56 @@
1995 QWindowSystemInterface::registerTouchDevice(device);
1996 }
1997
1998- void handleExtendedKeyEvent(ulong timestamp, QEvent::Type type, int key,
1999+ void handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key,
2000 Qt::KeyboardModifiers modifiers,
2001 quint32 nativeScanCode, quint32 nativeVirtualKey,
2002 quint32 nativeModifiers,
2003 const QString& text, bool autorep, ushort count) override
2004 {
2005- Q_ASSERT(!mTopLevelWindow.isNull());
2006- QWindowSystemInterface::handleExtendedKeyEvent(mTopLevelWindow.data(), timestamp, type, key, modifiers,
2007+ QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, type, key, modifiers,
2008 nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
2009 }
2010
2011- void handleTouchEvent(ulong timestamp, QTouchDevice *device,
2012+ void handleTouchEvent(QWindow *window, ulong timestamp, QTouchDevice *device,
2013 const QList<struct QWindowSystemInterface::TouchPoint> &points, Qt::KeyboardModifiers mods) override
2014 {
2015- Q_ASSERT(!mTopLevelWindow.isNull());
2016- QWindowSystemInterface::handleTouchEvent(mTopLevelWindow.data(), timestamp, device, points, mods);
2017- }
2018+ QWindowSystemInterface::handleTouchEvent(window, timestamp, device, points, mods);
2019+ }
2020
2021- void handleMouseEvent(ulong timestamp, QPointF point, Qt::MouseButton buttons, Qt::KeyboardModifiers modifiers) override
2022+ void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButton buttons,
2023+ Qt::KeyboardModifiers modifiers) override
2024 {
2025- Q_ASSERT(!mTopLevelWindow.isNull());
2026- QWindowSystemInterface::handleMouseEvent(mTopLevelWindow.data(), timestamp, point, point, // local and global point are the same
2027- buttons, modifiers);
2028+ // Send to the first screen that handles the mouse event
2029+ // TODO: Have a mechanism to tell which screen currently has the logical mouse pointer
2030+ // (because they all might have their own separate graphical mouse pointer item)
2031+ // This will probably come once we implement the feature of having the mouse pointer
2032+ // crossing adjacent screens.
2033+
2034+ QList<Screen*> screens = m_screenController->screens();
2035+ bool eventHandled = false;
2036+ int i = 0;
2037+ while (i < screens.count() && !eventHandled) {
2038+ auto platformCursor = static_cast<qtmir::Cursor*>(screens[i]->cursor());
2039+ eventHandled = platformCursor->handleMouseEvent(timestamp, movement, buttons, modifiers);
2040+ ++i;
2041+ }
2042 }
2043
2044-
2045 private:
2046- QPointer<QWindow> mTopLevelWindow;
2047+ QSharedPointer<ScreenController> m_screenController;
2048 };
2049
2050 } // anonymous namespace
2051
2052-
2053-QtEventFeeder::QtEventFeeder(QtEventFeeder::QtWindowSystemInterface *windowSystem)
2054-{
2055- if (windowSystem) {
2056- mQtWindowSystem = windowSystem;
2057- } else {
2058- mQtWindowSystem = new QtWindowSystem;
2059- }
2060-
2061+QtEventFeeder::QtEventFeeder(const QSharedPointer<ScreenController> &screenController)
2062+ : QtEventFeeder(screenController, new QtWindowSystem)
2063+{
2064+}
2065+
2066+QtEventFeeder::QtEventFeeder(const QSharedPointer<ScreenController> &screenController,
2067+ QtEventFeeder::QtWindowSystemInterface *windowSystem)
2068+ : mQtWindowSystem(windowSystem)
2069+{
2070 // Initialize touch device. Hardcoded just like in qtubuntu
2071 // TODO: Create them from info gathered from Mir and store things like device id and source
2072 // in a QTouchDevice-derived class created by us. So that we can properly assemble back
2073@@ -436,6 +457,7 @@
2074 mTouchDevice->setCapabilities(
2075 QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |
2076 QTouchDevice::NormalizedPosition);
2077+ mQtWindowSystem->setScreenController(screenController);
2078 mQtWindowSystem->registerTouchDevice(mTouchDevice);
2079 }
2080
2081@@ -449,6 +471,7 @@
2082 auto type = mir_event_get_type(&event);
2083 if (type != mir_event_type_input)
2084 return false;
2085+
2086 auto iev = mir_event_get_input_event(&event);
2087
2088 switch (mir_input_event_get_type(iev)) {
2089@@ -508,9 +531,6 @@
2090
2091 void QtEventFeeder::dispatchPointer(MirInputEvent const* ev)
2092 {
2093- if (!mQtWindowSystem->hasTargetWindow())
2094- return;
2095-
2096 auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
2097
2098 auto pev = mir_input_event_get_pointer_event(ev);
2099@@ -519,18 +539,14 @@
2100 auto modifiers = getQtModifiersFromMir(mir_pointer_event_modifiers(pev));
2101 auto buttons = getQtMouseButtonsfromMirPointerEvent(pev);
2102
2103- auto local_point = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),
2104- mir_pointer_event_axis_value(pev, mir_pointer_axis_y));
2105+ auto movement = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_x),
2106+ mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_y));
2107
2108- mQtWindowSystem->handleMouseEvent(timestamp, local_point,
2109- buttons, modifiers);
2110+ mQtWindowSystem->handleMouseEvent(timestamp, movement, buttons, modifiers);
2111 }
2112
2113 void QtEventFeeder::dispatchKey(MirInputEvent const* event)
2114 {
2115- if (!mQtWindowSystem->hasTargetWindow())
2116- return;
2117-
2118 ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
2119
2120 auto kev = mir_input_event_get_keyboard_event(event);
2121@@ -577,7 +593,8 @@
2122 }
2123 }
2124
2125- mQtWindowSystem->handleExtendedKeyEvent(timestamp, keyType, keyCode, modifiers,
2126+ mQtWindowSystem->handleExtendedKeyEvent(mQtWindowSystem->focusedWindow(),
2127+ timestamp, keyType, keyCode, modifiers,
2128 mir_keyboard_event_scan_code(kev),
2129 mir_keyboard_event_key_code(kev),
2130 mir_keyboard_event_modifiers(kev), text, is_auto_rep);
2131@@ -585,59 +602,69 @@
2132
2133 void QtEventFeeder::dispatchTouch(MirInputEvent const* event)
2134 {
2135- if (!mQtWindowSystem->hasTargetWindow())
2136- return;
2137-
2138 auto tev = mir_input_event_get_touch_event(event);
2139 qCDebug(QTMIR_MIR_INPUT) << "Received" << qPrintable(mirTouchEventToString(tev));
2140
2141 // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That
2142 // needs to be fixed as soon as the compat input lib adds query support.
2143 const float kMaxPressure = 1.28;
2144- const QRect kWindowGeometry = mQtWindowSystem->targetWindowGeometry();
2145+ const int kPointerCount = mir_touch_event_point_count(tev);
2146 QList<QWindowSystemInterface::TouchPoint> touchPoints;
2147-
2148- // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left
2149- // as Qt::TouchPointMoved
2150- const int kPointerCount = mir_touch_event_point_count(tev);
2151- for (int i = 0; i < kPointerCount; ++i) {
2152- QWindowSystemInterface::TouchPoint touchPoint;
2153-
2154- const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x);
2155- const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y);
2156- const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major);
2157- const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor);
2158- const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure);
2159- touchPoint.id = mir_touch_event_id(tev, i);
2160-
2161- touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height());
2162- touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH);
2163- touchPoint.pressure = kP / kMaxPressure;
2164- switch (mir_touch_event_action(tev, i))
2165- {
2166- case mir_touch_action_up:
2167- touchPoint.state = Qt::TouchPointReleased;
2168- break;
2169- case mir_touch_action_down:
2170- touchPoint.state = Qt::TouchPointPressed;
2171- break;
2172- case mir_touch_action_change:
2173- touchPoint.state = Qt::TouchPointMoved;
2174- break;
2175- default:
2176- break;
2177- }
2178-
2179- touchPoints.append(touchPoint);
2180+ QWindow *window = nullptr;
2181+
2182+ if (kPointerCount > 0) {
2183+ window = mQtWindowSystem->getWindowForTouchPoint(
2184+ QPoint(mir_touch_event_axis_value(tev, 0, mir_touch_axis_x),
2185+ mir_touch_event_axis_value(tev, 0, mir_touch_axis_y)));
2186+
2187+ if (!window) {
2188+ qCDebug(QTMIR_MIR_INPUT) << "REJECTING INPUT EVENT, no matching window";
2189+ return;
2190+ }
2191+
2192+ const QRect kWindowGeometry = window->geometry();
2193+
2194+ // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left
2195+ // as Qt::TouchPointMoved
2196+ for (int i = 0; i < kPointerCount; ++i) {
2197+ QWindowSystemInterface::TouchPoint touchPoint;
2198+
2199+ const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x);
2200+ const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y);
2201+ const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major);
2202+ const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor);
2203+ const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure);
2204+ touchPoint.id = mir_touch_event_id(tev, i);
2205+
2206+ touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height());
2207+ touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH);
2208+ touchPoint.pressure = kP / kMaxPressure;
2209+ switch (mir_touch_event_action(tev, i))
2210+ {
2211+ case mir_touch_action_up:
2212+ touchPoint.state = Qt::TouchPointReleased;
2213+ break;
2214+ case mir_touch_action_down:
2215+ touchPoint.state = Qt::TouchPointPressed;
2216+ break;
2217+ case mir_touch_action_change:
2218+ touchPoint.state = Qt::TouchPointMoved;
2219+ break;
2220+ default:
2221+ break;
2222+ }
2223+
2224+ touchPoints.append(touchPoint);
2225+ }
2226 }
2227
2228 // Qt needs a happy, sane stream of touch events. So let's make sure we're not forwarding
2229 // any insanity.
2230- validateTouches(mir_input_event_get_event_time(event) / 1000000, touchPoints);
2231+ validateTouches(window, mir_input_event_get_event_time(event) / 1000000, touchPoints);
2232
2233 // Touch event propagation.
2234 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));
2235- mQtWindowSystem->handleTouchEvent(
2236+ mQtWindowSystem->handleTouchEvent(window,
2237 //scales down the nsec_t (int64) to fit a ulong, precision lost but time difference suitable
2238 mir_input_event_get_event_time(event) / 1000000,
2239 mTouchDevice,
2240@@ -654,7 +681,7 @@
2241 // not used
2242 }
2243
2244-void QtEventFeeder::validateTouches(ulong timestamp,
2245+void QtEventFeeder::validateTouches(QWindow *window, ulong timestamp,
2246 QList<QWindowSystemInterface::TouchPoint> &touchPoints)
2247 {
2248 QSet<int> updatedTouches;
2249@@ -678,7 +705,7 @@
2250 if (!updatedTouches.contains(it.key())) {
2251 qCWarning(QTMIR_MIR_INPUT)
2252 << "There's a touch (id =" << it.key() << ") missing. Releasing it.";
2253- sendActiveTouchRelease(timestamp, it.key());
2254+ sendActiveTouchRelease(window, timestamp, it.key());
2255 it = mActiveTouches.erase(it);
2256 } else {
2257 ++it;
2258@@ -696,7 +723,7 @@
2259 }
2260 }
2261
2262-void QtEventFeeder::sendActiveTouchRelease(ulong timestamp, int id)
2263+void QtEventFeeder::sendActiveTouchRelease(QWindow *window, ulong timestamp, int id)
2264 {
2265 QList<QWindowSystemInterface::TouchPoint> touchPoints = mActiveTouches.values();
2266
2267@@ -710,7 +737,7 @@
2268 }
2269
2270 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));
2271- mQtWindowSystem->handleTouchEvent(timestamp, mTouchDevice, touchPoints);
2272+ mQtWindowSystem->handleTouchEvent(window, timestamp, mTouchDevice, touchPoints);
2273 }
2274
2275 bool QtEventFeeder::validateTouch(QWindowSystemInterface::TouchPoint &touchPoint)
2276
2277=== modified file 'src/platforms/mirserver/qteventfeeder.h'
2278--- src/platforms/mirserver/qteventfeeder.h 2015-08-11 12:08:32 +0000
2279+++ src/platforms/mirserver/qteventfeeder.h 2015-09-30 13:40:22 +0000
2280@@ -23,6 +23,7 @@
2281 #include <qpa/qwindowsysteminterface.h>
2282
2283 class QTouchDevice;
2284+class ScreenController;
2285
2286 /*
2287 Fills Qt's event loop with input events from Mir
2288@@ -33,26 +34,29 @@
2289 // Interface between QtEventFeeder and the actual QWindowSystemInterface functions
2290 // and other related Qt methods and objects to enable replacing them with mocks in
2291 // pure unit tests.
2292- // TODO - Make it work with multimonitor scenarios
2293 class QtWindowSystemInterface {
2294 public:
2295 virtual ~QtWindowSystemInterface() {}
2296- virtual bool hasTargetWindow() = 0;
2297- virtual QRect targetWindowGeometry() = 0;
2298+ virtual void setScreenController(const QSharedPointer<ScreenController> &sc) = 0;
2299+ virtual QWindow* getWindowForTouchPoint(const QPoint &point) = 0;
2300+ virtual QWindow* focusedWindow() = 0;
2301 virtual void registerTouchDevice(QTouchDevice *device) = 0;
2302- virtual void handleExtendedKeyEvent(ulong timestamp, QEvent::Type type, int key,
2303+ virtual void handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key,
2304 Qt::KeyboardModifiers modifiers,
2305 quint32 nativeScanCode, quint32 nativeVirtualKey,
2306 quint32 nativeModifiers,
2307 const QString& text = QString(), bool autorep = false,
2308 ushort count = 1) = 0;
2309- virtual void handleTouchEvent(ulong timestamp, QTouchDevice *device,
2310+ virtual void handleTouchEvent(QWindow *window, ulong timestamp, QTouchDevice *device,
2311 const QList<struct QWindowSystemInterface::TouchPoint> &points,
2312 Qt::KeyboardModifiers mods = Qt::NoModifier) = 0;
2313- virtual void handleMouseEvent(ulong timestamp, QPointF point, Qt::MouseButton buttons, Qt::KeyboardModifiers modifiers) = 0;
2314+ virtual void handleMouseEvent(ulong timestamp, QPointF movement,
2315+ Qt::MouseButton buttons, Qt::KeyboardModifiers modifiers) = 0;
2316 };
2317
2318- QtEventFeeder(QtWindowSystemInterface *windowSystem = nullptr);
2319+ QtEventFeeder(const QSharedPointer<ScreenController> &screenController);
2320+ QtEventFeeder(const QSharedPointer<ScreenController> &screenController,
2321+ QtWindowSystemInterface *windowSystem);
2322 virtual ~QtEventFeeder();
2323
2324 static const int MirEventActionMask;
2325@@ -67,9 +71,9 @@
2326 void dispatchKey(MirInputEvent const* event);
2327 void dispatchTouch(MirInputEvent const* event);
2328 void dispatchPointer(MirInputEvent const* event);
2329- void validateTouches(ulong timestamp, QList<QWindowSystemInterface::TouchPoint> &touchPoints);
2330+ void validateTouches(QWindow *window, ulong timestamp, QList<QWindowSystemInterface::TouchPoint> &touchPoints);
2331 bool validateTouch(QWindowSystemInterface::TouchPoint &touchPoint);
2332- void sendActiveTouchRelease(ulong timestamp, int id);
2333+ void sendActiveTouchRelease(QWindow *window, ulong timestamp, int id);
2334
2335 QString touchesToString(const QList<struct QWindowSystemInterface::TouchPoint> &points);
2336
2337
2338=== modified file 'src/platforms/mirserver/screen.cpp'
2339--- src/platforms/mirserver/screen.cpp 2015-08-11 12:08:32 +0000
2340+++ src/platforms/mirserver/screen.cpp 2015-09-30 13:40:22 +0000
2341@@ -20,12 +20,13 @@
2342
2343 // Mir
2344 #include "mir/geometry/size.h"
2345+#include "mir/graphics/buffer.h"
2346+#include "mir/graphics/display_buffer.h"
2347+#include "mir/graphics/display.h"
2348
2349 // Qt
2350 #include <QCoreApplication>
2351 #include <qpa/qwindowsysteminterface.h>
2352-#include <QtSensors/QOrientationSensor>
2353-#include <QtSensors/QOrientationReading>
2354 #include <QThread>
2355
2356 // Qt sensors
2357@@ -102,12 +103,15 @@
2358
2359 bool Screen::skipDBusRegistration = false;
2360
2361-Screen::Screen(mir::graphics::DisplayConfigurationOutput const &screen)
2362+Screen::Screen(const mir::graphics::DisplayConfigurationOutput &screen)
2363 : QObject(nullptr)
2364+ , m_displayBuffer(nullptr)
2365+ , m_displayGroup(nullptr)
2366 , m_orientationSensor(new QOrientationSensor(this))
2367+ , m_screenWindow(nullptr)
2368 , m_unityScreen(nullptr)
2369 {
2370- readMirDisplayConfiguration(screen);
2371+ setMirDisplayConfiguration(screen);
2372
2373 // Set the default orientation based on the initial screen dimmensions.
2374 m_nativeOrientation = (m_geometry.width() >= m_geometry.height())
2375@@ -139,6 +143,14 @@
2376 }
2377 }
2378
2379+Screen::~Screen()
2380+{
2381+ //if a ScreenWindow associated with this screen, kill it
2382+ if (m_screenWindow) {
2383+ m_screenWindow->window()->destroy(); // ends up destroying m_ScreenWindow
2384+ }
2385+}
2386+
2387 bool Screen::orientationSensorEnabled()
2388 {
2389 return m_orientationSensor->isActive();
2390@@ -150,8 +162,15 @@
2391 toggleSensors(status);
2392 }
2393
2394-void Screen::readMirDisplayConfiguration(mir::graphics::DisplayConfigurationOutput const &screen)
2395+void Screen::setMirDisplayConfiguration(const mir::graphics::DisplayConfigurationOutput &screen)
2396 {
2397+ // Note: DisplayConfigurationOutput will be destroyed after this function returns
2398+
2399+ // Output data - each output has a unique id and corresponding type. Can be multiple cards.
2400+ m_outputId = screen.id;
2401+ m_cardId = screen.card_id;
2402+ m_type = screen.type;
2403+
2404 // Physical screen size
2405 m_physicalSize.setWidth(screen.physical_size_mm.width.as_float());
2406 m_physicalSize.setHeight(screen.physical_size_mm.height.as_float());
2407@@ -162,12 +181,34 @@
2408 // Pixel depth
2409 m_depth = 8 * MIR_BYTES_PER_PIXEL(screen.current_format);
2410
2411- // Mode = Resolution & refresh rate
2412+ // Power mode
2413+ m_powerMode = screen.power_mode;
2414+
2415+ QRect oldGeometry = m_geometry;
2416+ // Position of screen in virtual desktop coordinate space
2417+ m_geometry.setTop(screen.top_left.y.as_int());
2418+ m_geometry.setLeft(screen.top_left.x.as_int());
2419+
2420+ // Mode = current resolution & refresh rate
2421 mir::graphics::DisplayConfigurationMode mode = screen.modes.at(screen.current_mode_index);
2422 m_geometry.setWidth(mode.size.width.as_int());
2423 m_geometry.setHeight(mode.size.height.as_int());
2424
2425- m_refreshRate = mode.vrefresh_hz;
2426+ // DPI - unnecessary to calculate, default implementation in QPlatformScreen is sufficient
2427+
2428+ // Check for Screen geometry change
2429+ if (m_geometry != oldGeometry) {
2430+ QWindowSystemInterface::handleScreenGeometryChange(this->screen(), m_geometry, m_geometry);
2431+ if (m_screenWindow) { // resize corresponding window immediately
2432+ m_screenWindow->setGeometry(m_geometry);
2433+ }
2434+ }
2435+
2436+ // Refresh rate
2437+ if (m_refreshRate != mode.vrefresh_hz) {
2438+ m_refreshRate = mode.vrefresh_hz;
2439+ QWindowSystemInterface::handleScreenRefreshRateChange(this->screen(), mode.vrefresh_hz);
2440+ }
2441 }
2442
2443 void Screen::toggleSensors(const bool enable) const
2444@@ -226,3 +267,58 @@
2445 OrientationReadingEvent::m_type,
2446 m_orientationSensor->reading()->orientation()));
2447 }
2448+
2449+QPlatformCursor *Screen::cursor() const
2450+{
2451+ const QPlatformCursor *platformCursor = &m_cursor;
2452+ return const_cast<QPlatformCursor *>(platformCursor);
2453+}
2454+
2455+ScreenWindow *Screen::window() const
2456+{
2457+ return m_screenWindow;
2458+}
2459+
2460+void Screen::setWindow(ScreenWindow *window)
2461+{
2462+ if (window && m_screenWindow) {
2463+ qCDebug(QTMIR_SENSOR_MESSAGES) << "Screen::setWindow - overwriting existing ScreenWindow";
2464+ }
2465+ m_screenWindow = window;
2466+}
2467+
2468+void Screen::setMirDisplayBuffer(mir::graphics::DisplayBuffer *buffer, mir::graphics::DisplaySyncGroup *group)
2469+{
2470+ qCDebug(QTMIR_SCREENS) << "Screen::setMirDisplayBuffer" << buffer << group;
2471+ // This operation should only be performed while rendering is stopped
2472+ m_displayBuffer = buffer;
2473+ m_displayGroup = group;
2474+}
2475+
2476+void Screen::swapBuffers()
2477+{
2478+ m_displayBuffer->gl_swap_buffers();
2479+
2480+ /* FIXME this exposes a QtMir architecture problem, as Screen is supposed to wrap a mg::DisplayBuffer.
2481+ * We use Qt's multithreaded renderer, where each Screen is rendered to relatively independently, and
2482+ * post() called also individually.
2483+ *
2484+ * But if this is a native server on Android, in the multimonitor case a DisplaySyncGroup can contain
2485+ * 2+ DisplayBuffers, one post() call will submit all mg::DisplayBuffers in the group for flipping.
2486+ * This will cause just one Screen to be updated, blocking the swap call for the other Screens, which
2487+ * will slow rendering dramatically.
2488+ *
2489+ * Integrating the Qt Scenegraph renderer as a Mir renderer should solve this issue.
2490+ */
2491+ m_displayGroup->post();
2492+}
2493+
2494+void Screen::makeCurrent()
2495+{
2496+ m_displayBuffer->make_current();
2497+}
2498+
2499+void Screen::doneCurrent()
2500+{
2501+ m_displayBuffer->release_current();
2502+}
2503
2504=== modified file 'src/platforms/mirserver/screen.h'
2505--- src/platforms/mirserver/screen.h 2015-08-11 12:08:32 +0000
2506+++ src/platforms/mirserver/screen.h 2015-09-30 13:40:22 +0000
2507@@ -17,20 +17,28 @@
2508 #ifndef SCREEN_H
2509 #define SCREEN_H
2510
2511+// Qt
2512 #include <QObject>
2513 #include <QTimer>
2514 #include <QtDBus/QDBusInterface>
2515 #include <qpa/qplatformscreen.h>
2516
2517-#include "mir/graphics/display_configuration.h"
2518+// Mir
2519+#include <mir/graphics/display_configuration.h>
2520+
2521+// local
2522+#include "cursor.h"
2523+#include "screenwindow.h"
2524
2525 class QOrientationSensor;
2526+namespace mir { namespace graphics { class DisplayBuffer; class DisplaySyncGroup; }}
2527
2528 class Screen : public QObject, public QPlatformScreen
2529 {
2530 Q_OBJECT
2531 public:
2532- Screen(mir::graphics::DisplayConfigurationOutput const&);
2533+ Screen(const mir::graphics::DisplayConfigurationOutput &);
2534+ ~Screen();
2535
2536 // QPlatformScreen methods.
2537 QRect geometry() const override { return m_geometry; }
2538@@ -40,8 +48,12 @@
2539 qreal refreshRate() const override { return m_refreshRate; }
2540 Qt::ScreenOrientation nativeOrientation() const override { return m_nativeOrientation; }
2541 Qt::ScreenOrientation orientation() const override { return m_currentOrientation; }
2542+ QPlatformCursor *cursor() const override;
2543
2544 void toggleSensors(const bool enable) const;
2545+ mir::graphics::DisplayConfigurationOutputType outputType() const { return m_type; }
2546+
2547+ ScreenWindow* window() const;
2548
2549 // QObject methods.
2550 void customEvent(QEvent* event) override;
2551@@ -54,20 +66,40 @@
2552 void onDisplayPowerStateChanged(int, int);
2553 void onOrientationReadingChanged();
2554
2555+protected:
2556+ void setWindow(ScreenWindow *window);
2557+
2558+ void setMirDisplayConfiguration(const mir::graphics::DisplayConfigurationOutput &);
2559+ void setMirDisplayBuffer(mir::graphics::DisplayBuffer *, mir::graphics::DisplaySyncGroup *);
2560+ void swapBuffers();
2561+ void makeCurrent();
2562+ void doneCurrent();
2563+
2564 private:
2565- void readMirDisplayConfiguration(mir::graphics::DisplayConfigurationOutput const&);
2566-
2567 QRect m_geometry;
2568 int m_depth;
2569 QImage::Format m_format;
2570 QSizeF m_physicalSize;
2571 qreal m_refreshRate;
2572
2573+ mir::graphics::DisplayBuffer *m_displayBuffer;
2574+ mir::graphics::DisplaySyncGroup *m_displayGroup;
2575+ mir::graphics::DisplayConfigurationOutputId m_outputId;
2576+ mir::graphics::DisplayConfigurationCardId m_cardId;
2577+ mir::graphics::DisplayConfigurationOutputType m_type;
2578+ MirPowerMode m_powerMode;
2579+
2580 Qt::ScreenOrientation m_nativeOrientation;
2581 Qt::ScreenOrientation m_currentOrientation;
2582 QOrientationSensor *m_orientationSensor;
2583
2584+ ScreenWindow *m_screenWindow;
2585 QDBusInterface *m_unityScreen;
2586+
2587+ qtmir::Cursor m_cursor;
2588+
2589+ friend class ScreenController;
2590+ friend class ScreenWindow;
2591 };
2592
2593 #endif // SCREEN_H
2594
2595=== added file 'src/platforms/mirserver/screencontroller.cpp'
2596--- src/platforms/mirserver/screencontroller.cpp 1970-01-01 00:00:00 +0000
2597+++ src/platforms/mirserver/screencontroller.cpp 2015-09-30 13:40:22 +0000
2598@@ -0,0 +1,258 @@
2599+/*
2600+ * Copyright (C) 2015 Canonical, Ltd.
2601+ *
2602+ * This program is free software: you can redistribute it and/or modify it under
2603+ * the terms of the GNU Lesser General Public License version 3, as published by
2604+ * the Free Software Foundation.
2605+ *
2606+ * This program is distributed in the hope that it will be useful, but WITHOUT
2607+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2608+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2609+ * Lesser General Public License for more details.
2610+ *
2611+ * You should have received a copy of the GNU Lesser General Public License
2612+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2613+ */
2614+
2615+#include "screencontroller.h"
2616+
2617+#include "screenwindow.h"
2618+#include "qtcompositor.h"
2619+#include "logging.h"
2620+#include "mirserverintegration.h"
2621+#include "screen.h"
2622+
2623+// Mir
2624+#include <mir/graphics/display.h>
2625+#include <mir/graphics/display_buffer.h>
2626+
2627+// Qt
2628+#include <QScreen>
2629+#include <QQuickWindow>
2630+#include <qpa/qwindowsysteminterface.h>
2631+
2632+// std
2633+#include <memory>
2634+
2635+Q_LOGGING_CATEGORY(QTMIR_SCREENS, "qtmir.screens")
2636+
2637+namespace mg = mir::graphics;
2638+
2639+
2640+ScreenController::ScreenController(QObject *parent)
2641+ : QObject(parent)
2642+ , m_compositing(false)
2643+{
2644+ qCDebug(QTMIR_SCREENS) << "ScreenController::ScreenController";
2645+}
2646+
2647+// init only after MirServer has initialized - runs on MirServerThread!!!
2648+void ScreenController::init(const std::shared_ptr<mir::graphics::Display> &display,
2649+ const std::shared_ptr<mir::compositor::Compositor> &compositor)
2650+{
2651+ m_display = display;
2652+ m_compositor = compositor;
2653+
2654+ // Use a Blocking Queued Connection to enforce synchronization of Qt GUI thread with Mir thread(s)
2655+ // on compositor shutdown. Compositor startup can be lazy.
2656+ // Queued connections work because the thread affinity of this class is with the Qt GUI thread.
2657+ auto qtCompositor = static_cast<QtCompositor *>(compositor.get());
2658+ connect(qtCompositor, &QtCompositor::starting,
2659+ this, &ScreenController::onCompositorStarting);
2660+ connect(qtCompositor, &QtCompositor::stopping,
2661+ this, &ScreenController::onCompositorStopping, Qt::BlockingQueuedConnection);
2662+}
2663+
2664+// terminate before shutting down the Mir server, or else liable to deadlock with the blocking connection above
2665+// Runs on MirServerThread!!!
2666+void ScreenController::terminate()
2667+{
2668+ auto qtCompositor = static_cast<QtCompositor *>(m_compositor.get());
2669+ qtCompositor->disconnect();
2670+}
2671+
2672+void ScreenController::onCompositorStarting()
2673+{
2674+ qCDebug(QTMIR_SCREENS) << "ScreenController::onCompositorStarting";
2675+ m_compositing = true;
2676+
2677+ update();
2678+
2679+ // (Re)Start Qt's render thread by setting all windows with a corresponding screen to exposed.
2680+ for (auto screen : m_screenList) {
2681+ auto window = static_cast<ScreenWindow *>(screen->window());
2682+ if (window && window->window()) {
2683+ window->setExposed(true);
2684+ }
2685+ }
2686+}
2687+
2688+void ScreenController::onCompositorStopping()
2689+{
2690+ qCDebug(QTMIR_SCREENS) << "ScreenController::onCompositorStopping";
2691+ m_compositing = false;
2692+
2693+ // Stop Qt's render threads by setting all its windows it obscured. Must
2694+ // block until all windows have their GL contexts released.
2695+ for (auto screen : m_screenList) {
2696+ auto window = static_cast<ScreenWindow *>(screen->window());
2697+ if (window && window->window()) {
2698+ window->setExposed(false);
2699+ }
2700+ }
2701+
2702+ update();
2703+}
2704+
2705+void ScreenController::update()
2706+{
2707+ qCDebug(QTMIR_SCREENS) << "ScreenController::update";
2708+ auto display = m_display.lock();
2709+ if (!display)
2710+ return;
2711+ auto displayConfig = display->configuration();
2712+
2713+ // Mir only tells us something changed, it is up to us to figure out what.
2714+ QList<Screen*> newScreenList;
2715+ QList<Screen*> oldScreenList = m_screenList;
2716+ m_screenList.clear();
2717+
2718+ displayConfig->for_each_output(
2719+ [this, &oldScreenList, &newScreenList](const mg::DisplayConfigurationOutput &output) {
2720+ if (output.used && output.connected) {
2721+ Screen *screen = findScreenWithId(oldScreenList, output.id);
2722+ if (screen) { // we've already set up this display before, refresh its internals
2723+ screen->setMirDisplayConfiguration(output);
2724+ oldScreenList.removeAll(screen);
2725+ } else {
2726+ // new display, so create Screen for it
2727+ screen = this->createScreen(output);
2728+ newScreenList.append(screen);
2729+ qCDebug(QTMIR_SCREENS) << "Added Screen with id" << output.id.as_value()
2730+ << "and geometry" << screen->geometry();
2731+ }
2732+ m_screenList.append(screen);
2733+ }
2734+ }
2735+ );
2736+
2737+ // Delete any old & unused Screens
2738+ for (auto screen: oldScreenList) {
2739+ qCDebug(QTMIR_SCREENS) << "Removed Screen with id" << screen->m_outputId.as_value()
2740+ << "and geometry" << screen->geometry();
2741+ // The screen is automatically removed from Qt's internal list by the QPlatformScreen destructor.
2742+ auto window = static_cast<ScreenWindow *>(screen->window());
2743+ if (window && window->window() && window->isExposed()) {
2744+ window->window()->hide();
2745+ }
2746+ delete screen;
2747+ }
2748+
2749+ // Match up the new Mir DisplayBuffers with each Screen
2750+ display->for_each_display_sync_group([&](mg::DisplaySyncGroup &group) {
2751+ group.for_each_display_buffer([&](mg::DisplayBuffer &buffer) {
2752+ // only way to match Screen to a DisplayBuffer is by matching the geometry
2753+ QRect dbGeom(buffer.view_area().top_left.x.as_int(),
2754+ buffer.view_area().top_left.y.as_int(),
2755+ buffer.view_area().size.width.as_int(),
2756+ buffer.view_area().size.height.as_int());
2757+
2758+ for (auto screen : m_screenList) {
2759+ if (dbGeom == screen->geometry()) {
2760+ screen->setMirDisplayBuffer(&buffer, &group);
2761+ break;
2762+ }
2763+ }
2764+ });
2765+ });
2766+
2767+ qCDebug(QTMIR_SCREENS) << "=======================================";
2768+ for (auto screen: m_screenList) {
2769+ qCDebug(QTMIR_SCREENS) << screen << "- id:" << screen->m_outputId.as_value()
2770+ << "geometry:" << screen->geometry()
2771+ << "window:" << screen->window()
2772+ << "type" << static_cast<int>(screen->outputType());
2773+ }
2774+ qCDebug(QTMIR_SCREENS) << "=======================================";
2775+
2776+ for (auto screen : newScreenList) {
2777+ Q_EMIT screenAdded(screen);
2778+ }
2779+}
2780+
2781+Screen* ScreenController::createScreen(const mir::graphics::DisplayConfigurationOutput &output) const
2782+{
2783+ return new Screen(output);
2784+}
2785+
2786+Screen* ScreenController::getUnusedScreen()
2787+{
2788+ if (m_screenList.empty()) {
2789+ return nullptr;
2790+ } else if (m_screenList.size() == 1) {
2791+ return m_screenList.at(0);
2792+ }
2793+
2794+ // FIXME: Until we have better way of identifying screens, prioritize outputs based on their output type.
2795+ // Note the priorities defined here are nothing more than guesses. It tries to select internal displays first,
2796+ // then digital outputs, and finally analogue.
2797+ QMap <int, Screen*> priorityList;
2798+ auto prioritize = [](const mg::DisplayConfigurationOutputType &type) {
2799+ using out = mg::DisplayConfigurationOutputType;
2800+ switch(type) {
2801+ case out::lvds:
2802+ case out::edp:
2803+ return 0;
2804+ case out::displayport:
2805+ case out::hdmia:
2806+ case out::hdmib:
2807+ return 1;
2808+ case out::dvii:
2809+ case out::dvid:
2810+ case out::dvia:
2811+ return 2;
2812+ case out::vga:
2813+ return 3;
2814+ case out::ninepindin:
2815+ return 4;
2816+ case out::component:
2817+ case out::composite:
2818+ case out::svideo:
2819+ return 5;
2820+ case out::tv:
2821+ return 6;
2822+ case out::unknown:
2823+ default:
2824+ return 9;
2825+ }
2826+ };
2827+
2828+ for (auto screen : m_screenList) {
2829+ if (!screen->window()) {
2830+ priorityList.insert(prioritize(screen->outputType()), screen);
2831+ }
2832+ }
2833+
2834+ qCDebug(QTMIR_SCREENS) << "Prioritized list of available outputs:" << priorityList;
2835+ return priorityList.first(); // Map sorted by key, so first is the key with highest priority.
2836+}
2837+
2838+Screen* ScreenController::findScreenWithId(const QList<Screen *> &list, const mg::DisplayConfigurationOutputId id)
2839+{
2840+ for (Screen *screen : list) {
2841+ if (screen->m_outputId == id) {
2842+ return screen;
2843+ }
2844+ }
2845+ return nullptr;
2846+}
2847+
2848+QWindow* ScreenController::getWindowForPoint(const QPoint &point) //FIXME - not thread safe & not efficient
2849+{
2850+ for (Screen *screen : m_screenList) {
2851+ if (screen->window() && screen->geometry().contains(point)) {
2852+ return screen->window()->window();
2853+ }
2854+ }
2855+ return nullptr;
2856+}
2857
2858=== added file 'src/platforms/mirserver/screencontroller.h'
2859--- src/platforms/mirserver/screencontroller.h 1970-01-01 00:00:00 +0000
2860+++ src/platforms/mirserver/screencontroller.h 2015-09-30 13:40:22 +0000
2861@@ -0,0 +1,96 @@
2862+/*
2863+ * Copyright (C) 2015 Canonical, Ltd.
2864+ *
2865+ * This program is free software: you can redistribute it and/or modify it under
2866+ * the terms of the GNU Lesser General Public License version 3, as published by
2867+ * the Free Software Foundation.
2868+ *
2869+ * This program is distributed in the hope that it will be useful, but WITHOUT
2870+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
2871+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
2872+ * Lesser General Public License for more details.
2873+ *
2874+ * You should have received a copy of the GNU Lesser General Public License
2875+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2876+ */
2877+
2878+#ifndef SCREENCONTROLLER_H
2879+#define SCREENCONTROLLER_H
2880+
2881+#include <QObject>
2882+#include <QPoint>
2883+
2884+// Mir
2885+#include <mir/graphics/display_configuration.h>
2886+
2887+// std
2888+#include <memory>
2889+
2890+namespace mir {
2891+ namespace graphics { class Display; }
2892+ namespace compositor { class Compositor; }
2893+}
2894+class Screen;
2895+class QWindow;
2896+
2897+/*
2898+ * ScreenController monitors the Mir display configuration and compositor status, and updates
2899+ * the relevant QScreen and QWindow states accordingly.
2900+ *
2901+ * Primary purposes are:
2902+ * 1. to update QScreen state on Mir display configuration changes
2903+ * 2. to stop the Qt renderer by hiding its QWindow when Mir wants to stop all compositing,
2904+ * and resume Qt's renderer by showing its QWindow when Mir wants to resume compositing.
2905+ *
2906+ *
2907+ * Threading Note:
2908+ * This object must have affinity to the main Qt GUI thread, as it creates & destroys Platform
2909+ * objects which Qt uses internally. However beware as the init() & terminate() methods need to
2910+ * be called on the MirServerThread thread, as we need to monitor the screen state *after*
2911+ * Mir has initialized but before Qt's event loop has started, and tear down before Mir terminates.
2912+ * Also note the MirServerThread does not have an QEventLoop.
2913+ *
2914+ * All other methods must be called on the Qt GUI thread.
2915+ */
2916+
2917+class ScreenController : public QObject
2918+{
2919+ Q_OBJECT
2920+public:
2921+ explicit ScreenController(QObject *parent = 0);
2922+
2923+ Screen* getUnusedScreen();
2924+ QList<Screen*> screens() const { return m_screenList; }
2925+ bool compositing() const { return m_compositing; }
2926+
2927+ QWindow* getWindowForPoint(const QPoint &point);
2928+
2929+Q_SIGNALS:
2930+ void screenAdded(Screen *screen);
2931+
2932+public Q_SLOTS:
2933+ void update();
2934+
2935+public:
2936+ // called by MirServer
2937+ void init(const std::shared_ptr<mir::graphics::Display> &display,
2938+ const std::shared_ptr<mir::compositor::Compositor> &compositor);
2939+ void terminate();
2940+
2941+ // override for testing purposes
2942+ virtual Screen *createScreen(const mir::graphics::DisplayConfigurationOutput &output) const;
2943+
2944+protected Q_SLOTS:
2945+ void onCompositorStarting();
2946+ void onCompositorStopping();
2947+
2948+private:
2949+ Screen* findScreenWithId(const QList<Screen*> &list, const mir::graphics::DisplayConfigurationOutputId id);
2950+
2951+ std::weak_ptr<mir::graphics::Display> m_display;
2952+ std::shared_ptr<mir::compositor::Compositor> m_compositor;
2953+ QList<Screen*> m_screenList;
2954+ bool m_compositing;
2955+};
2956+
2957+#endif // SCREENCONTROLLER_H
2958
2959=== renamed file 'src/platforms/mirserver/displaywindow.cpp' => 'src/platforms/mirserver/screenwindow.cpp'
2960--- src/platforms/mirserver/displaywindow.cpp 2015-08-11 12:08:32 +0000
2961+++ src/platforms/mirserver/screenwindow.cpp 2015-09-30 13:40:22 +0000
2962@@ -14,15 +14,22 @@
2963 * along with this program. If not, see <http://www.gnu.org/licenses/>.
2964 */
2965
2966-#include "displaywindow.h"
2967-
2968-#include "mir/geometry/size.h"
2969-
2970+#include "screenwindow.h"
2971+#include "screen.h"
2972+
2973+// Mir
2974+#include <mir/geometry/size.h>
2975+#include <mir/graphics/display_buffer.h>
2976+
2977+// Qt
2978 #include <qpa/qwindowsysteminterface.h>
2979 #include <qpa/qplatformscreen.h>
2980-
2981+#include <QQuickWindow>
2982+#include <QtQuick/private/qsgrenderloop_p.h>
2983 #include <QDebug>
2984
2985+#include "logging.h"
2986+
2987 static WId newWId()
2988 {
2989 static WId id = 0;
2990@@ -33,22 +40,22 @@
2991 return ++id;
2992 }
2993
2994-DisplayWindow::DisplayWindow(
2995- QWindow *window,
2996- mir::graphics::DisplaySyncGroup *displayGroup,
2997- mir::graphics::DisplayBuffer *displayBuffer)
2998- : QObject(nullptr), QPlatformWindow(window)
2999- , m_isExposed(true)
3000+ScreenWindow::ScreenWindow(QWindow *window)
3001+ : QPlatformWindow(window)
3002+ , m_exposed(false)
3003 , m_winId(newWId())
3004- , m_displayGroup(displayGroup)
3005- , m_displayBuffer(displayBuffer)
3006 {
3007- qDebug() << "DisplayWindow::DisplayWindow";
3008- qWarning("Window %p: %p 0x%x\n", this, window, uint(m_winId));
3009+ // Register with the Screen it is associated with
3010+ auto myScreen = static_cast<Screen *>(screen());
3011+ Q_ASSERT(myScreen);
3012+ myScreen->setWindow(this);
3013+
3014+ qCDebug(QTMIR_SCREENS) << "ScreenWindow" << this << "with window ID" << uint(m_winId) << "backed by" << myScreen;
3015
3016 QRect screenGeometry(screen()->availableGeometry());
3017 if (window->geometry() != screenGeometry) {
3018 setGeometry(screenGeometry);
3019+ window->setGeometry(screenGeometry);
3020 }
3021 window->setSurfaceType(QSurface::OpenGLSurface);
3022
3023@@ -57,70 +64,54 @@
3024 requestActivateWindow();
3025 }
3026
3027-QRect DisplayWindow::geometry() const
3028-{
3029- // For yet-to-become-fullscreen windows report the geometry covering the entire
3030- // screen. This is particularly important for Quick where the root object may get
3031- // sized to some geometry queried before calling create().
3032- return screen()->availableGeometry();
3033-}
3034-
3035-void DisplayWindow::setGeometry(const QRect &)
3036-{
3037- // We only support full-screen windows
3038- QRect rect(screen()->availableGeometry());
3039- QWindowSystemInterface::handleGeometryChange(window(), rect);
3040- QPlatformWindow::setGeometry(rect);
3041-}
3042-
3043-bool DisplayWindow::isExposed() const
3044-{
3045- return m_isExposed;
3046-}
3047-
3048-bool DisplayWindow::event(QEvent *event)
3049-{
3050- // Intercept Hide event and convert to Expose event, as Hide causes Qt to release GL
3051- // resources, which we don't want. Must intercept Show to un-do hide.
3052- if (event->type() == QEvent::Hide) {
3053- qDebug() << "DisplayWindow::event got QEvent::Hide";
3054- m_isExposed = false;
3055- QWindowSystemInterface::handleExposeEvent(window(), QRect());
3056- QWindowSystemInterface::flushWindowSystemEvents();
3057- return true;
3058- } else if (event->type() == QEvent::Show) {
3059- qDebug() << "DisplayWindow::event got QEvent::Show";
3060- m_isExposed = true;
3061- QRect rect(QPoint(), geometry().size());
3062- QWindowSystemInterface::handleExposeEvent(window(), rect);
3063- QWindowSystemInterface::flushWindowSystemEvents();
3064- return true;
3065+ScreenWindow::~ScreenWindow()
3066+{
3067+ qCDebug(QTMIR_SCREENS) << "Destroying ScreenWindow" << this;
3068+ static_cast<Screen *>(screen())->setWindow(nullptr);
3069+}
3070+
3071+bool ScreenWindow::isExposed() const
3072+{
3073+ return m_exposed;
3074+}
3075+
3076+void ScreenWindow::setExposed(const bool exposed)
3077+{
3078+ qCDebug(QTMIR_SCREENS) << "ScreenWindow::setExposed" << this << exposed;
3079+ if (m_exposed == exposed)
3080+ return;
3081+
3082+ m_exposed = exposed;
3083+ if (!window())
3084+ return;
3085+
3086+ // If backing a QQuickWindow, need to stop/start its renderer immediately
3087+ auto quickWindow = static_cast<QQuickWindow *>(window());
3088+ if (!quickWindow)
3089+ return;
3090+
3091+ auto renderer = QSGRenderLoop::instance();
3092+ if (exposed) {
3093+ renderer->show(quickWindow);
3094+ QWindowSystemInterface::handleExposeEvent(window(), QRegion()); // else it won't redraw
3095+ } else {
3096+ quickWindow->setPersistentOpenGLContext(false);
3097+ quickWindow->setPersistentSceneGraph(false);
3098+ renderer->hide(quickWindow); // ExposeEvent will arrive too late, need to stop compositor immediately
3099 }
3100- return QObject::event(event);
3101-}
3102-
3103-void DisplayWindow::swapBuffers()
3104-{
3105- m_displayBuffer->gl_swap_buffers();
3106-
3107- // FIXME this exposes a QtMir architecture problem now, as DisplayWindow
3108- // is supposed to wrap a mg::DisplayBuffer. We use Qt's multithreaded
3109- // renderer, where each DisplayWindow is rendered to relatively
3110- // independently, and post() called also individually.
3111- //
3112- // But in multimonitor case where a DisplaySyncGroup contains 2
3113- // DisplayBuffers, one post() call will submit both
3114- // mg::DisplayBuffers for flipping, which can happen before the other
3115- // DisplayWindow has been rendered to, causing visual artifacts
3116- m_displayGroup->post();
3117-}
3118-
3119-void DisplayWindow::makeCurrent()
3120-{
3121- m_displayBuffer->make_current();
3122-}
3123-
3124-void DisplayWindow::doneCurrent()
3125-{
3126- m_displayBuffer->release_current();
3127+}
3128+
3129+void ScreenWindow::swapBuffers()
3130+{
3131+ static_cast<Screen *>(screen())->swapBuffers();
3132+}
3133+
3134+void ScreenWindow::makeCurrent()
3135+{
3136+ static_cast<Screen *>(screen())->makeCurrent();
3137+}
3138+
3139+void ScreenWindow::doneCurrent()
3140+{
3141+ static_cast<Screen *>(screen())->doneCurrent();
3142 }
3143
3144=== renamed file 'src/platforms/mirserver/displaywindow.h' => 'src/platforms/mirserver/screenwindow.h'
3145--- src/platforms/mirserver/displaywindow.h 2015-08-11 12:08:32 +0000
3146+++ src/platforms/mirserver/screenwindow.h 2015-09-30 13:40:22 +0000
3147@@ -14,43 +14,33 @@
3148 * along with this program. If not, see <http://www.gnu.org/licenses/>.
3149 */
3150
3151-#ifndef DISPLAYWINDOW_H
3152-#define DISPLAYWINDOW_H
3153+#ifndef SCREENWINDOW_H
3154+#define SCREENWINDOW_H
3155
3156 #include <qpa/qplatformwindow.h>
3157
3158-#include <mir/graphics/display.h>
3159-#include <mir/graphics/display_buffer.h>
3160-
3161-#include <QObject>
3162-
3163-// DisplayWindow wraps the whatever implementation Mir creates of a DisplayBuffer,
3164-// which is the buffer output for an individual display.
3165-
3166-class DisplayWindow : public QObject, public QPlatformWindow
3167+// ScreenWindow implements the basics of a QPlatformWindow.
3168+// QtMir enforces one Window per Screen, so Window and Screen are tightly coupled.
3169+// All Mir specifics live in the associated Screen object.
3170+
3171+class ScreenWindow : public QPlatformWindow
3172 {
3173- Q_OBJECT
3174 public:
3175- explicit DisplayWindow(QWindow *window, mir::graphics::DisplaySyncGroup*, mir::graphics::DisplayBuffer*);
3176+ explicit ScreenWindow(QWindow *window);
3177+ virtual ~ScreenWindow();
3178
3179- QRect geometry() const override;
3180- void setGeometry(const QRect &rect) override;
3181+ bool isExposed() const override;
3182+ void setExposed(const bool exposed);
3183
3184 WId winId() const override { return m_winId; }
3185
3186- bool isExposed() const override;
3187-
3188- bool event(QEvent *event) override;
3189-
3190 void swapBuffers();
3191 void makeCurrent();
3192 void doneCurrent();
3193
3194 private:
3195- bool m_isExposed;
3196+ bool m_exposed;
3197 WId m_winId;
3198- mir::graphics::DisplaySyncGroup *m_displayGroup;
3199- mir::graphics::DisplayBuffer *m_displayBuffer;
3200 };
3201
3202-#endif // DISPLAYWINDOW_H
3203+#endif // SCREENWINDOW_H
3204
3205=== added file 'src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp'
3206--- src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp 1970-01-01 00:00:00 +0000
3207+++ src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp 2015-09-30 13:40:22 +0000
3208@@ -0,0 +1,44 @@
3209+/*
3210+ * Copyright (C) 2015 Canonical, Ltd.
3211+ *
3212+ * This program is free software: you can redistribute it and/or modify it under
3213+ * the terms of the GNU Lesser General Public License version 3, as published by
3214+ * the Free Software Foundation.
3215+ *
3216+ * This program is distributed in the hope that it will be useful, but WITHOUT
3217+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3218+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3219+ * Lesser General Public License for more details.
3220+ *
3221+ * You should have received a copy of the GNU Lesser General Public License
3222+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3223+ */
3224+
3225+#include "tileddisplayconfigurationpolicy.h"
3226+
3227+#include <mir/graphics/display_configuration.h>
3228+#include <mir/geometry/point.h>
3229+
3230+namespace mg = mir::graphics;
3231+
3232+TiledDisplayConfigurationPolicy::TiledDisplayConfigurationPolicy(
3233+ const std::shared_ptr<mir::graphics::DisplayConfigurationPolicy> &wrapped)
3234+ : m_wrapped(wrapped)
3235+{
3236+}
3237+
3238+void TiledDisplayConfigurationPolicy::apply_to(mg::DisplayConfiguration& conf)
3239+{
3240+ int nextTopLeftPosition = 0;
3241+
3242+ m_wrapped->apply_to(conf);
3243+
3244+ conf.for_each_output(
3245+ [&](mg::UserDisplayConfigurationOutput& output)
3246+ {
3247+ if (output.connected && output.used) {
3248+ output.top_left = mir::geometry::Point{nextTopLeftPosition, 0};
3249+ nextTopLeftPosition += output.modes[output.preferred_mode_index].size.width.as_int();
3250+ }
3251+ });
3252+}
3253
3254=== added file 'src/platforms/mirserver/tileddisplayconfigurationpolicy.h'
3255--- src/platforms/mirserver/tileddisplayconfigurationpolicy.h 1970-01-01 00:00:00 +0000
3256+++ src/platforms/mirserver/tileddisplayconfigurationpolicy.h 2015-09-30 13:40:22 +0000
3257@@ -0,0 +1,35 @@
3258+/*
3259+ * Copyright (C) 2015 Canonical, Ltd.
3260+ *
3261+ * This program is free software: you can redistribute it and/or modify it under
3262+ * the terms of the GNU Lesser General Public License version 3, as published by
3263+ * the Free Software Foundation.
3264+ *
3265+ * This program is distributed in the hope that it will be useful, but WITHOUT
3266+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3267+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3268+ * Lesser General Public License for more details.
3269+ *
3270+ * You should have received a copy of the GNU Lesser General Public License
3271+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3272+ */
3273+
3274+#ifndef TILEDDISPLAYCONFIGURATIONPOLICY_H
3275+#define TILEDDISPLAYCONFIGURATIONPOLICY_H
3276+
3277+#include <mir/graphics/display_configuration_policy.h>
3278+
3279+#include <memory>
3280+
3281+class TiledDisplayConfigurationPolicy : public mir::graphics::DisplayConfigurationPolicy
3282+{
3283+public:
3284+ TiledDisplayConfigurationPolicy(const std::shared_ptr<mir::graphics::DisplayConfigurationPolicy> &wrapped);
3285+
3286+ void apply_to(mir::graphics::DisplayConfiguration& conf) override;
3287+
3288+private:
3289+ const std::shared_ptr<mir::graphics::DisplayConfigurationPolicy> m_wrapped;
3290+};
3291+
3292+#endif // TILEDDISPLAYCONFIGURATIONPOLICY_H
3293
3294=== added directory 'tests/common'
3295=== added file 'tests/common/fake_displayconfigurationoutput.h'
3296--- tests/common/fake_displayconfigurationoutput.h 1970-01-01 00:00:00 +0000
3297+++ tests/common/fake_displayconfigurationoutput.h 2015-09-30 13:40:22 +0000
3298@@ -0,0 +1,73 @@
3299+/*
3300+ * Copyright (C) 2015 Canonical, Ltd.
3301+ *
3302+ * This program is free software: you can redistribute it and/or modify it under
3303+ * the terms of the GNU Lesser General Public License version 3, as published by
3304+ * the Free Software Foundation.
3305+ *
3306+ * This program is distributed in the hope that it will be useful, but WITHOUT
3307+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3308+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3309+ * Lesser General Public License for more details.
3310+ *
3311+ * You should have received a copy of the GNU Lesser General Public License
3312+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3313+ */
3314+
3315+#ifndef FAKE_DISPLAYCONFIGURATIONOUTPUT_H
3316+#define FAKE_DISPLAYCONFIGURATIONOUTPUT_H
3317+
3318+#include <mir/graphics/display_configuration.h>
3319+
3320+namespace mg = mir::graphics;
3321+namespace geom = mir::geometry;
3322+
3323+const mg::DisplayConfigurationOutput fakeOutput1
3324+{
3325+ mg::DisplayConfigurationOutputId{3},
3326+ mg::DisplayConfigurationCardId{2},
3327+ mg::DisplayConfigurationOutputType::dvid,
3328+ {
3329+ mir_pixel_format_abgr_8888
3330+ },
3331+ {
3332+ {geom::Size{100, 200}, 60.0},
3333+ {geom::Size{100, 200}, 59.0},
3334+ {geom::Size{150, 200}, 59.0}
3335+ },
3336+ 0,
3337+ geom::Size{1111, 2222},
3338+ true,
3339+ true,
3340+ geom::Point(),
3341+ 2,
3342+ mir_pixel_format_abgr_8888,
3343+ mir_power_mode_on,
3344+ mir_orientation_normal
3345+};
3346+
3347+const mg::DisplayConfigurationOutput fakeOutput2
3348+{
3349+ mg::DisplayConfigurationOutputId{2},
3350+ mg::DisplayConfigurationCardId{4},
3351+ mg::DisplayConfigurationOutputType::lvds,
3352+ {
3353+ mir_pixel_format_xbgr_8888
3354+ },
3355+ {
3356+ {geom::Size{800, 1200}, 90.0},
3357+ {geom::Size{1600, 2400}, 60.0},
3358+ {geom::Size{1500, 2000}, 75.0}
3359+ },
3360+ 0,
3361+ geom::Size{1000, 2000},
3362+ true,
3363+ true,
3364+ geom::Point(500, 600),
3365+ 2,
3366+ mir_pixel_format_xbgr_8888,
3367+ mir_power_mode_on,
3368+ mir_orientation_left
3369+};
3370+
3371+#endif // FAKE_DISPLAYCONFIGURATIONOUTPUT_H
3372
3373=== added file 'tests/common/gmock_fixes.h'
3374--- tests/common/gmock_fixes.h 1970-01-01 00:00:00 +0000
3375+++ tests/common/gmock_fixes.h 2015-09-30 13:40:22 +0000
3376@@ -0,0 +1,124 @@
3377+//
3378+// Copyright © 2012 Canonical Ltd. Copyright 2007, Google Inc.
3379+//
3380+// All rights reserved.
3381+//
3382+// Redistribution and use in source and binary forms, with or without
3383+// modification, are permitted provided that the following conditions are
3384+// met:
3385+//
3386+// * Redistributions of source code must retain the above copyright
3387+// notice, this list of conditions and the following disclaimer.
3388+// * Redistributions in binary form must reproduce the above
3389+// copyright notice, this list of conditions and the following disclaimer
3390+// in the documentation and/or other materials provided with the
3391+// distribution.
3392+// * Neither the name of Google Inc. nor the names of its
3393+// contributors may be used to endorse or promote products derived from
3394+// this software without specific prior written permission.
3395+//
3396+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3397+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3398+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3399+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3400+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3401+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3402+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3403+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3404+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3405+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3406+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3407+//
3408+// Author: wan@google.com (Zhanyong Wan)
3409+// Authored by: Alan Griffiths <alan@octopull.co.uk>
3410+
3411+#ifndef MIR_TEST_GMOCK_FIXES_H_
3412+#define MIR_TEST_GMOCK_FIXES_H_
3413+
3414+#include <memory>
3415+#include <gmock/gmock.h>
3416+
3417+namespace testing
3418+{
3419+namespace internal
3420+{
3421+
3422+template<typename T>
3423+class ActionResultHolder<std::unique_ptr<T>>
3424+: public UntypedActionResultHolderBase {
3425+ public:
3426+ explicit ActionResultHolder(std::unique_ptr<T>&& a_value) :
3427+ value_(std::move(a_value)) {}
3428+
3429+ // The compiler-generated copy constructor and assignment operator
3430+ // are exactly what we need, so we don't need to define them.
3431+
3432+ // Returns the held value and deletes this object.
3433+ std::unique_ptr<T> GetValueAndDelete() const {
3434+ std::unique_ptr<T> retval(std::move(value_));
3435+ delete this;
3436+ return retval;
3437+ }
3438+
3439+ // Prints the held value as an action's result to os.
3440+ virtual void PrintAsActionResult(::std::ostream* os) const {
3441+ *os << "\n Returns: ";
3442+ // T may be a reference type, so we don't use UniversalPrint().
3443+ UniversalPrinter<std::unique_ptr<T>>::Print(value_, os);
3444+ }
3445+
3446+ // Performs the given mock function's default action and returns the
3447+ // result in a new-ed ActionResultHolder.
3448+ template <typename F>
3449+ static ActionResultHolder* PerformDefaultAction(
3450+ const FunctionMockerBase<F>* func_mocker,
3451+ const typename Function<F>::ArgumentTuple& args,
3452+ const string& call_description) {
3453+ return new ActionResultHolder(
3454+ func_mocker->PerformDefaultAction(args, call_description));
3455+ }
3456+
3457+ // Performs the given action and returns the result in a new-ed
3458+ // ActionResultHolder.
3459+ template <typename F>
3460+ static ActionResultHolder*
3461+ PerformAction(const Action<F>& action,
3462+ const typename Function<F>::ArgumentTuple& args) {
3463+ return new ActionResultHolder(action.Perform(args));
3464+ }
3465+
3466+ private:
3467+ std::unique_ptr<T> mutable value_;
3468+
3469+ // T could be a reference type, so = isn't supported.
3470+ GTEST_DISALLOW_ASSIGN_(ActionResultHolder);
3471+};
3472+
3473+}
3474+
3475+template<typename T>
3476+class DefaultValue<std::unique_ptr<T>> {
3477+ public:
3478+ // Unsets the default value for type T.
3479+ static void Clear() {}
3480+
3481+ // Returns true iff the user has set the default value for type T.
3482+ static bool IsSet() { return false; }
3483+
3484+ // Returns true if T has a default return value set by the user or there
3485+ // exists a built-in default value.
3486+ static bool Exists() {
3487+ return true;
3488+ }
3489+
3490+ // Returns the default value for type T if the user has set one;
3491+ // otherwise returns the built-in default value if there is one;
3492+ // otherwise aborts the process.
3493+ static std::unique_ptr<T> Get() {
3494+ return std::unique_ptr<T>();
3495+ }
3496+};
3497+
3498+}
3499+
3500+#endif /* MIR_TEST_GMOCK_FIXES_H_ */
3501
3502=== added file 'tests/common/mock_display.h'
3503--- tests/common/mock_display.h 1970-01-01 00:00:00 +0000
3504+++ tests/common/mock_display.h 2015-09-30 13:40:22 +0000
3505@@ -0,0 +1,53 @@
3506+/*
3507+ * Copyright (C) 2015 Canonical, Ltd.
3508+ *
3509+ * This program is free software: you can redistribute it and/or modify it under
3510+ * the terms of the GNU Lesser General Public License version 3, as published by
3511+ * the Free Software Foundation.
3512+ *
3513+ * This program is distributed in the hope that it will be useful, but WITHOUT
3514+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3515+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3516+ * Lesser General Public License for more details.
3517+ *
3518+ * You should have received a copy of the GNU Lesser General Public License
3519+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3520+ */
3521+
3522+#ifndef MOCKDISPLAY_H
3523+#define MOCKDISPLAY_H
3524+
3525+#include <mir/graphics/display.h>
3526+#include <mir/graphics/gl_context.h>
3527+
3528+#include <gmock/gmock.h>
3529+#include "gmock_fixes.h"
3530+
3531+class MockDisplaySyncGroup : public mir::graphics::DisplaySyncGroup
3532+{
3533+public:
3534+ MOCK_METHOD1(for_each_display_buffer, void(std::function<void(mir::graphics::DisplayBuffer&)> const& f));
3535+ MOCK_METHOD0(post, void());
3536+};
3537+
3538+struct MockDisplay : public mir::graphics::Display
3539+{
3540+public:
3541+ MOCK_METHOD1(for_each_display_sync_group, void(std::function<void(mir::graphics::DisplaySyncGroup&)> const&));
3542+ MOCK_CONST_METHOD0(configuration, std::unique_ptr<mir::graphics::DisplayConfiguration>());
3543+ MOCK_METHOD1(configure, void(mir::graphics::DisplayConfiguration const&));
3544+ MOCK_METHOD2(register_configuration_change_handler,
3545+ void(mir::graphics::EventHandlerRegister&, mir::graphics::DisplayConfigurationChangeHandler const&));
3546+
3547+ MOCK_METHOD3(register_pause_resume_handlers, void(mir::graphics::EventHandlerRegister&,
3548+ mir::graphics::DisplayPauseHandler const&,
3549+ mir::graphics::DisplayResumeHandler const&));
3550+ MOCK_METHOD0(pause, void());
3551+ MOCK_METHOD0(resume, void());
3552+ MOCK_METHOD1(create_hardware_cursor, std::shared_ptr<mir::graphics::Cursor>(std::shared_ptr<mir::graphics::CursorImage> const&));
3553+ MOCK_METHOD0(create_gl_context, std::unique_ptr<mir::graphics::GLContext>());
3554+};
3555+
3556+
3557+
3558+#endif // MOCKDISPLAY_H
3559
3560=== added file 'tests/common/mock_display_buffer.h'
3561--- tests/common/mock_display_buffer.h 1970-01-01 00:00:00 +0000
3562+++ tests/common/mock_display_buffer.h 2015-09-30 13:40:22 +0000
3563@@ -0,0 +1,43 @@
3564+/*
3565+ * Copyright (C) 2015 Canonical, Ltd.
3566+ *
3567+ * This program is free software: you can redistribute it and/or modify it under
3568+ * the terms of the GNU Lesser General Public License version 3, as published by
3569+ * the Free Software Foundation.
3570+ *
3571+ * This program is distributed in the hope that it will be useful, but WITHOUT
3572+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3573+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3574+ * Lesser General Public License for more details.
3575+ *
3576+ * You should have received a copy of the GNU Lesser General Public License
3577+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3578+ */
3579+
3580+#ifndef MOCK_DISPLAY_BUFFER_H
3581+#define MOCK_DISPLAY_BUFFER_H
3582+
3583+#include <mir/graphics/display_buffer.h>
3584+
3585+#include <gmock/gmock.h>
3586+
3587+class MockDisplayBuffer : public mir::graphics::DisplayBuffer
3588+{
3589+public:
3590+ MockDisplayBuffer()
3591+ {
3592+ using namespace testing;
3593+ ON_CALL(*this, view_area())
3594+ .WillByDefault(Return(mir::geometry::Rectangle{{0,0},{0,0}}));
3595+ }
3596+ MOCK_CONST_METHOD0(view_area, mir::geometry::Rectangle());
3597+ MOCK_METHOD0(make_current, void());
3598+ MOCK_METHOD0(release_current, void());
3599+ MOCK_METHOD0(gl_swap_buffers, void());
3600+ MOCK_METHOD1(post_renderables_if_optimizable, bool(mir::graphics::RenderableList const&));
3601+ MOCK_CONST_METHOD0(orientation, MirOrientation());
3602+ MOCK_CONST_METHOD0(uses_alpha, bool());
3603+};
3604+
3605+
3606+#endif // MOCK_DISPLAY_BUFFER_H
3607
3608=== added file 'tests/common/mock_display_configuration.h'
3609--- tests/common/mock_display_configuration.h 1970-01-01 00:00:00 +0000
3610+++ tests/common/mock_display_configuration.h 2015-09-30 13:40:22 +0000
3611@@ -0,0 +1,35 @@
3612+/*
3613+ * Copyright (C) 2015 Canonical, Ltd.
3614+ *
3615+ * This program is free software: you can redistribute it and/or modify it under
3616+ * the terms of the GNU Lesser General Public License version 3, as published by
3617+ * the Free Software Foundation.
3618+ *
3619+ * This program is distributed in the hope that it will be useful, but WITHOUT
3620+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3621+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3622+ * Lesser General Public License for more details.
3623+ *
3624+ * You should have received a copy of the GNU Lesser General Public License
3625+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3626+ */
3627+
3628+#ifndef MOCK_DISPLAY_CONFIGURATION_H
3629+#define MOCK_DISPLAY_CONFIGURATION_H
3630+
3631+#include <mir/graphics/display_configuration.h>
3632+
3633+#include <gmock/gmock.h>
3634+#include "gmock_fixes.h"
3635+
3636+class MockDisplayConfiguration : public mir::graphics::DisplayConfiguration
3637+{
3638+public:
3639+ MOCK_CONST_METHOD1(for_each_card, void(std::function<void(mir::graphics::DisplayConfigurationCard const&)>));
3640+
3641+ MOCK_CONST_METHOD1(for_each_output, void(std::function<void(mir::graphics::DisplayConfigurationOutput const&)>));
3642+ MOCK_METHOD1(for_each_output, void(std::function<void(mir::graphics::UserDisplayConfigurationOutput&)>));
3643+
3644+ MOCK_CONST_METHOD0(valid, bool());
3645+};
3646+#endif // MOCK_DISPLAY_CONFIGURATION_H
3647
3648=== added file 'tests/common/mock_main_loop.h'
3649--- tests/common/mock_main_loop.h 1970-01-01 00:00:00 +0000
3650+++ tests/common/mock_main_loop.h 2015-09-30 13:40:22 +0000
3651@@ -0,0 +1,53 @@
3652+/*
3653+ * Copyright (C) 2015 Canonical, Ltd.
3654+ *
3655+ * This program is free software: you can redistribute it and/or modify it under
3656+ * the terms of the GNU Lesser General Public License version 3, as published by
3657+ * the Free Software Foundation.
3658+ *
3659+ * This program is distributed in the hope that it will be useful, but WITHOUT
3660+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
3661+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3662+ * Lesser General Public License for more details.
3663+ *
3664+ * You should have received a copy of the GNU Lesser General Public License
3665+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3666+ */
3667+
3668+#ifndef MOCKMAINLOOP_H
3669+#define MOCKMAINLOOP_H
3670+
3671+#include <gmock/gmock.h>
3672+
3673+#include <mir/main_loop.h>
3674+
3675+#include <memory>
3676+
3677+class MockMainLoop : public mir::MainLoop
3678+{
3679+public:
3680+ ~MockMainLoop() noexcept {}
3681+
3682+ void run() override {}
3683+ void stop() override {}
3684+
3685+ MOCK_METHOD2(register_signal_handler,
3686+ void(std::initializer_list<int>,
3687+ std::function<void(int)> const&));
3688+
3689+ MOCK_METHOD3(register_fd_handler,
3690+ void(std::initializer_list<int>, void const*,
3691+ std::function<void(int)> const&));
3692+
3693+ MOCK_METHOD1(unregister_fd_handler, void(void const*));
3694+
3695+ MOCK_METHOD2(enqueue, void(void const*, mir::ServerAction const&));
3696+ MOCK_METHOD1(pause_processing_for,void (void const*));
3697+ MOCK_METHOD1(resume_processing_for,void (void const*));
3698+
3699+ MOCK_METHOD1(create_alarm, std::unique_ptr<mir::time::Alarm>(std::function<void()> const& callback));
3700+ MOCK_METHOD1(create_alarm, std::unique_ptr<mir::time::Alarm>(std::shared_ptr<mir::LockableCallback> const& callback));
3701+};
3702+
3703+
3704+#endif // MOCKMAINLOOP_H
3705
3706=== modified file 'tests/mirserver/CMakeLists.txt'
3707--- tests/mirserver/CMakeLists.txt 2014-11-13 15:47:30 +0000
3708+++ tests/mirserver/CMakeLists.txt 2015-09-30 13:40:22 +0000
3709@@ -1,3 +1,4 @@
3710 add_subdirectory(QtEventFeeder)
3711 add_subdirectory(Clipboard)
3712 add_subdirectory(Screen)
3713+add_subdirectory(ScreenController)
3714
3715=== modified file 'tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h'
3716--- tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h 2015-04-01 15:02:36 +0000
3717+++ tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h 2015-09-30 13:40:22 +0000
3718@@ -19,19 +19,26 @@
3719 #define MOCK_QTWINDOWSYSTEM_H
3720
3721 #include <qteventfeeder.h>
3722+#include <QWindow>
3723
3724 class MockQtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {
3725 public:
3726- MOCK_METHOD0(hasTargetWindow, bool());
3727- MOCK_METHOD0(targetWindowGeometry, QRect());
3728+ MOCK_CONST_METHOD0(ready, bool());
3729+ MOCK_METHOD1(setScreenController, void(const QSharedPointer<ScreenController> &));
3730+ MOCK_METHOD1(getWindowForTouchPoint, QWindow*(const QPoint &point));
3731+ MOCK_METHOD0(lastWindow, QWindow*());
3732+ MOCK_METHOD0(focusedWindow, QWindow*());
3733 MOCK_METHOD1(registerTouchDevice, void(QTouchDevice* device));
3734- MOCK_METHOD10(handleExtendedKeyEvent, void(ulong timestamp, QEvent::Type type, int key,
3735- Qt::KeyboardModifiers modifiers,
3736- quint32 nativeScanCode, quint32 nativeVirtualKey,
3737- quint32 nativeModifiers,
3738- const QString& text, bool autorep,
3739- ushort count));
3740- MOCK_METHOD4(handleTouchEvent, void(ulong timestamp, QTouchDevice *device,
3741+
3742+ // Wanted to use GMock, but MOCK_METHOD11 not implemented
3743+ void handleExtendedKeyEvent(QWindow */*window*/, ulong /*timestamp*/, QEvent::Type /*type*/, int /*key*/,
3744+ Qt::KeyboardModifiers /*modifiers*/,
3745+ quint32 /*nativeScanCode*/, quint32 /*nativeVirtualKey*/,
3746+ quint32 /*nativeModifiers*/,
3747+ const QString& /*text*/ = QString(), bool /*autorep*/ = false,
3748+ ushort /*count*/ = 1) {}
3749+
3750+ MOCK_METHOD5(handleTouchEvent, void(QWindow *window, ulong timestamp, QTouchDevice *device,
3751 const QList<struct QWindowSystemInterface::TouchPoint> &points,
3752 Qt::KeyboardModifiers mods));
3753 MOCK_METHOD4(handleMouseEvent, void(ulong, QPointF, Qt::MouseButton, Qt::KeyboardModifiers));
3754
3755=== modified file 'tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp'
3756--- tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp 2015-09-18 16:33:06 +0000
3757+++ tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp 2015-09-30 13:40:22 +0000
3758@@ -69,33 +69,44 @@
3759
3760 MockQtWindowSystem *mockWindowSystem;
3761 QtEventFeeder *qtEventFeeder;
3762+ QWindow *window;
3763+ QGuiApplication *app;
3764 };
3765
3766 void QtEventFeederTest::SetUp()
3767 {
3768 mockWindowSystem = new MockQtWindowSystem;
3769+ auto screens = QSharedPointer<ScreenController>();
3770
3771 EXPECT_CALL(*mockWindowSystem, registerTouchDevice(_));
3772
3773- qtEventFeeder = new QtEventFeeder(mockWindowSystem);
3774+ qtEventFeeder = new QtEventFeeder(screens, mockWindowSystem);
3775
3776 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
3777+
3778+ int argc = 0;
3779+ char **argv = nullptr;
3780+ setenv("QT_QPA_PLATFORM", "minimal", 1);
3781+ app = new QGuiApplication(argc, argv);
3782+ window = new QWindow;
3783 }
3784
3785 void QtEventFeederTest::TearDown()
3786 {
3787 // mockWindowSystem will be deleted by QtEventFeeder
3788 delete qtEventFeeder;
3789+ delete window;
3790+ delete app;
3791 }
3792
3793 void QtEventFeederTest::setIrrelevantMockWindowSystemExpectations()
3794 {
3795- EXPECT_CALL(*mockWindowSystem, hasTargetWindow())
3796- .Times(AnyNumber())
3797- .WillRepeatedly(Return(true));
3798- EXPECT_CALL(*mockWindowSystem, targetWindowGeometry())
3799- .Times(AnyNumber())
3800- .WillRepeatedly(Return(QRect(0,0,100,100)));
3801+ EXPECT_CALL(*mockWindowSystem, getWindowForTouchPoint(_))
3802+ .Times(AnyNumber())
3803+ .WillRepeatedly(Return(window));
3804+ EXPECT_CALL(*mockWindowSystem, focusedWindow())
3805+ .Times(AnyNumber())
3806+ .WillRepeatedly(Return(window));
3807 }
3808
3809
3810@@ -113,7 +124,7 @@
3811
3812 setIrrelevantMockWindowSystemExpectations();
3813
3814- EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
3815+ EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
3816 Contains(AllOf(HasId(0),
3817 IsPressed()))),_)).Times(1);
3818
3819@@ -132,12 +143,12 @@
3820 InSequence sequence;
3821
3822 EXPECT_CALL(*mockWindowSystem,
3823- handleTouchEvent(_,_,AllOf(SizeIs(1),
3824+ handleTouchEvent(_,_,_,AllOf(SizeIs(1),
3825 Contains(AllOf(HasId(0),IsReleased()))
3826 ),_)).Times(1);
3827
3828 EXPECT_CALL(*mockWindowSystem,
3829- handleTouchEvent(_,_,AllOf(SizeIs(1),
3830+ handleTouchEvent(_,_,_,AllOf(SizeIs(1),
3831 Contains(AllOf(HasId(1),IsPressed()))
3832 ),_)).Times(1);
3833 }
3834@@ -161,7 +172,7 @@
3835 10, 10, 10 /* x, y, pressure*/,
3836 1, 1, 10 /* touch major, minor, size */);
3837
3838- EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
3839+ EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
3840 Contains(AllOf(HasId(0),
3841 IsPressed()))),_)).Times(1);
3842 qtEventFeeder->dispatch(*ev1);
3843@@ -181,7 +192,7 @@
3844 1, 1, 10 /* touch major, minor, size */);
3845
3846 EXPECT_CALL(*mockWindowSystem,
3847- handleTouchEvent(_,_,AllOf(SizeIs(2),
3848+ handleTouchEvent(_,_,_,AllOf(SizeIs(2),
3849 Contains(AllOf(HasId(0), StateIsMoved())),
3850 Contains(AllOf(HasId(1), IsPressed()))
3851 ),_)).Times(1);
3852@@ -208,14 +219,14 @@
3853
3854 // first release touch 0
3855 EXPECT_CALL(*mockWindowSystem,
3856- handleTouchEvent(_,_,AllOf(SizeIs(2),
3857+ handleTouchEvent(_,_,_,AllOf(SizeIs(2),
3858 Contains(AllOf(HasId(0), IsReleased())),
3859 Contains(AllOf(HasId(1), IsStationary()))
3860 ),_)).Times(1);
3861
3862 // then press touch 2
3863 EXPECT_CALL(*mockWindowSystem,
3864- handleTouchEvent(_,_,AllOf(SizeIs(2),
3865+ handleTouchEvent(_,_,_,AllOf(SizeIs(2),
3866 Contains(AllOf(HasId(1), StateIsMoved())),
3867 Contains(AllOf(HasId(2), IsPressed()))
3868 ),_)).Times(1);
3869@@ -230,7 +241,7 @@
3870 {
3871 setIrrelevantMockWindowSystemExpectations();
3872
3873- EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
3874+ EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
3875 Contains(AllOf(HasId(0),
3876 IsPressed()))),_)).Times(1);
3877
3878@@ -243,7 +254,7 @@
3879
3880 setIrrelevantMockWindowSystemExpectations();
3881
3882- EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),
3883+ EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
3884 Contains(AllOf(HasId(0), StateIsMoved()))
3885 ),_)).Times(1);
3886
3887
3888=== modified file 'tests/mirserver/Screen/CMakeLists.txt'
3889--- tests/mirserver/Screen/CMakeLists.txt 2014-12-03 08:56:35 +0000
3890+++ tests/mirserver/Screen/CMakeLists.txt 2015-09-30 13:40:22 +0000
3891@@ -5,6 +5,7 @@
3892 )
3893
3894 include_directories(
3895+ ${CMAKE_SOURCE_DIR}/tests/common
3896 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
3897 ${CMAKE_SOURCE_DIR}/src/common
3898 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
3899
3900=== modified file 'tests/mirserver/Screen/screen_test.cpp'
3901--- tests/mirserver/Screen/screen_test.cpp 2015-08-11 12:08:32 +0000
3902+++ tests/mirserver/Screen/screen_test.cpp 2015-09-30 13:40:22 +0000
3903@@ -18,37 +18,21 @@
3904 #include <gtest/gtest.h>
3905
3906 #include "mir/graphics/display_configuration.h"
3907+#include "fake_displayconfigurationoutput.h"
3908
3909 #include <screen.h>
3910
3911+using namespace ::testing;
3912+
3913 namespace mg = mir::graphics;
3914 namespace geom = mir::geometry;
3915
3916-mg::DisplayConfigurationOutput const fake_output
3917-{
3918- mg::DisplayConfigurationOutputId{3},
3919- mg::DisplayConfigurationCardId{2},
3920- mg::DisplayConfigurationOutputType::dvid,
3921- {
3922- mir_pixel_format_abgr_8888
3923- },
3924- {
3925- {geom::Size{10, 20}, 60.0},
3926- {geom::Size{10, 20}, 59.0},
3927- {geom::Size{15, 20}, 59.0}
3928- },
3929- 0,
3930- geom::Size{10, 20},
3931- true,
3932- true,
3933- geom::Point(),
3934- 2,
3935- mir_pixel_format_abgr_8888,
3936- mir_power_mode_on,
3937- mir_orientation_normal
3938+class ScreenTest : public ::testing::Test {
3939+protected:
3940+ void SetUp() override;
3941 };
3942
3943-TEST(ScreenTest, OrientationSensor)
3944+void ScreenTest::SetUp()
3945 {
3946 if (!qEnvironmentVariableIsSet("QT_ACCEL_FILEPATH")) {
3947 // Trick Qt >= 5.4.1 to load the generic sensors
3948@@ -56,7 +40,11 @@
3949 }
3950
3951 Screen::skipDBusRegistration = true;
3952- Screen *screen = new Screen(fake_output);
3953+}
3954+
3955+TEST_F(ScreenTest, OrientationSensor)
3956+{
3957+ Screen *screen = new Screen(fakeOutput1);
3958
3959 // Default state should be active
3960 ASSERT_TRUE(screen->orientationSensorEnabled());
3961@@ -67,3 +55,29 @@
3962 screen->onDisplayPowerStateChanged(1,0);
3963 ASSERT_TRUE(screen->orientationSensorEnabled());
3964 }
3965+
3966+TEST_F(ScreenTest, ReadConfigurationFromDisplayConfig)
3967+{
3968+ Screen *screen = new Screen(fakeOutput1);
3969+
3970+ EXPECT_EQ(screen->geometry(), QRect(0, 0, 150, 200));
3971+ EXPECT_EQ(screen->availableGeometry(), QRect(0, 0, 150, 200));
3972+ EXPECT_EQ(screen->depth(), 32);
3973+ EXPECT_EQ(screen->format(), QImage::Format_RGBA8888);
3974+ EXPECT_EQ(screen->refreshRate(), 59);
3975+ EXPECT_EQ(screen->physicalSize(), QSize(1111, 2222));
3976+ EXPECT_EQ(screen->outputType(), mg::DisplayConfigurationOutputType::dvid);
3977+}
3978+
3979+TEST_F(ScreenTest, ReadDifferentConfigurationFromDisplayConfig)
3980+{
3981+ Screen *screen = new Screen(fakeOutput2);
3982+
3983+ EXPECT_EQ(screen->geometry(), QRect(500, 600, 1500, 2000));
3984+ EXPECT_EQ(screen->availableGeometry(), QRect(500, 600, 1500, 2000));
3985+ EXPECT_EQ(screen->depth(), 32);
3986+ EXPECT_EQ(screen->format(), QImage::Format_RGBX8888);
3987+ EXPECT_EQ(screen->refreshRate(), 75);
3988+ EXPECT_EQ(screen->physicalSize(), QSize(1000, 2000));
3989+ EXPECT_EQ(screen->outputType(), mg::DisplayConfigurationOutputType::lvds);
3990+}
3991
3992=== added directory 'tests/mirserver/ScreenController'
3993=== added file 'tests/mirserver/ScreenController/CMakeLists.txt'
3994--- tests/mirserver/ScreenController/CMakeLists.txt 1970-01-01 00:00:00 +0000
3995+++ tests/mirserver/ScreenController/CMakeLists.txt 2015-09-30 13:40:22 +0000
3996@@ -0,0 +1,28 @@
3997+set(
3998+ SCREENCONTROLLER_TEST_SOURCES
3999+ screencontroller_test.cpp
4000+ ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
4001+ # to be moc-ed
4002+ stub_screen.h
4003+ testable_screencontroller.h
4004+)
4005+
4006+include_directories(
4007+ ${CMAKE_SOURCE_DIR}/tests/common
4008+ ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
4009+ ${CMAKE_SOURCE_DIR}/src/common
4010+ ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
4011+ ${MIRSERVER_INCLUDE_DIRS}
4012+)
4013+
4014+add_executable(ScreenControllerTest ${SCREENCONTROLLER_TEST_SOURCES})
4015+
4016+target_link_libraries(
4017+ ScreenControllerTest
4018+ qpa-mirserver
4019+
4020+ ${GTEST_BOTH_LIBRARIES}
4021+ ${GMOCK_LIBRARIES}
4022+)
4023+
4024+add_test(ScreenController, ScreenControllerTest)
4025
4026=== added file 'tests/mirserver/ScreenController/screencontroller_test.cpp'
4027--- tests/mirserver/ScreenController/screencontroller_test.cpp 1970-01-01 00:00:00 +0000
4028+++ tests/mirserver/ScreenController/screencontroller_test.cpp 2015-09-30 13:40:22 +0000
4029@@ -0,0 +1,189 @@
4030+/*
4031+ * Copyright (C) 2015 Canonical, Ltd.
4032+ *
4033+ * This program is free software: you can redistribute it and/or modify it under
4034+ * the terms of the GNU Lesser General Public License version 3, as published by
4035+ * the Free Software Foundation.
4036+ *
4037+ * This program is distributed in the hope that it will be useful, but WITHOUT
4038+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4039+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4040+ * Lesser General Public License for more details.
4041+ *
4042+ * You should have received a copy of the GNU Lesser General Public License
4043+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4044+ */
4045+
4046+#include <gtest/gtest.h>
4047+#include "gmock_fixes.h"
4048+
4049+#include "stub_display.h"
4050+#include "mock_main_loop.h"
4051+#include "qtcompositor.h"
4052+#include "fake_displayconfigurationoutput.h"
4053+
4054+#include "testable_screencontroller.h"
4055+#include "screen.h"
4056+#include "screenwindow.h"
4057+
4058+#include <QGuiApplication>
4059+
4060+using namespace ::testing;
4061+
4062+namespace mg = mir::graphics;
4063+namespace geom = mir::geometry;
4064+
4065+class ScreenControllerTest : public ::testing::Test {
4066+protected:
4067+ void SetUp() override;
4068+ void TearDown() override;
4069+
4070+ ScreenController *screenController;
4071+ std::shared_ptr<StubDisplay> display;
4072+ std::shared_ptr<QtCompositor> compositor;
4073+ QGuiApplication *app;
4074+};
4075+
4076+void ScreenControllerTest::SetUp()
4077+{
4078+ setenv("QT_QPA_PLATFORM", "minimal", 1);
4079+ Screen::skipDBusRegistration = true;
4080+
4081+ screenController = new TestableScreenController;
4082+ display = std::make_shared<StubDisplay>();
4083+ compositor = std::make_shared<QtCompositor>();
4084+
4085+ static_cast<TestableScreenController*>(screenController)->do_init(display, compositor);
4086+
4087+ int argc = 0;
4088+ char **argv = nullptr;
4089+ setenv("QT_QPA_PLATFORM", "minimal", 1);
4090+ app = new QGuiApplication(argc, argv);
4091+}
4092+
4093+void ScreenControllerTest::TearDown()
4094+{
4095+ delete screenController;
4096+}
4097+
4098+TEST_F(ScreenControllerTest, SingleScreenFound)
4099+{
4100+ // Set up display state
4101+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};
4102+ std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
4103+ display->setFakeConfiguration(config, bufferConfig);
4104+
4105+ screenController->update();
4106+
4107+ ASSERT_EQ(1, screenController->screens().count());
4108+ Screen* screen = screenController->screens().first();
4109+ EXPECT_EQ(QRect(0, 0, 150, 200), screen->geometry());
4110+}
4111+
4112+TEST_F(ScreenControllerTest, MultipleScreenFound)
4113+{
4114+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1, fakeOutput2};
4115+ std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
4116+ display->setFakeConfiguration(config, bufferConfig);
4117+
4118+ screenController->update();
4119+
4120+ ASSERT_EQ(2, screenController->screens().count());
4121+ EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(0)->geometry());
4122+ EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(1)->geometry());
4123+}
4124+
4125+TEST_F(ScreenControllerTest, ScreenAdded)
4126+{
4127+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};
4128+ std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
4129+ display->setFakeConfiguration(config, bufferConfig);
4130+
4131+ screenController->update();
4132+
4133+ config.push_back(fakeOutput2);
4134+ display->setFakeConfiguration(config, bufferConfig);
4135+
4136+ ASSERT_EQ(1, screenController->screens().count());
4137+ EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(0)->geometry());
4138+
4139+ screenController->update();
4140+
4141+ ASSERT_EQ(2, screenController->screens().count());
4142+ EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(0)->geometry());
4143+ EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(1)->geometry());
4144+}
4145+
4146+TEST_F(ScreenControllerTest, ScreenRemoved)
4147+{
4148+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput2, fakeOutput1};
4149+ std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
4150+ display->setFakeConfiguration(config, bufferConfig);
4151+
4152+ screenController->update();
4153+
4154+ config.pop_back();
4155+ display->setFakeConfiguration(config, bufferConfig);
4156+
4157+ ASSERT_EQ(2, screenController->screens().count());
4158+ EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(0)->geometry());
4159+ EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(1)->geometry());
4160+
4161+ screenController->update();
4162+
4163+ ASSERT_EQ(1, screenController->screens().count());
4164+ EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(0)->geometry());
4165+}
4166+
4167+TEST_F(ScreenControllerTest, CheckPrioritizedGetUnusedScreen)
4168+{
4169+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput2, fakeOutput1};
4170+ std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
4171+ display->setFakeConfiguration(config, bufferConfig);
4172+
4173+ screenController->update();
4174+
4175+ auto screen = screenController->getUnusedScreen();
4176+ EXPECT_EQ(mg::DisplayConfigurationOutputType::lvds, screen->outputType());
4177+}
4178+
4179+TEST_F(ScreenControllerTest, MatchBufferWithDisplay)
4180+{
4181+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};
4182+ MockDisplayBuffer buffer1;
4183+ std::vector<MockDisplayBuffer*> buffers {&buffer1};
4184+
4185+ geom::Rectangle buffer1Geom{{0, 0}, {150, 200}};
4186+ EXPECT_CALL(buffer1, view_area())
4187+ .WillRepeatedly(Return(buffer1Geom));
4188+
4189+ display->setFakeConfiguration(config, buffers);
4190+ screenController->update();
4191+
4192+ ASSERT_EQ(1, screenController->screens().count());
4193+ EXPECT_CALL(buffer1, make_current());
4194+ static_cast<StubScreen*>(screenController->screens().at(0))->makeCurrent();
4195+}
4196+
4197+TEST_F(ScreenControllerTest, MultipleMatchBuffersWithDisplays)
4198+{
4199+ std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1, fakeOutput2};
4200+ MockDisplayBuffer buffer1, buffer2;
4201+ std::vector<MockDisplayBuffer*> buffers {&buffer1, &buffer2};
4202+
4203+ geom::Rectangle buffer1Geom{{500, 600}, {1500, 2000}};
4204+ geom::Rectangle buffer2Geom{{0, 0}, {150, 200}};
4205+ EXPECT_CALL(buffer1, view_area())
4206+ .WillRepeatedly(Return(buffer1Geom));
4207+ EXPECT_CALL(buffer2, view_area())
4208+ .WillRepeatedly(Return(buffer2Geom));
4209+
4210+ display->setFakeConfiguration(config, buffers);
4211+ screenController->update();
4212+
4213+ ASSERT_EQ(2, screenController->screens().count());
4214+ EXPECT_CALL(buffer1, make_current());
4215+ EXPECT_CALL(buffer2, make_current());
4216+ static_cast<StubScreen*>(screenController->screens().at(0))->makeCurrent();
4217+ static_cast<StubScreen*>(screenController->screens().at(1))->makeCurrent();
4218+}
4219
4220=== added file 'tests/mirserver/ScreenController/stub_display.h'
4221--- tests/mirserver/ScreenController/stub_display.h 1970-01-01 00:00:00 +0000
4222+++ tests/mirserver/ScreenController/stub_display.h 2015-09-30 13:40:22 +0000
4223@@ -0,0 +1,96 @@
4224+/*
4225+ * Copyright (C) 2015 Canonical, Ltd.
4226+ *
4227+ * This program is free software: you can redistribute it and/or modify it under
4228+ * the terms of the GNU Lesser General Public License version 3, as published by
4229+ * the Free Software Foundation.
4230+ *
4231+ * This program is distributed in the hope that it will be useful, but WITHOUT
4232+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4233+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4234+ * Lesser General Public License for more details.
4235+ *
4236+ * You should have received a copy of the GNU Lesser General Public License
4237+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4238+ */
4239+
4240+#ifndef STUB_DISPLAY_H
4241+#define STUB_DISPLAY_H
4242+
4243+#include "mock_display.h"
4244+#include "mock_display_buffer.h"
4245+#include "mock_display_configuration.h"
4246+
4247+namespace mg = mir::graphics;
4248+namespace geom = mir::geometry;
4249+
4250+class StubDisplayConfiguration : public MockDisplayConfiguration
4251+{
4252+public:
4253+ StubDisplayConfiguration(const std::vector<mg::DisplayConfigurationOutput> &config)
4254+ : m_config(config)
4255+ {}
4256+
4257+ void for_each_output(std::function<void(mg::DisplayConfigurationOutput const&)> f) const override
4258+ {
4259+ for (auto config : m_config) {
4260+ f(config);
4261+ }
4262+ }
4263+
4264+private:
4265+ const std::vector<mg::DisplayConfigurationOutput> m_config;
4266+};
4267+
4268+
4269+class StubDisplaySyncGroup : public MockDisplaySyncGroup
4270+{
4271+public:
4272+ StubDisplaySyncGroup(MockDisplayBuffer *buffer) : buffer(buffer) {}
4273+
4274+ void for_each_display_buffer(std::function<void(mg::DisplayBuffer&)> const& f) override
4275+ {
4276+ f(*buffer);
4277+ }
4278+ std::chrono::milliseconds recommended_sleep() const
4279+ {
4280+ std::chrono::milliseconds one{1};
4281+ return one;
4282+ }
4283+private:
4284+ MockDisplayBuffer *buffer;
4285+};
4286+
4287+
4288+class StubDisplay : public MockDisplay
4289+{
4290+public:
4291+ // Note, GMock cannot mock functions which return non-copyable objects, so stubbing needed
4292+ std::unique_ptr<mg::DisplayConfiguration> configuration() const override
4293+ {
4294+ return std::unique_ptr<mg::DisplayConfiguration>(
4295+ new StubDisplayConfiguration(m_config)
4296+ );
4297+ }
4298+
4299+ void for_each_display_sync_group(std::function<void(mg::DisplaySyncGroup&)> const& f) override
4300+ {
4301+ for (auto displayBuffer : m_displayBuffers) {
4302+ StubDisplaySyncGroup b(displayBuffer);
4303+ f(b);
4304+ }
4305+ }
4306+
4307+ void setFakeConfiguration(std::vector<mg::DisplayConfigurationOutput> &config,
4308+ std::vector<MockDisplayBuffer*> &displayBuffers)
4309+ {
4310+ m_config = config;
4311+ m_displayBuffers = displayBuffers;
4312+ }
4313+
4314+private:
4315+ std::vector<mg::DisplayConfigurationOutput> m_config;
4316+ std::vector<MockDisplayBuffer*> m_displayBuffers;
4317+};
4318+
4319+#endif // STUB_DISPLAY_H
4320
4321=== added file 'tests/mirserver/ScreenController/stub_screen.h'
4322--- tests/mirserver/ScreenController/stub_screen.h 1970-01-01 00:00:00 +0000
4323+++ tests/mirserver/ScreenController/stub_screen.h 2015-09-30 13:40:22 +0000
4324@@ -0,0 +1,31 @@
4325+/*
4326+ * Copyright (C) 2015 Canonical, Ltd.
4327+ *
4328+ * This program is free software: you can redistribute it and/or modify it under
4329+ * the terms of the GNU Lesser General Public License version 3, as published by
4330+ * the Free Software Foundation.
4331+ *
4332+ * This program is distributed in the hope that it will be useful, but WITHOUT
4333+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4334+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4335+ * Lesser General Public License for more details.
4336+ *
4337+ * You should have received a copy of the GNU Lesser General Public License
4338+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4339+ */
4340+
4341+#ifndef STUBSCREEN_H
4342+#define STUBSCREEN_H
4343+
4344+#include "screen.h"
4345+
4346+class StubScreen : public Screen
4347+{
4348+ Q_OBJECT
4349+public:
4350+ StubScreen(const mir::graphics::DisplayConfigurationOutput &output) : Screen(output) {}
4351+
4352+ void makeCurrent() { Screen::makeCurrent(); }
4353+};
4354+
4355+#endif // STUBSCREEN_H
4356
4357=== added file 'tests/mirserver/ScreenController/testable_screencontroller.h'
4358--- tests/mirserver/ScreenController/testable_screencontroller.h 1970-01-01 00:00:00 +0000
4359+++ tests/mirserver/ScreenController/testable_screencontroller.h 2015-09-30 13:40:22 +0000
4360@@ -0,0 +1,37 @@
4361+/*
4362+ * Copyright (C) 2015 Canonical, Ltd.
4363+ *
4364+ * This program is free software: you can redistribute it and/or modify it under
4365+ * the terms of the GNU Lesser General Public License version 3, as published by
4366+ * the Free Software Foundation.
4367+ *
4368+ * This program is distributed in the hope that it will be useful, but WITHOUT
4369+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
4370+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
4371+ * Lesser General Public License for more details.
4372+ *
4373+ * You should have received a copy of the GNU Lesser General Public License
4374+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4375+ */
4376+
4377+#include "screencontroller.h"
4378+#include "stub_screen.h"
4379+
4380+struct TestableScreenController : public ScreenController
4381+{
4382+ Q_OBJECT
4383+
4384+public:
4385+ Screen *createScreen(const mir::graphics::DisplayConfigurationOutput &output) const override
4386+ {
4387+ return new StubScreen(output);
4388+ }
4389+
4390+ void do_init(const std::shared_ptr<mir::graphics::Display> &display,
4391+ const std::shared_ptr<mir::compositor::Compositor> &compositor)
4392+ {
4393+ init(display, compositor);
4394+ }
4395+
4396+ void do_terminate() { terminate(); }
4397+};
4398
4399=== modified file 'tests/modules/common/qtmir_test.h'
4400--- tests/modules/common/qtmir_test.h 2015-08-11 12:08:32 +0000
4401+++ tests/modules/common/qtmir_test.h 2015-09-30 13:40:22 +0000
4402@@ -78,7 +78,7 @@
4403 {
4404 public:
4405 FakeMirServer()
4406- : MirServer(0, argv)
4407+ : MirServer(0, argv, QSharedPointer<ScreenController>())
4408 {
4409 }
4410

Subscribers

People subscribed via source and target branches