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

Proposed by Daniel d'Andrada
Status: Superseded
Proposed branch: lp:~dandrader/qtmir/multimonitorNext
Merge into: lp:qtmir
Prerequisite: lp:~dandrader/qtmir/surviveEmptyTexture
Diff against target: 345 lines (+77/-84)
11 files modified
src/common/debughelpers.cpp (+30/-1)
src/common/debughelpers.h (+1/-0)
src/platforms/mirserver/mirserver.cpp (+7/-1)
src/platforms/mirserver/mirserverintegration.cpp (+3/-11)
src/platforms/mirserver/qtcompositor.cpp (+13/-0)
src/platforms/mirserver/qtcompositor.h (+12/-0)
src/platforms/mirserver/qteventfeeder.cpp (+6/-2)
src/platforms/mirserver/screencontroller.cpp (+5/-52)
src/platforms/mirserver/screencontroller.h (+0/-1)
src/platforms/mirserver/screenwindow.cpp (+0/-4)
tests/mirserver/ScreenController/screencontroller_test.cpp (+0/-12)
To merge this branch: bzr merge lp:~dandrader/qtmir/multimonitorNext
Reviewer Review Type Date Requested Status
Gerry Boland (community) Approve
PS Jenkins bot (community) continuous-integration Needs Fixing
Review via email: mp+273303@code.launchpad.net

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

Commit message

Improve multimonitor support

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

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

* Added logging to key events

Description of the change

Known issues:
- Input events come at very irregular intervals (if at all) when an external monitor is connected to a phone or tablet. Works fine on a laptop connected to an external monitor.

* Are there any related MPs required for this MP to build/function as expected? Please list.
No, but to see any difference you should use this untiy8 branch:
https://code.launchpad.net/~unity-team/unity8/externalMonitor/+merge/273829

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

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A

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

QGuiApplication::onScreenAboutToBeRemoved
How necessary is this? The QWindow has been hidden, so it has released its GL context and should be safe to hold onto until you get the screenRemoved signal.

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

On 06/10/2015 19:16, Gerry Boland wrote:
> Review: Needs Information
>
> QGuiApplication::onScreenAboutToBeRemoved
> How necessary is this? The QWindow has been hidden, so it has released its GL context and should be safe to hold onto until you get the screenRemoved signal.
Experience tells otherwise (it would crash).

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

> On 06/10/2015 19:16, Gerry Boland wrote:
> > Review: Needs Information
> >
> > QGuiApplication::onScreenAboutToBeRemoved
> > How necessary is this? The QWindow has been hidden, so it has released its
> GL context and should be safe to hold onto until you get the screenRemoved
> signal.
> Experience tells otherwise (it would crash).

It's not supposed to! :) But this it technically more correct, so am ok with it.

Revision history for this message
Gerry Boland (gerboland) :
review: Approve
lp:~dandrader/qtmir/multimonitorNext updated
389. By Daniel d'Andrada

