Merge lp:~gerboland/miral/qt-add-window-model-mir-side into lp:miral

Proposed by Gerry Boland
Status: Merged
Approved by: Alan Griffiths
Approved revision: 241
Merged at revision: 239
Proposed branch: lp:~gerboland/miral/qt-add-window-model-mir-side
Merge into: lp:miral
Diff against target: 394 lines (+294/-16)
7 files modified
miral-qt/src/common/mirqtconversion.h (+28/-0)
miral-qt/src/common/windowmodelinterface.h (+78/-0)
miral-qt/src/platforms/mirserver/CMakeLists.txt (+2/-0)
miral-qt/src/platforms/mirserver/windowmanagementpolicy.cpp (+5/-3)
miral-qt/src/platforms/mirserver/windowmanagementpolicy.h (+4/-13)
miral-qt/src/platforms/mirserver/windowmodel.cpp (+125/-0)
miral-qt/src/platforms/mirserver/windowmodel.h (+52/-0)
To merge this branch: bzr merge lp:~gerboland/miral/qt-add-window-model-mir-side
Reviewer Review Type Date Requested Status
Alan Griffiths Approve
Review via email: mp+301074@code.launchpad.net

Commit message

Qt: implement basic Mir-side window "model" which emits signals describing the model changes to a consumer (to come later)

Primary problem to be solved: have a model of windows that Qt can implement a view for.

Qt unfortunately requires that the model and the view have matching thread affinity - meaning I cannot manage the model on Mir's threads and use locking to keep consistent state for the view.

Instead I need to create and manage the model on Qt's GUI thread (where the view will live). I will do this by firing signals from this Mir-side WindowModel which will describe how the model changes, signals which a GUI thread model will listen for and use to synchronize its state with.

To post a comment you must log in.
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Nits:

+// miral::WindowInfo has a copy constructor, but implicitly shares a Self object between each
+// copy. If a miral::WindowInfo copy is sent over signal/slot connection across thread boundaries,
+// it could be changed in a Mir thread before the slot processes it.
+//
+// This is undesirable as we need to update the GUI thread model in a consistent way.
+//
+// Instead we duplicate the miral::WindowInfo data, in a way that can be sent over signal/slot
+// connections safely.

Obsolete/misleading comment?

1. miral::WindowInfo doesn't share self, it copies it.
2. I don't see the duplicate.

~~~~

+// We assign each Window with a unique ID that both Mir-side and Qt-side WindowModels can share

Can't we just use a weak_ptr<Surface> as an ID?

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

> Nits:
>
> +// miral::WindowInfo has a copy constructor, but implicitly shares a Self
> object between each
> +// copy. If a miral::WindowInfo copy is sent over signal/slot connection
> across thread boundaries,
> +// it could be changed in a Mir thread before the slot processes it.
> +//
> +// This is undesirable as we need to update the GUI thread model in a
> consistent way.
> +//
> +// Instead we duplicate the miral::WindowInfo data, in a way that can be sent
> over signal/slot
> +// connections safely.
>
> Obsolete/misleading comment?

Yep, sorry. Fixed.

> +// We assign each Window with a unique ID that both Mir-side and Qt-side
> WindowModels can share
> Can't we just use a weak_ptr<Surface> as an ID?
Yes that's a much better idea. I was worried about lifetimes, but the view (qtmir::MirSurface) holds a copy of the shared_ptr<mir::scene::Surface> until it is deleted - deletion which only happens after its corresponding entry is removed from the model.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > Can't we just use a weak_ptr<Surface> as an ID?
> Yes that's a much better idea. I was worried about lifetimes, but the view
> (qtmir::MirSurface) holds a copy of the shared_ptr<mir::scene::Surface> until
> it is deleted - deletion which only happens after its corresponding entry is
> removed from the model.

You can use a weak_ptr<Surface> - which avoids accidentally affecting the lifetime. (I do this in BasicWindowManager.)

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

