Merge lp:~dandrader/qtmir/mousePointer into lp:qtmir

Proposed by Daniel d'Andrada
Status: Superseded
Proposed branch: lp:~dandrader/qtmir/mousePointer
Merge into: lp:qtmir
Diff against target: 4388 lines (+2662/-538)
63 files modified
CMakeLists.txt (+1/-1)
debian/control (+1/-1)
demos/qml-demo-shell/ResizeArea.qml (+128/-0)
demos/qml-demo-shell/Shell.qml (+21/-1)
demos/qml-demo-shell/TitleBar.qml (+4/-1)
demos/qml-demo-shell/Window.qml (+4/-81)
demos/qml-demo-shell/qml-demo-shell.qml (+19/-0)
src/modules/Unity/Application/CMakeLists.txt (+0/-1)
src/modules/Unity/Application/plugin.cpp (+8/-1)
src/modules/Unity/CMakeLists.txt (+1/-0)
src/modules/Unity/Screens/CMakeLists.txt (+24/-0)
src/modules/Unity/Screens/plugin.cpp (+41/-0)
src/modules/Unity/Screens/qmldir (+2/-0)
src/modules/Unity/Screens/screens.cpp (+107/-0)
src/modules/Unity/Screens/screens.h (+82/-0)
src/platforms/mirserver/CMakeLists.txt (+10/-3)
src/platforms/mirserver/cursor.cpp (+152/-0)
src/platforms/mirserver/cursor.h (+66/-0)
src/platforms/mirserver/display.cpp (+0/-44)
src/platforms/mirserver/display.h (+0/-37)
src/platforms/mirserver/logging.h (+1/-0)
src/platforms/mirserver/miropenglcontext.cpp (+28/-11)
src/platforms/mirserver/miropenglcontext.h (+5/-0)
src/platforms/mirserver/mirserver.cpp (+38/-4)
src/platforms/mirserver/mirserver.h (+8/-2)
src/platforms/mirserver/mirserverintegration.cpp (+51/-40)
src/platforms/mirserver/mirserverintegration.h (+4/-7)
src/platforms/mirserver/mirsingleton.cpp (+33/-0)
src/platforms/mirserver/mirsingleton.h (+45/-0)
src/platforms/mirserver/offscreensurface.cpp (+61/-0)
src/platforms/mirserver/offscreensurface.h (+43/-0)
src/platforms/mirserver/qmirserver.cpp (+12/-2)
src/platforms/mirserver/qmirserver.h (+3/-0)
src/platforms/mirserver/qmirserver_p.h (+2/-0)
src/platforms/mirserver/qtcompositor.cpp (+9/-34)
src/platforms/mirserver/qtcompositor.h (+13/-5)
src/platforms/mirserver/qteventfeeder.cpp (+116/-89)
src/platforms/mirserver/qteventfeeder.h (+13/-9)
src/platforms/mirserver/screen.cpp (+104/-7)
src/platforms/mirserver/screen.h (+36/-4)
src/platforms/mirserver/screencontroller.cpp (+257/-0)
src/platforms/mirserver/screencontroller.h (+97/-0)
src/platforms/mirserver/screenwindow.cpp (+68/-80)
src/platforms/mirserver/screenwindow.h (+13/-23)
src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp (+44/-0)
src/platforms/mirserver/tileddisplayconfigurationpolicy.h (+35/-0)
tests/common/fake_displayconfigurationoutput.h (+73/-0)
tests/common/gmock_fixes.h (+124/-0)
tests/common/mock_display.h (+53/-0)
tests/common/mock_display_buffer.h (+43/-0)
tests/common/mock_display_configuration.h (+35/-0)
tests/common/mock_main_loop.h (+53/-0)
tests/mirserver/CMakeLists.txt (+1/-0)
tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h (+16/-9)
tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp (+27/-16)
tests/mirserver/Screen/CMakeLists.txt (+1/-0)
tests/mirserver/Screen/screen_test.cpp (+38/-24)
tests/mirserver/ScreenController/CMakeLists.txt (+28/-0)
tests/mirserver/ScreenController/screencontroller_test.cpp (+192/-0)
tests/mirserver/ScreenController/stub_display.h (+99/-0)
tests/mirserver/ScreenController/stub_screen.h (+31/-0)
tests/mirserver/ScreenController/testable_screencontroller.h (+37/-0)
tests/modules/common/qtmir_test.h (+1/-1)
To merge this branch: bzr merge lp:~dandrader/qtmir/mousePointer
Reviewer Review Type Date Requested Status
Gerry Boland (community) Needs Fixing
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+269787@code.launchpad.net

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