Merge lp:~dandrader/qtmir/surviveEmptyTexture again

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/common/debughelpers.cpp'
2--- src/common/debughelpers.cpp 2015-08-27 16:10:20 +0000
3+++ src/common/debughelpers.cpp 2015-10-14 13:01:18 +0000
4@@ -223,7 +223,7 @@
5 {
6 const int pointerCount = mir_touch_event_point_count(event);
7
8- QString string("MirTouchInputEvent(");
9+ QString string("MirTouchEvent(");
10
11 for (int i = 0; i < pointerCount; ++i) {
12
13@@ -265,3 +265,32 @@
14 break;
15 }
16 }
17+
18+namespace {
19+const char *mirKeyboardActionToString(MirKeyboardAction keyboardAction)
20+{
21+ switch (keyboardAction)
22+ {
23+ case mir_keyboard_action_up:
24+ return "up";
25+ case mir_keyboard_action_down:
26+ return "down";
27+ case mir_keyboard_action_repeat:
28+ return "repeat";
29+ default:
30+ return "???";
31+ break;
32+ }
33+}
34+}
35+
36+QString mirKeyboardEventToString(MirKeyboardEvent const* event)
37+{
38+ MirKeyboardAction keyboardAction = mir_keyboard_event_action(event);
39+
40+ xkb_keysym_t keyCode = mir_keyboard_event_key_code(event);
41+
42+ return QString("MirKeyboardEvent(action=%1,key_code=0x%2)")
43+ .arg(mirKeyboardActionToString(keyboardAction))
44+ .arg(keyCode, 4, 16, QLatin1Char('0'));
45+}
46
47=== modified file 'src/common/debughelpers.h'
48--- src/common/debughelpers.h 2015-08-27 16:10:20 +0000
49+++ src/common/debughelpers.h 2015-10-14 13:01:18 +0000
50@@ -38,6 +38,7 @@
51
52 QString mirPointerEventToString(MirPointerEvent const* event);
53 QString mirTouchEventToString(MirTouchEvent const* event);
54+QString mirKeyboardEventToString(MirKeyboardEvent const* event);
55 const char *mirTouchActionToString(MirTouchAction touchAction);
56
57 #endif // UBUNTUGESTURES_DEBUG_HELPER_H
58
59=== modified file 'src/platforms/mirserver/mirserver.cpp'
60--- src/platforms/mirserver/mirserver.cpp 2015-10-14 13:01:18 +0000
61+++ src/platforms/mirserver/mirserver.cpp 2015-10-14 13:01:18 +0000
62@@ -124,7 +124,13 @@
63 apply_settings();
64
65 // We will draw our own cursor.
66- add_init_callback([this](){ the_cursor()->hide(); });
67+ // FIXME: Call override_the_cusor() instead once this method becomes available in a
68+ // future version of Mir.
69+ add_init_callback([this]() {
70+ the_cursor()->hide();
71+ // Hack to work around https://bugs.launchpad.net/mir/+bug/1502200
72+ static_cast<QtCompositor*>(the_compositor().get())->setCursor(the_cursor());
73+ });
74
75 qCDebug(QTMIR_MIR_MESSAGES) << "MirServer created";
76 }
77
78=== modified file 'src/platforms/mirserver/mirserverintegration.cpp'
79--- src/platforms/mirserver/mirserverintegration.cpp 2015-10-14 13:01:18 +0000
80+++ src/platforms/mirserver/mirserverintegration.cpp 2015-10-14 13:01:18 +0000
81@@ -103,28 +103,20 @@
82 {
83 QWindowSystemInterface::flushWindowSystemEvents();
84
85- // FIXME: QWindow can be created specifying a destination QScreen. For now we
86- // will ignore it and just associate any unused Screen, if available.
87 auto screens = m_mirServer->screenController().lock();
88 if (!screens) {
89 qCritical("Screens are not initialized, unable to create a new QWindow/ScreenWindow");
90 return nullptr;
91 }
92- Screen *screen = screens->getUnusedScreen();
93- if (!screen) {
94- qCritical("No available Screens to create a new QWindow/ScreenWindow for");
95- return nullptr;
96- }
97- QScreen *qscreen = screen->screen();
98- window->setScreen(qscreen);
99
100 auto platformWindow = new ScreenWindow(window);
101 if (screens->compositing()) {
102 platformWindow->setExposed(true);
103 }
104
105- qCDebug(QTMIR_SCREENS) << "New" << window << "with geom" << window->geometry()
106- << "is backed by a" << screen << "with geometry" << screen->geometry();
107+ qCDebug(QTMIR_SCREENS) << "QWindow" << window << "with geom" << window->geometry()
108+ << "is backed by a" << static_cast<Screen *>(window->screen()->handle())
109+ << "with geometry" << window->screen()->geometry();
110 return platformWindow;
111 }
112
113
114=== modified file 'src/platforms/mirserver/qtcompositor.cpp'
115--- src/platforms/mirserver/qtcompositor.cpp 2015-10-14 13:01:18 +0000
116+++ src/platforms/mirserver/qtcompositor.cpp 2015-10-14 13:01:18 +0000
117@@ -17,11 +17,19 @@
118 #include "qtcompositor.h"
119 #include "logging.h"
120
121+#include <mir/graphics/cursor.h>
122+
123 // Lives in a Mir thread
124 void QtCompositor::start()
125 {
126 qCDebug(QTMIR_SCREENS) << "QtCompositor::start";
127
128+ // FIXME: Hack to work around https://bugs.launchpad.net/mir/+bug/1502200
129+ // See the FIXME in mirserver.cpp
130+ if (m_cursor) {
131+ m_cursor->hide();
132+ }
133+
134 Q_EMIT starting(); // blocks
135 }
136
137@@ -31,3 +39,8 @@
138
139 Q_EMIT stopping(); // blocks
140 }
141+
142+void QtCompositor::setCursor(std::shared_ptr<mir::graphics::Cursor> cursor)
143+{
144+ m_cursor = cursor;
145+}
146
147=== modified file 'src/platforms/mirserver/qtcompositor.h'
148--- src/platforms/mirserver/qtcompositor.h 2015-10-14 13:01:18 +0000
149+++ src/platforms/mirserver/qtcompositor.h 2015-10-14 13:01:18 +0000
150@@ -19,9 +19,18 @@
151
152 #include <mir/compositor/compositor.h>
153
154+// std lib
155+#include <memory>
156+
157 // Qt
158 #include <QObject>
159
160+namespace mir {
161+ namespace graphics {
162+ class Cursor;
163+ }
164+}
165+
166 class QtCompositor : public QObject, public mir::compositor::Compositor
167 {
168 Q_OBJECT
169@@ -32,11 +41,14 @@
170 void start();
171 void stop();
172
173+ void setCursor(std::shared_ptr<mir::graphics::Cursor>);
174+
175 Q_SIGNALS:
176 void starting();
177 void stopping();
178
179 private:
180+ std::shared_ptr<mir::graphics::Cursor> m_cursor;
181 };
182
183 #endif // QTCOMPOSITOR_H
184
185=== modified file 'src/platforms/mirserver/qteventfeeder.cpp'
186--- src/platforms/mirserver/qteventfeeder.cpp 2015-10-14 13:01:18 +0000
187+++ src/platforms/mirserver/qteventfeeder.cpp 2015-10-14 13:01:18 +0000
188@@ -412,7 +412,7 @@
189 const QList<struct QWindowSystemInterface::TouchPoint> &points, Qt::KeyboardModifiers mods) override
190 {
191 QWindowSystemInterface::handleTouchEvent(window, timestamp, device, points, mods);
192- }
193+ }
194
195 void handleMouseEvent(ulong timestamp, QPointF movement, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) override
196 {
197@@ -587,11 +587,15 @@
198 text, is_auto_rep);
199 qKeyEvent.setTimestamp(timestamp);
200 if (context->filterEvent(&qKeyEvent)) {
201- // key event filtered out by input context
202+ qCDebug(QTMIR_MIR_INPUT) << "Received" << qPrintable(mirKeyboardEventToString(kev))
203+ << "but not dispatching as it was filtered out by input context";
204 return;
205 }
206 }
207
208+ qCDebug(QTMIR_MIR_INPUT).nospace() << "Received" << qPrintable(mirKeyboardEventToString(kev))
209+ << ". Dispatching to " << mQtWindowSystem->focusedWindow();
210+
211 mQtWindowSystem->handleExtendedKeyEvent(mQtWindowSystem->focusedWindow(),
212 timestamp, keyType, keyCode, modifiers,
213 mir_keyboard_event_scan_code(kev),
214
215=== modified file 'src/platforms/mirserver/screencontroller.cpp'
216--- src/platforms/mirserver/screencontroller.cpp 2015-10-14 13:01:18 +0000
217+++ src/platforms/mirserver/screencontroller.cpp 2015-10-14 13:01:18 +0000
218@@ -30,6 +30,7 @@
219 #include <QScreen>
220 #include <QQuickWindow>
221 #include <qpa/qwindowsysteminterface.h>
222+#include <QGuiApplication> // for qApp
223
224 // std
225 #include <memory>
226@@ -145,6 +146,10 @@
227 if (window && window->window() && window->isExposed()) {
228 window->window()->hide();
229 }
230+ bool ok = QMetaObject::invokeMethod(qApp, "onScreenAboutToBeRemoved", Qt::DirectConnection, Q_ARG(QScreen*, screen->screen()));
231+ if (!ok) {
232+ qCWarning(QTMIR_SCREENS) << "Failed to invoke QGuiApplication::onScreenAboutToBeRemoved(QScreen*) slot.";
233+ }
234 delete screen;
235 }
236
237@@ -185,58 +190,6 @@
238 return new Screen(output);
239 }
240
241-Screen* ScreenController::getUnusedScreen()
242-{
243- if (m_screenList.empty()) {
244- return nullptr;
245- } else if (m_screenList.size() == 1) {
246- return m_screenList.at(0);
247- }
248-
249- // FIXME: Until we have better way of identifying screens, prioritize outputs based on their output type.
250- // Note the priorities defined here are nothing more than guesses. It tries to select internal displays first,
251- // then digital outputs, and finally analogue.
252- QMap <int, Screen*> priorityList;
253- auto prioritize = [](const mg::DisplayConfigurationOutputType &type) {
254- using out = mg::DisplayConfigurationOutputType;
255- switch(type) {
256- case out::lvds:
257- case out::edp:
258- return 0;
259- case out::displayport:
260- case out::hdmia:
261- case out::hdmib:
262- return 1;
263- case out::dvii:
264- case out::dvid:
265- case out::dvia:
266- return 2;
267- case out::vga:
268- return 3;
269- case out::ninepindin:
270- return 4;
271- case out::component:
272- case out::composite:
273- case out::svideo:
274- return 5;
275- case out::tv:
276- return 6;
277- case out::unknown:
278- default:
279- return 9;
280- }
281- };
282-
283- for (auto screen : m_screenList) {
284- if (!screen->window()) {
285- priorityList.insert(prioritize(screen->outputType()), screen);
286- }
287- }
288-
289- qCDebug(QTMIR_SCREENS) << "Prioritized list of available outputs:" << priorityList;
290- return priorityList.first(); // Map sorted by key, so first is the key with highest priority.
291-}
292-
293 Screen* ScreenController::findScreenWithId(const QList<Screen *> &list, const mg::DisplayConfigurationOutputId id)
294 {
295 for (Screen *screen : list) {
296
297=== modified file 'src/platforms/mirserver/screencontroller.h'
298--- src/platforms/mirserver/screencontroller.h 2015-10-14 13:01:18 +0000
299+++ src/platforms/mirserver/screencontroller.h 2015-10-14 13:01:18 +0000
300@@ -59,7 +59,6 @@
301 public:
302 explicit ScreenController(QObject *parent = 0);
303
304- Screen* getUnusedScreen();
305 QList<Screen*> screens() const { return m_screenList; }
306 bool compositing() const { return m_compositing; }
307
308
309=== modified file 'src/platforms/mirserver/screenwindow.cpp'
310--- src/platforms/mirserver/screenwindow.cpp 2015-10-14 13:01:18 +0000
311+++ src/platforms/mirserver/screenwindow.cpp 2015-10-14 13:01:18 +0000
312@@ -58,10 +58,6 @@
313 window->setGeometry(screenGeometry);
314 }
315 window->setSurfaceType(QSurface::OpenGLSurface);
316-
317- // The compositor window is always active. I.e., it's always focused so that
318- // it always processes key events, etc
319- requestActivateWindow();
320 }
321
322 ScreenWindow::~ScreenWindow()
323
324=== modified file 'tests/mirserver/ScreenController/screencontroller_test.cpp'
325--- tests/mirserver/ScreenController/screencontroller_test.cpp 2015-10-14 13:01:18 +0000
326+++ tests/mirserver/ScreenController/screencontroller_test.cpp 2015-10-14 13:01:18 +0000
327@@ -135,18 +135,6 @@
328 EXPECT_EQ(QRect(500, 600, 1500, 2000), screenController->screens().at(0)->geometry());
329 }
330
331-TEST_F(ScreenControllerTest, CheckPrioritizedGetUnusedScreen)
332-{
333- std::vector<mg::DisplayConfigurationOutput> config{fakeOutput2, fakeOutput1};
334- std::vector<MockDisplayBuffer*> bufferConfig; // only used to match buffer with display, unecessary here
335- display->setFakeConfiguration(config, bufferConfig);
336-
337- screenController->update();
338-
339- auto screen = screenController->getUnusedScreen();
340- EXPECT_EQ(mg::DisplayConfigurationOutputType::lvds, screen->outputType());
341-}
342-
343 TEST_F(ScreenControllerTest, MatchBufferWithDisplay)
344 {
345 std::vector<mg::DisplayConfigurationOutput> config{fakeOutput1};

Subscribers

People subscribed via source and target branches