> > Nits:
> >
> > +// miral::WindowInfo has a copy constructor, but implicitly shares a Self
> > object between each
> > +// copy. If a miral::WindowInfo copy is sent over signal/slot connection
> > across thread boundaries,
> > +// it could be changed in a Mir thread before the slot processes it.
> > +//
> > +// This is undesirable as we need to update the GUI thread model in a
> > consistent way.
> > +//
> > +// Instead we duplicate the miral::WindowInfo data, in a way that can be
> sent
> > over signal/slot
> > +// connections safely.
> >
> > Obsolete/misleading comment?
>
> Yep, sorry. Fixed.
>
>
> > +// We assign each Window with a unique ID that both Mir-side and Qt-side
> > WindowModels can share
> > Can't we just use a weak_ptr<Surface> as an ID?
> Yes that's a much better idea. I was worried about lifetimes, but the view
> (qtmir::MirSurface) holds a copy of the shared_ptr<mir::scene::Surface> until
> it is deleted - deletion which only happens after its corresponding entry is
> removed from the model.

Ah, not quite, there is more going on. I want the Mir-side window model (mirserver/windowmodel) to tell the Qt-side at what position in the stack a window is. I was (ab)using this id to indicate the position in the window stack.

So the comment is not quite accurate, I've updated it to be more correct.

240. By Gerry Boland

Old comment not relevent, corrected

241. By Gerry Boland

Another badly written comment updated

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

