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

Proposed by Daniel d'Andrada on 2015-10-14
Status: Superseded
Proposed branch: lp:~dandrader/qtmir/surfaceItemFillMode
Merge into: lp:qtmir
Prerequisite: lp:~dandrader/qtmir/mousePointer
Diff against target: 4154 lines (+2325/-508)
62 files modified
CMakeLists.txt (+3/-3)
demos/qml-demo-shell/qml-demo-shell.qml (+19/-0)
src/common/debughelpers.cpp (+30/-1)
src/common/debughelpers.h (+1/-0)
src/modules/Unity/Application/mirbuffersgtexture.cpp (+29/-11)
src/modules/Unity/Application/mirbuffersgtexture.h (+1/-0)
src/modules/Unity/Application/mirsurface.cpp (+38/-18)
src/modules/Unity/Application/mirsurface.h (+11/-2)
src/modules/Unity/Application/mirsurfaceinterface.h (+5/-1)
src/modules/Unity/Application/mirsurfaceitem.cpp (+52/-10)
src/modules/Unity/Application/mirsurfaceitem.h (+9/-0)
src/modules/Unity/CMakeLists.txt (+1/-0)
src/modules/Unity/Screens/CMakeLists.txt (+24/-0)
src/modules/Unity/Screens/plugin.cpp (+41/-0)
src/modules/Unity/Screens/qmldir (+2/-0)
src/modules/Unity/Screens/screens.cpp (+107/-0)
src/modules/Unity/Screens/screens.h (+82/-0)
src/platforms/mirserver/CMakeLists.txt (+5/-2)
src/platforms/mirserver/display.cpp (+0/-44)
src/platforms/mirserver/display.h (+0/-37)
src/platforms/mirserver/logging.h (+1/-0)
src/platforms/mirserver/miropenglcontext.cpp (+25/-10)
src/platforms/mirserver/miropenglcontext.h (+1/-0)
src/platforms/mirserver/mirserver.cpp (+39/-5)
src/platforms/mirserver/mirserver.h (+8/-2)
src/platforms/mirserver/mirserverintegration.cpp (+42/-39)
src/platforms/mirserver/mirserverintegration.h (+4/-7)
src/platforms/mirserver/offscreensurface.cpp (+61/-0)
src/platforms/mirserver/offscreensurface.h (+43/-0)
src/platforms/mirserver/qmirserver.cpp (+12/-2)
src/platforms/mirserver/qmirserver.h (+3/-0)
src/platforms/mirserver/qmirserver_p.h (+2/-0)
src/platforms/mirserver/qtcompositor.cpp (+19/-31)
src/platforms/mirserver/qtcompositor.h (+25/-5)
src/platforms/mirserver/qteventfeeder.cpp (+108/-85)
src/platforms/mirserver/qteventfeeder.h (+11/-8)
src/platforms/mirserver/screen.cpp (+109/-7)
src/platforms/mirserver/screen.h (+33/-3)
src/platforms/mirserver/screencontroller.cpp (+211/-0)
src/platforms/mirserver/screencontroller.h (+95/-0)
src/platforms/mirserver/screenwindow.cpp (+73/-98)
src/platforms/mirserver/screenwindow.h (+13/-24)
src/platforms/mirserver/tileddisplayconfigurationpolicy.cpp (+44/-0)
src/platforms/mirserver/tileddisplayconfigurationpolicy.h (+35/-0)
tests/common/fake_displayconfigurationoutput.h (+77/-0)
tests/common/gmock_fixes.h (+124/-0)
tests/common/mock_display.h (+53/-0)
tests/common/mock_display_configuration.h (+35/-0)
tests/common/mock_gl_display_buffer.h (+49/-0)
tests/common/mock_main_loop.h (+53/-0)
tests/mirserver/CMakeLists.txt (+1/-0)
tests/mirserver/QtEventFeeder/mock_qtwindowsystem.h (+16/-9)
tests/mirserver/QtEventFeeder/qteventfeeder_test.cpp (+27/-16)
tests/mirserver/Screen/CMakeLists.txt (+1/-0)
tests/mirserver/Screen/screen_test.cpp (+38/-26)
tests/mirserver/ScreenController/CMakeLists.txt (+29/-0)
tests/mirserver/ScreenController/screencontroller_test.cpp (+177/-0)
tests/mirserver/ScreenController/stub_display.h (+96/-0)
tests/mirserver/ScreenController/stub_screen.h (+31/-0)
tests/mirserver/ScreenController/testable_screencontroller.h (+37/-0)
tests/modules/common/fake_mirsurface.h (+3/-1)
tests/modules/common/qtmir_test.h (+1/-1)
To merge this branch: bzr merge lp:~dandrader/qtmir/surfaceItemFillMode
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration 2015-10-14 Needs Fixing on 2015-10-14
Michael Zanetti 2015-10-14 Pending
Review via email: mp+274449@code.launchpad.net

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

