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

Proposed by Gerry Boland
Status: Merged
Approved by: Michał Sawicz
Approved revision: 391
Merged at revision: 389
Proposed branch: lp:~gerboland/qtmir/multimonitor
Merge into: lp:qtmir
Prerequisite: lp:~dandrader/qtmir/mousePointer
Diff against target: 3744 lines (+2189/-462)
55 files modified
CMakeLists.txt (+3/-3)
demos/qml-demo-shell/qml-demo-shell.qml (+19/-0)
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/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 (+5/-2)
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 (+32/-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/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 (+104/-85)
src/platforms/mirserver/qteventfeeder.h (+11/-8)
src/platforms/mirserver/screen.cpp (+109/-7)
src/platforms/mirserver/screen.h (+33/-3)
src/platforms/mirserver/screencontroller.cpp (+258/-0)
src/platforms/mirserver/screencontroller.h (+96/-0)
src/platforms/mirserver/screenwindow.cpp (+71/-92)
src/platforms/mirserver/screenwindow.h (+13/-24)
src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp (+44/-0)
src/platforms/mirserver/tileddisplayconfigurationpolicy.h (+35/-0)
tests/common/fake_displayconfigurationoutput.h (+77/-0)
tests/common/gmock_fixes.h (+124/-0)
tests/common/mock_display.h (+53/-0)
tests/common/mock_display_configuration.h (+35/-0)
tests/common/mock_gl_display_buffer.h (+49/-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/-26)
tests/mirserver/ScreenController/CMakeLists.txt (+29/-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
Michał Sawicz Abstain
PS Jenkins bot (community) continuous-integration Needs Fixing
Daniel d'Andrada (community) Approve
Review via email: mp+272912@code.launchpad.net

This proposal supersedes a proposal from 2015-09-02.

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

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

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Revision history for this message
Michael Terry (mterry) wrote : Posted in a previous version of this proposal

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

Revision history for this message
Michael Terry (mterry) wrote : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

ScreenController test fails.

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

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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

> > 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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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 : Posted in a previous version of this proposal

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
Revision history for this message
Daniel d'Andrada (dandrader) wrote :

LGTM

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

This merge breaks unity8's autopilot panel tests:

$ autopilot3 run unity8.indicators.tests.test_indicators.IndicatorPageTitleMatchesWidgetTestCase.test_indicator_page_title_matches_widget

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/unity8/indicators/tests/test_indicators.py", line 79, in test_indicator_page_title_matches_widget
    self.indicator_name)
  File "/usr/lib/python3/dist-packages/autopilot/logging.py", line 46, in inner
    return f(instance, *args, **kwargs)
  File "/usr/lib/python3/dist-packages/unity8/shell/__init__.py", line 146, in open_indicator_page
    self.wait_select_single('IndicatorsMenu', fullyOpened=True)
  File "/usr/lib/python3/dist-packages/autopilot/introspection/dbus.py", line 295, in wait_select_single
    return self.select_single(type_name, **kwargs)
  File "/usr/lib/python3/dist-packages/autopilot/introspection/dbus.py", line 244, in select_single
    raise StateNotFoundError(type_name_str, **kwargs)
autopilot.exceptions.StateNotFoundError: Object not found with name 'IndicatorsMenu' and properties {'fullyOpened': True}.

You can reproduce with unity8 trunk by installing unity8-fake-env along with qtmir from silo 22. The problem appears to be that when the panel drag gesture reaches the bottom, about 50% of the time the touch isn't ended, which results in the panel getting stuck a few pixels above the bottom edge, never completing the gesture.

I'll be providing some debugging output from DirectionalDragArea and TouchRegistry soon.

review: Needs Fixing
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

http://paste.ubuntu.com/12878612/ shows that there's no TouchEnd delivered to the DirectionalDragArea, compared to working example before this MP: http://paste.ubuntu.com/12878646/

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

On 20/10/2015 16:24, Michał Sawicz wrote:
> Review: Needs Fixing
>
> http://paste.ubuntu.com/12878612/ shows that there's no TouchEnd delivered to the DirectionalDragArea, compared to working example before this MP: http://paste.ubuntu.com/12878646/

There's no TouchEnd delivered to the QQuickWindow at all. Maybe qtmir's
QtEventFeeder discarded it somehow?

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

MirTouchEvent((id=1,action=change,x=1048.09,y=1920.35))
qtmir.mir.input: REJECTING INPUT EVENT, no matching window
qtmir.mir.input: Received MirTouchEvent((id=1,action=up,x=1048.09,y=1920.35))
qtmir.mir.input: REJECTING INPUT EVENT, no matching window

Position technically beyond bottom edge of window, but will workaround

Revision history for this message
Gerry Boland (gerboland) wrote :
Revision history for this message
Michał Sawicz (saviq) :
review: Abstain

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

Subscribers

People subscribed via source and target branches