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