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