1 /*
2     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "item.h"
8 #include "abstract_output.h"
9 #include "composite.h"
10 #include "main.h"
11 #include "platform.h"
12 #include "renderloop.h"
13 #include "screens.h"
14 #include "utils.h"
15 
16 namespace KWin
17 {
18 
Item(Item * parent)19 Item::Item(Item *parent)
20 {
21     setParentItem(parent);
22     connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Item::removeRepaints);
23 }
24 
~Item()25 Item::~Item()
26 {
27     setParentItem(nullptr);
28     for (const auto &dirty : qAsConst(m_repaints)) {
29         if (!dirty.isEmpty()) {
30             Compositor::self()->addRepaint(dirty);
31         }
32     }
33 }
34 
z() const35 int Item::z() const
36 {
37     return m_z;
38 }
39 
setZ(int z)40 void Item::setZ(int z)
41 {
42     if (m_z == z) {
43         return;
44     }
45     m_z = z;
46     if (m_parentItem) {
47         m_parentItem->markSortedChildItemsDirty();
48     }
49     scheduleRepaint(boundingRect());
50 }
51 
parentItem() const52 Item *Item::parentItem() const
53 {
54     return m_parentItem;
55 }
56 
setParentItem(Item * item)57 void Item::setParentItem(Item *item)
58 {
59     if (m_parentItem == item) {
60         return;
61     }
62     if (m_parentItem) {
63         m_parentItem->removeChild(this);
64     }
65     m_parentItem = item;
66     if (m_parentItem) {
67         m_parentItem->addChild(this);
68     }
69     updateEffectiveVisibility();
70 }
71 
addChild(Item * item)72 void Item::addChild(Item *item)
73 {
74     Q_ASSERT(!m_childItems.contains(item));
75 
76     m_childItems.append(item);
77     markSortedChildItemsDirty();
78 
79     updateBoundingRect();
80     scheduleRepaint(item->boundingRect().translated(item->position()));
81 }
82 
removeChild(Item * item)83 void Item::removeChild(Item *item)
84 {
85     Q_ASSERT(m_childItems.contains(item));
86     scheduleRepaint(item->boundingRect().translated(item->position()));
87 
88     m_childItems.removeOne(item);
89     markSortedChildItemsDirty();
90 
91     updateBoundingRect();
92 }
93 
childItems() const94 QList<Item *> Item::childItems() const
95 {
96     return m_childItems;
97 }
98 
position() const99 QPoint Item::position() const
100 {
101     return m_position;
102 }
103 
setPosition(const QPoint & point)104 void Item::setPosition(const QPoint &point)
105 {
106     if (m_position != point) {
107         scheduleRepaint(boundingRect());
108         m_position = point;
109         if (m_parentItem) {
110             m_parentItem->updateBoundingRect();
111         }
112         scheduleRepaint(boundingRect());
113         Q_EMIT positionChanged();
114     }
115 }
116 
size() const117 QSize Item::size() const
118 {
119     return m_size;
120 }
121 
setSize(const QSize & size)122 void Item::setSize(const QSize &size)
123 {
124     if (m_size != size) {
125         scheduleRepaint(rect());
126         m_size = size;
127         updateBoundingRect();
128         scheduleRepaint(rect());
129         discardQuads();
130         Q_EMIT sizeChanged();
131     }
132 }
133 
rect() const134 QRect Item::rect() const
135 {
136     return QRect(QPoint(0, 0), size());
137 }
138 
boundingRect() const139 QRect Item::boundingRect() const
140 {
141     return m_boundingRect;
142 }
143 
updateBoundingRect()144 void Item::updateBoundingRect()
145 {
146     QRect boundingRect = rect();
147     for (Item *item : qAsConst(m_childItems)) {
148         boundingRect |= item->boundingRect().translated(item->position());
149     }
150     if (m_boundingRect != boundingRect) {
151         m_boundingRect = boundingRect;
152         Q_EMIT boundingRectChanged();
153         if (m_parentItem) {
154             m_parentItem->updateBoundingRect();
155         }
156     }
157 }
158 
rootPosition() const159 QPoint Item::rootPosition() const
160 {
161     QPoint ret = position();
162 
163     Item *parent = parentItem();
164     while (parent) {
165         ret += parent->position();
166         parent = parent->parentItem();
167     }
168 
169     return ret;
170 }
171 
transform() const172 QMatrix4x4 Item::transform() const
173 {
174     return m_transform;
175 }
176 
setTransform(const QMatrix4x4 & transform)177 void Item::setTransform(const QMatrix4x4 &transform)
178 {
179     m_transform = transform;
180 }
181 
mapToGlobal(const QRegion & region) const182 QRegion Item::mapToGlobal(const QRegion &region) const
183 {
184     return region.translated(rootPosition());
185 }
186 
mapToGlobal(const QRect & rect) const187 QRect Item::mapToGlobal(const QRect &rect) const
188 {
189     return rect.translated(rootPosition());
190 }
191 
stackBefore(Item * sibling)192 void Item::stackBefore(Item *sibling)
193 {
194     if (Q_UNLIKELY(!sibling)) {
195         qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires a valid sibling";
196         return;
197     }
198     if (Q_UNLIKELY(!sibling->parentItem() || sibling->parentItem() != parentItem())) {
199         qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires items to be siblings";
200         return;
201     }
202     if (Q_UNLIKELY(sibling == this)) {
203         return;
204     }
205 
206     const int selfIndex = m_parentItem->m_childItems.indexOf(this);
207     const int siblingIndex = m_parentItem->m_childItems.indexOf(sibling);
208 
209     if (selfIndex == siblingIndex - 1) {
210         return;
211     }
212 
213     m_parentItem->m_childItems.move(selfIndex, selfIndex > siblingIndex ? siblingIndex : siblingIndex - 1);
214     markSortedChildItemsDirty();
215 
216     scheduleRepaint(boundingRect());
217     sibling->scheduleRepaint(sibling->boundingRect());
218 }
219 
stackAfter(Item * sibling)220 void Item::stackAfter(Item *sibling)
221 {
222     if (Q_UNLIKELY(!sibling)) {
223         qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires a valid sibling";
224         return;
225     }
226     if (Q_UNLIKELY(!sibling->parentItem() || sibling->parentItem() != parentItem())) {
227         qCDebug(KWIN_CORE) << Q_FUNC_INFO << "requires items to be siblings";
228         return;
229     }
230     if (Q_UNLIKELY(sibling == this)) {
231         return;
232     }
233 
234     const int selfIndex = m_parentItem->m_childItems.indexOf(this);
235     const int siblingIndex = m_parentItem->m_childItems.indexOf(sibling);
236 
237     if (selfIndex == siblingIndex + 1) {
238         return;
239     }
240 
241     m_parentItem->m_childItems.move(selfIndex, selfIndex > siblingIndex ? siblingIndex + 1 : siblingIndex);
242     markSortedChildItemsDirty();
243 
244     scheduleRepaint(boundingRect());
245     sibling->scheduleRepaint(sibling->boundingRect());
246 }
247 
scheduleRepaint(const QRegion & region)248 void Item::scheduleRepaint(const QRegion &region)
249 {
250     if (isVisible()) {
251         scheduleRepaintInternal(region);
252     }
253 }
254 
scheduleRepaintInternal(const QRegion & region)255 void Item::scheduleRepaintInternal(const QRegion &region)
256 {
257     const QRegion globalRegion = mapToGlobal(region);
258     if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
259         const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
260         for (const auto &output : outputs) {
261             const QRegion dirtyRegion = globalRegion & output->geometry();
262             if (!dirtyRegion.isEmpty()) {
263                 m_repaints[output] += dirtyRegion;
264                 output->renderLoop()->scheduleRepaint(this);
265             }
266         }
267     } else {
268         m_repaints[nullptr] += globalRegion;
269         kwinApp()->platform()->renderLoop()->scheduleRepaint(this);
270     }
271 }
272 
scheduleFrame()273 void Item::scheduleFrame()
274 {
275     if (!isVisible()) {
276         return;
277     }
278     if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
279         const QRect geometry = mapToGlobal(rect());
280         const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
281         for (const AbstractOutput *output : outputs) {
282             if (output->geometry().intersects(geometry)) {
283                 output->renderLoop()->scheduleRepaint(this);
284             }
285         }
286     } else {
287         kwinApp()->platform()->renderLoop()->scheduleRepaint(this);
288     }
289 }
290 
preprocess()291 void Item::preprocess()
292 {
293 }
294 
buildQuads() const295 WindowQuadList Item::buildQuads() const
296 {
297     return WindowQuadList();
298 }
299 
discardQuads()300 void Item::discardQuads()
301 {
302     m_quads.reset();
303 }
304 
quads() const305 WindowQuadList Item::quads() const
306 {
307     if (!m_quads.has_value()) {
308         m_quads = buildQuads();
309     }
310     return m_quads.value();
311 }
312 
repaints(AbstractOutput * output) const313 QRegion Item::repaints(AbstractOutput *output) const
314 {
315     return m_repaints.value(output, QRect(QPoint(0, 0), screens()->size()));
316 }
317 
resetRepaints(AbstractOutput * output)318 void Item::resetRepaints(AbstractOutput *output)
319 {
320     m_repaints.insert(output, QRegion());
321 }
322 
removeRepaints(AbstractOutput * output)323 void Item::removeRepaints(AbstractOutput *output)
324 {
325     m_repaints.remove(output);
326 }
327 
isVisible() const328 bool Item::isVisible() const
329 {
330     return m_effectiveVisible;
331 }
332 
setVisible(bool visible)333 void Item::setVisible(bool visible)
334 {
335     if (m_visible != visible) {
336         m_visible = visible;
337         updateEffectiveVisibility();
338     }
339 }
340 
computeEffectiveVisibility() const341 bool Item::computeEffectiveVisibility() const
342 {
343     return m_visible && (!m_parentItem || m_parentItem->isVisible());
344 }
345 
updateEffectiveVisibility()346 void Item::updateEffectiveVisibility()
347 {
348     const bool effectiveVisible = computeEffectiveVisibility();
349     if (m_effectiveVisible == effectiveVisible) {
350         return;
351     }
352 
353     m_effectiveVisible = effectiveVisible;
354     scheduleRepaintInternal(boundingRect());
355 
356     for (Item *childItem : qAsConst(m_childItems)) {
357         childItem->updateEffectiveVisibility();
358     }
359 }
360 
compareZ(const Item * a,const Item * b)361 static bool compareZ(const Item *a, const Item *b)
362 {
363     return a->z() < b->z();
364 }
365 
sortedChildItems() const366 QList<Item *> Item::sortedChildItems() const
367 {
368     if (!m_sortedChildItems.has_value()) {
369         QList<Item *> items = m_childItems;
370         std::stable_sort(items.begin(), items.end(), compareZ);
371         m_sortedChildItems = items;
372     }
373     return m_sortedChildItems.value();
374 }
375 
markSortedChildItemsDirty()376 void Item::markSortedChildItemsDirty()
377 {
378     m_sortedChildItems.reset();
379 }
380 
381 } // namespace KWin
382