OK

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'miral-qt/src/common/mirqtconversion.h'
2--- miral-qt/src/common/mirqtconversion.h 1970-01-01 00:00:00 +0000
3+++ miral-qt/src/common/mirqtconversion.h 2016-07-27 16:33:34 +0000
4@@ -0,0 +1,28 @@
5+#ifndef MIRQTCONVERSION_H
6+#define MIRQTCONVERSION_H
7+
8+#include <QSize>
9+#include <QPoint>
10+
11+#include <mir/geometry/size.h>
12+#include <mir/geometry/point.h>
13+
14+namespace qtmir {
15+
16+/*
17+ * Some handy conversions from Mir types to Qt types
18+ */
19+
20+inline QSize toQSize(mir::geometry::Size size)
21+{
22+ return QSize(size.width.as_int(), size.height.as_int());
23+}
24+
25+inline QPoint toQPoint(mir::geometry::Point point)
26+{
27+ return QPoint(point.x.as_int(), point.y.as_int());
28+}
29+
30+} // namespace qtmir
31+
32+#endif // MIRQTCONVERSION_H
33
34=== added file 'miral-qt/src/common/windowmodelinterface.h'
35--- miral-qt/src/common/windowmodelinterface.h 1970-01-01 00:00:00 +0000
36+++ miral-qt/src/common/windowmodelinterface.h 2016-07-27 16:33:34 +0000
37@@ -0,0 +1,78 @@
38+/*
39+ * Copyright (C) 2016 Canonical, Ltd.
40+ *
41+ * This program is free software: you can redistribute it and/or modify it under
42+ * the terms of the GNU Lesser General Public License version 3, as published by
43+ * the Free Software Foundation.
44+ *
45+ * This program is distributed in the hope that it will be useful, but WITHOUT
46+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
47+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
48+ * Lesser General Public License for more details.
49+ *
50+ * You should have received a copy of the GNU Lesser General Public License
51+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
52+ */
53+
54+#ifndef WINDOWMODELINTERFACE_H
55+#define WINDOWMODELINTERFACE_H
56+
57+#include <QObject>
58+#include <QSize>
59+#include <QPoint>
60+#include <QVector>
61+
62+#include <mir/scene/surface.h>
63+
64+namespace qtmir {
65+
66+// miral::WindowInfo contains all the metadata the WindowManager{,Policy} needs. However the
67+// WindowModel only needs a read-only subset of this data, which is what the struct is for.
68+struct WindowInfo {
69+ QSize size;
70+ QPoint position;
71+ bool focused;
72+ const std::shared_ptr<mir::scene::Surface> surface;
73+
74+ enum class DirtyStates {
75+ Size = 1,
76+ Position = 2,
77+ Focus = 4
78+ };
79+};
80+
81+// We assign each Window with an index which corresponds to the position it has in the window stack.
82+struct NumberedWindow {
83+ unsigned int index;
84+ WindowInfo windowInfo;
85+};
86+
87+struct DirtiedWindow {
88+ unsigned int index;
89+ WindowInfo windowInfo;
90+ WindowInfo::DirtyStates dirtyWindowInfo;
91+};
92+
93+
94+class WindowModelInterface : public QObject
95+{
96+ Q_OBJECT
97+public:
98+ WindowModelInterface() = default;
99+ virtual ~WindowModelInterface() = default;
100+
101+Q_SIGNALS:
102+ void windowAdded(const NumberedWindow);
103+ void windowRemoved(const unsigned int index);
104+ void windowChanged(const DirtiedWindow);
105+
106+private:
107+ Q_DISABLE_COPY(WindowModelInterface)
108+};
109+
110+} // namespace qtmir
111+
112+Q_DECLARE_METATYPE(qtmir::NumberedWindow)
113+Q_DECLARE_METATYPE(qtmir::DirtiedWindow)
114+
115+#endif // WINDOWMODELINTERFACE_H
116
117=== modified file 'miral-qt/src/platforms/mirserver/CMakeLists.txt'
118--- miral-qt/src/platforms/mirserver/CMakeLists.txt 2016-06-21 13:38:53 +0000
119+++ miral-qt/src/platforms/mirserver/CMakeLists.txt 2016-07-27 16:33:34 +0000
120@@ -81,10 +81,12 @@
121 clipboard.cpp
122 creationhints.cpp
123 windowmanagementpolicy.cpp
124+ windowmodel.cpp
125 tracepoints.c
126 # We need to run moc on these headers
127 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/Mir.h
128 ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirMousePointerInterface.h
129+ ${MIRAL_QT_SOURCE_DIR}/src/common/windowmodelinterface.h
130 )
131
132 add_library(qpa-mirserver SHARED
133
134=== modified file 'miral-qt/src/platforms/mirserver/windowmanagementpolicy.cpp'
135--- miral-qt/src/platforms/mirserver/windowmanagementpolicy.cpp 2016-07-11 16:14:37 +0000
136+++ miral-qt/src/platforms/mirserver/windowmanagementpolicy.cpp 2016-07-27 16:33:34 +0000
137@@ -20,12 +20,13 @@
138
139 #include "miral/window_specification.h"
140
141-WindowManagementPolicy::WindowManagementPolicy(const miral::WindowManagerTools *tools,
142+WindowManagementPolicy::WindowManagementPolicy(miral::WindowManagerTools * const tools,
143 const QSharedPointer<ScreensModel> screensModel)
144- : m_tools(tools)
145+ : CanonicalWindowManagerPolicy(tools)
146+ , m_tools(tools)
147 , m_eventFeeder(new QtEventFeeder(screensModel))
148 {
149- Q_UNUSED(m_tools); // REMOVEME once m_tools is used (keep clang happy)
150+
151 }
152
153 void WindowManagementPolicy::advise_displays_updated(const Rectangles&/*displays*/)
154@@ -103,6 +104,7 @@
155
156 void WindowManagementPolicy::advise_move_to(const miral::WindowInfo &/*window_info*/, Point /*top_left*/)
157 {
158+
159 }
160
161 void WindowManagementPolicy::advise_resize(const miral::WindowInfo &/*info*/, const Size &/*newSize*/)
162
163=== modified file 'miral-qt/src/platforms/mirserver/windowmanagementpolicy.h'
164--- miral-qt/src/platforms/mirserver/windowmanagementpolicy.h 2016-07-11 16:14:37 +0000
165+++ miral-qt/src/platforms/mirserver/windowmanagementpolicy.h 2016-07-27 16:33:34 +0000
166@@ -17,7 +17,7 @@
167 #ifndef WINDOWMANAGEMENTPOLICY_H
168 #define WINDOWMANAGEMENTPOLICY_H
169
170-#include "miral/window_management_policy.h"
171+#include "miral/canonical_window_manager.h"
172
173 #include "qteventfeeder.h"
174
175@@ -29,10 +29,10 @@
176
177 class ScreensModel;
178
179-class WindowManagementPolicy : public QObject, public miral::WindowManagementPolicy
180+class WindowManagementPolicy : public QObject, public miral::CanonicalWindowManagerPolicy
181 {
182 public:
183- WindowManagementPolicy(const miral::WindowManagerTools *tools,
184+ WindowManagementPolicy(miral::WindowManagerTools * const tools,
185 const QSharedPointer<ScreensModel> screensModel);
186
187
188@@ -68,18 +68,9 @@
189 void advise_displays_updated(const Rectangles &displays) override;
190
191 Q_SIGNALS:
192-// void sessionCreatedSurface(mir::scene::Session const*,
193-// std::shared_ptr<mir::scene::Surface> const&,
194-// std::shared_ptr<SurfaceObserver> const&,
195-// qtmir::CreationHints);
196-// void sessionDestroyingSurface(mir::scene::Session const*, std::shared_ptr<mir::scene::Surface> const&);
197-
198-// // requires Qt::BlockingQueuedConnection!!
199-// void sessionAboutToCreateSurface(const miral::ApplicationInfo &app_info,
200-// const miral::WindowSpecification &request_parameters);
201
202 private:
203- const miral::WindowManagerTools *m_tools;
204+ const miral::WindowManagerTools * const m_tools;
205 const QScopedPointer<QtEventFeeder> m_eventFeeder;
206 };
207
208
209=== added file 'miral-qt/src/platforms/mirserver/windowmodel.cpp'
210--- miral-qt/src/platforms/mirserver/windowmodel.cpp 1970-01-01 00:00:00 +0000
211+++ miral-qt/src/platforms/mirserver/windowmodel.cpp 2016-07-27 16:33:34 +0000
212@@ -0,0 +1,125 @@
213+/*
214+ * Copyright (C) 2016 Canonical, Ltd.
215+ *
216+ * This program is free software: you can redistribute it and/or modify it under
217+ * the terms of the GNU Lesser General Public License version 3, as published by
218+ * the Free Software Foundation.
219+ *
220+ * This program is distributed in the hope that it will be useful, but WITHOUT
221+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
222+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
223+ * Lesser General Public License for more details.
224+ *
225+ * You should have received a copy of the GNU Lesser General Public License
226+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
227+ */
228+
229+#include "windowmodel.h"
230+
231+#include "mirqtconversion.h"
232+#include <QDebug>
233+
234+/*
235+ * WindowModel - tracks Mir Window Manager operations and duplicates the window stack
236+ * that Mir has created internally. Any changes to this model are emitted as change
237+ * signals to the Qt GUI thread which will effectively duplicate this model again.
238+ *
239+ * Use a window ID as a shared identifier between this Mir-side model and the Qt-side model
240+ */
241+
242+using namespace qtmir;
243+
244+WindowModel::WindowModel()
245+{
246+ qDebug("WindowModel::WindowModel");
247+ qRegisterMetaType<NumberedWindow>();
248+ qRegisterMetaType<DirtiedWindow>();
249+}
250+
251+WindowModel::~WindowModel()
252+{
253+
254+}
255+
256+void WindowModel::addWindow(const miral::WindowInfo &windowInfo)
257+{
258+ qDebug("WindowModel::addWindow");
259+ auto stackPosition = static_cast<unsigned int>(m_windowIdStack.count());
260+ m_windowIdStack.push_back(windowInfo.window().surface_id()); // ASSUMPTION: Mir should tell us where in stack
261+
262+ QSize size = toQSize(windowInfo.window().size());
263+ QPoint position = toQPoint(windowInfo.window().top_left());
264+
265+ WindowInfo info{ size, position, false, windowInfo.window() };
266+ NumberedWindow window{ stackPosition, info };
267+ Q_EMIT windowAdded(window);
268+}
269+
270+void WindowModel::removeWindow(const miral::WindowInfo &windowInfo)
271+{
272+ qDebug("WindowModel::removeWindow");
273+ const int pos = m_windowIdStack.indexOf(windowInfo.window().surface_id());
274+ if (pos < 0) {
275+ qDebug("Unknown window removed");
276+ return;
277+ }
278+ m_windowIdStack.removeAt(pos);
279+ auto upos = static_cast<unsigned int>(pos);
280+ Q_EMIT windowRemoved(upos);
281+}
282+
283+void WindowModel::focusWindow(const miral::WindowInfo &windowInfo, const bool focus)
284+{
285+ const int pos = m_windowIdStack.indexOf(windowInfo.window().surface_id());
286+ if (pos < 0) {
287+ qDebug("Unknown window focused");
288+ return;
289+ }
290+ auto upos = static_cast<unsigned int>(pos);
291+ m_focusedWindowIndex = upos;
292+ QSize size = toQSize(windowInfo.window().size());
293+ QPoint position = toQPoint(windowInfo.window().top_left());
294+
295+ WindowInfo info{ size, position, focus, windowInfo.window() };
296+ DirtiedWindow window{ upos, info, WindowInfo::DirtyStates::Focus};
297+ Q_EMIT windowChanged(window);
298+}
299+
300+void WindowModel::moveWindow(const miral::WindowInfo &windowInfo, mir::geometry::Point topLeft)
301+{
302+ const int pos = m_windowIdStack.indexOf(windowInfo.window().surface_id());
303+ if (pos < 0) {
304+ qDebug("Unknown window moved");
305+ return;
306+ }
307+ auto upos = static_cast<unsigned int>(pos);
308+ const bool focused = (m_focusedWindowIndex == upos);
309+ QSize size = toQSize(windowInfo.window().size());
310+ QPoint position = toQPoint(topLeft);
311+
312+ WindowInfo info{ size, position, focused, windowInfo.window() };
313+ DirtiedWindow window{ upos, info, WindowInfo::DirtyStates::Position};
314+ Q_EMIT windowChanged(window);
315+}
316+
317+void WindowModel::resizeWindow(const miral::WindowInfo &windowInfo, mir::geometry::Size newSize)
318+{
319+ const int pos = m_windowIdStack.indexOf(windowInfo.window().surface_id());
320+ if (pos < 0) {
321+ qDebug("Unknown window resized");
322+ return;
323+ }
324+ auto upos = static_cast<unsigned int>(pos);
325+ const bool focused = (m_focusedWindowIndex == upos);
326+ QSize size = toQSize(newSize);
327+ QPoint position = toQPoint(windowInfo.window().top_left());
328+
329+ WindowInfo info{ size, position, focused, windowInfo.window() };
330+ DirtiedWindow window{ upos, info, WindowInfo::DirtyStates::Size};
331+ Q_EMIT windowChanged(window);
332+}
333+
334+void WindowModel::raiseWindows(const std::vector<miral::Window> &/*windows*/)
335+{
336+
337+}
338
339=== added file 'miral-qt/src/platforms/mirserver/windowmodel.h'
340--- miral-qt/src/platforms/mirserver/windowmodel.h 1970-01-01 00:00:00 +0000
341+++ miral-qt/src/platforms/mirserver/windowmodel.h 2016-07-27 16:33:34 +0000
342@@ -0,0 +1,52 @@
343+/*
344+ * Copyright (C) 2016 Canonical, Ltd.
345+ *
346+ * This program is free software: you can redistribute it and/or modify it under
347+ * the terms of the GNU Lesser General Public License version 3, as published by
348+ * the Free Software Foundation.
349+ *
350+ * This program is distributed in the hope that it will be useful, but WITHOUT
351+ * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
352+ * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
353+ * Lesser General Public License for more details.
354+ *
355+ * You should have received a copy of the GNU Lesser General Public License
356+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
357+ */
358+
359+#ifndef WINDOWMODEL_H
360+#define WINDOWMODEL_H
361+
362+#include "windowmodelinterface.h"
363+
364+#include "miral/window_info.h"
365+#include <mir/frontend/surface_id.h>
366+
367+#include <QPair>
368+
369+namespace qtmir {
370+
371+class WindowModel : public WindowModelInterface
372+{
373+ Q_OBJECT
374+public:
375+ WindowModel();
376+ virtual ~WindowModel();
377+
378+ void addWindow(const miral::WindowInfo &windowInfo);
379+ void removeWindow(const miral::WindowInfo &windowInfo);
380+
381+ void moveWindow(const miral::WindowInfo &windowInfo, mir::geometry::Point topLeft);
382+ void resizeWindow(const miral::WindowInfo &windowInfo, mir::geometry::Size newSize);
383+
384+ void focusWindow(const miral::WindowInfo &windowInfo, const bool focus);
385+ void raiseWindows(const std::vector<miral::Window> &windows); //window?? Not WindowInfo??
386+
387+private:
388+ QVector<mir::frontend::SurfaceId> m_windowIdStack;
389+ unsigned int m_focusedWindowIndex;
390+};
391+
392+} // namespace qtmir
393+
394+#endif // WINDOWMODEL_H

Subscribers

People subscribed via source and target branches