Commit message

Shell draws its own cursor using the new Cursor QML element

Description of the change

* Are there any related MPs required for this MP to build/function as expected? Please list.
https://code.launchpad.net/~dandrader/unity-api/mousePointer/+merge/269783
https://code.launchpad.net/~dandrader/unity8/mousePointer/+merge/269779

Needs lp:~dandrader/mir/0.14+mouse or Mir 0.15 (not yet released in vivid+overlay)

* Did you perform an exploratory manual test run of your code change and any related functionality?
Not applicable.

* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
Not applicable.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Gerry Boland (gerboland) wrote :

=== added file 'src/modules/Unity/Application/Cursor.qml'
=== added file 'src/modules/Unity/Application/cursorimageprovider.cpp'
I'm beginning to wonder if most of this code needs to live in qtmir at all. Since Unity8 draws it, why does QtMir need to do this work to find the image for it?

I also don't think it's QtMir's job to know anything about cursor themes.

Why doesn't QtMir just inform the shell of:
1. the position the cursor should be located at
2. the name of the desired cursor
Then unity8 can find the themed cursor image, figure out the hotspot and draw the cursor itself.

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

=== added file 'demos/qml-demo-shell/ResizeArea.qml'
I like your code, neat and clean as always. But if I was writing this, I would have a single MouseArea behind the surface, instead of 8. It's ok for a demo, but that's a lot for a real shell.

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

> === added file 'demos/qml-demo-shell/ResizeArea.qml'
> I like your code, neat and clean as always. But if I was writing this, I would
> have a single MouseArea behind the surface, instead of 8. It's ok for a demo,
> but that's a lot for a real shell.

If I recall correctly, the main reason was because of the MouseArea.cursorShape property. If you set it, the cursor will automatically assume the given shape when hovering the mouse area. So I cannot be a single one.

But later on the cursor shape enumeration proved insufficient for exposing all the different shapes unity uses, forcing me to introduce the Mir.cursorName API.

So yeah, now that I've given up using MouseArea.cursorShape, maybe I could use a single mouse area.

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

> > === added file 'demos/qml-demo-shell/ResizeArea.qml'
> > I like your code, neat and clean as always. But if I was writing this, I
> would
> > have a single MouseArea behind the surface, instead of 8. It's ok for a
> demo,
> > but that's a lot for a real shell.
>
> If I recall correctly, the main reason was because of the
> MouseArea.cursorShape property. If you set it, the cursor will automatically
> assume the given shape when hovering the mouse area. So I cannot be a single
> one.
>
> But later on the cursor shape enumeration proved insufficient for exposing all
> the different shapes unity uses, forcing me to introduce the Mir.cursorName
> API.
>
> So yeah, now that I've given up using MouseArea.cursorShape, maybe I could use
> a single mouse area.

FYI: I also copy-pasted ResizeArea.qml into unity8/mousePointer

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

> === added file 'src/modules/Unity/Application/Cursor.qml'
> === added file 'src/modules/Unity/Application/cursorimageprovider.cpp'
> I'm beginning to wonder if most of this code needs to live in qtmir at all.
> Since Unity8 draws it, why does QtMir need to do this work to find the image
> for it?
>
> I also don't think it's QtMir's job to know anything about cursor themes.
>
>
> Why doesn't QtMir just inform the shell of:
> 1. the position the cursor should be located at
> 2. the name of the desired cursor
> Then unity8 can find the themed cursor image, figure out the hotspot and draw
> the cursor itself.

