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 ®ion) 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 ®ion)
249 {
250 if (isVisible()) {
251 scheduleRepaintInternal(region);
252 }
253 }
254
scheduleRepaintInternal(const QRegion & region)255 void Item::scheduleRepaintInternal(const QRegion ®ion)
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