This proposal has been superseded by a proposal from 2015-10-16.

Commit Message

Add MirSurfaceItem.fillMode and ensure items and buffer are in sync

Ensure that by the time we enter the phase of updating the scene graph, all qml items are up to date regarding the size of the buffer about to be rendered.

NB: This rendering scheme needs triple buffering to work.

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/surfaceItemFillMode/+merge/274446
https://code.launchpad.net/~dandrader/unity8/noStretchOnResize/+merge/274447
* Did you perform an exploratory manual test run of your code change and any related functionality?

Yes

* 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.
PS Jenkins bot (ps-jenkins) wrote : Posted in a previous version of this proposal
review: Needs Fixing (continuous-integration)
Daniel van Vugt (vanvugt) wrote : Posted in a previous version of this proposal

Glad to have helped. This isn't the first time someone has told me I'm wrong and then they went ahead and copied my code :)

Gerry Boland (gerboland) wrote : Posted in a previous version of this proposal

What's the use-case of this fillMode? The crop ability might have use in the spread? But it's not a proper solution for the stretched frames issue on rotation.

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

On 18/09/15 08:02, Gerry Boland wrote:
> What's the use-case of this fillMode? The crop ability might have use in the spread? But it's not a proper solution for the stretched frames issue on rotation.

Resize untiy8-dash in desktop mode on a Nexus device and you'll see.

Michael Zanetti (mzanetti) wrote : Posted in a previous version of this proposal

Code looks ok, but as noticed on the related unity8 branch, I'm not sure if this is the proper place to calculate the innerRect stuff. It has the effect that it makes window content and window decoration/shadow go out of sync.

review: Needs Information
Daniel d'Andrada (dandrader) wrote : Posted in a previous version of this proposal

On 23/09/2015 07:32, Michael Zanetti wrote:
> Review: Needs Information
>
> Code looks ok, but as noticed on the related unity8 branch, I'm not sure if this is the proper place to calculate the innerRect stuff. It has the effect that it makes window content and window decoration/shadow go out of sync.

They're two separate things. fillMode is there to ensure that you never
see a stretched surface even if the MirSurfaceItem size doesn't match
it. See it as yet another MirSurface feature that shells (unity8) can
take and use it on their UI/interaction designs.

What unity8 *does with it* is another thing. That shadow "going out of
sync" is a crude implementation or that unity7 window resize mode where
the window is not resize live, but you drag a translucent orang bounding
rect of it and only on release does the window get committed to that
bounding rect size (or actually a hint for the possibility doing the same).

387. By Albert Astals Cid on 2015-10-21

Don't search for the element again

Approved by: Gerry Boland, PS Jenkins bot

388. By Daniel d'Andrada on 2015-10-21

Shell draws its own cursor using the new Cursor QML element
Approved by: Lukáš Tinkl, Gerry Boland

389. By Gerry Boland on 2015-10-21

Initial multimonitor support - react correctly to Mir DisplayConfiguration changes.

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

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

Add Unity.Screens qml module to advertise current screen state to QML. Fixes: #1436735, #1488831, #1488863
Approved by: Daniel d'Andrada

390. By Michał Sawicz on 2015-10-21

MirSurfaceItem: Survive holding a surface with an empty texture