Following the same rationale, one might also argue: "Why does qtmir has to know about application processes and upstart at all? let unity8 figure it out. qtmir should just spit out Sessions, MirSurfaces and MirSurfaceItems".

Also, by having this in qtmir, we pretty much shield unity8 from architectural changes down the road, once we ditch the QML cursor approach in favor of a unity-system-settings one.

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

On 02/09/15 13:16, Gerry Boland wrote:
> === added file 'demos/qml-demo-shell/ResizeArea.qml'
> I like your code, neat and clean as always. But if I was writing this, I would have a single MouseArea behind the surface, instead of 8. It's ok for a demo, but that's a lot for a real shell.
>
>

Fixed. Will do likewise in untiy8 tomorrow.

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

> > === added file 'src/modules/Unity/Application/Cursor.qml'
> > === added file 'src/modules/Unity/Application/cursorimageprovider.cpp'
> > I'm beginning to wonder if most of this code needs to live in qtmir at all.
> > Since Unity8 draws it, why does QtMir need to do this work to find the image
> > for it?
> >
> > I also don't think it's QtMir's job to know anything about cursor themes.
> >
> >
> > Why doesn't QtMir just inform the shell of:
> > 1. the position the cursor should be located at
> > 2. the name of the desired cursor
> > Then unity8 can find the themed cursor image, figure out the hotspot and
> draw
> > the cursor itself.
>
> Following the same rationale, one might also argue: "Why does qtmir has to
> know about application processes and upstart at all? let unity8 figure it out.
> qtmir should just spit out Sessions, MirSurfaces and MirSurfaceItems".

Actually that's the longer-term goal.
https://trello.com/c/4uBsKNVT/133-split-applicationmanager-out-of-qtmir
Zanetti wants to move such logic into Unity itself. This will make QtMir a thin wrapper of Mir functionality for Qt users, which is a much clearer purpose than what it currently has.

If somebody would like to make their own shell, then can make use of QtMir without being forced to use upstart for instance. If they want to use upstart, then they can use the Unity.Application plugin.

> Also, by having this in qtmir, we pretty much shield unity8 from architectural
> changes down the road, once we ditch the QML cursor approach in favor of a
> unity-system-settings one.

I don't think that will happen. We need the shell to manage the cursor position, in order to do fancy things like edge-push detection, slowing cursor over buttons (a11y) or moving cursor with keyboard (a11y).

My main reasons for wanting USC to draw cursor were:
1. latency, cursor will react as quickly as possible
2. hardware compositing, cursor lives in a special hardware buffer which the hardware composites on top of everything. Means moving cursor doesn't require entire screen re-renders.

Mir team have plans for both problems
1. not use the mir protobuf for input, but have unity8 open socket with event stream coming in. Should reduce latency
2. Mir adding API to allow shell to designate certain buffers/surfaces as being hardware composable. Cursor will definitely be one of those, if available.

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

Ok, moved all Cursor stuff from qtmir to unity8.

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

=== modified file 'debian/copyright'
can undo this.

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

On 09/09/15 07:57, Gerry Boland wrote:
> === modified file 'debian/copyright'
> can undo this.
Done.

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

Ok ignore my relativeMovement thing. I'll not object to the usual terminology.

However I fear I need to ask you to rebase on top of
https://code.launchpad.net/~gerboland/qtmir/multimonitor/+merge/269906
as there's a few conflicts, and the concept of multiple qwindows does make this more complex.

+ // We will draw our own cursor.
+ add_init_callback([this](){ the_cursor()->hide(); });
This isn't great, as the mir cursor object is still being created. Can we replace Mir's implementation with our own one? -- not a blocker on this MR, can consider this later.

++ src/platforms/mirserver/mirsingleton.cpp
+qtmir::Mir::~Mir()
+{
+ m_instance = nullptr;
+}
You're not deleting what you potentially "new"ed. QScopedPointer helps prevent such accidents...

