1 | // Copyright (C) 2017 Jolla Ltd, author: <giulio.camuffo@jollamobile.com> |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qwaylandview.h" |
5 | #include "qwaylandview_p.h" |
6 | #include "qwaylandsurface.h" |
7 | #include <QtWaylandCompositor/QWaylandSeat> |
8 | #include <QtWaylandCompositor/QWaylandCompositor> |
9 | |
10 | #include <QtWaylandCompositor/private/qwaylandsurface_p.h> |
11 | #include <QtWaylandCompositor/private/qwaylandoutput_p.h> |
12 | |
13 | #include <QtCore/QMutex> |
14 | #include <QtCore/qpointer.h> |
15 | |
16 | QT_BEGIN_NAMESPACE |
17 | |
18 | void QWaylandViewPrivate::markSurfaceAsDestroyed(QWaylandSurface *surface) |
19 | { |
20 | Q_Q(QWaylandView); |
21 | Q_ASSERT(surface == this->surface); |
22 | |
23 | setSurface(nullptr); |
24 | QPointer<QWaylandView> deleteGuard(q); |
25 | emit q->surfaceDestroyed(); |
26 | if (!deleteGuard.isNull()) |
27 | clearFrontBuffer(); |
28 | } |
29 | |
30 | /*! |
31 | * \qmltype WaylandView |
32 | * \nativetype QWaylandView |
33 | * \inqmlmodule QtWayland.Compositor |
34 | * \since 5.8 |
35 | * \brief Represents a view of a surface on an output. |
36 | * |
37 | * The WaylandView corresponds to the presentation of a surface on a specific |
38 | * output, managing the buffers that contain the contents to be rendered. |
39 | * You can have several views into the same surface. |
40 | */ |
41 | |
42 | /*! |
43 | * \class QWaylandView |
44 | * \inmodule QtWaylandCompositor |
45 | * \since 5.8 |
46 | * \brief The QWaylandView class represents a view of a surface on an output. |
47 | * |
48 | * The QWaylandView corresponds to the presentation of a surface on a specific |
49 | * output, managing the buffers that contain the contents to be rendered. |
50 | * You can have several views into the same surface. |
51 | */ |
52 | |
53 | /*! |
54 | * Constructs a QWaylandView with the given \a renderObject and \a parent. |
55 | */ |
56 | QWaylandView::QWaylandView(QObject *renderObject, QObject *parent) |
57 | : QObject(*new QWaylandViewPrivate(),parent) |
58 | { |
59 | d_func()->renderObject = renderObject; |
60 | } |
61 | |
62 | /*! |
63 | * Destroys the QWaylandView. |
64 | */ |
65 | QWaylandView::~QWaylandView() |
66 | { |
67 | Q_D(QWaylandView); |
68 | if (d->surface) { |
69 | if (d->output) |
70 | QWaylandOutputPrivate::get(output: d->output)->removeView(view: this, surface: d->surface); |
71 | |
72 | QWaylandSurfacePrivate::get(surface: d->surface)->derefView(view: this); |
73 | } |
74 | |
75 | } |
76 | |
77 | /*! |
78 | * \internal |
79 | * Didn't we decide to remove this property? |
80 | */ |
81 | QObject *QWaylandView::renderObject() const |
82 | { |
83 | Q_D(const QWaylandView); |
84 | return d->renderObject; |
85 | } |
86 | |
87 | /*! |
88 | * \qmlproperty WaylandSurface QtWayland.Compositor::WaylandView::surface |
89 | * |
90 | * This property holds the surface viewed by this WaylandView. |
91 | */ |
92 | |
93 | /*! |
94 | * \property QWaylandView::surface |
95 | * |
96 | * This property holds the surface viewed by this QWaylandView. |
97 | */ |
98 | QWaylandSurface *QWaylandView::surface() const |
99 | { |
100 | Q_D(const QWaylandView); |
101 | return d->surface; |
102 | } |
103 | |
104 | |
105 | void QWaylandViewPrivate::setSurface(QWaylandSurface *newSurface) |
106 | { |
107 | Q_Q(QWaylandView); |
108 | if (surface) { |
109 | QWaylandSurfacePrivate::get(surface)->derefView(view: q); |
110 | if (output) |
111 | QWaylandOutputPrivate::get(output)->removeView(view: q, surface); |
112 | } |
113 | |
114 | surface = newSurface; |
115 | |
116 | nextBuffer = QWaylandBufferRef(); |
117 | nextBufferCommitted = false; |
118 | nextDamage = QRegion(); |
119 | |
120 | if (surface) { |
121 | QWaylandSurfacePrivate::get(surface)->refView(view: q); |
122 | if (output) |
123 | QWaylandOutputPrivate::get(output)->addView(view: q, surface); |
124 | } |
125 | } |
126 | |
127 | void QWaylandViewPrivate::clearFrontBuffer() |
128 | { |
129 | if (!bufferLocked) { |
130 | currentBuffer = QWaylandBufferRef(); |
131 | currentDamage = QRegion(); |
132 | } |
133 | } |
134 | |
135 | void QWaylandView::setSurface(QWaylandSurface *newSurface) |
136 | { |
137 | Q_D(QWaylandView); |
138 | if (d->surface == newSurface) |
139 | return; |
140 | |
141 | d->setSurface(newSurface); |
142 | d->clearFrontBuffer(); |
143 | emit surfaceChanged(); |
144 | } |
145 | |
146 | /*! |
147 | * \qmlproperty WaylandOutput QtWayland.Compositor::WaylandView::output |
148 | * |
149 | * This property holds the output on which this view displays its surface. |
150 | */ |
151 | |
152 | /*! |
153 | * \property QWaylandView::output |
154 | * |
155 | * This property holds the output on which this view displays its surface. |
156 | */ |
157 | QWaylandOutput *QWaylandView::output() const |
158 | { |
159 | Q_D(const QWaylandView); |
160 | return d->output; |
161 | } |
162 | |
163 | void QWaylandView::setOutput(QWaylandOutput *newOutput) |
164 | { |
165 | Q_D(QWaylandView); |
166 | if (d->output == newOutput) |
167 | return; |
168 | |
169 | if (d->output && d->surface) |
170 | QWaylandOutputPrivate::get(output: d->output)->removeView(view: this, surface: d->surface); |
171 | |
172 | d->output = newOutput; |
173 | |
174 | if (d->output && d->surface) |
175 | QWaylandOutputPrivate::get(output: d->output)->addView(view: this, surface: d->surface); |
176 | |
177 | emit outputChanged(); |
178 | } |
179 | |
180 | /*! |
181 | * This function is called when a new \a buffer is committed to this view's surface. |
182 | * \a damage contains the region that is different from the current buffer, i.e. the |
183 | * region that needs to be updated. |
184 | * The new \a buffer will become current on the next call to advance(). |
185 | * |
186 | * Subclasses that reimplement this function \e must call the base implementation. |
187 | */ |
188 | void QWaylandView::bufferCommitted(const QWaylandBufferRef &buffer, const QRegion &damage) |
189 | { |
190 | Q_D(QWaylandView); |
191 | QMutexLocker locker(&d->bufferMutex); |
192 | d->nextBuffer = buffer; |
193 | d->nextDamage = damage; |
194 | d->nextBufferCommitted = true; |
195 | } |
196 | |
197 | /*! |
198 | * Updates the current buffer and damage region to the latest version committed by the client. |
199 | * Returns true if new content was committed since the previous call to advance(). |
200 | * Otherwise returns false. |
201 | * |
202 | * \sa currentBuffer(), currentDamage() |
203 | */ |
204 | bool QWaylandView::advance() |
205 | { |
206 | Q_D(QWaylandView); |
207 | |
208 | if (!d->nextBufferCommitted && !d->forceAdvanceSucceed) |
209 | return false; |
210 | |
211 | if (d->bufferLocked) |
212 | return false; |
213 | |
214 | if (d->surface && d->surface->primaryView() == this) { |
215 | const auto views = d->surface->views(); |
216 | for (QWaylandView *view : views) { |
217 | if (view != this && view->allowDiscardFrontBuffer() && view->d_func()->currentBuffer == d->currentBuffer) |
218 | view->discardCurrentBuffer(); |
219 | } |
220 | } |
221 | |
222 | QMutexLocker locker(&d->bufferMutex); |
223 | d->forceAdvanceSucceed = false; |
224 | d->nextBufferCommitted = false; |
225 | d->currentBuffer = d->nextBuffer; |
226 | d->currentDamage = d->nextDamage; |
227 | return true; |
228 | } |
229 | |
230 | /*! |
231 | * Force the view to discard its current buffer, to allow it to be reused on the client side. |
232 | */ |
233 | void QWaylandView::discardCurrentBuffer() |
234 | { |
235 | Q_D(QWaylandView); |
236 | QMutexLocker locker(&d->bufferMutex); |
237 | d->currentBuffer = QWaylandBufferRef(); |
238 | d->forceAdvanceSucceed = true; |
239 | } |
240 | |
241 | /*! |
242 | * Returns a reference to this view's current buffer. |
243 | */ |
244 | QWaylandBufferRef QWaylandView::currentBuffer() |
245 | { |
246 | Q_D(QWaylandView); |
247 | QMutexLocker locker(&d->bufferMutex); |
248 | return d->currentBuffer; |
249 | } |
250 | |
251 | /*! |
252 | * Returns the current damage region of this view. |
253 | */ |
254 | QRegion QWaylandView::currentDamage() |
255 | { |
256 | Q_D(QWaylandView); |
257 | QMutexLocker locker(&d->bufferMutex); |
258 | return d->currentDamage; |
259 | } |
260 | |
261 | /*! |
262 | * \qmlproperty bool QtWayland.Compositor::WaylandView::bufferLocked |
263 | * |
264 | * This property holds whether the view's buffer is currently locked. When |
265 | * the buffer is locked, advance() will not advance to the next buffer and |
266 | * returns \c false. |
267 | * |
268 | * The default is \c false. |
269 | */ |
270 | |
271 | /*! |
272 | * \property QWaylandView::bufferLocked |
273 | * |
274 | * This property holds whether the view's buffer is currently locked. When |
275 | * the buffer is locked, advance() will not advance to the next buffer |
276 | * and returns \c false. |
277 | * |
278 | * The default is \c false. |
279 | */ |
280 | bool QWaylandView::isBufferLocked() const |
281 | { |
282 | Q_D(const QWaylandView); |
283 | return d->bufferLocked; |
284 | } |
285 | |
286 | void QWaylandView::setBufferLocked(bool locked) |
287 | { |
288 | Q_D(QWaylandView); |
289 | if (d->bufferLocked == locked) |
290 | return; |
291 | d->bufferLocked = locked; |
292 | emit bufferLockedChanged(); |
293 | } |
294 | /*! |
295 | * \qmlproperty bool QtWayland.Compositor::WaylandView::allowDiscardFrontBuffer |
296 | * |
297 | * By default, the view locks the current buffer until advance() is called. Set this property |
298 | * to true to allow Qt to release the buffer when the primary view is no longer using it. |
299 | * |
300 | * This can be used to avoid the situation where a secondary view that updates on a lower |
301 | * frequency will throttle the frame rate of the client application. |
302 | */ |
303 | |
304 | /*! |
305 | * \property QWaylandView::allowDiscardFrontBuffer |
306 | * |
307 | * By default, the view locks the current buffer until advance() is called. Set this property |
308 | * to \c true to allow Qt to release the buffer when the primary view is no longer using it. |
309 | * |
310 | * This can be used to avoid the situation where a secondary view that updates on a lower |
311 | * frequency will throttle the frame rate of the client application. |
312 | */ |
313 | bool QWaylandView::allowDiscardFrontBuffer() const |
314 | { |
315 | Q_D(const QWaylandView); |
316 | return d->allowDiscardFrontBuffer; |
317 | } |
318 | |
319 | void QWaylandView::setAllowDiscardFrontBuffer(bool discard) |
320 | { |
321 | Q_D(QWaylandView); |
322 | if (d->allowDiscardFrontBuffer == discard) |
323 | return; |
324 | d->allowDiscardFrontBuffer = discard; |
325 | emit allowDiscardFrontBufferChanged(); |
326 | } |
327 | |
328 | /*! |
329 | * Makes this QWaylandView the primary view for the surface. |
330 | * |
331 | * It has no effect if this QWaylandView is not holding any QWaylandSurface |
332 | * |
333 | * \sa QWaylandSurface::primaryView |
334 | */ |
335 | void QWaylandView::setPrimary() |
336 | { |
337 | Q_D(QWaylandView); |
338 | if (d->surface) |
339 | d->surface->setPrimaryView(this); |
340 | else |
341 | qWarning(msg: "Calling setPrimary() on a QWaylandView without a surface has no effect." ); |
342 | } |
343 | |
344 | /*! |
345 | * Returns true if this QWaylandView is the primary view for the QWaylandSurface |
346 | * |
347 | * \sa QWaylandSurface::primaryView |
348 | */ |
349 | bool QWaylandView::isPrimary() const |
350 | { |
351 | Q_D(const QWaylandView); |
352 | return d->surface && d->surface->primaryView() == this; |
353 | } |
354 | |
355 | /*! |
356 | * Returns the Wayland surface resource for this QWaylandView. |
357 | */ |
358 | struct wl_resource *QWaylandView::surfaceResource() const |
359 | { |
360 | Q_D(const QWaylandView); |
361 | if (!d->surface) |
362 | return nullptr; |
363 | return d->surface->resource(); |
364 | } |
365 | |
366 | QT_END_NAMESPACE |
367 | |
368 | #include "moc_qwaylandview.cpp" |
369 | |