Survive having a surface whose texture holds no mir buffer at all.
Instead of crashing in such situation we simply don't render it.
Approved by: Gerry Boland

391. By Michał Sawicz on 2015-10-21

Improve multimonitor support

* Removed magic:
 - Don't automagically select the screen where a window will be show.
   Let shell decide.
 - Don't automagically focus a window. Let shell handle it.

* Let shell know when a screen is about to be removed so that it has
  the opportunity to move or destroy a window in it before it's too late.
  - Added QGuiApplication::onScreenAboutToBeRemoved

* Added logging to key events
Approved by: Michał Sawicz

392. By Alan Griffiths on 2015-10-21

Opaquify MirWindowManager to control visibility of upcoming Window Management work
Approved by: Gerry Boland, PS Jenkins bot

393. By Lukáš Tinkl on 2015-10-21

React to surface modifications (window caption)
Approved by: Daniel d'Andrada

394. By Nick Dedekind on 2015-10-21

Removed the manipulation of the CMAKE_INSTALL_PREFIX from debian/rules
Approved by: Gerry Boland

395. By Nick Dedekind on 2015-10-21

Added touch performance tracing and test.
Approved by: Gerry Boland

396. By Lukáš Tinkl on 2015-10-21

Implement support for mouse wheel events; correctly pass around buttons Fixes: #1497091
Approved by: Gerry Boland

397. By Gerry Boland on 2015-10-21

Workaround for AutoPilot input coordinate positioning being outside screen geometry

398. By CI Train Bot Account on 2015-10-21

Releasing 0.4.6+15.10.20151021-0ubuntu1

399. By Michał Sawicz on 2015-11-02

Clean up packaging and fix autopkgtest on armhf
Approved by: Gerry Boland

400. By Michael Terry on 2015-11-02

Support new isTouchApp property to ApplicationInfoInterface and move lifecycle policy logic out of qtmir.

Now that qtmir won't decide policy for suspending anymore, we don't need all the lifecycleException handling in qtmir either. That can move to unity8.