+++ src/platforms/mirserver/mirsingleton.h
+private:
+ Mir();
maybe http://doc.qt.io/qt-5/qobject.html#Q_DISABLE_COPY

Using this Mir singleton to save the cursorName will do fine for now, but I'm wary of it being a dumping ground for lots of little things.

review: Needs Fixing
lp:~dandrader/qtmir/mousePointer updated
370. By Michał Sawicz

No-change rebuild to resync vivid+overlay and wily
Approved by: Michael Zanetti

371. By CI Train Bot Account

Releasing 0.4.6+15.10.20150904-0ubuntu1

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

On 09/09/15 10:36, Gerry Boland wrote:
> Review: Needs Fixing
>
> Ok ignore my relativeMovement thing. I'll not object to the usual terminology.
>
> However I fear I need to ask you to rebase on top of
> https://code.launchpad.net/~gerboland/qtmir/multimonitor/+merge/269906
> as there's a few conflicts, and the concept of multiple qwindows does make this more complex.

Done.

>
> + // We will draw our own cursor.
> + add_init_callback([this](){ the_cursor()->hide(); });
> This isn't great, as the mir cursor object is still being created. Can we replace Mir's implementation with our own one? -- not a blocker on this MR, can consider this later.

This will be the u-s-c cursor, and u-s-c will always have its own cursor
no matter what, I think. Don't think it's worth investigating this idea.

>
> ++ src/platforms/mirserver/mirsingleton.cpp
> +qtmir::Mir::~Mir()
> +{
> + m_instance = nullptr;
> +}
> You're not deleting what you potentially "new"ed. QScopedPointer helps prevent such accidents...

I am. The only place that does "new Mir" is Mir::instance() and it's
done only once. This is a simple, no-nonsense, singleton implementation
that does its job AFAICT. You want the global, static, Mir::m_instance
to be a QScopedPointer? I fail to see how would that work and how it
would be better than the current code. If you really want to see a
QScopedPointer there please give me a diff as I didn't get the point.

>
> +++ src/platforms/mirserver/mirsingleton.h
> +private:
> + Mir();
> maybe http://doc.qt.io/qt-5/qobject.html#Q_DISABLE_COPY

Done.

> Using this Mir singleton to save the cursorName will do fine for now, but I'm wary of it being a dumping ground for lots of little things.

Yes, Mir.cursorName is surely not the best solution but anything better
would require way more work. This is like a stop-gap measure. Also given
the uncertainty of the current cursor approach (as we will eventually
move to u-s-c cursor) I didn't want to invest a lot of effort on
something that could evaporate in the near term.

lp:~dandrader/qtmir/mousePointer updated
372. By Daniel van Vugt

Stop waking up every 200ms if there's nothing to wake up for.
It's just wasting battery.

This solves at least half of LP: #1479250. Although the regular
wakeup problem is solved by this, Unity8 continues to use noticeable
CPU while idle (obviously in much less frequent intervals now). Fixes: #1479250
Approved by: Gerry Boland

373. By Daniel d'Andrada

MirSurfaceItem gets dirty when it's set to draw a different (or no) surface Fixes: #1492185
Approved by: Gerry Boland

374. By Daniel d'Andrada

QtEventFeeder: log the pointer events it gets from Mir
Approved by: PS Jenkins bot, Gerry Boland

375. By CI Train Bot Account

Releasing 0.4.6+15.10.20150914-0ubuntu1

376. By Daniel d'Andrada

Shell draws its own cursor using the new Cursor QML element

377. By Daniel d'Andrada

Merge trunk

378. By Daniel d'Andrada

Update unity-application-impl in debian/control

379. By Daniel d'Andrada

Merge trunk

380. By Daniel d'Andrada

s/Qt::MouseButton/Qt::MouseButtons

Unmerged revisions

Preview Diff

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

Subscribers

People subscribed via source and target branches