1 /********************************************************************
2 Copyright © 2014  Martin Gräßlin <mgraesslin@kde.org>
3 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
4 
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12 
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 Lesser General Public License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
21 #include "surface.h"
22 #include "surface_p.h"
23 
24 #include "buffer.h"
25 
26 #include "client.h"
27 #include "compositor.h"
28 #include "idle_inhibit_v1.h"
29 #include "idle_inhibit_v1_p.h"
30 #include "layer_shell_v1_p.h"
31 #include "pointer_constraints_v1.h"
32 #include "pointer_constraints_v1_p.h"
33 #include "presentation_time.h"
34 #include "region.h"
35 #include "subcompositor.h"
36 #include "subsurface_p.h"
37 #include "viewporter_p.h"
38 #include "wl_output_p.h"
39 #include "xdg_shell_surface_p.h"
40 
41 #include <QListIterator>
42 
43 #include <algorithm>
44 #include <cassert>
45 #include <wayland-server.h>
46 #include <wayland-viewporter-server-protocol.h>
47 
48 namespace Wrapland::Server
49 {
50 
Private(Client * client,uint32_t version,uint32_t id,Surface * q)51 Surface::Private::Private(Client* client, uint32_t version, uint32_t id, Surface* q)
52     : Wayland::Resource<Surface>(client, version, id, &wl_surface_interface, &s_interface, q)
53     , q_ptr{q}
54 {
55 }
56 
~Private()57 Surface::Private::~Private()
58 {
59     // Copy all existing callbacks to new list and clear existing lists.
60     // The wl_resource_destroy on the callback resource goes into destroyFrameCallback which would
61     // modify the list we are iterating on.
62     std::vector<wl_resource*> callbacksToDestroy;
63     callbacksToDestroy.insert(
64         callbacksToDestroy.end(), current.callbacks.begin(), current.callbacks.end());
65     current.callbacks.clear();
66 
67     callbacksToDestroy.insert(
68         callbacksToDestroy.end(), pending.callbacks.begin(), pending.callbacks.end());
69     pending.callbacks.clear();
70 
71     for (auto callback : callbacksToDestroy) {
72         wl_resource_destroy(callback);
73     }
74 
75     if (subsurface) {
76         subsurface->d_ptr->surface = nullptr;
77         subsurface = nullptr;
78     }
79 
80     for (auto child : current.pub.children) {
81         child->d_ptr->parent = nullptr;
82     }
83     for (auto child : pending.pub.children) {
84         child->d_ptr->parent = nullptr;
85     }
86 }
87 
addChild(Subsurface * child)88 void Surface::Private::addChild(Subsurface* child)
89 {
90     if (subsurface) {
91         // We add it indiscriminately to the cached state. If the subsurface state is synchronized
92         // on next parent commit it is added, if not it will be ignored here.
93         subsurface->d_ptr->cached.pub.children.push_back(child);
94     }
95     pending.pub.children.push_back(child);
96     pending.pub.updates |= surface_change::children;
97 
98     QObject::connect(child->surface(),
99                      &Surface::subsurfaceTreeChanged,
100                      handle(),
101                      &Surface::subsurfaceTreeChanged);
102 }
103 
removeChild(Subsurface * child)104 void Surface::Private::removeChild(Subsurface* child)
105 {
106     if (subsurface) {
107         auto& cached = subsurface->d_ptr->cached;
108         cached.pub.children.erase(
109             std::remove(cached.pub.children.begin(), cached.pub.children.end(), child),
110             cached.pub.children.end());
111     }
112     pending.pub.children.erase(
113         std::remove(pending.pub.children.begin(), pending.pub.children.end(), child),
114         pending.pub.children.end());
115     current.pub.children.erase(
116         std::remove(current.pub.children.begin(), current.pub.children.end(), child),
117         current.pub.children.end());
118 
119     // TODO(romangg): only emit that if the child was mapped.
120     Q_EMIT handle()->subsurfaceTreeChanged();
121 
122     if (child->surface()) {
123         QObject::disconnect(child->surface(),
124                             &Surface::subsurfaceTreeChanged,
125                             handle(),
126                             &Surface::subsurfaceTreeChanged);
127     }
128 }
129 
raiseChild(Subsurface * subsurface,Surface * sibling)130 bool Surface::Private::raiseChild(Subsurface* subsurface, Surface* sibling)
131 {
132     auto it = std::find(pending.pub.children.begin(), pending.pub.children.end(), subsurface);
133 
134     if (it == pending.pub.children.end()) {
135         return false;
136     }
137 
138     if (pending.pub.children.size() == 1) {
139         // Nothing to do.
140         return true;
141     }
142 
143     if (sibling == handle()) {
144         // It's sibling to the parent, so needs to become last item.
145         pending.pub.children.erase(it);
146         pending.pub.children.push_back(subsurface);
147         pending.pub.updates |= surface_change::children;
148         return true;
149     }
150 
151     if (!sibling->subsurface()) {
152         // Not a sub surface.
153         return false;
154     }
155 
156     auto siblingIt = std::find(
157         pending.pub.children.begin(), pending.pub.children.end(), sibling->subsurface());
158     if (siblingIt == pending.pub.children.end() || siblingIt == it) {
159         // Not a sibling.
160         return false;
161     }
162 
163     auto value = (*it);
164     pending.pub.children.erase(it);
165 
166     // Find the iterator again.
167     siblingIt = std::find(
168         pending.pub.children.begin(), pending.pub.children.end(), sibling->subsurface());
169     pending.pub.children.insert(++siblingIt, value);
170     pending.pub.updates |= surface_change::children;
171     return true;
172 }
173 
lowerChild(Subsurface * subsurface,Surface * sibling)174 bool Surface::Private::lowerChild(Subsurface* subsurface, Surface* sibling)
175 {
176     auto it = std::find(pending.pub.children.begin(), pending.pub.children.end(), subsurface);
177     if (it == pending.pub.children.end()) {
178         return false;
179     }
180     if (pending.pub.children.size() == 1) {
181         // nothing to do
182         return true;
183     }
184     if (sibling == handle()) {
185         // it's to the parent, so needs to become first item
186         auto value = *it;
187         pending.pub.children.erase(it);
188         pending.pub.children.insert(pending.pub.children.begin(), value);
189         pending.pub.updates |= surface_change::children;
190         return true;
191     }
192     if (!sibling->subsurface()) {
193         // not a sub surface
194         return false;
195     }
196     auto siblingIt = std::find(
197         pending.pub.children.begin(), pending.pub.children.end(), sibling->subsurface());
198     if (siblingIt == pending.pub.children.end() || siblingIt == it) {
199         // not a sibling
200         return false;
201     }
202     auto value = (*it);
203     pending.pub.children.erase(it);
204     // find the iterator again
205     siblingIt = std::find(
206         pending.pub.children.begin(), pending.pub.children.end(), sibling->subsurface());
207     pending.pub.children.insert(siblingIt, value);
208     pending.pub.updates |= surface_change::children;
209     return true;
210 }
211 
setShadow(const QPointer<Shadow> & shadow)212 void Surface::Private::setShadow(const QPointer<Shadow>& shadow)
213 {
214     pending.pub.shadow = shadow;
215     pending.pub.updates |= surface_change::shadow;
216 }
217 
setBlur(const QPointer<Blur> & blur)218 void Surface::Private::setBlur(const QPointer<Blur>& blur)
219 {
220     pending.pub.blur = blur;
221     pending.pub.updates |= surface_change::blur;
222 }
223 
setSlide(const QPointer<Slide> & slide)224 void Surface::Private::setSlide(const QPointer<Slide>& slide)
225 {
226     pending.pub.slide = slide;
227     pending.pub.updates |= surface_change::slide;
228 }
229 
setContrast(const QPointer<Contrast> & contrast)230 void Surface::Private::setContrast(const QPointer<Contrast>& contrast)
231 {
232     pending.pub.contrast = contrast;
233     pending.pub.updates |= surface_change::contrast;
234 }
235 
setSourceRectangle(const QRectF & source)236 void Surface::Private::setSourceRectangle(const QRectF& source)
237 {
238     pending.pub.source_rectangle = source;
239     pending.pub.updates |= surface_change::source_rectangle;
240 }
241 
setDestinationSize(const QSize & dest)242 void Surface::Private::setDestinationSize(const QSize& dest)
243 {
244     pending.destinationSize = dest;
245     pending.destinationSizeIsSet = true;
246 }
247 
installViewport(Viewport * vp)248 void Surface::Private::installViewport(Viewport* vp)
249 {
250     Q_ASSERT(viewport.isNull());
251     viewport = QPointer<Viewport>(vp);
252     connect(viewport, &Viewport::destinationSizeSet, handle(), [this](const QSize& size) {
253         setDestinationSize(size);
254     });
255     connect(viewport, &Viewport::sourceRectangleSet, handle(), [this](const QRectF& rect) {
256         setSourceRectangle(rect);
257     });
258     connect(viewport, &Viewport::resourceDestroyed, handle(), [this] {
259         setDestinationSize(QSize());
260         setSourceRectangle(QRectF());
261     });
262 }
263 
addPresentationFeedback(PresentationFeedback * feedback) const264 void Surface::Private::addPresentationFeedback(PresentationFeedback* feedback) const
265 {
266     pending.feedbacks->add(feedback);
267 }
268 
installPointerConstraint(LockedPointerV1 * lock)269 void Surface::Private::installPointerConstraint(LockedPointerV1* lock)
270 {
271     Q_ASSERT(lockedPointer.isNull());
272     Q_ASSERT(confinedPointer.isNull());
273     lockedPointer = QPointer<LockedPointerV1>(lock);
274 
275     auto cleanUp = [this]() {
276         lockedPointer.clear();
277         disconnect(constrainsOneShotConnection);
278         constrainsOneShotConnection = QMetaObject::Connection();
279         disconnect(constrainsUnboundConnection);
280         constrainsUnboundConnection = QMetaObject::Connection();
281         Q_EMIT handle()->pointerConstraintsChanged();
282     };
283 
284     if (lock->lifeTime() == LockedPointerV1::LifeTime::OneShot) {
285         constrainsOneShotConnection
286             = QObject::connect(lock, &LockedPointerV1::lockedChanged, handle(), [this, cleanUp] {
287                   if (lockedPointer.isNull() || lockedPointer->isLocked()) {
288                       return;
289                   }
290                   cleanUp();
291               });
292     }
293     constrainsUnboundConnection
294         = QObject::connect(lock, &LockedPointerV1::resourceDestroyed, handle(), [this, cleanUp] {
295               if (lockedPointer.isNull()) {
296                   return;
297               }
298               cleanUp();
299           });
300     Q_EMIT handle()->pointerConstraintsChanged();
301 }
302 
installPointerConstraint(ConfinedPointerV1 * confinement)303 void Surface::Private::installPointerConstraint(ConfinedPointerV1* confinement)
304 {
305     Q_ASSERT(lockedPointer.isNull());
306     Q_ASSERT(confinedPointer.isNull());
307     confinedPointer = QPointer<ConfinedPointerV1>(confinement);
308 
309     auto cleanUp = [this]() {
310         confinedPointer.clear();
311         disconnect(constrainsOneShotConnection);
312         constrainsOneShotConnection = QMetaObject::Connection();
313         disconnect(constrainsUnboundConnection);
314         constrainsUnboundConnection = QMetaObject::Connection();
315         Q_EMIT handle()->pointerConstraintsChanged();
316     };
317 
318     if (confinement->lifeTime() == ConfinedPointerV1::LifeTime::OneShot) {
319         constrainsOneShotConnection = QObject::connect(
320             confinement, &ConfinedPointerV1::confinedChanged, handle(), [this, cleanUp] {
321                 if (confinedPointer.isNull() || confinedPointer->isConfined()) {
322                     return;
323                 }
324                 cleanUp();
325             });
326     }
327     constrainsUnboundConnection = QObject::connect(
328         confinement, &ConfinedPointerV1::resourceDestroyed, handle(), [this, cleanUp] {
329             if (confinedPointer.isNull()) {
330                 return;
331             }
332             cleanUp();
333         });
334     Q_EMIT handle()->pointerConstraintsChanged();
335 }
336 
installIdleInhibitor(IdleInhibitor * inhibitor)337 void Surface::Private::installIdleInhibitor(IdleInhibitor* inhibitor)
338 {
339     idleInhibitors << inhibitor;
340     QObject::connect(inhibitor, &IdleInhibitor::resourceDestroyed, handle(), [this, inhibitor] {
341         idleInhibitors.removeOne(inhibitor);
342         if (idleInhibitors.isEmpty()) {
343             Q_EMIT handle()->inhibitsIdleChanged();
344         }
345     });
346     if (idleInhibitors.count() == 1) {
347         Q_EMIT handle()->inhibitsIdleChanged();
348     }
349 }
350 
351 const struct wl_surface_interface Surface::Private::s_interface = {
352     destroyCallback,
353     attachCallback,
354     damageCallback,
355     frameCallback,
356     opaqueRegionCallback,
357     inputRegionCallback,
358     commitCallback,
359     bufferTransformCallback,
360     bufferScaleCallback,
361     damageBufferCallback,
362 };
363 
Surface(Client * client,uint32_t version,uint32_t id)364 Surface::Surface(Client* client, uint32_t version, uint32_t id)
365     : QObject(nullptr)
366     , d_ptr(new Private(client, version, id, this))
367 {
368 }
369 
state() const370 surface_state const& Surface::state() const
371 {
372     return d_ptr->current.pub;
373 }
374 
frameRendered(quint32 msec)375 void Surface::frameRendered(quint32 msec)
376 {
377     while (!d_ptr->current.callbacks.empty()) {
378         auto resource = d_ptr->current.callbacks.front();
379         d_ptr->current.callbacks.pop_front();
380         wl_callback_send_done(resource, msec);
381         wl_resource_destroy(resource);
382     }
383     for (auto& subsurface : d_ptr->current.pub.children) {
384         subsurface->d_ptr->surface->frameRendered(msec);
385     }
386 }
387 
has_role() const388 bool Surface::Private::has_role() const
389 {
390     auto const has_xdg_shell_role
391         = shellSurface && (shellSurface->d_ptr->toplevel || shellSurface->d_ptr->popup);
392     return has_xdg_shell_role || subsurface || layer_surface;
393 }
394 
soureRectangleIntegerCheck(const QSize & destinationSize,const QRectF & sourceRectangle) const395 void Surface::Private::soureRectangleIntegerCheck(const QSize& destinationSize,
396                                                   const QRectF& sourceRectangle) const
397 {
398     if (destinationSize.isValid()) {
399         // Source rectangle must be integer valued only when the destination size is not set.
400         return;
401     }
402     if (!sourceRectangle.isValid()) {
403         // When the source rectangle is unset there is no integer condition.
404         return;
405     }
406     Q_ASSERT(viewport);
407 
408     const double width = sourceRectangle.width();
409     const double height = sourceRectangle.height();
410 
411     if (!qFuzzyCompare(width, static_cast<int>(width))
412         || !qFuzzyCompare(height, static_cast<int>(height))) {
413         viewport->d_ptr->postError(WP_VIEWPORT_ERROR_BAD_SIZE,
414                                    "Source rectangle not integer valued");
415     }
416 }
417 
soureRectangleContainCheck(const Buffer * buffer,Output::Transform transform,qint32 scale,const QRectF & sourceRectangle) const418 void Surface::Private::soureRectangleContainCheck(const Buffer* buffer,
419                                                   Output::Transform transform,
420                                                   qint32 scale,
421                                                   const QRectF& sourceRectangle) const
422 {
423     if (!buffer || !viewport || !sourceRectangle.isValid()) {
424         return;
425     }
426     QSizeF bufferSize = buffer->size() / scale;
427 
428     if (transform == Output::Transform::Rotated90 || transform == Output::Transform::Rotated270
429         || transform == Output::Transform::Flipped90
430         || transform == Output::Transform::Flipped270) {
431         bufferSize.transpose();
432     }
433 
434     if (!QRectF(QPointF(), bufferSize).contains(sourceRectangle)) {
435         viewport->d_ptr->postError(WP_VIEWPORT_ERROR_OUT_OF_BUFFER,
436                                    "Source rectangle not contained in buffer");
437     }
438 }
439 
synced_child_update()440 void Surface::Private::synced_child_update()
441 {
442     current.pub.updates |= surface_change::children;
443 
444     if (subsurface && subsurface->isSynchronized() && subsurface->parentSurface()) {
445         subsurface->parentSurface()->d_ptr->synced_child_update();
446     }
447 }
448 
update_buffer(SurfaceState const & source,bool & resized)449 void Surface::Private::update_buffer(SurfaceState const& source, bool& resized)
450 {
451     if (!(source.pub.updates & surface_change::buffer)) {
452         // TODO(romangg): Should we set the pending damage even when no new buffer got attached?
453         current.pub.damage = {};
454         current.bufferDamage = {};
455         return;
456     }
457 
458     QSize oldSize;
459 
460     auto const was_mapped = current.pub.buffer != nullptr;
461     auto const now_mapped = source.pub.buffer != nullptr;
462 
463     if (was_mapped) {
464         oldSize = current.pub.buffer->size();
465     }
466 
467     current.pub.buffer = source.pub.buffer;
468 
469     if (was_mapped != now_mapped) {
470         current.pub.updates |= surface_change::mapped;
471     }
472 
473     if (!now_mapped) {
474         if (subsurface && subsurface->isSynchronized() && subsurface->parentSurface()) {
475             subsurface->parentSurface()->d_ptr->synced_child_update();
476         }
477         return;
478     }
479 
480     current.pub.buffer->setCommitted();
481 
482     current.pub.offset = source.pub.offset;
483     current.pub.damage = source.pub.damage;
484     current.bufferDamage = source.bufferDamage;
485 
486     auto const newSize = current.pub.buffer->size();
487     resized = newSize.isValid() && newSize != oldSize;
488 
489     if (current.pub.damage.isEmpty() && current.bufferDamage.isEmpty()) {
490         // No damage submitted yet for the new buffer.
491 
492         // TODO(romangg): Does this mean size is not change, i.e. return false always?
493         return;
494     }
495 
496     auto const surfaceSize = handle()->size();
497     auto const surfaceRegion = QRegion(0, 0, surfaceSize.width(), surfaceSize.height());
498     if (surfaceRegion.isEmpty()) {
499         return;
500     }
501 
502     auto bufferDamage = QRegion();
503 
504     if (!current.bufferDamage.isEmpty()) {
505         auto const tr = current.pub.transform;
506         auto const sc = current.pub.scale;
507 
508         using Tr = Output::Transform;
509         if (tr == Tr::Rotated90 || tr == Tr::Rotated270 || tr == Tr::Flipped90
510             || tr == Tr::Flipped270) {
511 
512             // Calculate transformed + scaled buffer damage.
513             for (const auto& rect : current.bufferDamage) {
514                 const auto add
515                     = QRegion(rect.x() / sc, rect.y() / sc, rect.height() / sc, rect.width() / sc);
516                 bufferDamage = bufferDamage.united(add);
517             }
518 
519         } else if (sc != 1) {
520 
521             // Calculate scaled buffer damage.
522             for (auto const& rect : current.bufferDamage) {
523                 auto const add
524                     = QRegion(rect.x() / sc, rect.y() / sc, rect.width() / sc, rect.height() / sc);
525                 bufferDamage = bufferDamage.united(add);
526             }
527 
528         } else {
529             bufferDamage = current.bufferDamage;
530         }
531     }
532 
533     current.pub.damage = surfaceRegion.intersected(current.pub.damage.united(bufferDamage));
534     trackedDamage = trackedDamage.united(current.pub.damage);
535 }
536 
copy_to_current(SurfaceState const & source,bool & resized)537 void Surface::Private::copy_to_current(SurfaceState const& source, bool& resized)
538 {
539     if (source.pub.updates & surface_change::children) {
540         current.pub.children = source.pub.children;
541     }
542     current.callbacks.insert(
543         current.callbacks.end(), source.callbacks.begin(), source.callbacks.end());
544     if (!current.callbacks.empty()) {
545         current.pub.updates |= surface_change::frame;
546     }
547 
548     if (source.pub.updates & surface_change::shadow) {
549         current.pub.shadow = source.pub.shadow;
550     }
551     if (source.pub.updates & surface_change::blur) {
552         current.pub.blur = source.pub.blur;
553     }
554     if (source.pub.updates & surface_change::contrast) {
555         current.pub.contrast = source.pub.contrast;
556     }
557     if (source.pub.updates & surface_change::slide) {
558         current.pub.slide = source.pub.slide;
559     }
560     if (source.pub.updates & surface_change::input) {
561         current.pub.input = source.pub.input;
562         current.pub.input_is_infinite = source.pub.input_is_infinite;
563     }
564     if (source.pub.updates & surface_change::opaque) {
565         current.pub.opaque = source.pub.opaque;
566     }
567     if (source.pub.updates & surface_change::scale) {
568         current.pub.scale = source.pub.scale;
569     }
570     if (source.pub.updates & surface_change::transform) {
571         current.pub.transform = source.pub.transform;
572     }
573 
574     if (source.destinationSizeIsSet) {
575         current.destinationSize = source.destinationSize;
576         resized = current.pub.buffer != nullptr;
577     }
578 
579     if (source.pub.updates & surface_change::source_rectangle) {
580         if (current.pub.buffer && !source.destinationSize.isValid()
581             && source.pub.source_rectangle.isValid()) {
582             // TODO(unknown author): We should make this dependent on the previous size being
583             //      different. But looking at above resized calculation when setting the buffer
584             //      we need to do fix this there as well (does not look at buffer transform
585             //      and destination size).
586             resized = true;
587         }
588         current.pub.source_rectangle = source.pub.source_rectangle;
589     }
590 }
591 
updateCurrentState(bool forceChildren)592 void Surface::Private::updateCurrentState(bool forceChildren)
593 {
594     updateCurrentState(pending, forceChildren);
595 }
596 
updateCurrentState(SurfaceState & source,bool forceChildren)597 void Surface::Private::updateCurrentState(SurfaceState& source, bool forceChildren)
598 {
599     auto const scaleFactorChanged
600         = (source.pub.updates & surface_change::scale) && (current.pub.scale != source.pub.scale);
601 
602     auto resized = false;
603     current.pub.updates = source.pub.updates;
604 
605     update_buffer(source, resized);
606     copy_to_current(source, resized);
607 
608     // Now check that source rectangle is (still) well defined.
609     soureRectangleIntegerCheck(current.destinationSize, current.pub.source_rectangle);
610     soureRectangleContainCheck(current.pub.buffer.get(),
611                                current.pub.transform,
612                                current.pub.scale,
613                                current.pub.source_rectangle);
614 
615     if (!lockedPointer.isNull()) {
616         lockedPointer->d_ptr->commit();
617     }
618     if (!confinedPointer.isNull()) {
619         confinedPointer->d_ptr->commit();
620     }
621 
622     if (scaleFactorChanged) {
623         resized = current.pub.buffer != nullptr;
624     }
625     if (resized) {
626         current.pub.updates |= surface_change::size;
627     }
628 
629     current.feedbacks = std::move(source.feedbacks);
630 
631     source = SurfaceState();
632     source.pub.children = current.pub.children;
633 
634     for (auto& subsurface : current.pub.children) {
635         subsurface->d_ptr->applyCached(forceChildren);
636     }
637 }
638 
commit()639 void Surface::Private::commit()
640 {
641     if (subsurface) {
642         // Surface has associated subsurface. We delegate committing to there.
643         subsurface->d_ptr->commit();
644         return;
645     }
646 
647     updateCurrentState(false);
648 
649     if (shellSurface) {
650         shellSurface->commit();
651     }
652 
653     if (layer_surface && !layer_surface->d_ptr->commit()) {
654         // Error on layer-surface commit.
655         return;
656     }
657 
658     Q_EMIT handle()->committed();
659 }
660 
damage(const QRect & rect)661 void Surface::Private::damage(const QRect& rect)
662 {
663     pending.pub.damage = pending.pub.damage.united(rect);
664 }
665 
damageBuffer(const QRect & rect)666 void Surface::Private::damageBuffer(const QRect& rect)
667 {
668     auto const has_buffer_update = pending.pub.updates & surface_change::buffer;
669     if (!has_buffer_update || (has_buffer_update && !pending.pub.buffer)) {
670         // TODO(unknown author): should we send an error?
671         return;
672     }
673     pending.bufferDamage = pending.bufferDamage.united(rect);
674 }
675 
setScale(qint32 scale)676 void Surface::Private::setScale(qint32 scale)
677 {
678     pending.pub.scale = scale;
679     pending.pub.updates |= surface_change::scale;
680 }
681 
setTransform(Output::Transform transform)682 void Surface::Private::setTransform(Output::Transform transform)
683 {
684     pending.pub.transform = transform;
685 }
686 
addFrameCallback(uint32_t callback)687 void Surface::Private::addFrameCallback(uint32_t callback)
688 {
689     // TODO(unknown author): put the frame callback in a separate class inheriting Resource.
690     wl_resource* frameCallback = client()->createResource(&wl_callback_interface, 1, callback);
691     if (!frameCallback) {
692         wl_resource_post_no_memory(resource());
693         return;
694     }
695     wl_resource_set_implementation(frameCallback, nullptr, this, destroyFrameCallback);
696     pending.callbacks.push_back(frameCallback);
697 }
698 
attachBuffer(wl_resource * wlBuffer,const QPoint & offset)699 void Surface::Private::attachBuffer(wl_resource* wlBuffer, const QPoint& offset)
700 {
701     had_buffer_attached = true;
702 
703     pending.pub.updates |= surface_change::buffer;
704     pending.pub.offset = offset;
705 
706     if (!wlBuffer) {
707         // Got a null buffer, deletes content in next frame.
708         pending.pub.buffer.reset();
709         pending.pub.damage = QRegion();
710         pending.bufferDamage = QRegion();
711         return;
712     }
713 
714     pending.pub.buffer = Buffer::make(wlBuffer, q_ptr);
715 
716     QObject::connect(pending.pub.buffer.get(),
717                      &Buffer::resourceDestroyed,
718                      handle(),
719                      [this, buffer = pending.pub.buffer.get()]() {
720                          if (pending.pub.buffer.get() == buffer) {
721                              pending.pub.buffer.reset();
722                          } else if (current.pub.buffer.get() == buffer) {
723                              current.pub.buffer.reset();
724                          } else if (subsurface
725                                     && subsurface->d_ptr->cached.pub.buffer.get() == buffer) {
726                              subsurface->d_ptr->cached.pub.buffer.reset();
727                          }
728                      });
729 }
730 
destroyFrameCallback(wl_resource * wlResource)731 void Surface::Private::destroyFrameCallback(wl_resource* wlResource)
732 {
733     auto priv = static_cast<Private*>(wl_resource_get_user_data(wlResource));
734 
735     auto removeCallback = [wlResource](SurfaceState& state) {
736         auto it = std::find(state.callbacks.begin(), state.callbacks.end(), wlResource);
737         if (it != state.callbacks.end()) {
738             state.callbacks.erase(it);
739         }
740     };
741 
742     removeCallback(priv->current);
743     removeCallback(priv->pending);
744     if (priv->subsurface) {
745         removeCallback(priv->subsurface->d_ptr->cached);
746     }
747 }
748 
attachCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * buffer,int32_t sx,int32_t sy)749 void Surface::Private::attachCallback([[maybe_unused]] wl_client* wlClient,
750                                       wl_resource* wlResource,
751                                       wl_resource* buffer,
752                                       int32_t sx,
753                                       int32_t sy)
754 {
755     auto priv = handle(wlResource)->d_ptr;
756     priv->attachBuffer(buffer, QPoint(sx, sy));
757 }
758 
damageCallback(wl_client * wlClient,wl_resource * wlResource,int32_t x,int32_t y,int32_t width,int32_t height)759 void Surface::Private::damageCallback([[maybe_unused]] wl_client* wlClient,
760                                       wl_resource* wlResource,
761                                       int32_t x,
762                                       int32_t y,
763                                       int32_t width,
764                                       int32_t height)
765 {
766     auto priv = handle(wlResource)->d_ptr;
767     priv->damage(QRect(x, y, width, height));
768 }
769 
damageBufferCallback(wl_client * wlClient,wl_resource * wlResource,int32_t x,int32_t y,int32_t width,int32_t height)770 void Surface::Private::damageBufferCallback([[maybe_unused]] wl_client* wlClient,
771                                             wl_resource* wlResource,
772                                             int32_t x,
773                                             int32_t y,
774                                             int32_t width,
775                                             int32_t height)
776 {
777     auto priv = handle(wlResource)->d_ptr;
778     priv->damageBuffer(QRect(x, y, width, height));
779 }
780 
frameCallback(wl_client * wlClient,wl_resource * wlResource,uint32_t callback)781 void Surface::Private::frameCallback([[maybe_unused]] wl_client* wlClient,
782                                      wl_resource* wlResource,
783                                      uint32_t callback)
784 {
785     auto priv = handle(wlResource)->d_ptr;
786     priv->addFrameCallback(callback);
787 }
788 
opaqueRegionCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlRegion)789 void Surface::Private::opaqueRegionCallback([[maybe_unused]] wl_client* wlClient,
790                                             wl_resource* wlResource,
791                                             wl_resource* wlRegion)
792 {
793     auto priv = handle(wlResource)->d_ptr;
794     auto region = wlRegion ? Wayland::Resource<Region>::handle(wlRegion) : nullptr;
795     priv->setOpaque(region ? region->region() : QRegion());
796 }
797 
setOpaque(const QRegion & region)798 void Surface::Private::setOpaque(const QRegion& region)
799 {
800     pending.pub.opaque = region;
801     pending.pub.updates |= surface_change::opaque;
802 }
803 
inputRegionCallback(wl_client * wlClient,wl_resource * wlResource,wl_resource * wlRegion)804 void Surface::Private::inputRegionCallback([[maybe_unused]] wl_client* wlClient,
805                                            wl_resource* wlResource,
806                                            wl_resource* wlRegion)
807 {
808     auto priv = handle(wlResource)->d_ptr;
809     auto region = wlRegion ? Wayland::Resource<Region>::handle(wlRegion) : nullptr;
810     priv->setInput(region ? region->region() : QRegion(), !region);
811 }
812 
setInput(const QRegion & region,bool isInfinite)813 void Surface::Private::setInput(const QRegion& region, bool isInfinite)
814 {
815     pending.pub.input_is_infinite = isInfinite;
816     pending.pub.input = region;
817     pending.pub.updates |= surface_change::input;
818 }
819 
commitCallback(wl_client * wlClient,wl_resource * wlResource)820 void Surface::Private::commitCallback([[maybe_unused]] wl_client* wlClient, wl_resource* wlResource)
821 {
822     auto priv = handle(wlResource)->d_ptr;
823     priv->commit();
824 }
825 
bufferTransformCallback(wl_client * wlClient,wl_resource * wlResource,int32_t transform)826 void Surface::Private::bufferTransformCallback([[maybe_unused]] wl_client* wlClient,
827                                                wl_resource* wlResource,
828                                                int32_t transform)
829 {
830     auto priv = handle(wlResource)->d_ptr;
831     priv->setTransform(Output::Transform(transform));
832 }
833 
bufferScaleCallback(wl_client * wlClient,wl_resource * wlResource,int32_t scale)834 void Surface::Private::bufferScaleCallback([[maybe_unused]] wl_client* wlClient,
835                                            wl_resource* wlResource,
836                                            int32_t scale)
837 {
838     auto priv = handle(wlResource)->d_ptr;
839     priv->setScale(scale);
840 }
841 
subsurface() const842 Subsurface* Surface::subsurface() const
843 {
844     return d_ptr->subsurface;
845 }
846 
size() const847 QSize Surface::size() const
848 {
849     if (!d_ptr->current.pub.buffer) {
850         return QSize();
851     }
852     if (d_ptr->current.destinationSize.isValid()) {
853         return d_ptr->current.destinationSize;
854     }
855     if (d_ptr->current.pub.source_rectangle.isValid()) {
856         return d_ptr->current.pub.source_rectangle.size().toSize();
857     }
858     // TODO(unknown author): Apply transform to the buffer size.
859     return d_ptr->current.pub.buffer->size() / d_ptr->current.pub.scale;
860 }
861 
expanse() const862 QRect Surface::expanse() const
863 {
864     auto ret = QRect(QPoint(), size());
865 
866     for (auto const& sub : state().children) {
867         ret = ret.united(sub->surface()->expanse().translated(sub->position()));
868     }
869     return ret;
870 }
871 
isMapped() const872 bool Surface::isMapped() const
873 {
874     if (d_ptr->subsurface) {
875         // From the spec: "A sub-surface becomes mapped, when a non-NULL wl_buffer is applied and
876         // the parent surface is mapped."
877         return d_ptr->current.pub.buffer && d_ptr->subsurface->parentSurface()
878             && d_ptr->subsurface->parentSurface()->isMapped();
879     }
880     return d_ptr->current.pub.buffer != nullptr;
881 }
882 
trackedDamage() const883 QRegion Surface::trackedDamage() const
884 {
885     return d_ptr->trackedDamage;
886 }
887 
resetTrackedDamage()888 void Surface::resetTrackedDamage()
889 {
890     d_ptr->trackedDamage = QRegion();
891 }
892 
outputs() const893 std::vector<WlOutput*> Surface::outputs() const
894 {
895     return d_ptr->outputs;
896 }
897 
setOutputs(std::vector<Output * > const & outputs)898 void Surface::setOutputs(std::vector<Output*> const& outputs)
899 {
900     std::vector<WlOutput*> wayland_outputs;
901     wayland_outputs.reserve(outputs.size());
902 
903     for (auto const& output : outputs) {
904         wayland_outputs.push_back(output->wayland_output());
905     }
906     setOutputs(wayland_outputs);
907 }
908 
setOutputs(std::vector<WlOutput * > const & outputs)909 void Surface::setOutputs(std::vector<WlOutput*> const& outputs)
910 {
911     auto removed_outputs = d_ptr->outputs;
912 
913     for (auto stays : outputs) {
914         removed_outputs.erase(std::remove(removed_outputs.begin(), removed_outputs.end(), stays),
915                               removed_outputs.end());
916     }
917 
918     for (auto output : removed_outputs) {
919         auto const binds = output->d_ptr->getBinds(d_ptr->client()->handle());
920         for (auto bind : binds) {
921             d_ptr->send<wl_surface_send_leave>(bind->resource());
922         }
923         disconnect(d_ptr->outputDestroyedConnections.take(output));
924     }
925 
926     auto added_outputs = outputs;
927     for (auto keeping : d_ptr->outputs) {
928         added_outputs.erase(std::remove(added_outputs.begin(), added_outputs.end(), keeping),
929                             added_outputs.end());
930     }
931 
932     for (auto output : added_outputs) {
933         auto const binds = output->d_ptr->getBinds(d_ptr->client()->handle());
934         for (auto bind : binds) {
935             d_ptr->send<wl_surface_send_enter>(bind->resource());
936         }
937 
938         d_ptr->outputDestroyedConnections[output]
939             = connect(output, &WlOutput::removed, this, [this, output] {
940                   auto outputs = d_ptr->outputs;
941                   bool removed = false;
942                   outputs.erase(std::remove_if(outputs.begin(),
943                                                outputs.end(),
944                                                [&removed, output](WlOutput* out) {
945                                                    if (output == out) {
946                                                        removed = true;
947                                                        return true;
948                                                    }
949                                                    return false;
950                                                }),
951                                 outputs.end());
952 
953                   if (removed) {
954                       setOutputs(outputs);
955                   }
956               });
957     }
958     // TODO(unknown author): send enter when the client binds the Output another time
959 
960     d_ptr->outputs = outputs;
961 }
962 
lockedPointer() const963 QPointer<LockedPointerV1> Surface::lockedPointer() const
964 {
965     return d_ptr->lockedPointer;
966 }
967 
confinedPointer() const968 QPointer<ConfinedPointerV1> Surface::confinedPointer() const
969 {
970     return d_ptr->confinedPointer;
971 }
972 
inhibitsIdle() const973 bool Surface::inhibitsIdle() const
974 {
975     return !d_ptr->idleInhibitors.isEmpty();
976 }
977 
setDataProxy(Surface * surface)978 void Surface::setDataProxy(Surface* surface)
979 {
980     d_ptr->dataProxy = surface;
981 }
982 
dataProxy() const983 Surface* Surface::dataProxy() const
984 {
985     return d_ptr->dataProxy;
986 }
987 
client() const988 Client* Surface::client() const
989 {
990     return d_ptr->client()->handle();
991 }
992 
resource() const993 wl_resource* Surface::resource() const
994 {
995     return d_ptr->resource();
996 }
997 
id() const998 uint32_t Surface::id() const
999 {
1000     return d_ptr->id();
1001 }
1002 
lockPresentation(Output * output)1003 uint32_t Surface::lockPresentation(Output* output)
1004 {
1005     if (!d_ptr->current.feedbacks) {
1006         return 0;
1007     }
1008     if (!d_ptr->current.feedbacks->active()) {
1009         return 0;
1010     }
1011     d_ptr->current.feedbacks->setOutput(output);
1012 
1013     if (++d_ptr->feedbackId == 0) {
1014         d_ptr->feedbackId++;
1015     }
1016 
1017     d_ptr->waitingFeedbacks[d_ptr->feedbackId] = std::move(d_ptr->current.feedbacks);
1018     return d_ptr->feedbackId;
1019 }
1020 
presentationFeedback(uint32_t presentationId,uint32_t tvSecHi,uint32_t tvSecLo,uint32_t tvNsec,uint32_t refresh,uint32_t seqHi,uint32_t seqLo,PresentationKinds kinds)1021 void Surface::presentationFeedback(uint32_t presentationId,
1022                                    uint32_t tvSecHi,
1023                                    uint32_t tvSecLo,
1024                                    uint32_t tvNsec,
1025                                    uint32_t refresh,
1026                                    uint32_t seqHi,
1027                                    uint32_t seqLo,
1028                                    PresentationKinds kinds)
1029 {
1030     auto feedbacksIt = d_ptr->waitingFeedbacks.find(presentationId);
1031     assert(feedbacksIt != d_ptr->waitingFeedbacks.end());
1032 
1033     feedbacksIt->second->presented(tvSecHi, tvSecLo, tvNsec, refresh, seqHi, seqLo, kinds);
1034     d_ptr->waitingFeedbacks.erase(feedbacksIt);
1035 }
1036 
presentationDiscarded(uint32_t presentationId)1037 void Surface::presentationDiscarded(uint32_t presentationId)
1038 {
1039     auto feedbacksIt = d_ptr->waitingFeedbacks.find(presentationId);
1040     assert(feedbacksIt != d_ptr->waitingFeedbacks.end());
1041     d_ptr->waitingFeedbacks.erase(feedbacksIt);
1042 }
1043 
Feedbacks(QObject * parent)1044 Feedbacks::Feedbacks(QObject* parent)
1045     : QObject(parent)
1046 {
1047 }
1048 
~Feedbacks()1049 Feedbacks::~Feedbacks()
1050 {
1051     discard();
1052 }
1053 
active()1054 bool Feedbacks::active()
1055 {
1056     return !m_feedbacks.empty();
1057 }
1058 
add(PresentationFeedback * feedback)1059 void Feedbacks::add(PresentationFeedback* feedback)
1060 {
1061     connect(feedback, &PresentationFeedback::resourceDestroyed, this, [this, feedback] {
1062         m_feedbacks.erase(std::find(m_feedbacks.begin(), m_feedbacks.end(), feedback));
1063     });
1064     m_feedbacks.push_back(feedback);
1065 }
1066 
setOutput(Output * output)1067 void Feedbacks::setOutput(Output* output)
1068 {
1069     assert(!m_output);
1070     m_output = output;
1071     QObject::connect(
1072         output->wayland_output(), &WlOutput::removed, this, &Feedbacks::handleOutputRemoval);
1073 }
1074 
handleOutputRemoval()1075 void Feedbacks::handleOutputRemoval()
1076 {
1077     assert(m_output);
1078     m_output = nullptr;
1079     discard();
1080 }
1081 
toKinds(Surface::PresentationKinds kinds)1082 PresentationFeedback::Kinds toKinds(Surface::PresentationKinds kinds)
1083 {
1084     using PresentationKind = Surface::PresentationKind;
1085     using FeedbackKind = PresentationFeedback::Kind;
1086 
1087     PresentationFeedback::Kinds ret;
1088     if (kinds.testFlag(PresentationKind::Vsync)) {
1089         ret |= FeedbackKind::Vsync;
1090     }
1091     if (kinds.testFlag(PresentationKind::HwClock)) {
1092         ret |= FeedbackKind::HwClock;
1093     }
1094     if (kinds.testFlag(PresentationKind::HwCompletion)) {
1095         ret |= FeedbackKind::HwCompletion;
1096     }
1097     if (kinds.testFlag(PresentationKind::ZeroCopy)) {
1098         ret |= FeedbackKind::ZeroCopy;
1099     }
1100     return ret;
1101 }
1102 
presented(uint32_t tvSecHi,uint32_t tvSecLo,uint32_t tvNsec,uint32_t refresh,uint32_t seqHi,uint32_t seqLo,Surface::PresentationKinds kinds)1103 void Feedbacks::presented(uint32_t tvSecHi,
1104                           uint32_t tvSecLo,
1105                           uint32_t tvNsec,
1106                           uint32_t refresh,
1107                           uint32_t seqHi,
1108                           uint32_t seqLo,
1109                           Surface::PresentationKinds kinds)
1110 {
1111     std::for_each(m_feedbacks.begin(), m_feedbacks.end(), [=](PresentationFeedback* fb) {
1112         fb->sync(m_output);
1113         fb->presented(tvSecHi, tvSecLo, tvNsec, refresh, seqHi, seqLo, toKinds(kinds));
1114         delete fb;
1115     });
1116     m_feedbacks.clear();
1117 }
1118 
discard()1119 void Feedbacks::discard()
1120 {
1121     std::for_each(m_feedbacks.begin(), m_feedbacks.end(), [=](PresentationFeedback* feedback) {
1122         feedback->discarded();
1123         delete feedback;
1124     });
1125     m_feedbacks.clear();
1126 }
1127 
1128 }
1129