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