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

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

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

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

Commit message

Initial multimonitor support - react correctly to Mir DisplayConfiguration changes.

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

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

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

Description of the change

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

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

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

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

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

Gah, input on second display not working

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

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

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

Shouldn't they be private?

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

src/platforms/mirserver/miropenglcontext.h

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

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

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

In MirServerIntegration::createPlatformWindow:

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

A leftover.

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

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

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

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

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

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

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

In MirServerIntegration::initialize()

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

More qDebug. Maybe this should be a qFatal?

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

In src/platforms/mirserver/offscreensurface.cpp

"""
#include <QDebug>
"""

You don't need that.

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

In src/platforms/mirserver/qmirserver.h:

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

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

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

QtEventFeeder::QtEventFeeder

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

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

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

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

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

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

Why remove it?

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

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

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

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

Please update...

Read more...

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

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

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

Rebooted the laptop.

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

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

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

I'll be addressing your code comments soon.

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

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

Can you compare with mir_proving_server too?

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

"""

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

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

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

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

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

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

src/platforms/mirserver/screencontroller.cpp

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

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

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

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

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

In tests/mirserver/ScreenController/screencontroller_test.cpp

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

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

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

Same goes for EXPECT_EQ() and friends.

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

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

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

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

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

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

Code looks ok.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

So I'm guessing I need another branch?

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

ScreenController test fails.

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

Merge trunk

382. By Gerry Boland

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

383. By Gerry Boland

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

384. By Gerry Boland

Add reassuring debug message

385. By Gerry Boland

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

386. By Gerry Boland

Fix test fail

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

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

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

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

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

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

Code still looks ok and tests pass again.

Proceeding with manual tests now...

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

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

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

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

Merge trunk

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

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

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

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

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

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

Tested with a fresh image (no multimonitor branch).

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

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

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

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

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

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

Should it work? or is the crash expected?

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

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

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

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

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

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

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

389. By Gerry Boland

Merge mousePointer again

390. By Gerry Boland

Fix conflicts but have ScreenController test failing

391. By Gerry Boland

Fix failing test, RenderTarget not derived from NativeDisplayBuffer

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-09-17 20:17:17 +0000
+++ CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -75,7 +75,7 @@
75pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)75pkg_check_modules(GSETTINGS_QT REQUIRED gsettings-qt)
76pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)76pkg_check_modules(QTDBUSTEST libqtdbustest-1 REQUIRED)
77pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)77pkg_check_modules(QTDBUSMOCK libqtdbusmock-1 REQUIRED)
78pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=8)78pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=9)
7979
80include_directories(${APPLICATION_API_INCLUDE_DIRS})80include_directories(${APPLICATION_API_INCLUDE_DIRS})
8181
8282
=== modified file 'debian/control'
--- debian/control 2015-09-25 12:11:08 +0000
+++ debian/control 2015-09-30 13:40:22 +0000
@@ -23,7 +23,7 @@
23 libubuntu-app-launch2-dev,23 libubuntu-app-launch2-dev,
24 libubuntu-application-api-dev (>= 2.1.0),24 libubuntu-application-api-dev (>= 2.1.0),
25 libudev-dev,25 libudev-dev,
26 libunity-api-dev (>= 7.100),26 libunity-api-dev (>= 7.101),
27 liburl-dispatcher1-dev,27 liburl-dispatcher1-dev,
28 libxkbcommon-dev,28 libxkbcommon-dev,
29 libxrender-dev,29 libxrender-dev,
@@ -93,7 +93,7 @@
93Conflicts: libqtmir,93Conflicts: libqtmir,
94 libunity-mir1,94 libunity-mir1,
95Provides: unity-application-impl,95Provides: unity-application-impl,
96 unity-application-impl-8,96 unity-application-impl-9,
97Description: Qt plugin for Unity specific Mir APIs97Description: Qt plugin for Unity specific Mir APIs
98 QtMir provides Qt/QML bindings for Mir features that are exposed through the98 QtMir provides Qt/QML bindings for Mir features that are exposed through the
99 qtmir-desktop or qtmir-android QPA plugin such as Application management99 qtmir-desktop or qtmir-android QPA plugin such as Application management
100100
=== added file 'demos/qml-demo-shell/ResizeArea.qml'
--- demos/qml-demo-shell/ResizeArea.qml 1970-01-01 00:00:00 +0000
+++ demos/qml-demo-shell/ResizeArea.qml 2015-09-30 13:40:22 +0000
@@ -0,0 +1,128 @@
1import QtQuick 2.4
2import Unity.Application 0.1
3
4MouseArea {
5 id: root
6
7 // to be set from outside
8 property Item target
9 property real borderThickness
10
11 property bool leftBorder: false
12 property bool rightBorder: false
13 property bool topBorder: false
14 property bool bottomBorder: false
15
16 property bool dragging: false
17 property real startX
18 property real startY
19 property real startWidth
20 property real startHeight
21
22 hoverEnabled: true
23
24 property string cursorName: {
25 if (containsMouse || pressed) {
26 if (leftBorder && !topBorder && !bottomBorder) {
27 return "left_side";
28 } else if (rightBorder && !topBorder && !bottomBorder) {
29 return "right_side";
30 } else if (topBorder && !leftBorder && !rightBorder) {
31 return "top_side";
32 } else if (bottomBorder && !leftBorder && !rightBorder) {
33 return "bottom_side";
34 } else if (leftBorder && topBorder) {
35 return "top_left_corner";
36 } else if (leftBorder && bottomBorder) {
37 return "bottom_left_corner";
38 } else if (rightBorder && topBorder) {
39 return "top_right_corner";
40 } else if (rightBorder && bottomBorder) {
41 return "bottom_right_corner";
42 } else {
43 return "";
44 }
45 } else {
46 return "";
47 }
48 }
49 onCursorNameChanged: {
50 Mir.cursorName = cursorName;
51 }
52
53 function updateBorders() {
54 leftBorder = mouseX <= borderThickness;
55 rightBorder = mouseX >= width - borderThickness;
56 topBorder = mouseY <= borderThickness;
57 bottomBorder = mouseY >= height - borderThickness;
58 }
59
60 onPressedChanged: {
61 if (pressed) {
62 var pos = mapToItem(target.parent, mouseX, mouseY);
63 startX = pos.x;
64 startY = pos.y;
65 startWidth = target.width;
66 startHeight = target.height;
67 dragging = true;
68 } else {
69 dragging = false;
70 if (containsMouse) {
71 updateBorders();
72 }
73 }
74 }
75
76 onEntered: {
77 if (!pressed) {
78 updateBorders();
79 }
80 }
81
82 onPositionChanged: {
83 if (!pressed) {
84 updateBorders();
85 }
86
87 if (!dragging) {
88 return;
89 }
90
91 var pos = mapToItem(target.parent, mouse.x, mouse.y);
92
93 if (leftBorder) {
94 if (startX + startWidth - pos.x > target.minWidth) {
95 target.x = pos.x;
96 target.width = startX + startWidth - target.x;
97 startX = target.x;
98 startWidth = target.width;
99 }
100
101 } else if (rightBorder) {
102 var deltaX = pos.x - startX;
103 if (startWidth + deltaX >= target.minWidth) {
104 target.width = startWidth + deltaX;
105 } else {
106 target.width = target.minWidth;
107 }
108 }
109
110 if (topBorder) {
111 if (startY + startHeight - pos.y > target.minHeight) {
112 target.y = pos.y;
113 target.height = startY + startHeight - target.y;
114 startY = target.y;
115 startHeight = target.height;
116 }
117
118 } else if (bottomBorder) {
119 var deltaY = pos.y - startY;
120 if (startHeight + deltaY >= target.minHeight) {
121 target.height = startHeight + deltaY;
122 } else {
123 target.height = target.minHeight;
124 }
125 }
126 }
127}
128
0129
=== renamed file 'demos/qml-demo-shell/qml-demo-shell.qml' => 'demos/qml-demo-shell/Shell.qml'
--- demos/qml-demo-shell/qml-demo-shell.qml 2015-08-31 09:51:28 +0000
+++ demos/qml-demo-shell/Shell.qml 2015-09-30 13:40:22 +0000
@@ -1,4 +1,4 @@
1import QtQuick 2.01import QtQuick 2.4
2import Unity.Application 0.12import Unity.Application 0.1
33
4Rectangle {4Rectangle {
@@ -88,6 +88,7 @@
88 }88 }
8989
90 Rectangle {90 Rectangle {
91 id: resizeButton
91 width: 9092 width: 90
92 height: 4093 height: 40
93 color: "blue"94 color: "blue"
@@ -103,6 +104,23 @@
103 }104 }
104 }105 }
105106
107 Rectangle {
108 width: 40
109 height: 40
110 color: "green"
111 anchors { right: resizeButton.left; bottom: parent.bottom }
112 Text {
113 anchors.centerIn: parent
114 text: "⟳"
115 color: "white"
116 font.pixelSize: 35
117 }
118 MouseArea {
119 anchors.fill: parent
120 onClicked: { root.rotation += 180; }
121 }
122 }
123
106 Component {124 Component {
107 id: windowStretchComponent125 id: windowStretchComponent
108 Window {126 Window {
109127
=== modified file 'demos/qml-demo-shell/TitleBar.qml'
--- demos/qml-demo-shell/TitleBar.qml 2015-08-24 12:43:01 +0000
+++ demos/qml-demo-shell/TitleBar.qml 2015-09-30 13:40:22 +0000
@@ -1,4 +1,5 @@
1import QtQuick 2.01import QtQuick 2.4
2import Unity.Application 0.1
23
3Rectangle {4Rectangle {
4 id: root5 id: root
@@ -21,8 +22,10 @@
21 distanceX = pos.x;22 distanceX = pos.x;
22 distanceY = pos.y;23 distanceY = pos.y;
23 dragging = true;24 dragging = true;
25 Mir.cursorName = "grabbing";
24 } else {26 } else {
25 dragging = false;27 dragging = false;
28 Mir.cursorName = "";
26 }29 }
27 }30 }
28 onMouseXChanged: {31 onMouseXChanged: {
2932
=== modified file 'demos/qml-demo-shell/Window.qml'
--- demos/qml-demo-shell/Window.qml 2015-08-31 09:51:28 +0000
+++ demos/qml-demo-shell/Window.qml 2015-09-30 13:40:22 +0000
@@ -58,87 +58,10 @@
58 }58 }
59 ]59 ]
6060
6161 ResizeArea {
62 MouseArea {62 anchors.fill: root
63 anchors.fill: parent63 borderThickness: root.borderThickness
6464 target: root
65 property real startX
66 property real startY
67 property real startWidth
68 property real startHeight
69 property bool leftBorder
70 property bool rightBorder
71 property bool topBorder
72 property bool bottomBorder
73 property bool dragging
74 onPressedChanged: {
75 if (pressed) {
76 var pos = mapToItem(root.parent, mouseX, mouseY);
77 startX = pos.x;
78 startY = pos.y;
79 startWidth = width;
80 startHeight = height;
81 leftBorder = mouseX > 0 && mouseX < root.borderThickness;
82 rightBorder = mouseX > (root.width - root.borderThickness) && mouseX < root.width;
83 topBorder = mouseY > 0 && mouseY < root.borderThickness;
84 bottomBorder = mouseY > (root.height - root.borderThickness) && mouseY < root.height;
85 dragging = true;
86 } else {
87 dragging = false;
88 }
89 }
90
91 onMouseXChanged: {
92 if (!pressed || !dragging) {
93 return;
94 }
95
96 var pos = mapToItem(root.parent, mouseX, mouseY);
97
98 if (leftBorder) {
99
100 if (startX + startWidth - pos.x > root.minWidth) {
101 root.x = pos.x;
102 root.width = startX + startWidth - root.x;
103 startX = root.x;
104 startWidth = root.width;
105 }
106
107 } else if (rightBorder) {
108 var deltaX = pos.x - startX;
109 if (startWidth + deltaX >= root.minWidth) {
110 root.width = startWidth + deltaX;
111 } else {
112 root.width = root.minWidth;
113 }
114 }
115 }
116
117 onMouseYChanged: {
118 if (!pressed || !dragging) {
119 return;
120 }
121
122 var pos = mapToItem(root.parent, mouseX, mouseY);
123
124 if (topBorder) {
125
126 if (startY + startHeight - pos.y > root.minHeight) {
127 root.y = pos.y;
128 root.height = startY + startHeight - root.y;
129 startY = root.y;
130 startHeight = root.height;
131 }
132
133 } else if (bottomBorder) {
134 var deltaY = pos.y - startY;
135 if (startHeight + deltaY >= root.minHeight) {
136 root.height = startHeight + deltaY;
137 } else {
138 root.height = root.minHeight;
139 }
140 }
141 }
142 }65 }
14366
144 TitleBar {67 TitleBar {
14568
=== added file 'demos/qml-demo-shell/qml-demo-shell.qml'
--- demos/qml-demo-shell/qml-demo-shell.qml 1970-01-01 00:00:00 +0000
+++ demos/qml-demo-shell/qml-demo-shell.qml 2015-09-30 13:40:22 +0000
@@ -0,0 +1,19 @@
1import QtQuick 2.3
2import QtQuick.Window 2.2 as QQW
3import Unity.Screens 0.1
4
5Instantiator {
6 id: root
7
8 property var screens: Screens{}
9
10 model: screens
11 QQW.Window {
12 id: window
13 visible: true
14 Shell{ anchors.fill: parent }
15 Component.onCompleted: {
16 print("HEY", screen, screen.geometry, outputType, Screens.HDMIA)
17 }
18 }
19}
020
=== modified file 'src/modules/Unity/Application/CMakeLists.txt'
--- src/modules/Unity/Application/CMakeLists.txt 2015-09-17 16:17:17 +0000
+++ src/modules/Unity/Application/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -92,4 +92,3 @@
92# install92# install
93add_qml_plugin(Unity.Application 0.1 Unity/Application TARGETS unityapplicationplugin)93add_qml_plugin(Unity.Application 0.1 Unity/Application TARGETS unityapplicationplugin)
94install(FILES com.canonical.qtmir.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)94install(FILES com.canonical.qtmir.gschema.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/glib-2.0/schemas)
95
9695
=== modified file 'src/modules/Unity/Application/mirbuffersgtexture.cpp'
--- src/modules/Unity/Application/mirbuffersgtexture.cpp 2015-09-17 16:46:37 +0000
+++ src/modules/Unity/Application/mirbuffersgtexture.cpp 2015-09-30 13:40:22 +0000
@@ -59,6 +59,11 @@
59 m_width = size.width.as_int();59 m_width = size.width.as_int();
60}60}
6161
62bool MirBufferSGTexture::hasBuffer() const
63{
64 return !!m_mirBuffer;
65}
66
62int MirBufferSGTexture::textureId() const67int MirBufferSGTexture::textureId() const
63{68{
64 return m_textureId;69 return m_textureId;
6570
=== modified file 'src/modules/Unity/Application/mirbuffersgtexture.h'
--- src/modules/Unity/Application/mirbuffersgtexture.h 2015-08-31 09:51:28 +0000
+++ src/modules/Unity/Application/mirbuffersgtexture.h 2015-09-30 13:40:22 +0000
@@ -34,6 +34,7 @@
3434
35 void setBuffer(std::shared_ptr<mir::graphics::Buffer> buffer);35 void setBuffer(std::shared_ptr<mir::graphics::Buffer> buffer);
36 void freeBuffer();36 void freeBuffer();
37 bool hasBuffer() const;
3738
38 int textureId() const override;39 int textureId() const override;
39 QSize textureSize() const override;40 QSize textureSize() const override;
4041
=== modified file 'src/modules/Unity/Application/mirsurface.cpp'
--- src/modules/Unity/Application/mirsurface.cpp 2015-09-18 16:33:06 +0000
+++ src/modules/Unity/Application/mirsurface.cpp 2015-09-30 13:40:22 +0000
@@ -323,7 +323,9 @@
323 const void* const userId = (void*)123;323 const void* const userId = (void*)123;
324 auto renderables = m_surface->generate_renderables(userId);324 auto renderables = m_surface->generate_renderables(userId);
325325
326 if (m_surface->buffers_ready_for_compositor(userId) > 0 && renderables.size() > 0) {326 if (renderables.size() > 0 &&
327 (m_surface->buffers_ready_for_compositor(userId) > 0 || !texture->hasBuffer())
328 ) {
327 // Avoid holding two buffers for the compositor at the same time. Thus free the current329 // Avoid holding two buffers for the compositor at the same time. Thus free the current
328 // before acquiring the next330 // before acquiring the next
329 texture->freeBuffer();331 texture->freeBuffer();
330332
=== modified file 'src/modules/Unity/Application/plugin.cpp'
--- src/modules/Unity/Application/plugin.cpp 2015-08-31 09:51:28 +0000
+++ src/modules/Unity/Application/plugin.cpp 2015-09-30 13:40:22 +0000
@@ -27,6 +27,9 @@
27#include "sessionmanager.h"27#include "sessionmanager.h"
28#include "ubuntukeyboardinfo.h"28#include "ubuntukeyboardinfo.h"
2929
30// platforms/mirserver
31#include <mirsingleton.h>
32
30// qtmir33// qtmir
31#include "logging.h"34#include "logging.h"
3235
@@ -64,6 +67,10 @@
64 }67 }
65 return UbuntuKeyboardInfo::instance();68 return UbuntuKeyboardInfo::instance();
66}69}
70
71QObject* mirSingleton(QQmlEngine* /*engine*/, QJSEngine* /*scriptEngine*/) {
72 return qtmir::Mir::instance();
73}
67} // anonymous namespace74} // anonymous namespace
6875
69class UnityApplicationPlugin : public QQmlExtensionPlugin {76class UnityApplicationPlugin : public QQmlExtensionPlugin {
@@ -102,7 +109,7 @@
102 uri, 0, 1, "Session", "Session can't be instantiated from QML");109 uri, 0, 1, "Session", "Session can't be instantiated from QML");
103 qmlRegisterSingletonType<qtmir::UbuntuKeyboardInfo>(110 qmlRegisterSingletonType<qtmir::UbuntuKeyboardInfo>(
104 uri, 0, 1, "UbuntuKeyboardInfo", ubuntuKeyboardInfoSingleton);111 uri, 0, 1, "UbuntuKeyboardInfo", ubuntuKeyboardInfoSingleton);
105 qmlRegisterUncreatableType<Mir>(uri, 0, 1, "Mir", "Mir provides enum values, it can't be instantiated");112 qmlRegisterSingletonType<qtmir::Mir>(uri, 0, 1, "Mir", mirSingleton);
106 }113 }
107114
108 virtual void initializeEngine(QQmlEngine *engine, const char *uri)115 virtual void initializeEngine(QQmlEngine *engine, const char *uri)
109116
=== modified file 'src/modules/Unity/CMakeLists.txt'
--- src/modules/Unity/CMakeLists.txt 2014-09-22 18:06:58 +0000
+++ src/modules/Unity/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -1,1 +1,2 @@
1add_subdirectory(Application)1add_subdirectory(Application)
2add_subdirectory(Screens)
23
=== added directory 'src/modules/Unity/Screens'
=== added file 'src/modules/Unity/Screens/CMakeLists.txt'
--- src/modules/Unity/Screens/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/modules/Unity/Screens/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -0,0 +1,24 @@
1include_directories(
2 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
3 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
4 ${MIRSERVER_INCLUDE_DIRS}
5 )
6
7set(SCREENSPLUGIN_SRC
8 plugin.cpp
9 screens.cpp
10 )
11
12add_library(unityscreensplugin SHARED
13 ${SCREENSPLUGIN_SRC}
14)
15
16target_link_libraries(
17 unityscreensplugin
18
19 Qt5::Gui
20 Qt5::Qml
21)
22
23# install
24add_qml_plugin(Unity.Screens 0.1 Unity/Screens TARGETS unityscreensplugin)
025
=== added file 'src/modules/Unity/Screens/plugin.cpp'
--- src/modules/Unity/Screens/plugin.cpp 1970-01-01 00:00:00 +0000
+++ src/modules/Unity/Screens/plugin.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,41 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17// Qt
18#include <QQmlExtensionPlugin>
19#include <QtQml/qqml.h>
20#include <QScreen>
21
22// local
23#include "screens.h"
24
25using namespace qtmir;
26
27class UnityScreensPlugin : public QQmlExtensionPlugin {
28 Q_OBJECT
29 Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0")
30
31 virtual void registerTypes(const char* uri)
32 {
33 Q_ASSERT(QLatin1String(uri) == QLatin1String("Unity.Screens"));
34
35 qRegisterMetaType<QScreen*>("QScreen*");
36
37 qmlRegisterType<qtmir::Screens>(uri, 0, 1, "Screens");
38 }
39};
40
41#include "plugin.moc"
042
=== added file 'src/modules/Unity/Screens/qmldir'
--- src/modules/Unity/Screens/qmldir 1970-01-01 00:00:00 +0000
+++ src/modules/Unity/Screens/qmldir 2015-09-30 13:40:22 +0000
@@ -0,0 +1,2 @@
1module Unity.Screens
2plugin unityscreensplugin
03
=== added file 'src/modules/Unity/Screens/screens.cpp'
--- src/modules/Unity/Screens/screens.cpp 1970-01-01 00:00:00 +0000
+++ src/modules/Unity/Screens/screens.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,107 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "screens.h"
18
19// mirserver
20#include "screen.h"
21
22// Qt
23#include <QGuiApplication>
24#include <QScreen>
25
26Q_DECLARE_METATYPE(QScreen*)
27
28namespace qtmir {
29
30Screens::Screens(QObject *parent) :
31 QAbstractListModel(parent)
32{
33 auto app = static_cast<QGuiApplication *>(QGuiApplication::instance());
34 if (!app) {
35 return;
36 }
37 connect(app, &QGuiApplication::screenAdded, this, &Screens::onScreenAdded);
38 connect(app, &QGuiApplication::screenRemoved, this, &Screens::onScreenRemoved);
39
40 m_screenList = QGuiApplication::screens();
41}
42
43QHash<int, QByteArray> Screens::roleNames() const
44{
45 QHash<int, QByteArray> roles;
46 roles[ScreenRole] = "screen";
47 roles[OutputTypeRole] = "outputType";
48 return roles;
49}
50
51QVariant Screens::data(const QModelIndex &index, int role) const
52{
53 if (!index.isValid() || index.row() >= m_screenList.size()) {
54 return QVariant();
55 }
56
57 switch(role) {
58 case ScreenRole:
59 return QVariant::fromValue(m_screenList.at(index.row()));
60 case OutputTypeRole:
61 auto screen = static_cast<Screen*>(m_screenList.at(index.row())->handle());
62 if (screen) {
63 return QVariant(static_cast<OutputTypes>(screen->outputType())); //FIXME: cheeky
64 } else
65 return OutputTypes::Unknown;
66 }
67
68 return QVariant();
69}
70
71int Screens::rowCount(const QModelIndex &) const
72{
73 return count();
74}
75
76int Screens::count() const
77{
78 return m_screenList.size();
79}
80
81void Screens::onScreenAdded(QScreen *screen)
82{
83 if (m_screenList.contains(screen))
84 return;
85
86 beginInsertRows(QModelIndex(), count(), count());
87 m_screenList.push_back(screen);
88 endInsertRows();
89 Q_EMIT screenAdded(screen);
90 Q_EMIT countChanged();
91}
92
93void Screens::onScreenRemoved(QScreen *screen)
94{
95 int index = m_screenList.indexOf(screen);
96 if (index < 0)
97 return;
98
99 beginRemoveRows(QModelIndex(), index, index);
100 m_screenList.removeAt(index);
101 endRemoveRows();
102 Q_EMIT screenRemoved(screen);
103 Q_EMIT countChanged();
104}
105
106
107} // namespace qtmir
0108
=== added file 'src/modules/Unity/Screens/screens.h'
--- src/modules/Unity/Screens/screens.h 1970-01-01 00:00:00 +0000
+++ src/modules/Unity/Screens/screens.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,82 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef SCREENS_H
18#define SCREENS_H
19
20#include <QAbstractListModel>
21
22class QScreen;
23
24namespace qtmir {
25
26class Screens : public QAbstractListModel
27{
28 Q_OBJECT
29 Q_ENUMS(OutputTypes)
30
31 Q_PROPERTY(int count READ count NOTIFY countChanged)
32
33public:
34 enum ItemRoles {
35 ScreenRole = Qt::UserRole + 1,
36 OutputTypeRole
37 };
38
39 enum OutputTypes {
40 Unknown,
41 VGA,
42 DVII,
43 DVID,
44 DVIA,
45 Composite,
46 SVideo,
47 LVDS,
48 Component,
49 NinePinDIN,
50 DisplayPort,
51 HDMIA,
52 HDMIB,
53 TV,
54 EDP
55 };
56
57 explicit Screens(QObject *parent = 0);
58 virtual ~Screens() noexcept = default;
59
60 /* QAbstractItemModel */
61 QHash<int, QByteArray> roleNames() const override;
62 QVariant data(const QModelIndex &index, int role = ScreenRole) const override;
63 int rowCount(const QModelIndex &parent = QModelIndex()) const override;
64
65 int count() const;
66
67Q_SIGNALS:
68 void countChanged();
69 void screenAdded(QScreen *screen);
70 void screenRemoved(QScreen *screen);
71
72private Q_SLOTS:
73 void onScreenAdded(QScreen *screen);
74 void onScreenRemoved(QScreen *screen);
75
76private:
77 QList<QScreen *> m_screenList;
78};
79
80} // namespace qtmir
81
82#endif // SCREENS_H
083
=== modified file 'src/platforms/mirserver/CMakeLists.txt'
--- src/platforms/mirserver/CMakeLists.txt 2015-08-11 19:25:04 +0000
+++ src/platforms/mirserver/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -30,6 +30,7 @@
30 ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS}30 ${QT5PLATFORM_SUPPORT_INCLUDE_DIRS}
31 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}31 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
32 ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS}32 ${QT5_PLATFORMSUPPORT_INCLUDE_DIRS}
33 ${Qt5Quick_PRIVATE_INCLUDE_DIRS}
3334
34 ${APPLICATION_API_INCLUDE_DIRS}35 ${APPLICATION_API_INCLUDE_DIRS}
35)36)
@@ -41,7 +42,9 @@
4142
42set(MIRSERVER_QPA_PLUGIN_SRC43set(MIRSERVER_QPA_PLUGIN_SRC
43 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp44 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
45 cursor.cpp
44 mirwindowmanager.cpp46 mirwindowmanager.cpp
47 mirsingleton.cpp
45 qteventfeeder.cpp48 qteventfeeder.cpp
46 plugin.cpp49 plugin.cpp
47 qmirserver.cpp50 qmirserver.cpp
@@ -52,17 +55,21 @@
52 promptsessionlistener.cpp55 promptsessionlistener.cpp
53 mirserver.cpp56 mirserver.cpp
54 mirserverstatuslistener.cpp57 mirserverstatuslistener.cpp
55 display.cpp
56 screen.cpp58 screen.cpp
57 displaywindow.cpp59 screencontroller.cpp
60 screenwindow.cpp
58 mirserverintegration.cpp61 mirserverintegration.cpp
59 miropenglcontext.cpp62 miropenglcontext.cpp
60 nativeinterface.cpp63 nativeinterface.cpp
64 offscreensurface.cpp
61 qtcompositor.cpp65 qtcompositor.cpp
62 services.cpp66 services.cpp
63 ubuntutheme.cpp67 ubuntutheme.cpp
64 clipboard.cpp68 clipboard.cpp
69 tileddisplayconfigurationpolicy.cpp
65 tracepoints.c70 tracepoints.c
71# We need to run moc on these headers
72 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
66 )73 )
6774
68add_library(qpa-mirserver SHARED75add_library(qpa-mirserver SHARED
@@ -82,7 +89,7 @@
82 ${QT5PLATFORM_SUPPORT_LDFLAGS}89 ${QT5PLATFORM_SUPPORT_LDFLAGS}
83 # TODO Qt5Platform support LDFLAGS dont provide actual required ldflags...90 # TODO Qt5Platform support LDFLAGS dont provide actual required ldflags...
84 # I found these were needed...perhaps there is some way to query qmake/qconfig?91 # I found these were needed...perhaps there is some way to query qmake/qconfig?
85 -lfreetype 92 -lfreetype
86 ${GIO_LDFLAGS}93 ${GIO_LDFLAGS}
87 ${FONTCONFIG_LDFLAGS}94 ${FONTCONFIG_LDFLAGS}
8895
8996
=== added file 'src/platforms/mirserver/cursor.cpp'
--- src/platforms/mirserver/cursor.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/cursor.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,152 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#include "cursor.h"
19#include "logging.h"
20
21#include "mirsingleton.h"
22
23// Unity API
24#include <unity/shell/application/MirMousePointerInterface.h>
25
26using namespace qtmir;
27
28Cursor::Cursor()
29{
30 m_shapeToCursorName[Qt::ArrowCursor] = "left_ptr";
31 m_shapeToCursorName[Qt::UpArrowCursor] = "up_arrow";
32 m_shapeToCursorName[Qt::CrossCursor] = "cross";
33 m_shapeToCursorName[Qt::WaitCursor] = "watch";
34 m_shapeToCursorName[Qt::IBeamCursor] = "xterm";
35 m_shapeToCursorName[Qt::SizeVerCursor] = "size_ver";
36 m_shapeToCursorName[Qt::SizeHorCursor] = "size_hor";
37 m_shapeToCursorName[Qt::SizeBDiagCursor] = "size_bdiag";
38 m_shapeToCursorName[Qt::SizeFDiagCursor] = "size_fdiag";
39 m_shapeToCursorName[Qt::SizeAllCursor] = "size_all";
40 m_shapeToCursorName[Qt::BlankCursor] = "blank";
41 m_shapeToCursorName[Qt::SplitVCursor] = "split_v";
42 m_shapeToCursorName[Qt::SplitHCursor] = "split_h";
43 m_shapeToCursorName[Qt::PointingHandCursor] = "pointing_hand";
44 m_shapeToCursorName[Qt::ForbiddenCursor] = "forbidden";
45 m_shapeToCursorName[Qt::WhatsThisCursor] = "whats_this";
46 m_shapeToCursorName[Qt::BusyCursor] = "left_ptr_watch";
47 m_shapeToCursorName[Qt::OpenHandCursor] = "openhand";
48 m_shapeToCursorName[Qt::ClosedHandCursor] = "closedhand";
49 m_shapeToCursorName[Qt::DragCopyCursor] = "copy";
50 m_shapeToCursorName[Qt::DragMoveCursor] = "move";
51 m_shapeToCursorName[Qt::DragLinkCursor] = "link";
52
53 connect(Mir::instance(), &Mir::cursorNameChanged, this, &Cursor::setMirCursorName);
54}
55
56void Cursor::changeCursor(QCursor *windowCursor, QWindow * /*window*/)
57{
58 if (m_mousePointer.isNull()) {
59 return;
60 }
61
62 if (windowCursor) {
63 m_qtCursorName = m_shapeToCursorName.value(windowCursor->shape(), QString("left_ptr"));
64 } else {
65 m_qtCursorName.clear();
66 }
67
68 updateMousePointerCursorName();
69}
70
71void Cursor::setMirCursorName(const QString &mirCursorName)
72{
73 m_mirCursorName = mirCursorName;
74 updateMousePointerCursorName();
75}
76
77void Cursor::setMousePointer(MirMousePointerInterface *mousePointer)
78{
79 QMutexLocker locker(&m_mutex);
80
81 if (mousePointer && !m_mousePointer.isNull()) {
82 qFatal("QPA mirserver: Only one MousePointer per screen is allowed!");
83 }
84
85 m_mousePointer = mousePointer;
86 updateMousePointerCursorName();
87}
88
89bool Cursor::handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButton buttons,
90 Qt::KeyboardModifiers modifiers)
91{
92 QMutexLocker locker(&m_mutex);
93
94 if (!m_mousePointer || !m_mousePointer->isVisible()) {
95 return false;
96 }
97
98 // Must not be called directly as we're most likely not in Qt's GUI (main) thread.
99 bool ok = QMetaObject::invokeMethod(m_mousePointer, "handleMouseEvent", Qt::AutoConnection,
100 Q_ARG(ulong, timestamp),
101 Q_ARG(QPointF, movement),
102 Q_ARG(Qt::MouseButton, buttons),
103 Q_ARG(Qt::KeyboardModifiers, modifiers));
104
105 if (!ok) {
106 qCWarning(QTMIR_MIR_INPUT) << "Failed to invoke MousePointer::handleMouseEvent";
107 }
108
109 return ok;
110}
111
112void Cursor::setPos(const QPoint &pos)
113{
114 if (!m_mousePointer) {
115 QPlatformCursor::setPos(pos);
116 return;
117 }
118
119 QPointF movement;
120 QPointF mouseScenePos = m_mousePointer->mapToItem(nullptr, QPointF(0, 0));
121
122 movement.setX(pos.x() - mouseScenePos.x());
123 movement.setY(pos.y() - mouseScenePos.y());
124
125 m_mousePointer->handleMouseEvent(0 /*timestamp*/, movement, Qt::NoButton, Qt::NoModifier);
126}
127
128QPoint Cursor::pos() const
129{
130 if (m_mousePointer) {
131 return m_mousePointer->mapToItem(nullptr, QPointF(0, 0)).toPoint();
132 } else {
133 return QPlatformCursor::pos();
134 }
135}
136
137void Cursor::updateMousePointerCursorName()
138{
139 if (!m_mousePointer) {
140 return;
141 }
142
143 if (m_mirCursorName.isEmpty()) {
144 if (m_qtCursorName.isEmpty()) {
145 m_mousePointer->setCursorName("left_ptr");
146 } else {
147 m_mousePointer->setCursorName(m_qtCursorName);
148 }
149 } else {
150 m_mousePointer->setCursorName(m_mirCursorName);
151 }
152}
0153
=== added file 'src/platforms/mirserver/cursor.h'
--- src/platforms/mirserver/cursor.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/cursor.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,66 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18#ifndef QTMIR_CURSOR_H
19#define QTMIR_CURSOR_H
20
21#include <QMutex>
22#include <QPointer>
23
24// Unity API
25#include <unity/shell/application/MirPlatformCursor.h>
26
27namespace qtmir {
28
29class Cursor : public MirPlatformCursor
30{
31public:
32 Cursor();
33
34 // Called form Mir input thread
35 bool handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButton buttons,
36 Qt::KeyboardModifiers modifiers);
37
38 ////
39 // MirPlatformCursor
40
41 // Called from Qt's GUI thread
42 void setMousePointer(MirMousePointerInterface *mousePointer) override;
43
44 ////
45 // QPlatformCursor
46
47 void changeCursor(QCursor *windowCursor, QWindow *window) override;
48
49 void setPos(const QPoint &pos) override;
50 QPoint pos() const override;
51
52private Q_SLOTS:
53 void setMirCursorName(const QString &mirCursorName);
54
55private:
56 void updateMousePointerCursorName();
57 QMutex m_mutex;
58 QPointer<MirMousePointerInterface> m_mousePointer;
59 QMap<int,QString> m_shapeToCursorName;
60 QString m_qtCursorName;
61 QString m_mirCursorName;
62};
63
64} // namespace qtmir
65
66#endif // QTMIR_CURSOR_H
067
=== removed file 'src/platforms/mirserver/display.cpp'
--- src/platforms/mirserver/display.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/display.cpp 1970-01-01 00:00:00 +0000
@@ -1,44 +0,0 @@
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "display.h"
18
19#include "screen.h"
20#include "mirserver.h"
21
22#include <mir/graphics/display.h>
23#include <mir/graphics/display_configuration.h>
24
25namespace mg = mir::graphics;
26
27// TODO: Listen for display changes and update the list accordingly
28
29Display::Display(const std::shared_ptr<mir::graphics::DisplayConfiguration> &displayConfig)
30{
31 displayConfig->for_each_output([this](mg::DisplayConfigurationOutput const& output) {
32 if (output.used) {
33 auto screen = new Screen(output);
34 m_screens.push_back(screen);
35 }
36 });
37}
38
39Display::~Display()
40{
41 for (auto screen : m_screens)
42 delete screen;
43 m_screens.clear();
44}
450
=== removed file 'src/platforms/mirserver/display.h'
--- src/platforms/mirserver/display.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/display.h 1970-01-01 00:00:00 +0000
@@ -1,37 +0,0 @@
1/*
2 * Copyright (C) 2013-2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef DISPLAY_H
18#define DISPLAY_H
19
20#include <qpa/qplatformscreen.h>
21#include <memory>
22
23namespace mir { namespace graphics { class DisplayConfiguration; }}
24
25class Display
26{
27public:
28 Display(const std::shared_ptr<mir::graphics::DisplayConfiguration> &displayConfig);
29 virtual ~Display();
30
31 QList<QPlatformScreen *> screens() const { return m_screens; }
32
33private:
34 QList<QPlatformScreen *> m_screens;
35};
36
37#endif // DISPLAY_H
380
=== modified file 'src/platforms/mirserver/logging.h'
--- src/platforms/mirserver/logging.h 2014-10-01 18:42:26 +0000
+++ src/platforms/mirserver/logging.h 2015-09-30 13:40:22 +0000
@@ -25,5 +25,6 @@
25Q_DECLARE_LOGGING_CATEGORY(QTMIR_SENSOR_MESSAGES)25Q_DECLARE_LOGGING_CATEGORY(QTMIR_SENSOR_MESSAGES)
26Q_DECLARE_LOGGING_CATEGORY(QTMIR_MIR_INPUT)26Q_DECLARE_LOGGING_CATEGORY(QTMIR_MIR_INPUT)
27Q_DECLARE_LOGGING_CATEGORY(QTMIR_CLIPBOARD)27Q_DECLARE_LOGGING_CATEGORY(QTMIR_CLIPBOARD)
28Q_DECLARE_LOGGING_CATEGORY(QTMIR_SCREENS)
2829
29#endif // UBUNTU_APPLICATION_PLUGIN_LOGGING_H30#endif // UBUNTU_APPLICATION_PLUGIN_LOGGING_H
3031
=== modified file 'src/platforms/mirserver/miropenglcontext.cpp'
--- src/platforms/mirserver/miropenglcontext.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/miropenglcontext.cpp 2015-09-30 13:40:22 +0000
@@ -16,12 +16,14 @@
1616
17#include "miropenglcontext.h"17#include "miropenglcontext.h"
1818
19#include "displaywindow.h"19#include "offscreensurface.h"
20#include "mirglconfig.h"
20#include "mirserver.h"21#include "mirserver.h"
21#include "mirglconfig.h"22#include "screenwindow.h"
2223
23#include <QDebug>24#include <QDebug>
2425
26#include <QOpenGLFramebufferObject>
25#include <QSurfaceFormat>27#include <QSurfaceFormat>
26#include <QtPlatformSupport/private/qeglconvenience_p.h>28#include <QtPlatformSupport/private/qeglconvenience_p.h>
2729
@@ -38,7 +40,7 @@
38 : m_logger(new QOpenGLDebugLogger(this))40 : m_logger(new QOpenGLDebugLogger(this))
39#endif41#endif
40{42{
41 std::shared_ptr<mir::graphics::Display> display = server->the_display();43 auto display = server->the_display();
4244
43 // create a temporary GL context to fetch the EGL display and config, so Qt can determine the surface format45 // create a temporary GL context to fetch the EGL display and config, so Qt can determine the surface format
44 std::unique_ptr<mir::graphics::GLContext> mirContext = display->create_gl_context();46 std::unique_ptr<mir::graphics::GLContext> mirContext = display->create_gl_context();
@@ -106,17 +108,30 @@
106108
107void MirOpenGLContext::swapBuffers(QPlatformSurface *surface)109void MirOpenGLContext::swapBuffers(QPlatformSurface *surface)
108{110{
109 // ultimately calls Mir's DisplayBuffer::post_update()111 if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
110 DisplayWindow *displayBuffer = static_cast<DisplayWindow*>(surface);112 // NOOP
111 displayBuffer->swapBuffers(); //blocks for vsync113 } else {
114 // ultimately calls Mir's DisplayBuffer::post_update()
115 ScreenWindow *screenWindow = static_cast<ScreenWindow*>(surface);
116 screenWindow->swapBuffers(); //blocks for vsync
117 }
112}118}
113119
114bool MirOpenGLContext::makeCurrent(QPlatformSurface *surface)120bool MirOpenGLContext::makeCurrent(QPlatformSurface *surface)
115{121{
122 if (surface->surface()->surfaceClass() == QSurface::Offscreen) {
123 auto offscreen = static_cast<OffscreenSurface *>(surface);
124 if (!offscreen->buffer()) {
125 auto buffer = new QOpenGLFramebufferObject(surface->surface()->size());
126 offscreen->setBuffer(buffer);
127 }
128 return offscreen->buffer()->bind();
129 }
130
116 // ultimately calls Mir's DisplayBuffer::make_current()131 // ultimately calls Mir's DisplayBuffer::make_current()
117 DisplayWindow *displayBuffer = static_cast<DisplayWindow*>(surface);132 ScreenWindow *screenWindow = static_cast<ScreenWindow*>(surface);
118 if (displayBuffer) {133 if (screenWindow) {
119 displayBuffer->makeCurrent();134 screenWindow->makeCurrent();
120135
121#ifndef QT_NO_DEBUG136#ifndef QT_NO_DEBUG
122 if (!m_logger->isLogging() && m_logger->initialize()) {137 if (!m_logger->isLogging() && m_logger->initialize()) {
@@ -133,7 +148,7 @@
133148
134void MirOpenGLContext::doneCurrent()149void MirOpenGLContext::doneCurrent()
135{150{
136 // could call Mir's DisplayBuffer::release_current(), but for what DisplayBuffer?151 // FIXME: create a temporary GL context just to release? Would be better to get existing one.
137}152}
138153
139QFunctionPointer MirOpenGLContext::getProcAddress(const QByteArray &procName)154QFunctionPointer MirOpenGLContext::getProcAddress(const QByteArray &procName)
140155
=== modified file 'src/platforms/mirserver/miropenglcontext.h'
--- src/platforms/mirserver/miropenglcontext.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/miropenglcontext.h 2015-09-30 13:40:22 +0000
@@ -23,6 +23,7 @@
23#include <QOpenGLDebugLogger>23#include <QOpenGLDebugLogger>
24#endif24#endif
2525
26
26class MirServer;27class MirServer;
2728
28class MirOpenGLContext : public QObject, public QPlatformOpenGLContext29class MirOpenGLContext : public QObject, public QPlatformOpenGLContext
2930
=== modified file 'src/platforms/mirserver/mirserver.cpp'
--- src/platforms/mirserver/mirserver.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/mirserver.cpp 2015-09-30 13:40:22 +0000
@@ -23,15 +23,25 @@
23#include "mirglconfig.h"23#include "mirglconfig.h"
24#include "mirserverstatuslistener.h"24#include "mirserverstatuslistener.h"
25#include "promptsessionlistener.h"25#include "promptsessionlistener.h"
26#include "screencontroller.h"
26#include "sessionlistener.h"27#include "sessionlistener.h"
27#include "sessionauthorizer.h"28#include "sessionauthorizer.h"
28#include "qtcompositor.h"29#include "qtcompositor.h"
29#include "qteventfeeder.h"30#include "qteventfeeder.h"
31#include "tileddisplayconfigurationpolicy.h"
30#include "logging.h"32#include "logging.h"
3133
34// std
35#include <memory>
36
32// egl37// egl
38#define MESA_EGL_NO_X11_HEADERS
33#include <EGL/egl.h>39#include <EGL/egl.h>
3440
41// mir
42#include <mir/graphics/cursor.h>
43
44namespace mg = mir::graphics;
35namespace mo = mir::options;45namespace mo = mir::options;
36namespace msh = mir::shell;46namespace msh = mir::shell;
37namespace ms = mir::scene;47namespace ms = mir::scene;
@@ -45,8 +55,10 @@
4555
46Q_LOGGING_CATEGORY(QTMIR_MIR_MESSAGES, "qtmir.mir")56Q_LOGGING_CATEGORY(QTMIR_MIR_MESSAGES, "qtmir.mir")
4757
48MirServer::MirServer(int argc, char const* argv[], QObject* parent)58MirServer::MirServer(int argc, char const* argv[],
59 const QSharedPointer<ScreenController> &screenController, QObject* parent)
49 : QObject(parent)60 : QObject(parent)
61 , m_screenController(screenController)
50{62{
51 set_command_line_handler(&ignore_unparsed_arguments);63 set_command_line_handler(&ignore_unparsed_arguments);
52 set_command_line(argc, argv);64 set_command_line(argc, argv);
@@ -71,9 +83,9 @@
71 return std::make_shared<QtCompositor>();83 return std::make_shared<QtCompositor>();
72 });84 });
7385
74 override_the_input_dispatcher([]86 override_the_input_dispatcher([&screenController]
75 {87 {
76 return std::make_shared<QtEventFeeder>();88 return std::make_shared<QtEventFeeder>(screenController);
77 });89 });
7890
79 override_the_gl_config([]91 override_the_gl_config([]
@@ -92,17 +104,39 @@
92 return std::make_shared<MirWindowManager>(the_shell_display_layout());104 return std::make_shared<MirWindowManager>(the_shell_display_layout());
93 });105 });
94106
95 set_terminator([&](int)107 wrap_display_configuration_policy(
108 [](const std::shared_ptr<mg::DisplayConfigurationPolicy> &wrapped)
109 -> std::shared_ptr<mg::DisplayConfigurationPolicy>
110 {
111 return std::make_shared<TiledDisplayConfigurationPolicy>(wrapped);
112 });
113
114 set_terminator([](int)
96 {115 {
97 qDebug() << "Signal caught by Mir, stopping Mir server..";116 qDebug() << "Signal caught by Mir, stopping Mir server..";
98 QCoreApplication::quit();117 QCoreApplication::quit();
99 });118 });
100119
120 add_init_callback([this, &screenController] {
121 screenController->init(the_display(), the_compositor());
122 });
123
101 apply_settings();124 apply_settings();
102125
126 // We will draw our own cursor.
127 add_init_callback([this](){ the_cursor()->hide(); });
128
103 qCDebug(QTMIR_MIR_MESSAGES) << "MirServer created";129 qCDebug(QTMIR_MIR_MESSAGES) << "MirServer created";
104}130}
105131
132// Override default implementation to ensure we terminate the ScreenController first.
133// Code path followed when Qt tries to shutdown the server.
134void MirServer::stop()
135{
136 m_screenController->terminate();
137 mir::Server::stop();
138}
139
106140
107/************************************ Shell side ************************************/141/************************************ Shell side ************************************/
108142
109143
=== modified file 'src/platforms/mirserver/mirserver.h'
--- src/platforms/mirserver/mirserver.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/mirserver.h 2015-09-30 13:40:22 +0000
@@ -18,6 +18,7 @@
18#define MIRSERVER_H18#define MIRSERVER_H
1919
20#include <QObject>20#include <QObject>
21#include <QSharedPointer>
21#include <mir/server.h>22#include <mir/server.h>
2223
23class QtEventFeeder;24class QtEventFeeder;
@@ -25,6 +26,7 @@
25class SessionAuthorizer;26class SessionAuthorizer;
26using MirShell = mir::shell::Shell;27using MirShell = mir::shell::Shell;
27class PromptSessionListener;28class PromptSessionListener;
29class ScreenController;
2830
29// We use virtual inheritance of mir::Server to facilitate derived classes (e.g. testing)31// We use virtual inheritance of mir::Server to facilitate derived classes (e.g. testing)
30// calling initialization functions before MirServer is constructed.32// calling initialization functions before MirServer is constructed.
@@ -38,12 +40,12 @@
38 Q_PROPERTY(PromptSessionListener* promptSessionListener READ promptSessionListener CONSTANT)40 Q_PROPERTY(PromptSessionListener* promptSessionListener READ promptSessionListener CONSTANT)
3941
40public:42public:
41 MirServer(int argc, char const* argv[], QObject* parent = 0);43 MirServer(int argc, char const* argv[], const QSharedPointer<ScreenController> &, QObject* parent = 0);
42 ~MirServer() = default;44 ~MirServer() = default;
4345
44 /* mir specific */46 /* mir specific */
45 using mir::Server::run;47 using mir::Server::run;
46 using mir::Server::stop;48 using mir::Server::the_compositor;
47 using mir::Server::the_display;49 using mir::Server::the_display;
48 using mir::Server::the_gl_config;50 using mir::Server::the_gl_config;
49 using mir::Server::the_main_loop;51 using mir::Server::the_main_loop;
@@ -52,6 +54,8 @@
52 using mir::Server::the_session_authorizer;54 using mir::Server::the_session_authorizer;
53 using mir::Server::the_session_listener;55 using mir::Server::the_session_listener;
5456
57 void stop();
58
55 /* qt specific */59 /* qt specific */
56 // getters60 // getters
57 SessionAuthorizer *sessionAuthorizer();61 SessionAuthorizer *sessionAuthorizer();
@@ -60,7 +64,9 @@
60 MirShell *shell();64 MirShell *shell();
6165
62private:66private:
67 std::weak_ptr<MirShell> m_shell;
63 std::shared_ptr<QtEventFeeder> m_qtEventFeeder;68 std::shared_ptr<QtEventFeeder> m_qtEventFeeder;
69 const QSharedPointer<ScreenController> m_screenController;
64};70};
6571
66#endif // MIRSERVER_H72#endif // MIRSERVER_H
6773
=== modified file 'src/platforms/mirserver/mirserverintegration.cpp'
--- src/platforms/mirserver/mirserverintegration.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/mirserverintegration.cpp 2015-09-30 13:40:22 +0000
@@ -26,7 +26,8 @@
26#include <qpa/qplatforminputcontextfactory_p.h>26#include <qpa/qplatforminputcontextfactory_p.h>
27#include <qpa/qwindowsysteminterface.h>27#include <qpa/qwindowsysteminterface.h>
2828
29#include <QCoreApplication>29#include <QGuiApplication>
30#include <QStringList>
30#include <QOpenGLContext>31#include <QOpenGLContext>
31#include <QDebug>32#include <QDebug>
3233
@@ -36,13 +37,16 @@
3637
37// local38// local
38#include "clipboard.h"39#include "clipboard.h"
39#include "display.h"
40#include "displaywindow.h"
41#include "miropenglcontext.h"40#include "miropenglcontext.h"
42#include "nativeinterface.h"41#include "nativeinterface.h"
42#include "offscreensurface.h"
43#include "qmirserver.h"43#include "qmirserver.h"
44#include "screen.h"
45#include "screencontroller.h"
46#include "screenwindow.h"
44#include "services.h"47#include "services.h"
45#include "ubuntutheme.h"48#include "ubuntutheme.h"
49#include "logging.h"
4650
47namespace mg = mir::graphics;51namespace mg = mir::graphics;
48using qtmir::Clipboard;52using qtmir::Clipboard;
@@ -52,7 +56,6 @@
52 , m_fontDb(new QGenericUnixFontDatabase())56 , m_fontDb(new QGenericUnixFontDatabase())
53 , m_services(new Services)57 , m_services(new Services)
54 , m_mirServer(new QMirServer(QCoreApplication::arguments()))58 , m_mirServer(new QMirServer(QCoreApplication::arguments()))
55 , m_display(nullptr)
56 , m_nativeInterface(nullptr)59 , m_nativeInterface(nullptr)
57 , m_clipboard(new Clipboard)60 , m_clipboard(new Clipboard)
58{61{
@@ -72,12 +75,14 @@
72 QCoreApplication::instance(), &QCoreApplication::quit);75 QCoreApplication::instance(), &QCoreApplication::quit);
7376
74 m_inputContext = QPlatformInputContextFactory::create();77 m_inputContext = QPlatformInputContextFactory::create();
78
79 // Default Qt behaviour doesn't match a shell's intentions, so customize:
80 qGuiApp->setQuitOnLastWindowClosed(false);
75}81}
7682
77MirServerIntegration::~MirServerIntegration()83MirServerIntegration::~MirServerIntegration()
78{84{
79 delete m_nativeInterface;85 delete m_nativeInterface;
80 delete m_display;
81}86}
8287
83bool MirServerIntegration::hasCapability(QPlatformIntegration::Capability cap) const88bool MirServerIntegration::hasCapability(QPlatformIntegration::Capability cap) const
@@ -87,7 +92,7 @@
87 case OpenGL: return true;92 case OpenGL: return true;
88 case ThreadedOpenGL: return true;93 case ThreadedOpenGL: return true;
89 case BufferQueueingOpenGL: return true;94 case BufferQueueingOpenGL: return true;
90 case MultipleWindows: return false; // multi-monitor support95 case MultipleWindows: return true; // multi-monitor support
91 case WindowManagement: return false; // platform has no WM, as this implements the WM!96 case WindowManagement: return false; // platform has no WM, as this implements the WM!
92 case NonFullScreenWindows: return false;97 case NonFullScreenWindows: return false;
93 default: return QPlatformIntegration::hasCapability(cap);98 default: return QPlatformIntegration::hasCapability(cap);
@@ -98,44 +103,38 @@
98{103{
99 QWindowSystemInterface::flushWindowSystemEvents();104 QWindowSystemInterface::flushWindowSystemEvents();
100105
101 DisplayWindow* displayWindow = nullptr;106 // FIXME: QWindow can be created specifying a destination QScreen. For now we
102107 // will ignore it and just associate any unused Screen, if available.
103 auto const mirServer = m_mirServer->mirServer().lock();108 auto screens = m_mirServer->screenController().lock();
104 mg::DisplayBuffer* first_buffer{nullptr};109 if (!screens) {
105 mg::DisplaySyncGroup* first_group{nullptr};110 qCritical("Screens are not initialized, unable to create a new QWindow/ScreenWindow");
106 if (mirServer) {111 return nullptr;
107 mirServer->the_display()->for_each_display_sync_group([&](mg::DisplaySyncGroup &group) {112 }
108 if (!first_group) {113 Screen *screen = screens->getUnusedScreen();
109 first_group = &group;114 if (!screen) {
110 }115 qCritical("No available Screens to create a new QWindow/ScreenWindow for");
111 group.for_each_display_buffer([&](mg::DisplayBuffer &buffer) {116 return nullptr;
112 if (!first_buffer) {117 }
113 first_buffer = &buffer;118 QScreen *qscreen = screen->screen();
114 }119 window->setScreen(qscreen);
115 });120
116 });121 auto platformWindow = new ScreenWindow(window);
117 }122 if (screens->compositing()) {
118123 platformWindow->setExposed(true);
119 // FIXME(gerry) this will go very bad for >1 display buffer124 }
120 if (first_group && first_buffer)125
121 displayWindow = new DisplayWindow(window, first_group, first_buffer);126 qCDebug(QTMIR_SCREENS) << "New" << window << "with geom" << window->geometry()
122127 << "is backed by a" << screen << "with geometry" << screen->geometry();
123 if (!displayWindow)128 return platformWindow;
124 return nullptr;
125
126 //displayWindow->requestActivateWindow();
127 return displayWindow;
128}129}
129130
130QPlatformBackingStore *MirServerIntegration::createPlatformBackingStore(QWindow *window) const131QPlatformBackingStore *MirServerIntegration::createPlatformBackingStore(QWindow */*window*/) const
131{132{
132 qDebug() << "createPlatformBackingStore" << window;
133 return nullptr;133 return nullptr;
134}134}
135135
136QPlatformOpenGLContext *MirServerIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const136QPlatformOpenGLContext *MirServerIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
137{137{
138 qDebug() << "createPlatformOpenGLContext" << context;
139 return new MirOpenGLContext(m_mirServer->mirServer(), context->format());138 return new MirOpenGLContext(m_mirServer->mirServer(), context->format());
140}139}
141140
@@ -151,12 +150,18 @@
151 exit(2);150 exit(2);
152 }151 }
153152
154 m_display = new Display(m_mirServer->mirServer().data()->the_display()->configuration());153 auto screens = m_mirServer->screenController().lock();
154 if (!screens) {
155 qFatal("ScreenController not initialized");
156 }
157 QObject::connect(screens.data(), &ScreenController::screenAdded,
158 [this](Screen *screen) { this->screenAdded(screen); });
159 Q_FOREACH(auto screen, screens->screens()) {
160 screenAdded(screen);
161 }
162
155 m_nativeInterface = new NativeInterface(m_mirServer->mirServer());163 m_nativeInterface = new NativeInterface(m_mirServer->mirServer());
156164
157 for (QPlatformScreen *screen : m_display->screens())
158 screenAdded(screen);
159
160 m_clipboard->setupDBusService();165 m_clipboard->setupDBusService();
161}166}
162167
@@ -195,3 +200,9 @@
195{200{
196 return m_clipboard.data();201 return m_clipboard.data();
197}202}
203
204QPlatformOffscreenSurface *MirServerIntegration::createPlatformOffscreenSurface(
205 QOffscreenSurface *surface) const
206{
207 return new OffscreenSurface(surface);
208}
198209
=== modified file 'src/platforms/mirserver/mirserverintegration.h'
--- src/platforms/mirserver/mirserverintegration.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/mirserverintegration.h 2015-09-30 13:40:22 +0000
@@ -19,13 +19,9 @@
1919
20// qt20// qt
21#include <qpa/qplatformintegration.h>21#include <qpa/qplatformintegration.h>
2222#include <QScopedPointer>
23// local23
24#include "mirserver.h"
25
26class Display;
27class NativeInterface;24class NativeInterface;
28class MirServer;
29class QMirServer;25class QMirServer;
3026
31namespace qtmir {27namespace qtmir {
@@ -60,6 +56,8 @@
6056
61 QPlatformNativeInterface *nativeInterface() const override;57 QPlatformNativeInterface *nativeInterface() const override;
6258
59 QPlatformOffscreenSurface *createPlatformOffscreenSurface(QOffscreenSurface *surface) const override;
60
63private:61private:
64 QScopedPointer<QPlatformAccessibility> m_accessibility;62 QScopedPointer<QPlatformAccessibility> m_accessibility;
65 QScopedPointer<QPlatformFontDatabase> m_fontDb;63 QScopedPointer<QPlatformFontDatabase> m_fontDb;
@@ -67,7 +65,6 @@
6765
68 QScopedPointer<QMirServer> m_mirServer;66 QScopedPointer<QMirServer> m_mirServer;
6967
70 Display *m_display;
71 NativeInterface *m_nativeInterface;68 NativeInterface *m_nativeInterface;
72 QPlatformInputContext* m_inputContext;69 QPlatformInputContext* m_inputContext;
73 QScopedPointer<qtmir::Clipboard> m_clipboard;70 QScopedPointer<qtmir::Clipboard> m_clipboard;
7471
=== added file 'src/platforms/mirserver/mirsingleton.cpp'
--- src/platforms/mirserver/mirsingleton.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/mirsingleton.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,33 @@
1#include "mirsingleton.h"
2
3qtmir::Mir *qtmir::Mir::m_instance = nullptr;
4
5qtmir::Mir::Mir()
6{
7}
8
9qtmir::Mir::~Mir()
10{
11 m_instance = nullptr;
12}
13
14qtmir::Mir *qtmir::Mir::instance()
15{
16 if (!m_instance) {
17 m_instance = new qtmir::Mir;
18 }
19 return m_instance;
20}
21
22void qtmir::Mir::setCursorName(const QString &cursorName)
23{
24 if (m_cursorName != cursorName) {
25 m_cursorName = cursorName;
26 Q_EMIT cursorNameChanged(m_cursorName);
27 }
28}
29
30QString qtmir::Mir::cursorName() const
31{
32 return m_cursorName;
33}
034
=== added file 'src/platforms/mirserver/mirsingleton.h'
--- src/platforms/mirserver/mirsingleton.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/mirsingleton.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,46 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef QTMIR_MIRSINGLETON_H
18#define QTMIR_MIRSINGLETON_H
19
20// unity-api
21#include <unity/shell/application/Mir.h>
22
23namespace qtmir {
24
25class Mir : public ::Mir
26{
27 Q_OBJECT
28public:
29 virtual ~Mir();
30
31 static Mir *instance();
32
33 void setCursorName(const QString &cursorName) override;
34 QString cursorName() const override;
35
36private:
37 Mir();
38 Q_DISABLE_COPY(Mir)
39
40 QString m_cursorName;
41 static qtmir::Mir *m_instance;
42};
43
44} // namespace qtmir
45
46#endif // QTMIR_MIRSINGLETON_H
047
=== added file 'src/platforms/mirserver/offscreensurface.cpp'
--- src/platforms/mirserver/offscreensurface.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/offscreensurface.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,61 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "offscreensurface.h"
18
19#include "mirserver.h"
20
21// Mir
22#include <mir/graphics/display.h>
23#include <mir/graphics/gl_context.h>
24
25//Qt
26#include <QOffscreenSurface>
27#include <QOpenGLFramebufferObject>
28#include <QSurfaceFormat>
29#include <QtPlatformSupport/private/qeglconvenience_p.h>
30
31namespace mg = mir::graphics;
32
33OffscreenSurface::OffscreenSurface(QOffscreenSurface *offscreenSurface)
34 : QPlatformOffscreenSurface(offscreenSurface)
35 , m_buffer(nullptr)
36 , m_format(offscreenSurface->requestedFormat())
37{
38}
39
40QSurfaceFormat OffscreenSurface::format() const
41{
42 return m_format;
43}
44
45bool OffscreenSurface::isValid() const
46{
47 if (m_buffer) {
48 return m_buffer->isValid();
49 }
50 return false;
51}
52
53QOpenGLFramebufferObject* OffscreenSurface::buffer() const
54{
55 return m_buffer;
56}
57
58void OffscreenSurface::setBuffer(QOpenGLFramebufferObject *buffer)
59{
60 m_buffer = buffer;
61}
062
=== added file 'src/platforms/mirserver/offscreensurface.h'
--- src/platforms/mirserver/offscreensurface.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/offscreensurface.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef OFFSCREENSURFACE_H
18#define OFFSCREENSURFACE_H
19
20#include <qpa/qplatformoffscreensurface.h>
21#include <QSurfaceFormat>
22#include <QSharedPointer>
23
24class MirServer;
25class QOpenGLFramebufferObject;
26
27class OffscreenSurface : public QPlatformOffscreenSurface
28{
29public:
30 OffscreenSurface(QOffscreenSurface *offscreenSurface);
31
32 QSurfaceFormat format() const override;
33 bool isValid() const override;
34
35 QOpenGLFramebufferObject* buffer() const;
36 void setBuffer(QOpenGLFramebufferObject *buffer);
37
38private:
39 QOpenGLFramebufferObject *m_buffer;
40 QSurfaceFormat m_format;
41};
42
43#endif // OFFSCREENSURFACE_H
044
=== modified file 'src/platforms/mirserver/qmirserver.cpp'
--- src/platforms/mirserver/qmirserver.cpp 2015-05-19 15:36:17 +0000
+++ src/platforms/mirserver/qmirserver.cpp 2015-09-30 13:40:22 +0000
@@ -23,7 +23,8 @@
23#include "mirserver.h"23#include "mirserver.h"
24#include "qmirserver.h"24#include "qmirserver.h"
25#include "qmirserver_p.h"25#include "qmirserver_p.h"
2626#include "screencontroller.h"
27#include "screen.h"
2728
28QMirServer::QMirServer(const QStringList &arguments, QObject *parent)29QMirServer::QMirServer(const QStringList &arguments, QObject *parent)
29 : QObject(parent)30 : QObject(parent)
@@ -40,7 +41,9 @@
40 }41 }
41 argv[argc] = '\0';42 argv[argc] = '\0';
4243
43 d->server = QSharedPointer<MirServer>(new MirServer(argc, const_cast<const char**>(argv)));44 d->screenController = QSharedPointer<ScreenController>(new ScreenController());
45
46 d->server = QSharedPointer<MirServer>(new MirServer(argc, const_cast<const char**>(argv), d->screenController));
4447
45 d->serverThread = new MirServerThread(d->server);48 d->serverThread = new MirServerThread(d->server);
4649
@@ -63,6 +66,7 @@
63 qCritical() << "ERROR: QMirServer - Mir failed to start";66 qCritical() << "ERROR: QMirServer - Mir failed to start";
64 return false;67 return false;
65 }68 }
69 d->screenController->update();
6670
67 Q_EMIT started();71 Q_EMIT started();
68 return true;72 return true;
@@ -93,3 +97,9 @@
93 Q_D(const QMirServer);97 Q_D(const QMirServer);
94 return d->server.toWeakRef();98 return d->server.toWeakRef();
95}99}
100
101QWeakPointer<ScreenController> QMirServer::screenController() const
102{
103 Q_D(const QMirServer);
104 return d->screenController;
105}
96106
=== modified file 'src/platforms/mirserver/qmirserver.h'
--- src/platforms/mirserver/qmirserver.h 2015-05-18 20:39:09 +0000
+++ src/platforms/mirserver/qmirserver.h 2015-09-30 13:40:22 +0000
@@ -23,6 +23,7 @@
2323
24class QMirServerPrivate;24class QMirServerPrivate;
25class MirServer;25class MirServer;
26class ScreenController;
2627
27class QMirServer: public QObject28class QMirServer: public QObject
28{29{
@@ -38,6 +39,8 @@
3839
39 QWeakPointer<MirServer> mirServer() const;40 QWeakPointer<MirServer> mirServer() const;
4041
42 QWeakPointer<ScreenController> screenController() const;
43
41Q_SIGNALS:44Q_SIGNALS:
42 void started();45 void started();
43 void stopped();46 void stopped();
4447
=== modified file 'src/platforms/mirserver/qmirserver_p.h'
--- src/platforms/mirserver/qmirserver_p.h 2015-05-18 18:30:33 +0000
+++ src/platforms/mirserver/qmirserver_p.h 2015-09-30 13:40:22 +0000
@@ -27,6 +27,7 @@
2727
28// local28// local
29#include "mirserver.h"29#include "mirserver.h"
30#include "screencontroller.h"
3031
31class QMirServer;32class QMirServer;
32class MirServerThread;33class MirServerThread;
@@ -34,6 +35,7 @@
34struct QMirServerPrivate35struct QMirServerPrivate
35{36{
36 QSharedPointer<MirServer> server;37 QSharedPointer<MirServer> server;
38 QSharedPointer<ScreenController> screenController;
37 MirServerThread *serverThread;39 MirServerThread *serverThread;
38};40};
3941
4042
=== modified file 'src/platforms/mirserver/qtcompositor.cpp'
--- src/platforms/mirserver/qtcompositor.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/qtcompositor.cpp 2015-09-30 13:40:22 +0000
@@ -15,44 +15,19 @@
15 */15 */
1616
17#include "qtcompositor.h"17#include "qtcompositor.h"
18#include "displaywindow.h"18#include "logging.h"
1919
20#include <QGuiApplication>20// Lives in a Mir thread
21#include <QWindow>
22
23#include <QDebug>
24
25QtCompositor::QtCompositor()
26{
27
28}
29
30void QtCompositor::start()21void QtCompositor::start()
31{22{
32 // (Re)Start Qt's render thread by setting all its windows to exposed23 qCDebug(QTMIR_SCREENS) << "QtCompositor::start";
33 setAllWindowsExposed(true);24
25 Q_EMIT starting(); // blocks
34}26}
3527
36void QtCompositor::stop()28void QtCompositor::stop()
37{29{
38 // Stop Qt's render threads by setting all its windows it obscured30 qCDebug(QTMIR_SCREENS) << "QtCompositor::stop";
39 setAllWindowsExposed(false);31
40}32 Q_EMIT stopping(); // blocks
41
42void QtCompositor::setAllWindowsExposed(const bool exposed)
43{
44 qDebug() << "QtCompositor::setAllWindowsExposed" << exposed;
45 QList<QWindow *> windowList = QGuiApplication::allWindows();
46
47 // manipulate Qt object's indirectly via posted events as we're not in Qt's GUI thread
48 auto iterator = windowList.constBegin();
49 while (iterator != windowList.constEnd()) {
50 QWindow *window = *iterator;
51 DisplayWindow *displayWindow = static_cast<DisplayWindow*>(window->handle());
52 if (displayWindow) {
53 QCoreApplication::postEvent(displayWindow,
54 new QEvent( (exposed) ? QEvent::Show : QEvent::Hide));
55 }
56 iterator++;
57 }
58}33}
5934
=== modified file 'src/platforms/mirserver/qtcompositor.h'
--- src/platforms/mirserver/qtcompositor.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/qtcompositor.h 2015-09-30 13:40:22 +0000
@@ -17,18 +17,26 @@
17#ifndef QTCOMPOSITOR_H17#ifndef QTCOMPOSITOR_H
18#define QTCOMPOSITOR_H18#define QTCOMPOSITOR_H
1919
20#include "mir/compositor/compositor.h"20#include <mir/compositor/compositor.h>
2121
22class QtCompositor : public mir::compositor::Compositor22// Qt
23#include <QObject>
24
25class QtCompositor : public QObject, public mir::compositor::Compositor
23{26{
27 Q_OBJECT
24public:28public:
25 QtCompositor();29 QtCompositor() = default;
30 virtual ~QtCompositor() noexcept = default;
2631
27 void start();32 void start();
28 void stop();33 void stop();
2934
35Q_SIGNALS:
36 void starting();
37 void stopping();
38
30private:39private:
31 void setAllWindowsExposed(const bool exposed);
32};40};
3341
34#endif // QTCOMPOSITOR_H42#endif // QTCOMPOSITOR_H
3543
=== modified file 'src/platforms/mirserver/qteventfeeder.cpp'
--- src/platforms/mirserver/qteventfeeder.cpp 2015-08-27 16:10:20 +0000
+++ src/platforms/mirserver/qteventfeeder.cpp 2015-09-30 13:40:22 +0000
@@ -15,7 +15,10 @@
15 */15 */
1616
17#include "qteventfeeder.h"17#include "qteventfeeder.h"
18#include "cursor.h"
18#include "logging.h"19#include "logging.h"
20#include "screen.h" // NEEDED?
21#include "screencontroller.h"
1922
20#include <qpa/qplatforminputcontext.h>23#include <qpa/qplatforminputcontext.h>
21#include <qpa/qplatformintegration.h>24#include <qpa/qplatformintegration.h>
@@ -365,20 +368,29 @@
365368
366namespace {369namespace {
367370
368class QtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {371class QtWindowSystem : public QtEventFeeder::QtWindowSystemInterface
369372{
370 bool hasTargetWindow() override373public:
371 {374 QtWindowSystem()
372 if (mTopLevelWindow.isNull() && !QGuiApplication::topLevelWindows().isEmpty()) {375 {
373 mTopLevelWindow = QGuiApplication::topLevelWindows().first();376 // because we're using QMetaObject::invoke with arguments of those types
374 }377 qRegisterMetaType<Qt::KeyboardModifiers>("Qt::KeyboardModifiers");
375 return !mTopLevelWindow.isNull();378 qRegisterMetaType<Qt::MouseButton>("Qt::MouseButton");
376 }379 }
377380
378 QRect targetWindowGeometry() override381 void setScreenController(const QSharedPointer<ScreenController> &sc) override
379 {382 {
380 Q_ASSERT(!mTopLevelWindow.isNull());383 m_screenController = sc;
381 return mTopLevelWindow->geometry();384 }
385
386 virtual QWindow* focusedWindow() override
387 {
388 return QGuiApplication::focusWindow();
389 }
390
391 QWindow* getWindowForTouchPoint(const QPoint &point) override //FIXME: not efficient, not updating focused window
392 {
393 return m_screenController->getWindowForPoint(point);
382 }394 }
383395
384 void registerTouchDevice(QTouchDevice *device) override396 void registerTouchDevice(QTouchDevice *device) override
@@ -386,47 +398,56 @@
386 QWindowSystemInterface::registerTouchDevice(device);398 QWindowSystemInterface::registerTouchDevice(device);
387 }399 }
388400
389 void handleExtendedKeyEvent(ulong timestamp, QEvent::Type type, int key,401 void handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key,
390 Qt::KeyboardModifiers modifiers,402 Qt::KeyboardModifiers modifiers,
391 quint32 nativeScanCode, quint32 nativeVirtualKey,403 quint32 nativeScanCode, quint32 nativeVirtualKey,
392 quint32 nativeModifiers,404 quint32 nativeModifiers,
393 const QString& text, bool autorep, ushort count) override405 const QString& text, bool autorep, ushort count) override
394 {406 {
395 Q_ASSERT(!mTopLevelWindow.isNull());407 QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp, type, key, modifiers,
396 QWindowSystemInterface::handleExtendedKeyEvent(mTopLevelWindow.data(), timestamp, type, key, modifiers,
397 nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);408 nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
398 }409 }
399410
400 void handleTouchEvent(ulong timestamp, QTouchDevice *device,411 void handleTouchEvent(QWindow *window, ulong timestamp, QTouchDevice *device,
401 const QList<struct QWindowSystemInterface::TouchPoint> &points, Qt::KeyboardModifiers mods) override412 const QList<struct QWindowSystemInterface::TouchPoint> &points, Qt::KeyboardModifiers mods) override
402 {413 {
403 Q_ASSERT(!mTopLevelWindow.isNull());414 QWindowSystemInterface::handleTouchEvent(window, timestamp, device, points, mods);
404 QWindowSystemInterface::handleTouchEvent(mTopLevelWindow.data(), timestamp, device, points, mods);415 }
405 }
406416
407 void handleMouseEvent(ulong timestamp, QPointF point, Qt::MouseButton buttons, Qt::KeyboardModifiers modifiers) override417 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButton buttons,
418 Qt::KeyboardModifiers modifiers) override
408 {419 {
409 Q_ASSERT(!mTopLevelWindow.isNull());420 // Send to the first screen that handles the mouse event
410 QWindowSystemInterface::handleMouseEvent(mTopLevelWindow.data(), timestamp, point, point, // local and global point are the same421 // TODO: Have a mechanism to tell which screen currently has the logical mouse pointer
411 buttons, modifiers);422 // (because they all might have their own separate graphical mouse pointer item)
423 // This will probably come once we implement the feature of having the mouse pointer
424 // crossing adjacent screens.
425
426 QList<Screen*> screens = m_screenController->screens();
427 bool eventHandled = false;
428 int i = 0;
429 while (i < screens.count() && !eventHandled) {
430 auto platformCursor = static_cast<qtmir::Cursor*>(screens[i]->cursor());
431 eventHandled = platformCursor->handleMouseEvent(timestamp, movement, buttons, modifiers);
432 ++i;
433 }
412 }434 }
413435
414
415private:436private:
416 QPointer<QWindow> mTopLevelWindow;437 QSharedPointer<ScreenController> m_screenController;
417};438};
418439
419} // anonymous namespace440} // anonymous namespace
420441
421442QtEventFeeder::QtEventFeeder(const QSharedPointer<ScreenController> &screenController)
422QtEventFeeder::QtEventFeeder(QtEventFeeder::QtWindowSystemInterface *windowSystem)443 : QtEventFeeder(screenController, new QtWindowSystem)
423{444{
424 if (windowSystem) {445}
425 mQtWindowSystem = windowSystem;446
426 } else {447QtEventFeeder::QtEventFeeder(const QSharedPointer<ScreenController> &screenController,
427 mQtWindowSystem = new QtWindowSystem;448 QtEventFeeder::QtWindowSystemInterface *windowSystem)
428 }449 : mQtWindowSystem(windowSystem)
429450{
430 // Initialize touch device. Hardcoded just like in qtubuntu451 // Initialize touch device. Hardcoded just like in qtubuntu
431 // TODO: Create them from info gathered from Mir and store things like device id and source452 // TODO: Create them from info gathered from Mir and store things like device id and source
432 // in a QTouchDevice-derived class created by us. So that we can properly assemble back453 // in a QTouchDevice-derived class created by us. So that we can properly assemble back
@@ -436,6 +457,7 @@
436 mTouchDevice->setCapabilities(457 mTouchDevice->setCapabilities(
437 QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |458 QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::Pressure |
438 QTouchDevice::NormalizedPosition);459 QTouchDevice::NormalizedPosition);
460 mQtWindowSystem->setScreenController(screenController);
439 mQtWindowSystem->registerTouchDevice(mTouchDevice);461 mQtWindowSystem->registerTouchDevice(mTouchDevice);
440}462}
441463
@@ -449,6 +471,7 @@
449 auto type = mir_event_get_type(&event);471 auto type = mir_event_get_type(&event);
450 if (type != mir_event_type_input)472 if (type != mir_event_type_input)
451 return false;473 return false;
474
452 auto iev = mir_event_get_input_event(&event);475 auto iev = mir_event_get_input_event(&event);
453476
454 switch (mir_input_event_get_type(iev)) {477 switch (mir_input_event_get_type(iev)) {
@@ -508,9 +531,6 @@
508531
509void QtEventFeeder::dispatchPointer(MirInputEvent const* ev)532void QtEventFeeder::dispatchPointer(MirInputEvent const* ev)
510{533{
511 if (!mQtWindowSystem->hasTargetWindow())
512 return;
513
514 auto timestamp = mir_input_event_get_event_time(ev) / 1000000;534 auto timestamp = mir_input_event_get_event_time(ev) / 1000000;
515535
516 auto pev = mir_input_event_get_pointer_event(ev);536 auto pev = mir_input_event_get_pointer_event(ev);
@@ -519,18 +539,14 @@
519 auto modifiers = getQtModifiersFromMir(mir_pointer_event_modifiers(pev));539 auto modifiers = getQtModifiersFromMir(mir_pointer_event_modifiers(pev));
520 auto buttons = getQtMouseButtonsfromMirPointerEvent(pev);540 auto buttons = getQtMouseButtonsfromMirPointerEvent(pev);
521541
522 auto local_point = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_x),542 auto movement = QPointF(mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_x),
523 mir_pointer_event_axis_value(pev, mir_pointer_axis_y));543 mir_pointer_event_axis_value(pev, mir_pointer_axis_relative_y));
524544
525 mQtWindowSystem->handleMouseEvent(timestamp, local_point,545 mQtWindowSystem->handleMouseEvent(timestamp, movement, buttons, modifiers);
526 buttons, modifiers);
527}546}
528547
529void QtEventFeeder::dispatchKey(MirInputEvent const* event)548void QtEventFeeder::dispatchKey(MirInputEvent const* event)
530{549{
531 if (!mQtWindowSystem->hasTargetWindow())
532 return;
533
534 ulong timestamp = mir_input_event_get_event_time(event) / 1000000;550 ulong timestamp = mir_input_event_get_event_time(event) / 1000000;
535551
536 auto kev = mir_input_event_get_keyboard_event(event);552 auto kev = mir_input_event_get_keyboard_event(event);
@@ -577,7 +593,8 @@
577 }593 }
578 }594 }
579595
580 mQtWindowSystem->handleExtendedKeyEvent(timestamp, keyType, keyCode, modifiers,596 mQtWindowSystem->handleExtendedKeyEvent(mQtWindowSystem->focusedWindow(),
597 timestamp, keyType, keyCode, modifiers,
581 mir_keyboard_event_scan_code(kev),598 mir_keyboard_event_scan_code(kev),
582 mir_keyboard_event_key_code(kev),599 mir_keyboard_event_key_code(kev),
583 mir_keyboard_event_modifiers(kev), text, is_auto_rep);600 mir_keyboard_event_modifiers(kev), text, is_auto_rep);
@@ -585,59 +602,69 @@
585602
586void QtEventFeeder::dispatchTouch(MirInputEvent const* event)603void QtEventFeeder::dispatchTouch(MirInputEvent const* event)
587{604{
588 if (!mQtWindowSystem->hasTargetWindow())
589 return;
590
591 auto tev = mir_input_event_get_touch_event(event);605 auto tev = mir_input_event_get_touch_event(event);
592 qCDebug(QTMIR_MIR_INPUT) << "Received" << qPrintable(mirTouchEventToString(tev));606 qCDebug(QTMIR_MIR_INPUT) << "Received" << qPrintable(mirTouchEventToString(tev));
593607
594 // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That608 // FIXME(loicm) Max pressure is device specific. That one is for the Samsung Galaxy Nexus. That
595 // needs to be fixed as soon as the compat input lib adds query support.609 // needs to be fixed as soon as the compat input lib adds query support.
596 const float kMaxPressure = 1.28;610 const float kMaxPressure = 1.28;
597 const QRect kWindowGeometry = mQtWindowSystem->targetWindowGeometry();611 const int kPointerCount = mir_touch_event_point_count(tev);
598 QList<QWindowSystemInterface::TouchPoint> touchPoints;612 QList<QWindowSystemInterface::TouchPoint> touchPoints;
599613 QWindow *window = nullptr;
600 // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left614
601 // as Qt::TouchPointMoved615 if (kPointerCount > 0) {
602 const int kPointerCount = mir_touch_event_point_count(tev);616 window = mQtWindowSystem->getWindowForTouchPoint(
603 for (int i = 0; i < kPointerCount; ++i) {617 QPoint(mir_touch_event_axis_value(tev, 0, mir_touch_axis_x),
604 QWindowSystemInterface::TouchPoint touchPoint;618 mir_touch_event_axis_value(tev, 0, mir_touch_axis_y)));
605619
606 const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x);620 if (!window) {
607 const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y);621 qCDebug(QTMIR_MIR_INPUT) << "REJECTING INPUT EVENT, no matching window";
608 const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major);622 return;
609 const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor);623 }
610 const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure);624
611 touchPoint.id = mir_touch_event_id(tev, i);625 const QRect kWindowGeometry = window->geometry();
612626
613 touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height());627 // TODO: Is it worth setting the Qt::TouchPointStationary ones? Currently they are left
614 touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH);628 // as Qt::TouchPointMoved
615 touchPoint.pressure = kP / kMaxPressure;629 for (int i = 0; i < kPointerCount; ++i) {
616 switch (mir_touch_event_action(tev, i))630 QWindowSystemInterface::TouchPoint touchPoint;
617 {631
618 case mir_touch_action_up:632 const float kX = mir_touch_event_axis_value(tev, i, mir_touch_axis_x);
619 touchPoint.state = Qt::TouchPointReleased;633 const float kY = mir_touch_event_axis_value(tev, i, mir_touch_axis_y);
620 break;634 const float kW = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_major);
621 case mir_touch_action_down:635 const float kH = mir_touch_event_axis_value(tev, i, mir_touch_axis_touch_minor);
622 touchPoint.state = Qt::TouchPointPressed;636 const float kP = mir_touch_event_axis_value(tev, i, mir_touch_axis_pressure);
623 break;637 touchPoint.id = mir_touch_event_id(tev, i);
624 case mir_touch_action_change:638
625 touchPoint.state = Qt::TouchPointMoved;639 touchPoint.normalPosition = QPointF(kX / kWindowGeometry.width(), kY / kWindowGeometry.height());
626 break;640 touchPoint.area = QRectF(kX - (kW / 2.0), kY - (kH / 2.0), kW, kH);
627 default:641 touchPoint.pressure = kP / kMaxPressure;
628 break;642 switch (mir_touch_event_action(tev, i))
629 }643 {
630644 case mir_touch_action_up:
631 touchPoints.append(touchPoint);645 touchPoint.state = Qt::TouchPointReleased;
646 break;
647 case mir_touch_action_down:
648 touchPoint.state = Qt::TouchPointPressed;
649 break;
650 case mir_touch_action_change:
651 touchPoint.state = Qt::TouchPointMoved;
652 break;
653 default:
654 break;
655 }
656
657 touchPoints.append(touchPoint);
658 }
632 }659 }
633660
634 // Qt needs a happy, sane stream of touch events. So let's make sure we're not forwarding661 // Qt needs a happy, sane stream of touch events. So let's make sure we're not forwarding
635 // any insanity.662 // any insanity.
636 validateTouches(mir_input_event_get_event_time(event) / 1000000, touchPoints);663 validateTouches(window, mir_input_event_get_event_time(event) / 1000000, touchPoints);
637664
638 // Touch event propagation.665 // Touch event propagation.
639 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));666 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));
640 mQtWindowSystem->handleTouchEvent(667 mQtWindowSystem->handleTouchEvent(window,
641 //scales down the nsec_t (int64) to fit a ulong, precision lost but time difference suitable668 //scales down the nsec_t (int64) to fit a ulong, precision lost but time difference suitable
642 mir_input_event_get_event_time(event) / 1000000,669 mir_input_event_get_event_time(event) / 1000000,
643 mTouchDevice,670 mTouchDevice,
@@ -654,7 +681,7 @@
654 // not used681 // not used
655}682}
656683
657void QtEventFeeder::validateTouches(ulong timestamp,684void QtEventFeeder::validateTouches(QWindow *window, ulong timestamp,
658 QList<QWindowSystemInterface::TouchPoint> &touchPoints)685 QList<QWindowSystemInterface::TouchPoint> &touchPoints)
659{686{
660 QSet<int> updatedTouches;687 QSet<int> updatedTouches;
@@ -678,7 +705,7 @@
678 if (!updatedTouches.contains(it.key())) {705 if (!updatedTouches.contains(it.key())) {
679 qCWarning(QTMIR_MIR_INPUT)706 qCWarning(QTMIR_MIR_INPUT)
680 << "There's a touch (id =" << it.key() << ") missing. Releasing it.";707 << "There's a touch (id =" << it.key() << ") missing. Releasing it.";
681 sendActiveTouchRelease(timestamp, it.key());708 sendActiveTouchRelease(window, timestamp, it.key());
682 it = mActiveTouches.erase(it);709 it = mActiveTouches.erase(it);
683 } else {710 } else {
684 ++it;711 ++it;
@@ -696,7 +723,7 @@
696 }723 }
697}724}
698725
699void QtEventFeeder::sendActiveTouchRelease(ulong timestamp, int id)726void QtEventFeeder::sendActiveTouchRelease(QWindow *window, ulong timestamp, int id)
700{727{
701 QList<QWindowSystemInterface::TouchPoint> touchPoints = mActiveTouches.values();728 QList<QWindowSystemInterface::TouchPoint> touchPoints = mActiveTouches.values();
702729
@@ -710,7 +737,7 @@
710 }737 }
711738
712 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));739 qCDebug(QTMIR_MIR_INPUT) << "Sending to Qt" << qPrintable(touchesToString(touchPoints));
713 mQtWindowSystem->handleTouchEvent(timestamp, mTouchDevice, touchPoints);740 mQtWindowSystem->handleTouchEvent(window, timestamp, mTouchDevice, touchPoints);
714}741}
715742
716bool QtEventFeeder::validateTouch(QWindowSystemInterface::TouchPoint &touchPoint)743bool QtEventFeeder::validateTouch(QWindowSystemInterface::TouchPoint &touchPoint)
717744
=== modified file 'src/platforms/mirserver/qteventfeeder.h'
--- src/platforms/mirserver/qteventfeeder.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/qteventfeeder.h 2015-09-30 13:40:22 +0000
@@ -23,6 +23,7 @@
23#include <qpa/qwindowsysteminterface.h>23#include <qpa/qwindowsysteminterface.h>
2424
25class QTouchDevice;25class QTouchDevice;
26class ScreenController;
2627
27/*28/*
28 Fills Qt's event loop with input events from Mir29 Fills Qt's event loop with input events from Mir
@@ -33,26 +34,29 @@
33 // Interface between QtEventFeeder and the actual QWindowSystemInterface functions34 // Interface between QtEventFeeder and the actual QWindowSystemInterface functions
34 // and other related Qt methods and objects to enable replacing them with mocks in35 // and other related Qt methods and objects to enable replacing them with mocks in
35 // pure unit tests.36 // pure unit tests.
36 // TODO - Make it work with multimonitor scenarios
37 class QtWindowSystemInterface {37 class QtWindowSystemInterface {
38 public:38 public:
39 virtual ~QtWindowSystemInterface() {}39 virtual ~QtWindowSystemInterface() {}
40 virtual bool hasTargetWindow() = 0;40 virtual void setScreenController(const QSharedPointer<ScreenController> &sc) = 0;
41 virtual QRect targetWindowGeometry() = 0;41 virtual QWindow* getWindowForTouchPoint(const QPoint &point) = 0;
42 virtual QWindow* focusedWindow() = 0;
42 virtual void registerTouchDevice(QTouchDevice *device) = 0;43 virtual void registerTouchDevice(QTouchDevice *device) = 0;
43 virtual void handleExtendedKeyEvent(ulong timestamp, QEvent::Type type, int key,44 virtual void handleExtendedKeyEvent(QWindow *window, ulong timestamp, QEvent::Type type, int key,
44 Qt::KeyboardModifiers modifiers,45 Qt::KeyboardModifiers modifiers,
45 quint32 nativeScanCode, quint32 nativeVirtualKey,46 quint32 nativeScanCode, quint32 nativeVirtualKey,
46 quint32 nativeModifiers,47 quint32 nativeModifiers,
47 const QString& text = QString(), bool autorep = false,48 const QString& text = QString(), bool autorep = false,
48 ushort count = 1) = 0;49 ushort count = 1) = 0;
49 virtual void handleTouchEvent(ulong timestamp, QTouchDevice *device,50 virtual void handleTouchEvent(QWindow *window, ulong timestamp, QTouchDevice *device,
50 const QList<struct QWindowSystemInterface::TouchPoint> &points,51 const QList<struct QWindowSystemInterface::TouchPoint> &points,
51 Qt::KeyboardModifiers mods = Qt::NoModifier) = 0;52 Qt::KeyboardModifiers mods = Qt::NoModifier) = 0;
52 virtual void handleMouseEvent(ulong timestamp, QPointF point, Qt::MouseButton buttons, Qt::KeyboardModifiers modifiers) = 0;53 virtual void handleMouseEvent(ulong timestamp, QPointF movement,
54 Qt::MouseButton buttons, Qt::KeyboardModifiers modifiers) = 0;
53 };55 };
5456
55 QtEventFeeder(QtWindowSystemInterface *windowSystem = nullptr);57 QtEventFeeder(const QSharedPointer<ScreenController> &screenController);
58 QtEventFeeder(const QSharedPointer<ScreenController> &screenController,
59 QtWindowSystemInterface *windowSystem);
56 virtual ~QtEventFeeder();60 virtual ~QtEventFeeder();
5761
58 static const int MirEventActionMask;62 static const int MirEventActionMask;
@@ -67,9 +71,9 @@
67 void dispatchKey(MirInputEvent const* event);71 void dispatchKey(MirInputEvent const* event);
68 void dispatchTouch(MirInputEvent const* event);72 void dispatchTouch(MirInputEvent const* event);
69 void dispatchPointer(MirInputEvent const* event);73 void dispatchPointer(MirInputEvent const* event);
70 void validateTouches(ulong timestamp, QList<QWindowSystemInterface::TouchPoint> &touchPoints);74 void validateTouches(QWindow *window, ulong timestamp, QList<QWindowSystemInterface::TouchPoint> &touchPoints);
71 bool validateTouch(QWindowSystemInterface::TouchPoint &touchPoint);75 bool validateTouch(QWindowSystemInterface::TouchPoint &touchPoint);
72 void sendActiveTouchRelease(ulong timestamp, int id);76 void sendActiveTouchRelease(QWindow *window, ulong timestamp, int id);
7377
74 QString touchesToString(const QList<struct QWindowSystemInterface::TouchPoint> &points);78 QString touchesToString(const QList<struct QWindowSystemInterface::TouchPoint> &points);
7579
7680
=== modified file 'src/platforms/mirserver/screen.cpp'
--- src/platforms/mirserver/screen.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/screen.cpp 2015-09-30 13:40:22 +0000
@@ -20,12 +20,13 @@
2020
21// Mir21// Mir
22#include "mir/geometry/size.h"22#include "mir/geometry/size.h"
23#include "mir/graphics/buffer.h"
24#include "mir/graphics/display_buffer.h"
25#include "mir/graphics/display.h"
2326
24// Qt27// Qt
25#include <QCoreApplication>28#include <QCoreApplication>
26#include <qpa/qwindowsysteminterface.h>29#include <qpa/qwindowsysteminterface.h>
27#include <QtSensors/QOrientationSensor>
28#include <QtSensors/QOrientationReading>
29#include <QThread>30#include <QThread>
3031
31// Qt sensors32// Qt sensors
@@ -102,12 +103,15 @@
102103
103bool Screen::skipDBusRegistration = false;104bool Screen::skipDBusRegistration = false;
104105
105Screen::Screen(mir::graphics::DisplayConfigurationOutput const &screen)106Screen::Screen(const mir::graphics::DisplayConfigurationOutput &screen)
106 : QObject(nullptr)107 : QObject(nullptr)
108 , m_displayBuffer(nullptr)
109 , m_displayGroup(nullptr)
107 , m_orientationSensor(new QOrientationSensor(this))110 , m_orientationSensor(new QOrientationSensor(this))
111 , m_screenWindow(nullptr)
108 , m_unityScreen(nullptr)112 , m_unityScreen(nullptr)
109{113{
110 readMirDisplayConfiguration(screen);114 setMirDisplayConfiguration(screen);
111115
112 // Set the default orientation based on the initial screen dimmensions.116 // Set the default orientation based on the initial screen dimmensions.
113 m_nativeOrientation = (m_geometry.width() >= m_geometry.height())117 m_nativeOrientation = (m_geometry.width() >= m_geometry.height())
@@ -139,6 +143,14 @@
139 }143 }
140}144}
141145
146Screen::~Screen()
147{
148 //if a ScreenWindow associated with this screen, kill it
149 if (m_screenWindow) {
150 m_screenWindow->window()->destroy(); // ends up destroying m_ScreenWindow
151 }
152}
153
142bool Screen::orientationSensorEnabled()154bool Screen::orientationSensorEnabled()
143{155{
144 return m_orientationSensor->isActive();156 return m_orientationSensor->isActive();
@@ -150,8 +162,15 @@
150 toggleSensors(status);162 toggleSensors(status);
151}163}
152164
153void Screen::readMirDisplayConfiguration(mir::graphics::DisplayConfigurationOutput const &screen)165void Screen::setMirDisplayConfiguration(const mir::graphics::DisplayConfigurationOutput &screen)
154{166{
167 // Note: DisplayConfigurationOutput will be destroyed after this function returns
168
169 // Output data - each output has a unique id and corresponding type. Can be multiple cards.
170 m_outputId = screen.id;
171 m_cardId = screen.card_id;
172 m_type = screen.type;
173
155 // Physical screen size174 // Physical screen size
156 m_physicalSize.setWidth(screen.physical_size_mm.width.as_float());175 m_physicalSize.setWidth(screen.physical_size_mm.width.as_float());
157 m_physicalSize.setHeight(screen.physical_size_mm.height.as_float());176 m_physicalSize.setHeight(screen.physical_size_mm.height.as_float());
@@ -162,12 +181,34 @@
162 // Pixel depth181 // Pixel depth
163 m_depth = 8 * MIR_BYTES_PER_PIXEL(screen.current_format);182 m_depth = 8 * MIR_BYTES_PER_PIXEL(screen.current_format);
164183
165 // Mode = Resolution & refresh rate184 // Power mode
185 m_powerMode = screen.power_mode;
186
187 QRect oldGeometry = m_geometry;
188 // Position of screen in virtual desktop coordinate space
189 m_geometry.setTop(screen.top_left.y.as_int());
190 m_geometry.setLeft(screen.top_left.x.as_int());
191
192 // Mode = current resolution & refresh rate
166 mir::graphics::DisplayConfigurationMode mode = screen.modes.at(screen.current_mode_index);193 mir::graphics::DisplayConfigurationMode mode = screen.modes.at(screen.current_mode_index);
167 m_geometry.setWidth(mode.size.width.as_int());194 m_geometry.setWidth(mode.size.width.as_int());
168 m_geometry.setHeight(mode.size.height.as_int());195 m_geometry.setHeight(mode.size.height.as_int());
169196
170 m_refreshRate = mode.vrefresh_hz;197 // DPI - unnecessary to calculate, default implementation in QPlatformScreen is sufficient
198
199 // Check for Screen geometry change
200 if (m_geometry != oldGeometry) {
201 QWindowSystemInterface::handleScreenGeometryChange(this->screen(), m_geometry, m_geometry);
202 if (m_screenWindow) { // resize corresponding window immediately
203 m_screenWindow->setGeometry(m_geometry);
204 }
205 }
206
207 // Refresh rate
208 if (m_refreshRate != mode.vrefresh_hz) {
209 m_refreshRate = mode.vrefresh_hz;
210 QWindowSystemInterface::handleScreenRefreshRateChange(this->screen(), mode.vrefresh_hz);
211 }
171}212}
172213
173void Screen::toggleSensors(const bool enable) const214void Screen::toggleSensors(const bool enable) const
@@ -226,3 +267,58 @@
226 OrientationReadingEvent::m_type,267 OrientationReadingEvent::m_type,
227 m_orientationSensor->reading()->orientation()));268 m_orientationSensor->reading()->orientation()));
228}269}
270
271QPlatformCursor *Screen::cursor() const
272{
273 const QPlatformCursor *platformCursor = &m_cursor;
274 return const_cast<QPlatformCursor *>(platformCursor);
275}
276
277ScreenWindow *Screen::window() const
278{
279 return m_screenWindow;
280}
281
282void Screen::setWindow(ScreenWindow *window)
283{
284 if (window && m_screenWindow) {
285 qCDebug(QTMIR_SENSOR_MESSAGES) << "Screen::setWindow - overwriting existing ScreenWindow";
286 }
287 m_screenWindow = window;
288}
289
290void Screen::setMirDisplayBuffer(mir::graphics::DisplayBuffer *buffer, mir::graphics::DisplaySyncGroup *group)
291{
292 qCDebug(QTMIR_SCREENS) << "Screen::setMirDisplayBuffer" << buffer << group;
293 // This operation should only be performed while rendering is stopped
294 m_displayBuffer = buffer;
295 m_displayGroup = group;
296}
297
298void Screen::swapBuffers()
299{
300 m_displayBuffer->gl_swap_buffers();
301
302 /* FIXME this exposes a QtMir architecture problem, as Screen is supposed to wrap a mg::DisplayBuffer.
303 * We use Qt's multithreaded renderer, where each Screen is rendered to relatively independently, and
304 * post() called also individually.
305 *
306 * But if this is a native server on Android, in the multimonitor case a DisplaySyncGroup can contain
307 * 2+ DisplayBuffers, one post() call will submit all mg::DisplayBuffers in the group for flipping.
308 * This will cause just one Screen to be updated, blocking the swap call for the other Screens, which
309 * will slow rendering dramatically.
310 *
311 * Integrating the Qt Scenegraph renderer as a Mir renderer should solve this issue.
312 */
313 m_displayGroup->post();
314}
315
316void Screen::makeCurrent()
317{
318 m_displayBuffer->make_current();
319}
320
321void Screen::doneCurrent()
322{
323 m_displayBuffer->release_current();
324}
229325
=== modified file 'src/platforms/mirserver/screen.h'
--- src/platforms/mirserver/screen.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/screen.h 2015-09-30 13:40:22 +0000
@@ -17,20 +17,28 @@
17#ifndef SCREEN_H17#ifndef SCREEN_H
18#define SCREEN_H18#define SCREEN_H
1919
20// Qt
20#include <QObject>21#include <QObject>
21#include <QTimer>22#include <QTimer>
22#include <QtDBus/QDBusInterface>23#include <QtDBus/QDBusInterface>
23#include <qpa/qplatformscreen.h>24#include <qpa/qplatformscreen.h>
2425
25#include "mir/graphics/display_configuration.h"26// Mir
27#include <mir/graphics/display_configuration.h>
28
29// local
30#include "cursor.h"
31#include "screenwindow.h"
2632
27class QOrientationSensor;33class QOrientationSensor;
34namespace mir { namespace graphics { class DisplayBuffer; class DisplaySyncGroup; }}
2835
29class Screen : public QObject, public QPlatformScreen36class Screen : public QObject, public QPlatformScreen
30{37{
31 Q_OBJECT38 Q_OBJECT
32public:39public:
33 Screen(mir::graphics::DisplayConfigurationOutput const&);40 Screen(const mir::graphics::DisplayConfigurationOutput &);
41 ~Screen();
3442
35 // QPlatformScreen methods.43 // QPlatformScreen methods.
36 QRect geometry() const override { return m_geometry; }44 QRect geometry() const override { return m_geometry; }
@@ -40,8 +48,12 @@
40 qreal refreshRate() const override { return m_refreshRate; }48 qreal refreshRate() const override { return m_refreshRate; }
41 Qt::ScreenOrientation nativeOrientation() const override { return m_nativeOrientation; }49 Qt::ScreenOrientation nativeOrientation() const override { return m_nativeOrientation; }
42 Qt::ScreenOrientation orientation() const override { return m_currentOrientation; }50 Qt::ScreenOrientation orientation() const override { return m_currentOrientation; }
51 QPlatformCursor *cursor() const override;
4352
44 void toggleSensors(const bool enable) const;53 void toggleSensors(const bool enable) const;
54 mir::graphics::DisplayConfigurationOutputType outputType() const { return m_type; }
55
56 ScreenWindow* window() const;
4557
46 // QObject methods.58 // QObject methods.
47 void customEvent(QEvent* event) override;59 void customEvent(QEvent* event) override;
@@ -54,20 +66,40 @@
54 void onDisplayPowerStateChanged(int, int);66 void onDisplayPowerStateChanged(int, int);
55 void onOrientationReadingChanged();67 void onOrientationReadingChanged();
5668
69protected:
70 void setWindow(ScreenWindow *window);
71
72 void setMirDisplayConfiguration(const mir::graphics::DisplayConfigurationOutput &);
73 void setMirDisplayBuffer(mir::graphics::DisplayBuffer *, mir::graphics::DisplaySyncGroup *);
74 void swapBuffers();
75 void makeCurrent();
76 void doneCurrent();
77
57private:78private:
58 void readMirDisplayConfiguration(mir::graphics::DisplayConfigurationOutput const&);
59
60 QRect m_geometry;79 QRect m_geometry;
61 int m_depth;80 int m_depth;
62 QImage::Format m_format;81 QImage::Format m_format;
63 QSizeF m_physicalSize;82 QSizeF m_physicalSize;
64 qreal m_refreshRate;83 qreal m_refreshRate;
6584
85 mir::graphics::DisplayBuffer *m_displayBuffer;
86 mir::graphics::DisplaySyncGroup *m_displayGroup;
87 mir::graphics::DisplayConfigurationOutputId m_outputId;
88 mir::graphics::DisplayConfigurationCardId m_cardId;
89 mir::graphics::DisplayConfigurationOutputType m_type;
90 MirPowerMode m_powerMode;
91
66 Qt::ScreenOrientation m_nativeOrientation;92 Qt::ScreenOrientation m_nativeOrientation;
67 Qt::ScreenOrientation m_currentOrientation;93 Qt::ScreenOrientation m_currentOrientation;
68 QOrientationSensor *m_orientationSensor;94 QOrientationSensor *m_orientationSensor;
6995
96 ScreenWindow *m_screenWindow;
70 QDBusInterface *m_unityScreen;97 QDBusInterface *m_unityScreen;
98
99 qtmir::Cursor m_cursor;
100
101 friend class ScreenController;
102 friend class ScreenWindow;
71};103};
72104
73#endif // SCREEN_H105#endif // SCREEN_H
74106
=== added file 'src/platforms/mirserver/screencontroller.cpp'
--- src/platforms/mirserver/screencontroller.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/screencontroller.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,258 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "screencontroller.h"
18
19#include "screenwindow.h"
20#include "qtcompositor.h"
21#include "logging.h"
22#include "mirserverintegration.h"
23#include "screen.h"
24
25// Mir
26#include <mir/graphics/display.h>
27#include <mir/graphics/display_buffer.h>
28
29// Qt
30#include <QScreen>
31#include <QQuickWindow>
32#include <qpa/qwindowsysteminterface.h>
33
34// std
35#include <memory>
36
37Q_LOGGING_CATEGORY(QTMIR_SCREENS, "qtmir.screens")
38
39namespace mg = mir::graphics;
40
41
42ScreenController::ScreenController(QObject *parent)
43 : QObject(parent)
44 , m_compositing(false)
45{
46 qCDebug(QTMIR_SCREENS) << "ScreenController::ScreenController";
47}
48
49// init only after MirServer has initialized - runs on MirServerThread!!!
50void ScreenController::init(const std::shared_ptr<mir::graphics::Display> &display,
51 const std::shared_ptr<mir::compositor::Compositor> &compositor)
52{
53 m_display = display;
54 m_compositor = compositor;
55
56 // Use a Blocking Queued Connection to enforce synchronization of Qt GUI thread with Mir thread(s)
57 // on compositor shutdown. Compositor startup can be lazy.
58 // Queued connections work because the thread affinity of this class is with the Qt GUI thread.
59 auto qtCompositor = static_cast<QtCompositor *>(compositor.get());
60 connect(qtCompositor, &QtCompositor::starting,
61 this, &ScreenController::onCompositorStarting);
62 connect(qtCompositor, &QtCompositor::stopping,
63 this, &ScreenController::onCompositorStopping, Qt::BlockingQueuedConnection);
64}
65
66// terminate before shutting down the Mir server, or else liable to deadlock with the blocking connection above
67// Runs on MirServerThread!!!
68void ScreenController::terminate()
69{
70 auto qtCompositor = static_cast<QtCompositor *>(m_compositor.get());
71 qtCompositor->disconnect();
72}
73
74void ScreenController::onCompositorStarting()
75{
76 qCDebug(QTMIR_SCREENS) << "ScreenController::onCompositorStarting";
77 m_compositing = true;
78
79 update();
80
81 // (Re)Start Qt's render thread by setting all windows with a corresponding screen to exposed.
82 for (auto screen : m_screenList) {
83 auto window = static_cast<ScreenWindow *>(screen->window());
84 if (window && window->window()) {
85 window->setExposed(true);
86 }
87 }
88}
89
90void ScreenController::onCompositorStopping()
91{
92 qCDebug(QTMIR_SCREENS) << "ScreenController::onCompositorStopping";
93 m_compositing = false;
94
95 // Stop Qt's render threads by setting all its windows it obscured. Must
96 // block until all windows have their GL contexts released.
97 for (auto screen : m_screenList) {
98 auto window = static_cast<ScreenWindow *>(screen->window());
99 if (window && window->window()) {
100 window->setExposed(false);
101 }
102 }
103
104 update();
105}
106
107void ScreenController::update()
108{
109 qCDebug(QTMIR_SCREENS) << "ScreenController::update";
110 auto display = m_display.lock();
111 if (!display)
112 return;
113 auto displayConfig = display->configuration();
114
115 // Mir only tells us something changed, it is up to us to figure out what.
116 QList<Screen*> newScreenList;
117 QList<Screen*> oldScreenList = m_screenList;
118 m_screenList.clear();
119
120 displayConfig->for_each_output(
121 [this, &oldScreenList, &newScreenList](const mg::DisplayConfigurationOutput &output) {
122 if (output.used && output.connected) {
123 Screen *screen = findScreenWithId(oldScreenList, output.id);
124 if (screen) { // we've already set up this display before, refresh its internals
125 screen->setMirDisplayConfiguration(output);
126 oldScreenList.removeAll(screen);
127 } else {
128 // new display, so create Screen for it
129 screen = this->createScreen(output);
130 newScreenList.append(screen);
131 qCDebug(QTMIR_SCREENS) << "Added Screen with id" << output.id.as_value()
132 << "and geometry" << screen->geometry();
133 }
134 m_screenList.append(screen);
135 }
136 }
137 );
138
139 // Delete any old & unused Screens
140 for (auto screen: oldScreenList) {
141 qCDebug(QTMIR_SCREENS) << "Removed Screen with id" << screen->m_outputId.as_value()
142 << "and geometry" << screen->geometry();
143 // The screen is automatically removed from Qt's internal list by the QPlatformScreen destructor.
144 auto window = static_cast<ScreenWindow *>(screen->window());
145 if (window && window->window() && window->isExposed()) {
146 window->window()->hide();
147 }
148 delete screen;
149 }
150
151 // Match up the new Mir DisplayBuffers with each Screen
152 display->for_each_display_sync_group([&](mg::DisplaySyncGroup &group) {
153 group.for_each_display_buffer([&](mg::DisplayBuffer &buffer) {
154 // only way to match Screen to a DisplayBuffer is by matching the geometry
155 QRect dbGeom(buffer.view_area().top_left.x.as_int(),
156 buffer.view_area().top_left.y.as_int(),
157 buffer.view_area().size.width.as_int(),
158 buffer.view_area().size.height.as_int());
159
160 for (auto screen : m_screenList) {
161 if (dbGeom == screen->geometry()) {
162 screen->setMirDisplayBuffer(&buffer, &group);
163 break;
164 }
165 }
166 });
167 });
168
169 qCDebug(QTMIR_SCREENS) << "=======================================";
170 for (auto screen: m_screenList) {
171 qCDebug(QTMIR_SCREENS) << screen << "- id:" << screen->m_outputId.as_value()
172 << "geometry:" << screen->geometry()
173 << "window:" << screen->window()
174 << "type" << static_cast<int>(screen->outputType());
175 }
176 qCDebug(QTMIR_SCREENS) << "=======================================";
177
178 for (auto screen : newScreenList) {
179 Q_EMIT screenAdded(screen);
180 }
181}
182
183Screen* ScreenController::createScreen(const mir::graphics::DisplayConfigurationOutput &output) const
184{
185 return new Screen(output);
186}
187
188Screen* ScreenController::getUnusedScreen()
189{
190 if (m_screenList.empty()) {
191 return nullptr;
192 } else if (m_screenList.size() == 1) {
193 return m_screenList.at(0);
194 }
195
196 // FIXME: Until we have better way of identifying screens, prioritize outputs based on their output type.
197 // Note the priorities defined here are nothing more than guesses. It tries to select internal displays first,
198 // then digital outputs, and finally analogue.
199 QMap <int, Screen*> priorityList;
200 auto prioritize = [](const mg::DisplayConfigurationOutputType &type) {
201 using out = mg::DisplayConfigurationOutputType;
202 switch(type) {
203 case out::lvds:
204 case out::edp:
205 return 0;
206 case out::displayport:
207 case out::hdmia:
208 case out::hdmib:
209 return 1;
210 case out::dvii:
211 case out::dvid:
212 case out::dvia:
213 return 2;
214 case out::vga:
215 return 3;
216 case out::ninepindin:
217 return 4;
218 case out::component:
219 case out::composite:
220 case out::svideo:
221 return 5;
222 case out::tv:
223 return 6;
224 case out::unknown:
225 default:
226 return 9;
227 }
228 };
229
230 for (auto screen : m_screenList) {
231 if (!screen->window()) {
232 priorityList.insert(prioritize(screen->outputType()), screen);
233 }
234 }
235
236 qCDebug(QTMIR_SCREENS) << "Prioritized list of available outputs:" << priorityList;
237 return priorityList.first(); // Map sorted by key, so first is the key with highest priority.
238}
239
240Screen* ScreenController::findScreenWithId(const QList<Screen *> &list, const mg::DisplayConfigurationOutputId id)
241{
242 for (Screen *screen : list) {
243 if (screen->m_outputId == id) {
244 return screen;
245 }
246 }
247 return nullptr;
248}
249
250QWindow* ScreenController::getWindowForPoint(const QPoint &point) //FIXME - not thread safe & not efficient
251{
252 for (Screen *screen : m_screenList) {
253 if (screen->window() && screen->geometry().contains(point)) {
254 return screen->window()->window();
255 }
256 }
257 return nullptr;
258}
0259
=== added file 'src/platforms/mirserver/screencontroller.h'
--- src/platforms/mirserver/screencontroller.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/screencontroller.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,96 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef SCREENCONTROLLER_H
18#define SCREENCONTROLLER_H
19
20#include <QObject>
21#include <QPoint>
22
23// Mir
24#include <mir/graphics/display_configuration.h>
25
26// std
27#include <memory>
28
29namespace mir {
30 namespace graphics { class Display; }
31 namespace compositor { class Compositor; }
32}
33class Screen;
34class QWindow;
35
36/*
37 * ScreenController monitors the Mir display configuration and compositor status, and updates
38 * the relevant QScreen and QWindow states accordingly.
39 *
40 * Primary purposes are:
41 * 1. to update QScreen state on Mir display configuration changes
42 * 2. to stop the Qt renderer by hiding its QWindow when Mir wants to stop all compositing,
43 * and resume Qt's renderer by showing its QWindow when Mir wants to resume compositing.
44 *
45 *
46 * Threading Note:
47 * This object must have affinity to the main Qt GUI thread, as it creates & destroys Platform
48 * objects which Qt uses internally. However beware as the init() & terminate() methods need to
49 * be called on the MirServerThread thread, as we need to monitor the screen state *after*
50 * Mir has initialized but before Qt's event loop has started, and tear down before Mir terminates.
51 * Also note the MirServerThread does not have an QEventLoop.
52 *
53 * All other methods must be called on the Qt GUI thread.
54 */
55
56class ScreenController : public QObject
57{
58 Q_OBJECT
59public:
60 explicit ScreenController(QObject *parent = 0);
61
62 Screen* getUnusedScreen();
63 QList<Screen*> screens() const { return m_screenList; }
64 bool compositing() const { return m_compositing; }
65
66 QWindow* getWindowForPoint(const QPoint &point);
67
68Q_SIGNALS:
69 void screenAdded(Screen *screen);
70
71public Q_SLOTS:
72 void update();
73
74public:
75 // called by MirServer
76 void init(const std::shared_ptr<mir::graphics::Display> &display,
77 const std::shared_ptr<mir::compositor::Compositor> &compositor);
78 void terminate();
79
80 // override for testing purposes
81 virtual Screen *createScreen(const mir::graphics::DisplayConfigurationOutput &output) const;
82
83protected Q_SLOTS:
84 void onCompositorStarting();
85 void onCompositorStopping();
86
87private:
88 Screen* findScreenWithId(const QList<Screen*> &list, const mir::graphics::DisplayConfigurationOutputId id);
89
90 std::weak_ptr<mir::graphics::Display> m_display;
91 std::shared_ptr<mir::compositor::Compositor> m_compositor;
92 QList<Screen*> m_screenList;
93 bool m_compositing;
94};
95
96#endif // SCREENCONTROLLER_H
097
=== renamed file 'src/platforms/mirserver/displaywindow.cpp' => 'src/platforms/mirserver/screenwindow.cpp'
--- src/platforms/mirserver/displaywindow.cpp 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/screenwindow.cpp 2015-09-30 13:40:22 +0000
@@ -14,15 +14,22 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */15 */
1616
17#include "displaywindow.h"17#include "screenwindow.h"
1818#include "screen.h"
19#include "mir/geometry/size.h"19
2020// Mir
21#include <mir/geometry/size.h>
22#include <mir/graphics/display_buffer.h>
23
24// Qt
21#include <qpa/qwindowsysteminterface.h>25#include <qpa/qwindowsysteminterface.h>
22#include <qpa/qplatformscreen.h>26#include <qpa/qplatformscreen.h>
2327#include <QQuickWindow>
28#include <QtQuick/private/qsgrenderloop_p.h>
24#include <QDebug>29#include <QDebug>
2530
31#include "logging.h"
32
26static WId newWId()33static WId newWId()
27{34{
28 static WId id = 0;35 static WId id = 0;
@@ -33,22 +40,22 @@
33 return ++id;40 return ++id;
34}41}
3542
36DisplayWindow::DisplayWindow(43ScreenWindow::ScreenWindow(QWindow *window)
37 QWindow *window,44 : QPlatformWindow(window)
38 mir::graphics::DisplaySyncGroup *displayGroup,45 , m_exposed(false)
39 mir::graphics::DisplayBuffer *displayBuffer)
40 : QObject(nullptr), QPlatformWindow(window)
41 , m_isExposed(true)
42 , m_winId(newWId())46 , m_winId(newWId())
43 , m_displayGroup(displayGroup)
44 , m_displayBuffer(displayBuffer)
45{47{
46 qDebug() << "DisplayWindow::DisplayWindow";48 // Register with the Screen it is associated with
47 qWarning("Window %p: %p 0x%x\n", this, window, uint(m_winId));49 auto myScreen = static_cast<Screen *>(screen());
50 Q_ASSERT(myScreen);
51 myScreen->setWindow(this);
52
53 qCDebug(QTMIR_SCREENS) << "ScreenWindow" << this << "with window ID" << uint(m_winId) << "backed by" << myScreen;
4854
49 QRect screenGeometry(screen()->availableGeometry());55 QRect screenGeometry(screen()->availableGeometry());
50 if (window->geometry() != screenGeometry) {56 if (window->geometry() != screenGeometry) {
51 setGeometry(screenGeometry);57 setGeometry(screenGeometry);
58 window->setGeometry(screenGeometry);
52 }59 }
53 window->setSurfaceType(QSurface::OpenGLSurface);60 window->setSurfaceType(QSurface::OpenGLSurface);
5461
@@ -57,70 +64,54 @@
57 requestActivateWindow();64 requestActivateWindow();
58}65}
5966
60QRect DisplayWindow::geometry() const67ScreenWindow::~ScreenWindow()
61{68{
62 // For yet-to-become-fullscreen windows report the geometry covering the entire69 qCDebug(QTMIR_SCREENS) << "Destroying ScreenWindow" << this;
63 // screen. This is particularly important for Quick where the root object may get70 static_cast<Screen *>(screen())->setWindow(nullptr);
64 // sized to some geometry queried before calling create().71}
65 return screen()->availableGeometry();72
66}73bool ScreenWindow::isExposed() const
6774{
68void DisplayWindow::setGeometry(const QRect &)75 return m_exposed;
69{76}
70 // We only support full-screen windows77
71 QRect rect(screen()->availableGeometry());78void ScreenWindow::setExposed(const bool exposed)
72 QWindowSystemInterface::handleGeometryChange(window(), rect);79{
73 QPlatformWindow::setGeometry(rect);80 qCDebug(QTMIR_SCREENS) << "ScreenWindow::setExposed" << this << exposed;
74}81 if (m_exposed == exposed)
7582 return;
76bool DisplayWindow::isExposed() const83
77{84 m_exposed = exposed;
78 return m_isExposed;85 if (!window())
79}86 return;
8087
81bool DisplayWindow::event(QEvent *event)88 // If backing a QQuickWindow, need to stop/start its renderer immediately
82{89 auto quickWindow = static_cast<QQuickWindow *>(window());
83 // Intercept Hide event and convert to Expose event, as Hide causes Qt to release GL90 if (!quickWindow)
84 // resources, which we don't want. Must intercept Show to un-do hide.91 return;
85 if (event->type() == QEvent::Hide) {92
86 qDebug() << "DisplayWindow::event got QEvent::Hide";93 auto renderer = QSGRenderLoop::instance();
87 m_isExposed = false;94 if (exposed) {
88 QWindowSystemInterface::handleExposeEvent(window(), QRect());95 renderer->show(quickWindow);
89 QWindowSystemInterface::flushWindowSystemEvents();96 QWindowSystemInterface::handleExposeEvent(window(), QRegion()); // else it won't redraw
90 return true;97 } else {
91 } else if (event->type() == QEvent::Show) {98 quickWindow->setPersistentOpenGLContext(false);
92 qDebug() << "DisplayWindow::event got QEvent::Show";99 quickWindow->setPersistentSceneGraph(false);
93 m_isExposed = true;100 renderer->hide(quickWindow); // ExposeEvent will arrive too late, need to stop compositor immediately
94 QRect rect(QPoint(), geometry().size());
95 QWindowSystemInterface::handleExposeEvent(window(), rect);
96 QWindowSystemInterface::flushWindowSystemEvents();
97 return true;
98 }101 }
99 return QObject::event(event);102}
100}103
101104void ScreenWindow::swapBuffers()
102void DisplayWindow::swapBuffers()105{
103{106 static_cast<Screen *>(screen())->swapBuffers();
104 m_displayBuffer->gl_swap_buffers();107}
105108
106 // FIXME this exposes a QtMir architecture problem now, as DisplayWindow109void ScreenWindow::makeCurrent()
107 // is supposed to wrap a mg::DisplayBuffer. We use Qt's multithreaded110{
108 // renderer, where each DisplayWindow is rendered to relatively111 static_cast<Screen *>(screen())->makeCurrent();
109 // independently, and post() called also individually.112}
110 //113
111 // But in multimonitor case where a DisplaySyncGroup contains 2114void ScreenWindow::doneCurrent()
112 // DisplayBuffers, one post() call will submit both115{
113 // mg::DisplayBuffers for flipping, which can happen before the other116 static_cast<Screen *>(screen())->doneCurrent();
114 // DisplayWindow has been rendered to, causing visual artifacts
115 m_displayGroup->post();
116}
117
118void DisplayWindow::makeCurrent()
119{
120 m_displayBuffer->make_current();
121}
122
123void DisplayWindow::doneCurrent()
124{
125 m_displayBuffer->release_current();
126}117}
127118
=== renamed file 'src/platforms/mirserver/displaywindow.h' => 'src/platforms/mirserver/screenwindow.h'
--- src/platforms/mirserver/displaywindow.h 2015-08-11 12:08:32 +0000
+++ src/platforms/mirserver/screenwindow.h 2015-09-30 13:40:22 +0000
@@ -14,43 +14,33 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */15 */
1616
17#ifndef DISPLAYWINDOW_H17#ifndef SCREENWINDOW_H
18#define DISPLAYWINDOW_H18#define SCREENWINDOW_H
1919
20#include <qpa/qplatformwindow.h>20#include <qpa/qplatformwindow.h>
2121
22#include <mir/graphics/display.h>22// ScreenWindow implements the basics of a QPlatformWindow.
23#include <mir/graphics/display_buffer.h>23// QtMir enforces one Window per Screen, so Window and Screen are tightly coupled.
2424// All Mir specifics live in the associated Screen object.
25#include <QObject>25
2626class ScreenWindow : public QPlatformWindow
27// DisplayWindow wraps the whatever implementation Mir creates of a DisplayBuffer,
28// which is the buffer output for an individual display.
29
30class DisplayWindow : public QObject, public QPlatformWindow
31{27{
32 Q_OBJECT
33public:28public:
34 explicit DisplayWindow(QWindow *window, mir::graphics::DisplaySyncGroup*, mir::graphics::DisplayBuffer*);29 explicit ScreenWindow(QWindow *window);
30 virtual ~ScreenWindow();
3531
36 QRect geometry() const override;32 bool isExposed() const override;
37 void setGeometry(const QRect &rect) override;33 void setExposed(const bool exposed);
3834
39 WId winId() const override { return m_winId; }35 WId winId() const override { return m_winId; }
4036
41 bool isExposed() const override;
42
43 bool event(QEvent *event) override;
44
45 void swapBuffers();37 void swapBuffers();
46 void makeCurrent();38 void makeCurrent();
47 void doneCurrent();39 void doneCurrent();
4840
49private:41private:
50 bool m_isExposed;42 bool m_exposed;
51 WId m_winId;43 WId m_winId;
52 mir::graphics::DisplaySyncGroup *m_displayGroup;
53 mir::graphics::DisplayBuffer *m_displayBuffer;
54};44};
5545
56#endif // DISPLAYWINDOW_H46#endif // SCREENWINDOW_H
5747
=== added file 'src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp'
--- src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,44 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "tileddisplayconfigurationpolicy.h"
18
19#include <mir/graphics/display_configuration.h>
20#include <mir/geometry/point.h>
21
22namespace mg = mir::graphics;
23
24TiledDisplayConfigurationPolicy::TiledDisplayConfigurationPolicy(
25 const std::shared_ptr<mir::graphics::DisplayConfigurationPolicy> &wrapped)
26 : m_wrapped(wrapped)
27{
28}
29
30void TiledDisplayConfigurationPolicy::apply_to(mg::DisplayConfiguration& conf)
31{
32 int nextTopLeftPosition = 0;
33
34 m_wrapped->apply_to(conf);
35
36 conf.for_each_output(
37 [&](mg::UserDisplayConfigurationOutput& output)
38 {
39 if (output.connected && output.used) {
40 output.top_left = mir::geometry::Point{nextTopLeftPosition, 0};
41 nextTopLeftPosition += output.modes[output.preferred_mode_index].size.width.as_int();
42 }
43 });
44}
045
=== added file 'src/platforms/mirserver/tileddisplayconfigurationpolicy.h'
--- src/platforms/mirserver/tileddisplayconfigurationpolicy.h 1970-01-01 00:00:00 +0000
+++ src/platforms/mirserver/tileddisplayconfigurationpolicy.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef TILEDDISPLAYCONFIGURATIONPOLICY_H
18#define TILEDDISPLAYCONFIGURATIONPOLICY_H
19
20#include <mir/graphics/display_configuration_policy.h>
21
22#include <memory>
23
24class TiledDisplayConfigurationPolicy : public mir::graphics::DisplayConfigurationPolicy
25{
26public:
27 TiledDisplayConfigurationPolicy(const std::shared_ptr<mir::graphics::DisplayConfigurationPolicy> &wrapped);
28
29 void apply_to(mir::graphics::DisplayConfiguration& conf) override;
30
31private:
32 const std::shared_ptr<mir::graphics::DisplayConfigurationPolicy> m_wrapped;
33};
34
35#endif // TILEDDISPLAYCONFIGURATIONPOLICY_H
036
=== added directory 'tests/common'
=== added file 'tests/common/fake_displayconfigurationoutput.h'
--- tests/common/fake_displayconfigurationoutput.h 1970-01-01 00:00:00 +0000
+++ tests/common/fake_displayconfigurationoutput.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,73 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef FAKE_DISPLAYCONFIGURATIONOUTPUT_H
18#define FAKE_DISPLAYCONFIGURATIONOUTPUT_H
19
20#include <mir/graphics/display_configuration.h>
21
22namespace mg = mir::graphics;
23namespace geom = mir::geometry;
24
25const mg::DisplayConfigurationOutput fakeOutput1
26{
27 mg::DisplayConfigurationOutputId{3},
28 mg::DisplayConfigurationCardId{2},
29 mg::DisplayConfigurationOutputType::dvid,
30 {
31 mir_pixel_format_abgr_8888
32 },
33 {
34 {geom::Size{100, 200}, 60.0},
35 {geom::Size{100, 200}, 59.0},
36 {geom::Size{150, 200}, 59.0}
37 },
38 0,
39 geom::Size{1111, 2222},
40 true,
41 true,
42 geom::Point(),
43 2,
44 mir_pixel_format_abgr_8888,
45 mir_power_mode_on,
46 mir_orientation_normal
47};
48
49const mg::DisplayConfigurationOutput fakeOutput2
50{
51 mg::DisplayConfigurationOutputId{2},
52 mg::DisplayConfigurationCardId{4},
53 mg::DisplayConfigurationOutputType::lvds,
54 {
55 mir_pixel_format_xbgr_8888
56 },
57 {
58 {geom::Size{800, 1200}, 90.0},
59 {geom::Size{1600, 2400}, 60.0},
60 {geom::Size{1500, 2000}, 75.0}
61 },
62 0,
63 geom::Size{1000, 2000},
64 true,
65 true,
66 geom::Point(500, 600),
67 2,
68 mir_pixel_format_xbgr_8888,
69 mir_power_mode_on,
70 mir_orientation_left
71};
72
73#endif // FAKE_DISPLAYCONFIGURATIONOUTPUT_H
074
=== added file 'tests/common/gmock_fixes.h'
--- tests/common/gmock_fixes.h 1970-01-01 00:00:00 +0000
+++ tests/common/gmock_fixes.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,124 @@
1//
2// Copyright © 2012 Canonical Ltd. Copyright 2007, Google Inc.
3//
4// All rights reserved.
5//
6// Redistribution and use in source and binary forms, with or without
7// modification, are permitted provided that the following conditions are
8// met:
9//
10// * Redistributions of source code must retain the above copyright
11// notice, this list of conditions and the following disclaimer.
12// * Redistributions in binary form must reproduce the above
13// copyright notice, this list of conditions and the following disclaimer
14// in the documentation and/or other materials provided with the
15// distribution.
16// * Neither the name of Google Inc. nor the names of its
17// contributors may be used to endorse or promote products derived from
18// this software without specific prior written permission.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31//
32// Author: wan@google.com (Zhanyong Wan)
33// Authored by: Alan Griffiths <alan@octopull.co.uk>
34
35#ifndef MIR_TEST_GMOCK_FIXES_H_
36#define MIR_TEST_GMOCK_FIXES_H_
37
38#include <memory>
39#include <gmock/gmock.h>
40
41namespace testing
42{
43namespace internal
44{
45
46template<typename T>
47class ActionResultHolder<std::unique_ptr<T>>
48: public UntypedActionResultHolderBase {
49 public:
50 explicit ActionResultHolder(std::unique_ptr<T>&& a_value) :
51 value_(std::move(a_value)) {}
52
53 // The compiler-generated copy constructor and assignment operator
54 // are exactly what we need, so we don't need to define them.
55
56 // Returns the held value and deletes this object.
57 std::unique_ptr<T> GetValueAndDelete() const {
58 std::unique_ptr<T> retval(std::move(value_));
59 delete this;
60 return retval;
61 }
62
63 // Prints the held value as an action's result to os.
64 virtual void PrintAsActionResult(::std::ostream* os) const {
65 *os << "\n Returns: ";
66 // T may be a reference type, so we don't use UniversalPrint().
67 UniversalPrinter<std::unique_ptr<T>>::Print(value_, os);
68 }
69
70 // Performs the given mock function's default action and returns the
71 // result in a new-ed ActionResultHolder.
72 template <typename F>
73 static ActionResultHolder* PerformDefaultAction(
74 const FunctionMockerBase<F>* func_mocker,
75 const typename Function<F>::ArgumentTuple& args,
76 const string& call_description) {
77 return new ActionResultHolder(
78 func_mocker->PerformDefaultAction(args, call_description));
79 }
80
81 // Performs the given action and returns the result in a new-ed
82 // ActionResultHolder.
83 template <typename F>
84 static ActionResultHolder*
85 PerformAction(const Action<F>& action,
86 const typename Function<F>::ArgumentTuple& args) {
87 return new ActionResultHolder(action.Perform(args));
88 }
89
90 private:
91 std::unique_ptr<T> mutable value_;
92
93 // T could be a reference type, so = isn't supported.
94 GTEST_DISALLOW_ASSIGN_(ActionResultHolder);
95};
96
97}
98
99template<typename T>
100class DefaultValue<std::unique_ptr<T>> {
101 public:
102 // Unsets the default value for type T.
103 static void Clear() {}
104
105 // Returns true iff the user has set the default value for type T.
106 static bool IsSet() { return false; }
107
108 // Returns true if T has a default return value set by the user or there
109 // exists a built-in default value.
110 static bool Exists() {
111 return true;
112 }
113
114 // Returns the default value for type T if the user has set one;
115 // otherwise returns the built-in default value if there is one;
116 // otherwise aborts the process.
117 static std::unique_ptr<T> Get() {
118 return std::unique_ptr<T>();
119 }
120};
121
122}
123
124#endif /* MIR_TEST_GMOCK_FIXES_H_ */
0125
=== added file 'tests/common/mock_display.h'
--- tests/common/mock_display.h 1970-01-01 00:00:00 +0000
+++ tests/common/mock_display.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,53 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef MOCKDISPLAY_H
18#define MOCKDISPLAY_H
19
20#include <mir/graphics/display.h>
21#include <mir/graphics/gl_context.h>
22
23#include <gmock/gmock.h>
24#include "gmock_fixes.h"
25
26class MockDisplaySyncGroup : public mir::graphics::DisplaySyncGroup
27{
28public:
29 MOCK_METHOD1(for_each_display_buffer, void(std::function<void(mir::graphics::DisplayBuffer&)> const& f));
30 MOCK_METHOD0(post, void());
31};
32
33struct MockDisplay : public mir::graphics::Display
34{
35public:
36 MOCK_METHOD1(for_each_display_sync_group, void(std::function<void(mir::graphics::DisplaySyncGroup&)> const&));
37 MOCK_CONST_METHOD0(configuration, std::unique_ptr<mir::graphics::DisplayConfiguration>());
38 MOCK_METHOD1(configure, void(mir::graphics::DisplayConfiguration const&));
39 MOCK_METHOD2(register_configuration_change_handler,
40 void(mir::graphics::EventHandlerRegister&, mir::graphics::DisplayConfigurationChangeHandler const&));
41
42 MOCK_METHOD3(register_pause_resume_handlers, void(mir::graphics::EventHandlerRegister&,
43 mir::graphics::DisplayPauseHandler const&,
44 mir::graphics::DisplayResumeHandler const&));
45 MOCK_METHOD0(pause, void());
46 MOCK_METHOD0(resume, void());
47 MOCK_METHOD1(create_hardware_cursor, std::shared_ptr<mir::graphics::Cursor>(std::shared_ptr<mir::graphics::CursorImage> const&));
48 MOCK_METHOD0(create_gl_context, std::unique_ptr<mir::graphics::GLContext>());
49};
50
51
52
53#endif // MOCKDISPLAY_H
054
=== added file 'tests/common/mock_display_buffer.h'
--- tests/common/mock_display_buffer.h 1970-01-01 00:00:00 +0000
+++ tests/common/mock_display_buffer.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,43 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef MOCK_DISPLAY_BUFFER_H
18#define MOCK_DISPLAY_BUFFER_H
19
20#include <mir/graphics/display_buffer.h>
21
22#include <gmock/gmock.h>
23
24class MockDisplayBuffer : public mir::graphics::DisplayBuffer
25{
26public:
27 MockDisplayBuffer()
28 {
29 using namespace testing;
30 ON_CALL(*this, view_area())
31 .WillByDefault(Return(mir::geometry::Rectangle{{0,0},{0,0}}));
32 }
33 MOCK_CONST_METHOD0(view_area, mir::geometry::Rectangle());
34 MOCK_METHOD0(make_current, void());
35 MOCK_METHOD0(release_current, void());
36 MOCK_METHOD0(gl_swap_buffers, void());
37 MOCK_METHOD1(post_renderables_if_optimizable, bool(mir::graphics::RenderableList const&));
38 MOCK_CONST_METHOD0(orientation, MirOrientation());
39 MOCK_CONST_METHOD0(uses_alpha, bool());
40};
41
42
43#endif // MOCK_DISPLAY_BUFFER_H
044
=== added file 'tests/common/mock_display_configuration.h'
--- tests/common/mock_display_configuration.h 1970-01-01 00:00:00 +0000
+++ tests/common/mock_display_configuration.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,35 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef MOCK_DISPLAY_CONFIGURATION_H
18#define MOCK_DISPLAY_CONFIGURATION_H
19
20#include <mir/graphics/display_configuration.h>
21
22#include <gmock/gmock.h>
23#include "gmock_fixes.h"
24
25class MockDisplayConfiguration : public mir::graphics::DisplayConfiguration
26{
27public:
28 MOCK_CONST_METHOD1(for_each_card, void(std::function<void(mir::graphics::DisplayConfigurationCard const&)>));
29
30 MOCK_CONST_METHOD1(for_each_output, void(std::function<void(mir::graphics::DisplayConfigurationOutput const&)>));
31 MOCK_METHOD1(for_each_output, void(std::function<void(mir::graphics::UserDisplayConfigurationOutput&)>));
32
33 MOCK_CONST_METHOD0(valid, bool());
34};
35#endif // MOCK_DISPLAY_CONFIGURATION_H
036
=== added file 'tests/common/mock_main_loop.h'
--- tests/common/mock_main_loop.h 1970-01-01 00:00:00 +0000
+++ tests/common/mock_main_loop.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,53 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef MOCKMAINLOOP_H
18#define MOCKMAINLOOP_H
19
20#include <gmock/gmock.h>
21
22#include <mir/main_loop.h>
23
24#include <memory>
25
26class MockMainLoop : public mir::MainLoop
27{
28public:
29 ~MockMainLoop() noexcept {}
30
31 void run() override {}
32 void stop() override {}
33
34 MOCK_METHOD2(register_signal_handler,
35 void(std::initializer_list<int>,
36 std::function<void(int)> const&));
37
38 MOCK_METHOD3(register_fd_handler,
39 void(std::initializer_list<int>, void const*,
40 std::function<void(int)> const&));
41
42 MOCK_METHOD1(unregister_fd_handler, void(void const*));
43
44 MOCK_METHOD2(enqueue, void(void const*, mir::ServerAction const&));
45 MOCK_METHOD1(pause_processing_for,void (void const*));
46 MOCK_METHOD1(resume_processing_for,void (void const*));
47
48 MOCK_METHOD1(create_alarm, std::unique_ptr<mir::time::Alarm>(std::function<void()> const& callback));
49 MOCK_METHOD1(create_alarm, std::unique_ptr<mir::time::Alarm>(std::shared_ptr<mir::LockableCallback> const& callback));
50};
51
52
53#endif // MOCKMAINLOOP_H
054
=== modified file 'tests/mirserver/CMakeLists.txt'
--- tests/mirserver/CMakeLists.txt 2014-11-13 15:47:30 +0000
+++ tests/mirserver/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -1,3 +1,4 @@
1add_subdirectory(QtEventFeeder)1add_subdirectory(QtEventFeeder)
2add_subdirectory(Clipboard)2add_subdirectory(Clipboard)
3add_subdirectory(Screen)3add_subdirectory(Screen)
4add_subdirectory(ScreenController)
45
=== modified file 'tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h'
--- tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h 2015-04-01 15:02:36 +0000
+++ tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h 2015-09-30 13:40:22 +0000
@@ -19,19 +19,26 @@
19#define MOCK_QTWINDOWSYSTEM_H19#define MOCK_QTWINDOWSYSTEM_H
2020
21#include <qteventfeeder.h>21#include <qteventfeeder.h>
22#include <QWindow>
2223
23class MockQtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {24class MockQtWindowSystem : public QtEventFeeder::QtWindowSystemInterface {
24public:25public:
25 MOCK_METHOD0(hasTargetWindow, bool());26 MOCK_CONST_METHOD0(ready, bool());
26 MOCK_METHOD0(targetWindowGeometry, QRect());27 MOCK_METHOD1(setScreenController, void(const QSharedPointer<ScreenController> &));
28 MOCK_METHOD1(getWindowForTouchPoint, QWindow*(const QPoint &point));
29 MOCK_METHOD0(lastWindow, QWindow*());
30 MOCK_METHOD0(focusedWindow, QWindow*());
27 MOCK_METHOD1(registerTouchDevice, void(QTouchDevice* device));31 MOCK_METHOD1(registerTouchDevice, void(QTouchDevice* device));
28 MOCK_METHOD10(handleExtendedKeyEvent, void(ulong timestamp, QEvent::Type type, int key,32
29 Qt::KeyboardModifiers modifiers,33 // Wanted to use GMock, but MOCK_METHOD11 not implemented
30 quint32 nativeScanCode, quint32 nativeVirtualKey,34 void handleExtendedKeyEvent(QWindow */*window*/, ulong /*timestamp*/, QEvent::Type /*type*/, int /*key*/,
31 quint32 nativeModifiers,35 Qt::KeyboardModifiers /*modifiers*/,
32 const QString& text, bool autorep,36 quint32 /*nativeScanCode*/, quint32 /*nativeVirtualKey*/,
33 ushort count));37 quint32 /*nativeModifiers*/,
34 MOCK_METHOD4(handleTouchEvent, void(ulong timestamp, QTouchDevice *device,38 const QString& /*text*/ = QString(), bool /*autorep*/ = false,
39 ushort /*count*/ = 1) {}
40
41 MOCK_METHOD5(handleTouchEvent, void(QWindow *window, ulong timestamp, QTouchDevice *device,
35 const QList<struct QWindowSystemInterface::TouchPoint> &points,42 const QList<struct QWindowSystemInterface::TouchPoint> &points,
36 Qt::KeyboardModifiers mods));43 Qt::KeyboardModifiers mods));
37 MOCK_METHOD4(handleMouseEvent, void(ulong, QPointF, Qt::MouseButton, Qt::KeyboardModifiers));44 MOCK_METHOD4(handleMouseEvent, void(ulong, QPointF, Qt::MouseButton, Qt::KeyboardModifiers));
3845
=== modified file 'tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp'
--- tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp 2015-09-18 16:33:06 +0000
+++ tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp 2015-09-30 13:40:22 +0000
@@ -69,33 +69,44 @@
6969
70 MockQtWindowSystem *mockWindowSystem;70 MockQtWindowSystem *mockWindowSystem;
71 QtEventFeeder *qtEventFeeder;71 QtEventFeeder *qtEventFeeder;
72 QWindow *window;
73 QGuiApplication *app;
72};74};
7375
74void QtEventFeederTest::SetUp()76void QtEventFeederTest::SetUp()
75{77{
76 mockWindowSystem = new MockQtWindowSystem;78 mockWindowSystem = new MockQtWindowSystem;
79 auto screens = QSharedPointer<ScreenController>();
7780
78 EXPECT_CALL(*mockWindowSystem, registerTouchDevice(_));81 EXPECT_CALL(*mockWindowSystem, registerTouchDevice(_));
7982
80 qtEventFeeder = new QtEventFeeder(mockWindowSystem);83 qtEventFeeder = new QtEventFeeder(screens, mockWindowSystem);
8184
82 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));85 ASSERT_TRUE(Mock::VerifyAndClearExpectations(mockWindowSystem));
86
87 int argc = 0;
88 char **argv = nullptr;
89 setenv("QT_QPA_PLATFORM", "minimal", 1);
90 app = new QGuiApplication(argc, argv);
91 window = new QWindow;
83}92}
8493
85void QtEventFeederTest::TearDown()94void QtEventFeederTest::TearDown()
86{95{
87 // mockWindowSystem will be deleted by QtEventFeeder96 // mockWindowSystem will be deleted by QtEventFeeder
88 delete qtEventFeeder;97 delete qtEventFeeder;
98 delete window;
99 delete app;
89}100}
90101
91void QtEventFeederTest::setIrrelevantMockWindowSystemExpectations()102void QtEventFeederTest::setIrrelevantMockWindowSystemExpectations()
92{103{
93 EXPECT_CALL(*mockWindowSystem, hasTargetWindow())104 EXPECT_CALL(*mockWindowSystem, getWindowForTouchPoint(_))
94 .Times(AnyNumber())105 .Times(AnyNumber())
95 .WillRepeatedly(Return(true));106 .WillRepeatedly(Return(window));
96 EXPECT_CALL(*mockWindowSystem, targetWindowGeometry())107 EXPECT_CALL(*mockWindowSystem, focusedWindow())
97 .Times(AnyNumber())108 .Times(AnyNumber())
98 .WillRepeatedly(Return(QRect(0,0,100,100)));109 .WillRepeatedly(Return(window));
99}110}
100111
101112
@@ -113,7 +124,7 @@
113124
114 setIrrelevantMockWindowSystemExpectations();125 setIrrelevantMockWindowSystemExpectations();
115126
116 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),127 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
117 Contains(AllOf(HasId(0),128 Contains(AllOf(HasId(0),
118 IsPressed()))),_)).Times(1);129 IsPressed()))),_)).Times(1);
119130
@@ -132,12 +143,12 @@
132 InSequence sequence;143 InSequence sequence;
133144
134 EXPECT_CALL(*mockWindowSystem,145 EXPECT_CALL(*mockWindowSystem,
135 handleTouchEvent(_,_,AllOf(SizeIs(1),146 handleTouchEvent(_,_,_,AllOf(SizeIs(1),
136 Contains(AllOf(HasId(0),IsReleased()))147 Contains(AllOf(HasId(0),IsReleased()))
137 ),_)).Times(1);148 ),_)).Times(1);
138149
139 EXPECT_CALL(*mockWindowSystem,150 EXPECT_CALL(*mockWindowSystem,
140 handleTouchEvent(_,_,AllOf(SizeIs(1),151 handleTouchEvent(_,_,_,AllOf(SizeIs(1),
141 Contains(AllOf(HasId(1),IsPressed()))152 Contains(AllOf(HasId(1),IsPressed()))
142 ),_)).Times(1);153 ),_)).Times(1);
143 }154 }
@@ -161,7 +172,7 @@
161 10, 10, 10 /* x, y, pressure*/,172 10, 10, 10 /* x, y, pressure*/,
162 1, 1, 10 /* touch major, minor, size */);173 1, 1, 10 /* touch major, minor, size */);
163174
164 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),175 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
165 Contains(AllOf(HasId(0),176 Contains(AllOf(HasId(0),
166 IsPressed()))),_)).Times(1);177 IsPressed()))),_)).Times(1);
167 qtEventFeeder->dispatch(*ev1);178 qtEventFeeder->dispatch(*ev1);
@@ -181,7 +192,7 @@
181 1, 1, 10 /* touch major, minor, size */);192 1, 1, 10 /* touch major, minor, size */);
182193
183 EXPECT_CALL(*mockWindowSystem,194 EXPECT_CALL(*mockWindowSystem,
184 handleTouchEvent(_,_,AllOf(SizeIs(2),195 handleTouchEvent(_,_,_,AllOf(SizeIs(2),
185 Contains(AllOf(HasId(0), StateIsMoved())),196 Contains(AllOf(HasId(0), StateIsMoved())),
186 Contains(AllOf(HasId(1), IsPressed()))197 Contains(AllOf(HasId(1), IsPressed()))
187 ),_)).Times(1);198 ),_)).Times(1);
@@ -208,14 +219,14 @@
208219
209 // first release touch 0220 // first release touch 0
210 EXPECT_CALL(*mockWindowSystem,221 EXPECT_CALL(*mockWindowSystem,
211 handleTouchEvent(_,_,AllOf(SizeIs(2),222 handleTouchEvent(_,_,_,AllOf(SizeIs(2),
212 Contains(AllOf(HasId(0), IsReleased())),223 Contains(AllOf(HasId(0), IsReleased())),
213 Contains(AllOf(HasId(1), IsStationary()))224 Contains(AllOf(HasId(1), IsStationary()))
214 ),_)).Times(1);225 ),_)).Times(1);
215226
216 // then press touch 2227 // then press touch 2
217 EXPECT_CALL(*mockWindowSystem,228 EXPECT_CALL(*mockWindowSystem,
218 handleTouchEvent(_,_,AllOf(SizeIs(2),229 handleTouchEvent(_,_,_,AllOf(SizeIs(2),
219 Contains(AllOf(HasId(1), StateIsMoved())),230 Contains(AllOf(HasId(1), StateIsMoved())),
220 Contains(AllOf(HasId(2), IsPressed()))231 Contains(AllOf(HasId(2), IsPressed()))
221 ),_)).Times(1);232 ),_)).Times(1);
@@ -230,7 +241,7 @@
230{241{
231 setIrrelevantMockWindowSystemExpectations();242 setIrrelevantMockWindowSystemExpectations();
232243
233 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),244 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
234 Contains(AllOf(HasId(0),245 Contains(AllOf(HasId(0),
235 IsPressed()))),_)).Times(1);246 IsPressed()))),_)).Times(1);
236247
@@ -243,7 +254,7 @@
243254
244 setIrrelevantMockWindowSystemExpectations();255 setIrrelevantMockWindowSystemExpectations();
245256
246 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,AllOf(SizeIs(1),257 EXPECT_CALL(*mockWindowSystem, handleTouchEvent(_,_,_,AllOf(SizeIs(1),
247 Contains(AllOf(HasId(0), StateIsMoved()))258 Contains(AllOf(HasId(0), StateIsMoved()))
248 ),_)).Times(1);259 ),_)).Times(1);
249260
250261
=== modified file 'tests/mirserver/Screen/CMakeLists.txt'
--- tests/mirserver/Screen/CMakeLists.txt 2014-12-03 08:56:35 +0000
+++ tests/mirserver/Screen/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -5,6 +5,7 @@
5)5)
66
7include_directories(7include_directories(
8 ${CMAKE_SOURCE_DIR}/tests/common
8 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver9 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
9 ${CMAKE_SOURCE_DIR}/src/common10 ${CMAKE_SOURCE_DIR}/src/common
10 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}11 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
1112
=== modified file 'tests/mirserver/Screen/screen_test.cpp'
--- tests/mirserver/Screen/screen_test.cpp 2015-08-11 12:08:32 +0000
+++ tests/mirserver/Screen/screen_test.cpp 2015-09-30 13:40:22 +0000
@@ -18,37 +18,21 @@
18#include <gtest/gtest.h>18#include <gtest/gtest.h>
1919
20#include "mir/graphics/display_configuration.h"20#include "mir/graphics/display_configuration.h"
21#include "fake_displayconfigurationoutput.h"
2122
22#include <screen.h>23#include <screen.h>
2324
25using namespace ::testing;
26
24namespace mg = mir::graphics;27namespace mg = mir::graphics;
25namespace geom = mir::geometry;28namespace geom = mir::geometry;
2629
27mg::DisplayConfigurationOutput const fake_output30class ScreenTest : public ::testing::Test {
28{31protected:
29 mg::DisplayConfigurationOutputId{3},32 void SetUp() override;
30 mg::DisplayConfigurationCardId{2},
31 mg::DisplayConfigurationOutputType::dvid,
32 {
33 mir_pixel_format_abgr_8888
34 },
35 {
36 {geom::Size{10, 20}, 60.0},
37 {geom::Size{10, 20}, 59.0},
38 {geom::Size{15, 20}, 59.0}
39 },
40 0,
41 geom::Size{10, 20},
42 true,
43 true,
44 geom::Point(),
45 2,
46 mir_pixel_format_abgr_8888,
47 mir_power_mode_on,
48 mir_orientation_normal
49};33};
5034
51TEST(ScreenTest, OrientationSensor)35void ScreenTest::SetUp()
52{36{
53 if (!qEnvironmentVariableIsSet("QT_ACCEL_FILEPATH")) {37 if (!qEnvironmentVariableIsSet("QT_ACCEL_FILEPATH")) {
54 // Trick Qt >= 5.4.1 to load the generic sensors38 // Trick Qt >= 5.4.1 to load the generic sensors
@@ -56,7 +40,11 @@
56 }40 }
5741
58 Screen::skipDBusRegistration = true;42 Screen::skipDBusRegistration = true;
59 Screen *screen = new Screen(fake_output);43}
44
45TEST_F(ScreenTest, OrientationSensor)
46{
47 Screen *screen = new Screen(fakeOutput1);
6048
61 // Default state should be active49 // Default state should be active
62 ASSERT_TRUE(screen->orientationSensorEnabled());50 ASSERT_TRUE(screen->orientationSensorEnabled());
@@ -67,3 +55,29 @@
67 screen->onDisplayPowerStateChanged(1,0);55 screen->onDisplayPowerStateChanged(1,0);
68 ASSERT_TRUE(screen->orientationSensorEnabled());56 ASSERT_TRUE(screen->orientationSensorEnabled());
69}57}
58
59TEST_F(ScreenTest, ReadConfigurationFromDisplayConfig)
60{
61 Screen *screen = new Screen(fakeOutput1);
62
63 EXPECT_EQ(screen->geometry(), QRect(0, 0, 150, 200));
64 EXPECT_EQ(screen->availableGeometry(), QRect(0, 0, 150, 200));
65 EXPECT_EQ(screen->depth(), 32);
66 EXPECT_EQ(screen->format(), QImage::Format_RGBA8888);
67 EXPECT_EQ(screen->refreshRate(), 59);
68 EXPECT_EQ(screen->physicalSize(), QSize(1111, 2222));
69 EXPECT_EQ(screen->outputType(), mg::DisplayConfigurationOutputType::dvid);
70}
71
72TEST_F(ScreenTest, ReadDifferentConfigurationFromDisplayConfig)
73{
74 Screen *screen = new Screen(fakeOutput2);
75
76 EXPECT_EQ(screen->geometry(), QRect(500, 600, 1500, 2000));
77 EXPECT_EQ(screen->availableGeometry(), QRect(500, 600, 1500, 2000));
78 EXPECT_EQ(screen->depth(), 32);
79 EXPECT_EQ(screen->format(), QImage::Format_RGBX8888);
80 EXPECT_EQ(screen->refreshRate(), 75);
81 EXPECT_EQ(screen->physicalSize(), QSize(1000, 2000));
82 EXPECT_EQ(screen->outputType(), mg::DisplayConfigurationOutputType::lvds);
83}
7084
=== added directory 'tests/mirserver/ScreenController'
=== added file 'tests/mirserver/ScreenController/CMakeLists.txt'
--- tests/mirserver/ScreenController/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/mirserver/ScreenController/CMakeLists.txt 2015-09-30 13:40:22 +0000
@@ -0,0 +1,28 @@
1set(
2 SCREENCONTROLLER_TEST_SOURCES
3 screencontroller_test.cpp
4 ${CMAKE_SOURCE_DIR}/src/common/debughelpers.cpp
5 # to be moc-ed
6 stub_screen.h
7 testable_screencontroller.h
8)
9
10include_directories(
11 ${CMAKE_SOURCE_DIR}/tests/common
12 ${CMAKE_SOURCE_DIR}/src/platforms/mirserver
13 ${CMAKE_SOURCE_DIR}/src/common
14 ${Qt5Gui_PRIVATE_INCLUDE_DIRS}
15 ${MIRSERVER_INCLUDE_DIRS}
16)
17
18add_executable(ScreenControllerTest ${SCREENCONTROLLER_TEST_SOURCES})
19
20target_link_libraries(
21 ScreenControllerTest
22 qpa-mirserver
23
24 ${GTEST_BOTH_LIBRARIES}
25 ${GMOCK_LIBRARIES}
26)
27
28add_test(ScreenController, ScreenControllerTest)
029
=== added file 'tests/mirserver/ScreenController/screencontroller_test.cpp'
--- tests/mirserver/ScreenController/screencontroller_test.cpp 1970-01-01 00:00:00 +0000
+++ tests/mirserver/ScreenController/screencontroller_test.cpp 2015-09-30 13:40:22 +0000
@@ -0,0 +1,189 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include <gtest/gtest.h>
18#include "gmock_fixes.h"
19
20#include "stub_display.h"
21#include "mock_main_loop.h"
22#include "qtcompositor.h"
23#include "fake_displayconfigurationoutput.h"
24
25#include "testable_screencontroller.h"
26#include "screen.h"
27#include "screenwindow.h"
28
29#include <QGuiApplication>
30
31using namespace ::testing;
32
33namespace mg = mir::graphics;
34namespace geom = mir::geometry;
35
36class ScreenControllerTest : public ::testing::Test {
37protected:
38 void SetUp() override;
39 void TearDown() override;
40
41 ScreenController *screenController;
42 std::shared_ptr<StubDisplay> display;
43 std::shared_ptr<QtCompositor> compositor;
44 QGuiApplication *app;
45};
46
47void ScreenControllerTest::SetUp()
48{
49 setenv("QT_QPA_PLATFORM", "minimal", 1);
50 Screen::skipDBusRegistration = true;
51
52 screenController = new TestableScreenController;
53 display = std::make_shared<StubDisplay>();
54 compositor = std::make_shared<QtCompositor>();
55
56 static_cast<TestableScreenController*>(screenController)->do_init(display, compositor);
57
58 int argc = 0;
59 char **argv = nullptr;
60 setenv("QT_QPA_PLATFORM", "minimal", 1);
61 app = new QGuiApplication(argc, argv);
62}
63
64void ScreenControllerTest::TearDown()
65{
66 delete screenController;
67}
68
69TEST_F(ScreenControllerTest, SingleScreenFound)
70{
71 // Set up display state
72 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};
73 std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
74 display->setFakeConfiguration(config, bufferConfig);
75
76 screenController->update();
77
78 ASSERT_EQ(1, screenController->screens().count());
79 Screen* screen = screenController->screens().first();
80 EXPECT_EQ(QRect(0, 0, 150, 200), screen->geometry());
81}
82
83TEST_F(ScreenControllerTest, MultipleScreenFound)
84{
85 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1, fakeOutput2};
86 std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
87 display->setFakeConfiguration(config, bufferConfig);
88
89 screenController->update();
90
91 ASSERT_EQ(2, screenController->screens().count());
92 EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(0)->geometry());
93 EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(1)->geometry());
94}
95
96TEST_F(ScreenControllerTest, ScreenAdded)
97{
98 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};
99 std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
100 display->setFakeConfiguration(config, bufferConfig);
101
102 screenController->update();
103
104 config.push_back(fakeOutput2);
105 display->setFakeConfiguration(config, bufferConfig);
106
107 ASSERT_EQ(1, screenController->screens().count());
108 EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(0)->geometry());
109
110 screenController->update();
111
112 ASSERT_EQ(2, screenController->screens().count());
113 EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(0)->geometry());
114 EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(1)->geometry());
115}
116
117TEST_F(ScreenControllerTest, ScreenRemoved)
118{
119 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput2, fakeOutput1};
120 std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
121 display->setFakeConfiguration(config, bufferConfig);
122
123 screenController->update();
124
125 config.pop_back();
126 display->setFakeConfiguration(config, bufferConfig);
127
128 ASSERT_EQ(2, screenController->screens().count());
129 EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(0)->geometry());
130 EXPECT_EQ(QRect(0, 0, 150, 200), screenController->screens().at(1)->geometry());
131
132 screenController->update();
133
134 ASSERT_EQ(1, screenController->screens().count());
135 EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(0)->geometry());
136}
137
138TEST_F(ScreenControllerTest, CheckPrioritizedGetUnusedScreen)
139{
140 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput2, fakeOutput1};
141 std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
142 display->setFakeConfiguration(config, bufferConfig);
143
144 screenController->update();
145
146 auto screen = screenController->getUnusedScreen();
147 EXPECT_EQ(mg::DisplayConfigurationOutputType::lvds, screen->outputType());
148}
149
150TEST_F(ScreenControllerTest, MatchBufferWithDisplay)
151{
152 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};
153 MockDisplayBuffer buffer1;
154 std::vector<MockDisplayBuffer*> buffers {&buffer1};
155
156 geom::Rectangle buffer1Geom{{0, 0}, {150, 200}};
157 EXPECT_CALL(buffer1, view_area())
158 .WillRepeatedly(Return(buffer1Geom));
159
160 display->setFakeConfiguration(config, buffers);
161 screenController->update();
162
163 ASSERT_EQ(1, screenController->screens().count());
164 EXPECT_CALL(buffer1, make_current());
165 static_cast<StubScreen*>(screenController->screens().at(0))->makeCurrent();
166}
167
168TEST_F(ScreenControllerTest, MultipleMatchBuffersWithDisplays)
169{
170 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1, fakeOutput2};
171 MockDisplayBuffer buffer1, buffer2;
172 std::vector<MockDisplayBuffer*> buffers {&buffer1, &buffer2};
173
174 geom::Rectangle buffer1Geom{{500, 600}, {1500, 2000}};
175 geom::Rectangle buffer2Geom{{0, 0}, {150, 200}};
176 EXPECT_CALL(buffer1, view_area())
177 .WillRepeatedly(Return(buffer1Geom));
178 EXPECT_CALL(buffer2, view_area())
179 .WillRepeatedly(Return(buffer2Geom));
180
181 display->setFakeConfiguration(config, buffers);
182 screenController->update();
183
184 ASSERT_EQ(2, screenController->screens().count());
185 EXPECT_CALL(buffer1, make_current());
186 EXPECT_CALL(buffer2, make_current());
187 static_cast<StubScreen*>(screenController->screens().at(0))->makeCurrent();
188 static_cast<StubScreen*>(screenController->screens().at(1))->makeCurrent();
189}
0190
=== added file 'tests/mirserver/ScreenController/stub_display.h'
--- tests/mirserver/ScreenController/stub_display.h 1970-01-01 00:00:00 +0000
+++ tests/mirserver/ScreenController/stub_display.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,96 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef STUB_DISPLAY_H
18#define STUB_DISPLAY_H
19
20#include "mock_display.h"
21#include "mock_display_buffer.h"
22#include "mock_display_configuration.h"
23
24namespace mg = mir::graphics;
25namespace geom = mir::geometry;
26
27class StubDisplayConfiguration : public MockDisplayConfiguration
28{
29public:
30 StubDisplayConfiguration(const std::vector<mg::DisplayConfigurationOutput> &config)
31 : m_config(config)
32 {}
33
34 void for_each_output(std::function<void(mg::DisplayConfigurationOutput const&)> f) const override
35 {
36 for (auto config : m_config) {
37 f(config);
38 }
39 }
40
41private:
42 const std::vector<mg::DisplayConfigurationOutput> m_config;
43};
44
45
46class StubDisplaySyncGroup : public MockDisplaySyncGroup
47{
48public:
49 StubDisplaySyncGroup(MockDisplayBuffer *buffer) : buffer(buffer) {}
50
51 void for_each_display_buffer(std::function<void(mg::DisplayBuffer&)> const& f) override
52 {
53 f(*buffer);
54 }
55 std::chrono::milliseconds recommended_sleep() const
56 {
57 std::chrono::milliseconds one{1};
58 return one;
59 }
60private:
61 MockDisplayBuffer *buffer;
62};
63
64
65class StubDisplay : public MockDisplay
66{
67public:
68 // Note, GMock cannot mock functions which return non-copyable objects, so stubbing needed
69 std::unique_ptr<mg::DisplayConfiguration> configuration() const override
70 {
71 return std::unique_ptr<mg::DisplayConfiguration>(
72 new StubDisplayConfiguration(m_config)
73 );
74 }
75
76 void for_each_display_sync_group(std::function<void(mg::DisplaySyncGroup&)> const& f) override
77 {
78 for (auto displayBuffer : m_displayBuffers) {
79 StubDisplaySyncGroup b(displayBuffer);
80 f(b);
81 }
82 }
83
84 void setFakeConfiguration(std::vector<mg::DisplayConfigurationOutput> &config,
85 std::vector<MockDisplayBuffer*> &displayBuffers)
86 {
87 m_config = config;
88 m_displayBuffers = displayBuffers;
89 }
90
91private:
92 std::vector<mg::DisplayConfigurationOutput> m_config;
93 std::vector<MockDisplayBuffer*> m_displayBuffers;
94};
95
96#endif // STUB_DISPLAY_H
097
=== added file 'tests/mirserver/ScreenController/stub_screen.h'
--- tests/mirserver/ScreenController/stub_screen.h 1970-01-01 00:00:00 +0000
+++ tests/mirserver/ScreenController/stub_screen.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,31 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#ifndef STUBSCREEN_H
18#define STUBSCREEN_H
19
20#include "screen.h"
21
22class StubScreen : public Screen
23{
24 Q_OBJECT
25public:
26 StubScreen(const mir::graphics::DisplayConfigurationOutput &output) : Screen(output) {}
27
28 void makeCurrent() { Screen::makeCurrent(); }
29};
30
31#endif // STUBSCREEN_H
032
=== added file 'tests/mirserver/ScreenController/testable_screencontroller.h'
--- tests/mirserver/ScreenController/testable_screencontroller.h 1970-01-01 00:00:00 +0000
+++ tests/mirserver/ScreenController/testable_screencontroller.h 2015-09-30 13:40:22 +0000
@@ -0,0 +1,37 @@
1/*
2 * Copyright (C) 2015 Canonical, Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify it under
5 * the terms of the GNU Lesser General Public License version 3, as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
10 * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "screencontroller.h"
18#include "stub_screen.h"
19
20struct TestableScreenController : public ScreenController
21{
22 Q_OBJECT
23
24public:
25 Screen *createScreen(const mir::graphics::DisplayConfigurationOutput &output) const override
26 {
27 return new StubScreen(output);
28 }
29
30 void do_init(const std::shared_ptr<mir::graphics::Display> &display,
31 const std::shared_ptr<mir::compositor::Compositor> &compositor)
32 {
33 init(display, compositor);
34 }
35
36 void do_terminate() { terminate(); }
37};
038
=== modified file 'tests/modules/common/qtmir_test.h'
--- tests/modules/common/qtmir_test.h 2015-08-11 12:08:32 +0000
+++ tests/modules/common/qtmir_test.h 2015-09-30 13:40:22 +0000
@@ -78,7 +78,7 @@
78{78{
79public:79public:
80 FakeMirServer()80 FakeMirServer()
81 : MirServer(0, argv)81 : MirServer(0, argv, QSharedPointer<ScreenController>())
82 {82 {
83 }83 }
8484

Subscribers

People subscribed via source and target branches