But since the GSettings key for that was registered under the qtmir namespace (and there's no technical reason to migrate settings), I left the schema and classes dealing with GSettings alone, for future use.

401. By Nick Dedekind on 2015-11-02

Support server->client visibility change to stop rendering (lp:#1475678) Fixes: #1475678
Approved by: Daniel d'Andrada

402. By Alan Griffiths on 2015-11-02

Test harness for MirWindowManager (in preparation for more intelligent window management)
Approved by: Gerry Boland

403. By Nick Dedekind on 2015-11-02

Hand Qt millisecond timestamps rather than nanosecond. Fixes: #1510571, #1511076, #1511711
Approved by: Gerry Boland

404. By CI Train Bot Account on 2015-11-02

Releasing 0.4.6+16.04.20151102-0ubuntu1

405. By Nick Dedekind on 2015-11-10

Reverted occlusion detection (lp#1514556) Fixes: #1514556

406. By CI Train Bot Account on 2015-11-10

Releasing 0.4.6+16.04.20151110-0ubuntu1

407. By Gerry Boland on 2015-11-12

Fix armhf builds on Xenial by using -std=gnu99 instead of c99

Fixes this FTBFS on xenial:

In file included from /usr/include/lttng/tracepoint-rcu.h:26:0,
                 from /usr/include/lttng/tracepoint.h:29,
                 from /home/phablet/dev/projects/qtmir/qtmir/BUILD-xen/src/platforms/mirserver/tracepoints.h:10,
                 from /home/phablet/dev/projects/qtmir/qtmir/BUILD-xen/src/platforms/mirserver/tracepoints.c:7:
/usr/include/urcu/arch/generic.h: In function ‘caa_get_cycles’:
/usr/include/urcu/arch/generic.h:165:6: error: ‘CLOCK_MONOTONIC’ undeclared (first use in this function)
  if (caa_unlikely(clock_gettime(CLOCK_MONOTONIC, &ts)))
      ^
/usr/include/urcu/arch/generic.h:165:6: note: each undeclared identifier is reported only once for each function it appears in

Strictly should compile code with -std=gnu99 instead of -std=c99 to have the identifiers SIGEV_SIGNAL, sigeventStruct, and CLOCK_MONOTONIC available. These identifiers are declared when _POSIX_C_SOURCE is set to a value >= 199309L, which is the case with -std=gnu99. I could also have used -D_POSIX_C_SOURCE=199309L -std=c99 or have the macro defined in source code.

Did not impact wily as libuctu only started looking for CLOCK_MONOTONIC in Xenial release.
Approved by: Daniel d'Andrada

408. By CI Train Bot Account on 2015-11-12

Releasing 0.4.6+16.04.20151112-0ubuntu1

409. By Nick Dedekind on 2015-11-13

Update surface textures when dropping frames. Fixes: #1515356
Approved by: Gerry Boland

410. By CI Train Bot Account on 2015-11-13

Releasing 0.4.6+16.04.20151113-0ubuntu1

411. By Gerry Boland on 2015-11-19

Fix use of uninitialized variable
Approved by: Daniel d'Andrada, PS Jenkins bot

412. By Albert Astals Cid on 2015-11-19

Enable Efficient String Construction by default

See http://blog.qt.io/blog/2011/06/13/string-concatenation-with-qstringbuilder/
Approved by: Gerry Boland, PS Jenkins bot

413. By Michał Sawicz on 2015-11-19

Build with clang (tests/gmock fails and is unfixable on our side i'd say)
Approved by: Gerry Boland

414. By Gerry Boland on 2015-11-19

Use pid_t for PIDs.
Approved by: Daniel d'Andrada, PS Jenkins bot

415. By Loïc Molinari on 2015-11-19

Ensured Mir surface items with size less than or equal to zero are not rendered, as it's usually done for standard QtQuick items.
Approved by: Gerry Boland

416. By Nick Dedekind on 2015-11-19

Fix a crash when dropping a surface frame before Qt draws a surface item. Fixes: #1517139
Approved by: Gerry Boland

417. By CI Train Bot Account on 2015-11-19

Releasing 0.4.6+16.04.20151119-0ubuntu1

418. By Daniel d'Andrada on 2015-11-25

Implemented support for cursors set by client surfaces
Approved by: Lukáš Tinkl

419. By Gerry Boland on 2015-11-25

Manage frameSwapped signal/slot connection with MirSurface more strictly to avoid crash.

Direct Signal/slot connections across thread boundaries incur the same risks as any cross-thread calls. While connect/disconnect are thread safe methods, it is possible for a slot to be called while the slot owner is being deconstructed - and so not yet disconnected.

So watch for the Item's window change signal and disconnect signal immediately. Also move slot ownership to MirSurfaceItem to auto-disconnect more aggressively.
 Fixes: #1517571
Approved by: Daniel d'Andrada

420. By Daniel d'Andrada on 2015-11-25

Forward Mir mouse wheel events to the shell cursor Fixes: #1497091
Approved by: Lukáš Tinkl

421. By Daniel d'Andrada on 2015-11-25

Revert revision 415

The commit "Ensured Mir surface items with size less than or equal to zero are not rendered,
as it's usually done for standard QtQuick items." caused a regression.

MirSurface.size was being kept uninitialized, as QSize(-1,-1).

422. By CI Train Bot Account on 2015-11-25

Releasing 0.4.6+16.04.20151125-0ubuntu1

423. By Timo Jyrinki on 2015-12-01

Rebuild against Qt 5.5.1.

424. By Daniel d'Andrada on 2015-12-04

merge lp:~nick-dedekind/qtmir/polite-close

425. By Daniel d'Andrada on 2015-12-04

Add MirSurfaceItem.fillMode

426. By Daniel d'Andrada on 2015-12-04

Fix bad merge of polite-close

Unmerged revisions

426. By Daniel d'Andrada on 2015-12-04

Fix bad merge of polite-close

425. By Daniel d'Andrada on 2015-12-04

Add MirSurfaceItem.fillMode

424. By Daniel d'Andrada on 2015-12-04

merge lp:~nick-dedekind/qtmir/polite-close

Preview Diff

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

Subscribers

People subscribed via source and target branches