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

Proposed by Gerry Boland
Status: Superseded
Proposed branch: lp:~gerboland/qtmir/multimonitor-spike
Merge into: lp:qtmir
Diff against target: 3591 lines (+2090/-463)
51 files modified
debian/changelog (+6/-0)
demos/qml-demo-shell/qml-demo-shell.qml (+71/-1)
src/modules/Unity/CMakeLists.txt (+1/-0)
src/modules/Unity/Screens/CMakeLists.txt (+18/-0)
src/modules/Unity/Screens/plugin.cpp (+41/-0)
src/modules/Unity/Screens/qmldir (+2/-0)
src/modules/Unity/Screens/screens.cpp (+71/-0)
src/modules/Unity/Screens/screens.h (+54/-0)
src/platforms/mirserver/CMakeLists.txt (+4/-2)
src/platforms/mirserver/display.cpp (+0/-46)
src/platforms/mirserver/display.h (+0/-39)
src/platforms/mirserver/logging.h (+1/-0)
src/platforms/mirserver/miropenglcontext.cpp (+24/-9)
src/platforms/mirserver/mirserver.cpp (+32/-4)
src/platforms/mirserver/mirserver.h (+7/-3)
src/platforms/mirserver/mirserverintegration.cpp (+45/-40)
src/platforms/mirserver/mirserverintegration.h (+4/-7)
src/platforms/mirserver/offscreensurface.cpp (+61/-0)
src/platforms/mirserver/offscreensurface.h (+43/-0)
src/platforms/mirserver/qmirserver.cpp (+12/-2)
src/platforms/mirserver/qmirserver.h (+3/-0)
src/platforms/mirserver/qmirserver_p.h (+2/-0)
src/platforms/mirserver/qtcompositor.cpp (+10/-35)
src/platforms/mirserver/qtcompositor.h (+14/-6)
src/platforms/mirserver/qteventfeeder.cpp (+96/-87)
src/platforms/mirserver/qteventfeeder.h (+13/-9)
src/platforms/mirserver/screen.cpp (+97/-8)
src/platforms/mirserver/screen.h (+32/-4)
src/platforms/mirserver/screencontroller.cpp (+261/-0)
src/platforms/mirserver/screencontroller.h (+96/-0)
src/platforms/mirserver/screenwindow.cpp (+32/-83)
src/platforms/mirserver/screenwindow.h (+11/-27)
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 (+17/-10)
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 (+193/-0)
tests/mirserver/ScreenController/stub_display.h (+91/-0)
tests/mirserver/ScreenController/stub_screen.h (+31/-0)
tests/mirserver/ScreenController/testable_screencontroller.h (+38/-0)
tests/modules/common/qtmir_test.h (+1/-1)
To merge this branch: bzr merge lp:~gerboland/qtmir/multimonitor-spike
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Daniel d'Andrada (community) Needs Fixing
Review via email: mp+263602@code.launchpad.net

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

Commit message

Initial multimonitor support - react correctly to Mir DisplayConfiguration changes.

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

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

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

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 :

Gah, input on second display not working

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Daniel d'Andrada (dandrader) wrote :
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 :

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 :

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!

339. By Gerry Boland

Slots can be private, not public

340. By Gerry Boland

Merge trunk

341. By Gerry Boland

MirOpenGLContext does not need to keep copy of the egl display and context

342. By Gerry Boland

Clean up debug outputs in MirServerIntegration, use categories

343. By Gerry Boland

Remove commented out lines and useless debug outputs from ScreenController

344. By Gerry Boland

Update comment in MirServerIntegration to make actual sense

345. By Gerry Boland

Remove unneeded QDebug include from OffscreenSurface

346. By Gerry Boland

Update licence years

347. By Gerry Boland

Remove authors from \*screen.\* licences

348. By Gerry Boland

Remove useless debug prints from ScreenWindow, and use category logging elsewhere

349. By Gerry Boland

s/deconstructor/destructor/

350. By Gerry Boland

ScreenController - remove another useless debug statement

351. By Gerry Boland

TiledDisplayConfig - update licence year

352. By Gerry Boland

Fix FTBFS

353. By Gerry Boland

ScreenController not need mirserver header

354. By Gerry Boland

Fix mouse click on non-primary screen

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

Add second QtEventFeeder constructor which creates non-test QtWindowSystemInterface by default

356. By Gerry Boland

Drop ScreenWindow::isExposed

357. By Gerry Boland

Ensure touch validation events are passed to the correct window

358. By Gerry Boland

Move ScreenController explanation comment to the header

359. By Gerry Boland

screencontroller does not depend on mirserver any more, so remove header file include

360. By Gerry Boland

Simplify ScreenWindow further, drop QObject dependence and remove event handler

361. By Gerry Boland

Better variable name in ScreenWindow

362. By Gerry Boland

Use camelCase instead of under_score

363. By Gerry Boland

sc -> screenController

364. By Gerry Boland

screenFactory -> createScreen

365. By Gerry Boland

ScreenController: do not be friends, make public interfaces to be called by MirServer

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

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.

366. By Gerry Boland

Merge trunk

367. By Gerry Boland

Ensure deregister ScreenWindow from Screen on destruction

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

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 :
review: Approve (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

> 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 :

"""
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 :

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 :

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?

368. By Gerry Boland

Update changelog to mark latest as UNRELEASED

369. By Gerry Boland

Update copyright year on MirOpenGLContext

370. By Gerry Boland

debug & commented line removed from ScreenController

371. By Gerry Boland

ScreenControllerTest- flip argument in assert/expect_eq statements

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

Unmerged revisions

371. By Gerry Boland

ScreenControllerTest- flip argument in assert/expect_eq statements

370. By Gerry Boland

debug & commented line removed from ScreenController

369. By Gerry Boland

Update copyright year on MirOpenGLContext

368. By Gerry Boland

Update changelog to mark latest as UNRELEASED

367. By Gerry Boland

Ensure deregister ScreenWindow from Screen on destruction

366. By Gerry Boland

Merge trunk

365. By Gerry Boland

ScreenController: do not be friends, make public interfaces to be called by MirServer

364. By Gerry Boland

screenFactory -> createScreen

363. By Gerry Boland

sc -> screenController

362. By Gerry Boland

Use camelCase instead of under_score

Preview Diff

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

Subscribers

People subscribed via source and target branches