1 /*
2     KWin - the KDE window manager
3     This file is part of the KDE project.
4 
5     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 /*
11  Design:
12 
13  When compositing is turned on, XComposite extension is used to redirect
14  drawing of windows to pixmaps and XDamage extension is used to get informed
15  about damage (changes) to window contents. This code is mostly in composite.cpp .
16 
17  Compositor::performCompositing() starts one painting pass. Painting is done
18  by painting the screen, which in turn paints every window. Painting can be affected
19  using effects, which are chained. E.g. painting a screen means that actually
20  paintScreen() of the first effect is called, which possibly does modifications
21  and calls next effect's paintScreen() and so on, until Scene::finalPaintScreen()
22  is called.
23 
24  There are 3 phases of every paint (not necessarily done together):
25  The pre-paint phase, the paint phase and the post-paint phase.
26 
27  The pre-paint phase is used to find out about how the painting will be actually
28  done (i.e. what the effects will do). For example when only a part of the screen
29  needs to be updated and no effect will do any transformation it is possible to use
30  an optimized paint function. How the painting will be done is controlled
31  by the mask argument, see PAINT_WINDOW_* and PAINT_SCREEN_* flags in scene.h .
32  For example an effect that decides to paint a normal windows as translucent
33  will need to modify the mask in its prePaintWindow() to include
34  the PAINT_WINDOW_TRANSLUCENT flag. The paintWindow() function will then get
35  the mask with this flag turned on and will also paint using transparency.
36 
37  The paint pass does the actual painting, based on the information collected
38  using the pre-paint pass. After running through the effects' paintScreen()
39  either paintGenericScreen() or optimized paintSimpleScreen() are called.
40  Those call paintWindow() on windows (not necessarily all), possibly using
41  clipping to optimize performance and calling paintWindow() first with only
42  PAINT_WINDOW_OPAQUE to paint the opaque parts and then later
43  with PAINT_WINDOW_TRANSLUCENT to paint the transparent parts. Function
44  paintWindow() again goes through effects' paintWindow() until
45  finalPaintWindow() is called, which calls the window's performPaint() to
46  do the actual painting.
47 
48  The post-paint can be used for cleanups and is also used for scheduling
49  repaints during the next painting pass for animations. Effects wanting to
50  repaint certain parts can manually damage them during post-paint and repaint
51  of these parts will be done during the next paint pass.
52 
53 */
54 
55 #include "scene.h"
56 #include "abstract_output.h"
57 #include "internal_client.h"
58 #include "platform.h"
59 #include "shadowitem.h"
60 #include "surfaceitem.h"
61 #include "unmanaged.h"
62 #include "waylandclient.h"
63 #include "windowitem.h"
64 #include "x11client.h"
65 
66 #include <QQuickWindow>
67 #include <QVector2D>
68 
69 #include "x11client.h"
70 #include "deleted.h"
71 #include "effects.h"
72 #include "renderloop.h"
73 #include "screens.h"
74 #include "shadow.h"
75 #include "wayland_server.h"
76 #include "composite.h"
77 #include <QtMath>
78 
79 namespace KWin
80 {
81 
82 //****************************************
83 // Scene
84 //****************************************
85 
Scene(QObject * parent)86 Scene::Scene(QObject *parent)
87     : QObject(parent)
88 {
89     connect(kwinApp()->platform(), &Platform::outputDisabled, this, &Scene::removeRepaints);
90 }
91 
~Scene()92 Scene::~Scene()
93 {
94     Q_ASSERT(m_windows.isEmpty());
95 }
96 
addRepaint(const QRegion & region)97 void Scene::addRepaint(const QRegion &region)
98 {
99     if (kwinApp()->platform()->isPerScreenRenderingEnabled()) {
100         const QVector<AbstractOutput *> outputs = kwinApp()->platform()->enabledOutputs();
101         for (const auto &output : outputs) {
102             const QRegion dirtyRegion = region & output->geometry();
103             if (!dirtyRegion.isEmpty()) {
104                 m_repaints[output] += dirtyRegion;
105                 output->renderLoop()->scheduleRepaint();
106             }
107         }
108     } else {
109         m_repaints[0] += region;
110         kwinApp()->platform()->renderLoop()->scheduleRepaint();
111     }
112 }
113 
repaints(AbstractOutput * output) const114 QRegion Scene::repaints(AbstractOutput *output) const
115 {
116     return m_repaints.value(output, infiniteRegion());
117 }
118 
resetRepaints(AbstractOutput * output)119 void Scene::resetRepaints(AbstractOutput *output)
120 {
121     m_repaints.insert(output, QRegion());
122 }
123 
removeRepaints(AbstractOutput * output)124 void Scene::removeRepaints(AbstractOutput *output)
125 {
126     m_repaints.remove(output);
127 }
128 
129 
createProjectionMatrix(const QRect & rect)130 QMatrix4x4 Scene::createProjectionMatrix(const QRect &rect)
131 {
132     // Create a perspective projection with a 60° field-of-view,
133     // and an aspect ratio of 1.0.
134     QMatrix4x4 ret;
135     ret.setToIdentity();
136     const float fovY   =   std::tan(qDegreesToRadians(60.0f) / 2);
137     const float aspect =    1.0f;
138     const float zNear  =    0.1f;
139     const float zFar   =  100.0f;
140 
141     const float yMax   =  zNear * fovY;
142     const float yMin   = -yMax;
143     const float xMin   =  yMin * aspect;
144     const float xMax   =  yMax * aspect;
145 
146     ret.frustum(xMin, xMax, yMin, yMax, zNear, zFar);
147 
148     const float scaleFactor = 1.1 * fovY / yMax;
149     ret.translate(xMin * scaleFactor, yMax * scaleFactor, -1.1);
150     ret.scale( (xMax - xMin) * scaleFactor / rect.width(),
151                              -(yMax - yMin) * scaleFactor / rect.height(),
152                               0.001);
153     ret.translate(-rect.x(), -rect.y());
154     return ret;
155 }
156 
paintScreen(AbstractOutput * output,const QList<Toplevel * > & toplevels)157 void Scene::paintScreen(AbstractOutput *output, const QList<Toplevel *> &toplevels)
158 {
159     createStackingOrder(toplevels);
160 
161     const QRect geo = output->geometry();
162     QRegion update = geo, repaint = geo, valid;
163 
164     paintScreen(geo, repaint, &update, &valid, output->renderLoop(), createProjectionMatrix(output->geometry()));
165     clearStackingOrder();
166 }
167 // returns mask and possibly modified region
paintScreen(const QRegion & damage,const QRegion & repaint,QRegion * updateRegion,QRegion * validRegion,RenderLoop * renderLoop,const QMatrix4x4 & projection)168 void Scene::paintScreen(const QRegion &damage, const QRegion &repaint,
169                         QRegion *updateRegion, QRegion *validRegion, RenderLoop *renderLoop,
170                         const QMatrix4x4 &projection)
171 {
172     const QSize &screenSize = screens()->size();
173     const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
174 
175     const std::chrono::milliseconds presentTime =
176             std::chrono::duration_cast<std::chrono::milliseconds>(renderLoop->nextPresentationTimestamp());
177 
178     if (Q_UNLIKELY(presentTime < m_expectedPresentTimestamp)) {
179         qCDebug(KWIN_CORE, "Provided presentation timestamp is invalid: %ld (current: %ld)",
180                 presentTime.count(), m_expectedPresentTimestamp.count());
181     } else {
182         m_expectedPresentTimestamp = presentTime;
183     }
184 
185     // preparation step
186     static_cast<EffectsHandlerImpl*>(effects)->startPaint();
187 
188     QRegion region = damage;
189 
190     auto screen = effects->findScreen(kwinApp()->platform()->enabledOutputs().indexOf(painted_screen));
191     ScreenPrePaintData pdata;
192     pdata.mask = (damage == displayRegion) ? 0 : PAINT_SCREEN_REGION;
193     pdata.paint = region;
194     pdata.screen = screen;
195 
196     effects->prePaintScreen(pdata, m_expectedPresentTimestamp);
197     region = pdata.paint;
198 
199     int mask = pdata.mask;
200     if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) {
201         // Region painting is not possible with transformations,
202         // because screen damage doesn't match transformed positions.
203         mask &= ~PAINT_SCREEN_REGION;
204         region = infiniteRegion();
205     } else if (mask & PAINT_SCREEN_REGION) {
206         // make sure not to go outside visible screen
207         region &= displayRegion;
208     } else {
209         // whole screen, not transformed, force region to be full
210         region = displayRegion;
211     }
212 
213     painted_region = region;
214     repaint_region = repaint;
215 
216     ScreenPaintData data(projection, screen);
217     effects->paintScreen(mask, region, data);
218 
219     Q_EMIT frameRendered();
220 
221     Q_FOREACH (Window *w, stacking_order) {
222         effects->postPaintWindow(effectWindow(w));
223     }
224 
225     effects->postPaintScreen();
226 
227     // make sure not to go outside of the screen area
228     *updateRegion = damaged_region;
229     *validRegion = (region | painted_region) & displayRegion;
230 
231     repaint_region = QRegion();
232     damaged_region = QRegion();
233 
234     m_paintScreenCount = 0;
235 }
236 
237 // the function that'll be eventually called by paintScreen() above
finalPaintScreen(int mask,const QRegion & region,ScreenPaintData & data)238 void Scene::finalPaintScreen(int mask, const QRegion &region, ScreenPaintData& data)
239 {
240     m_paintScreenCount++;
241     if (mask & (PAINT_SCREEN_TRANSFORMED | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS))
242         paintGenericScreen(mask, data);
243     else
244         paintSimpleScreen(mask, region);
245 }
246 
resetRepaintsHelper(Item * item,AbstractOutput * output)247 static void resetRepaintsHelper(Item *item, AbstractOutput *output)
248 {
249     item->resetRepaints(output);
250 
251     const auto childItems = item->childItems();
252     for (Item *childItem : childItems) {
253         resetRepaintsHelper(childItem, output);
254     }
255 }
256 
257 // The generic painting code that can handle even transformations.
258 // It simply paints bottom-to-top.
paintGenericScreen(int orig_mask,const ScreenPaintData &)259 void Scene::paintGenericScreen(int orig_mask, const ScreenPaintData &)
260 {
261     QVector<Phase2Data> phase2;
262     phase2.reserve(stacking_order.size());
263     Q_FOREACH (Window * w, stacking_order) { // bottom to top
264         // Reset the repaint_region.
265         // This has to be done here because many effects schedule a repaint for
266         // the next frame within Effects::prePaintWindow.
267         resetRepaintsHelper(w->windowItem(), painted_screen);
268 
269         WindowPrePaintData data;
270         data.mask = orig_mask | (w->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
271         w->resetPaintingEnabled();
272         data.paint = infiniteRegion(); // no clipping, so doesn't really matter
273         data.clip = QRegion();
274         // preparation step
275         effects->prePaintWindow(effectWindow(w), data, m_expectedPresentTimestamp);
276         if (!w->isPaintingEnabled()) {
277             continue;
278         }
279         phase2.append({w, infiniteRegion(), data.clip, data.mask,});
280     }
281 
282     damaged_region = QRegion(QRect {{}, screens()->size()});
283     if (m_paintScreenCount == 1) {
284         aboutToStartPainting(painted_screen, damaged_region);
285 
286         if (orig_mask & PAINT_SCREEN_BACKGROUND_FIRST) {
287             paintBackground(infiniteRegion());
288         }
289     }
290 
291     if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) {
292         paintBackground(infiniteRegion());
293     }
294     Q_FOREACH (const Phase2Data & d, phase2) {
295         paintWindow(d.window, d.mask, d.region);
296     }
297 }
298 
accumulateRepaints(Item * item,AbstractOutput * output,QRegion * repaints)299 static void accumulateRepaints(Item *item, AbstractOutput *output, QRegion *repaints)
300 {
301     *repaints += item->repaints(output);
302     item->resetRepaints(output);
303 
304     const auto childItems = item->childItems();
305     for (Item *childItem : childItems) {
306         accumulateRepaints(childItem, output, repaints);
307     }
308 }
309 
310 // The optimized case without any transformations at all.
311 // It can paint only the requested region and can use clipping
312 // to reduce painting and improve performance.
paintSimpleScreen(int orig_mask,const QRegion & region)313 void Scene::paintSimpleScreen(int orig_mask, const QRegion &region)
314 {
315     Q_ASSERT((orig_mask & (PAINT_SCREEN_TRANSFORMED
316                          | PAINT_SCREEN_WITH_TRANSFORMED_WINDOWS)) == 0);
317     QVector<Phase2Data> phase2data;
318     phase2data.reserve(stacking_order.size());
319 
320     QRegion dirtyArea = region;
321     bool opaqueFullscreen = false;
322 
323     // Traverse the scene windows from bottom to top.
324     for (int i = 0; i < stacking_order.count(); ++i) {
325         Window *window = stacking_order[i];
326         Toplevel *toplevel = window->window();
327         WindowPrePaintData data;
328         data.mask = orig_mask | (window->isOpaque() ? PAINT_WINDOW_OPAQUE : PAINT_WINDOW_TRANSLUCENT);
329         window->resetPaintingEnabled();
330         data.paint = region;
331         accumulateRepaints(window->windowItem(), painted_screen, &data.paint);
332 
333         // Clip out the decoration for opaque windows; the decoration is drawn in the second pass
334         opaqueFullscreen = false; // TODO: do we care about unmanged windows here (maybe input windows?)
335         AbstractClient *client = dynamic_cast<AbstractClient *>(toplevel);
336         if (window->isOpaque()) {
337             if (client) {
338                 opaqueFullscreen = client->isFullScreen();
339             }
340 
341             const SurfaceItem *surfaceItem = window->surfaceItem();
342             if (surfaceItem) {
343                 data.clip |= surfaceItem->mapToGlobal(surfaceItem->shape());
344             }
345         } else if (toplevel->hasAlpha() && toplevel->opacity() == 1.0) {
346             const SurfaceItem *surfaceItem = window->surfaceItem();
347             if (surfaceItem) {
348                 const QRegion shape = surfaceItem->shape();
349                 const QRegion opaque = surfaceItem->opaque();
350                 data.clip = surfaceItem->mapToGlobal(shape & opaque);
351 
352                 if (opaque == shape) {
353                     data.mask = orig_mask | PAINT_WINDOW_OPAQUE;
354                 }
355             }
356         } else {
357             data.clip = QRegion();
358         }
359 
360         if (client && !client->decorationHasAlpha() && toplevel->opacity() == 1.0) {
361             data.clip |= window->decorationShape().translated(window->pos());
362         }
363 
364         // preparation step
365         effects->prePaintWindow(effectWindow(window), data, m_expectedPresentTimestamp);
366         if (!window->isPaintingEnabled()) {
367             continue;
368         }
369         dirtyArea |= data.paint;
370         // Schedule the window for painting
371         phase2data.append({ window, data.paint, data.clip, data.mask, });
372     }
373 
374     // Save the part of the repaint region that's exclusively rendered to
375     // bring a reused back buffer up to date. Then union the dirty region
376     // with the repaint region.
377     const QRegion repaintClip = repaint_region - dirtyArea;
378     dirtyArea |= repaint_region;
379 
380     const QSize &screenSize = screens()->size();
381     const QRegion displayRegion(0, 0, screenSize.width(), screenSize.height());
382     bool fullRepaint(dirtyArea == displayRegion); // spare some expensive region operations
383     if (!fullRepaint) {
384         extendPaintRegion(dirtyArea, opaqueFullscreen);
385         fullRepaint = (dirtyArea == displayRegion);
386     }
387 
388     QRegion allclips, upperTranslucentDamage;
389     upperTranslucentDamage = repaint_region;
390 
391     // This is the occlusion culling pass
392     for (int i = phase2data.count() - 1; i >= 0; --i) {
393         Phase2Data *data = &phase2data[i];
394 
395         if (fullRepaint) {
396             data->region = displayRegion;
397         } else {
398             data->region |= upperTranslucentDamage;
399         }
400 
401         // subtract the parts which will possibly been drawn as part of
402         // a higher opaque window
403         data->region -= allclips;
404 
405         // Here we rely on WindowPrePaintData::setTranslucent() to remove
406         // the clip if needed.
407         if (!data->clip.isEmpty() && !(data->mask & PAINT_WINDOW_TRANSLUCENT)) {
408             // clip away the opaque regions for all windows below this one
409             allclips |= data->clip;
410             // extend the translucent damage for windows below this by remaining (translucent) regions
411             if (!fullRepaint) {
412                 upperTranslucentDamage |= data->region - data->clip;
413             }
414         } else if (!fullRepaint) {
415             upperTranslucentDamage |= data->region;
416         }
417     }
418 
419     QRegion paintedArea;
420     // Fill any areas of the root window not covered by opaque windows
421     if (m_paintScreenCount == 1) {
422         aboutToStartPainting(painted_screen, dirtyArea);
423 
424         if (orig_mask & PAINT_SCREEN_BACKGROUND_FIRST) {
425             paintBackground(infiniteRegion());
426         }
427     }
428     if (!(orig_mask & PAINT_SCREEN_BACKGROUND_FIRST)) {
429         paintedArea = dirtyArea - allclips;
430         paintBackground(paintedArea);
431     }
432 
433     // Now walk the list bottom to top and draw the windows.
434     for (int i = 0; i < phase2data.count(); ++i) {
435         Phase2Data *data = &phase2data[i];
436 
437         // add all regions which have been drawn so far
438         paintedArea |= data->region;
439         data->region = paintedArea;
440 
441         paintWindow(data->window, data->mask, data->region);
442     }
443 
444     if (fullRepaint) {
445         painted_region = displayRegion;
446         damaged_region = displayRegion - repaintClip;
447     } else {
448         painted_region |= paintedArea;
449 
450         // Clip the repainted region from the damaged region.
451         // It's important that we don't add the union of the damaged region
452         // and the repainted region to the damage history. Otherwise the
453         // repaint region will grow with every frame until it eventually
454         // covers the whole back buffer, at which point we're always doing
455         // full repaints.
456         damaged_region = paintedArea - repaintClip;
457     }
458 }
459 
addToplevel(Toplevel * c)460 void Scene::addToplevel(Toplevel *c)
461 {
462     Q_ASSERT(!m_windows.contains(c));
463     Scene::Window *w = createWindow(c);
464     m_windows[ c ] = w;
465 
466     connect(c, &Toplevel::windowClosed, this, &Scene::windowClosed);
467 
468     c->effectWindow()->setSceneWindow(w);
469 }
470 
removeToplevel(Toplevel * toplevel)471 void Scene::removeToplevel(Toplevel *toplevel)
472 {
473     Q_ASSERT(m_windows.contains(toplevel));
474     delete m_windows.take(toplevel);
475     toplevel->effectWindow()->setSceneWindow(nullptr);
476 }
477 
windowClosed(Toplevel * toplevel,Deleted * deleted)478 void Scene::windowClosed(Toplevel *toplevel, Deleted *deleted)
479 {
480     if (!deleted) {
481         removeToplevel(toplevel);
482         return;
483     }
484 
485     Q_ASSERT(m_windows.contains(toplevel));
486     Window *window = m_windows.take(toplevel);
487     window->updateToplevel(deleted);
488     m_windows[deleted] = window;
489 }
490 
createStackingOrder(const QList<Toplevel * > & toplevels)491 void Scene::createStackingOrder(const QList<Toplevel *> &toplevels)
492 {
493     // TODO: cache the stacking_order in case it has not changed
494     Q_FOREACH (Toplevel *c, toplevels) {
495         Q_ASSERT(m_windows.contains(c));
496         stacking_order.append(m_windows[ c ]);
497     }
498 }
499 
clearStackingOrder()500 void Scene::clearStackingOrder()
501 {
502     stacking_order.clear();
503 }
504 
paintWindow(Window * w,int mask,const QRegion & _region)505 void Scene::paintWindow(Window* w, int mask, const QRegion &_region)
506 {
507     // no painting outside visible screen (and no transformations)
508     const QRegion region = _region & QRect({0, 0}, screens()->size());
509     if (region.isEmpty())  // completely clipped
510         return;
511     if (w->window()->isDeleted() && w->window()->skipsCloseAnimation()) {
512         // should not get painted
513         return;
514     }
515 
516     WindowPaintData data(w->window()->effectWindow(), screenProjectionMatrix());
517     effects->paintWindow(effectWindow(w), mask, region, data);
518 }
519 
paintDesktop(int desktop,int mask,const QRegion & region,ScreenPaintData & data)520 void Scene::paintDesktop(int desktop, int mask, const QRegion &region, ScreenPaintData &data)
521 {
522     static_cast<EffectsHandlerImpl*>(effects)->paintDesktop(desktop, mask, region, data);
523 }
524 
aboutToStartPainting(AbstractOutput * output,const QRegion & damage)525 void Scene::aboutToStartPainting(AbstractOutput *output, const QRegion &damage)
526 {
527     Q_UNUSED(output)
528     Q_UNUSED(damage)
529 }
530 
531 // the function that'll be eventually called by paintWindow() above
finalPaintWindow(EffectWindowImpl * w,int mask,const QRegion & region,WindowPaintData & data)532 void Scene::finalPaintWindow(EffectWindowImpl* w, int mask, const QRegion &region, WindowPaintData& data)
533 {
534     effects->drawWindow(w, mask, region, data);
535 }
536 
537 // will be eventually called from drawWindow()
finalDrawWindow(EffectWindowImpl * w,int mask,const QRegion & region,WindowPaintData & data)538 void Scene::finalDrawWindow(EffectWindowImpl* w, int mask, const QRegion &region, WindowPaintData& data)
539 {
540     if (waylandServer() && waylandServer()->isScreenLocked() && !w->window()->isLockScreen() && !w->window()->isInputMethod()) {
541         return;
542     }
543     w->sceneWindow()->performPaint(mask, region, data);
544 }
545 
extendPaintRegion(QRegion & region,bool opaqueFullscreen)546 void Scene::extendPaintRegion(QRegion &region, bool opaqueFullscreen)
547 {
548     Q_UNUSED(region);
549     Q_UNUSED(opaqueFullscreen);
550 }
551 
makeOpenGLContextCurrent()552 bool Scene::makeOpenGLContextCurrent()
553 {
554     return false;
555 }
556 
doneOpenGLContextCurrent()557 void Scene::doneOpenGLContextCurrent()
558 {
559 }
560 
supportsNativeFence() const561 bool Scene::supportsNativeFence() const
562 {
563     return false;
564 }
565 
screenProjectionMatrix() const566 QMatrix4x4 Scene::screenProjectionMatrix() const
567 {
568     return QMatrix4x4();
569 }
570 
scenePainter() const571 QPainter *Scene::scenePainter() const
572 {
573     return nullptr;
574 }
575 
qpainterRenderBuffer(AbstractOutput * output) const576 QImage *Scene::qpainterRenderBuffer(AbstractOutput *output) const
577 {
578     Q_UNUSED(output)
579     return nullptr;
580 }
581 
openGLPlatformInterfaceExtensions() const582 QVector<QByteArray> Scene::openGLPlatformInterfaceExtensions() const
583 {
584     return QVector<QByteArray>{};
585 }
586 
createPlatformSurfaceTextureInternal(SurfacePixmapInternal * pixmap)587 PlatformSurfaceTexture *Scene::createPlatformSurfaceTextureInternal(SurfacePixmapInternal *pixmap)
588 {
589     Q_UNUSED(pixmap)
590     return nullptr;
591 }
592 
createPlatformSurfaceTextureX11(SurfacePixmapX11 * pixmap)593 PlatformSurfaceTexture *Scene::createPlatformSurfaceTextureX11(SurfacePixmapX11 *pixmap)
594 {
595     Q_UNUSED(pixmap)
596     return nullptr;
597 }
598 
createPlatformSurfaceTextureWayland(SurfacePixmapWayland * pixmap)599 PlatformSurfaceTexture *Scene::createPlatformSurfaceTextureWayland(SurfacePixmapWayland *pixmap)
600 {
601     Q_UNUSED(pixmap)
602     return nullptr;
603 }
604 
605 //****************************************
606 // Scene::Window
607 //****************************************
608 
Window(Toplevel * client,QObject * parent)609 Scene::Window::Window(Toplevel *client, QObject *parent)
610     : QObject(parent)
611     , toplevel(client)
612     , disable_painting(0)
613 {
614     if (qobject_cast<WaylandClient *>(client)) {
615         m_windowItem.reset(new WindowItemWayland(toplevel));
616     } else if (qobject_cast<X11Client *>(client) || qobject_cast<Unmanaged *>(client)) {
617         m_windowItem.reset(new WindowItemX11(toplevel));
618     } else if (qobject_cast<InternalClient *>(client)) {
619         m_windowItem.reset(new WindowItemInternal(toplevel));
620     } else {
621         Q_UNREACHABLE();
622     }
623 
624     connect(toplevel, &Toplevel::frameGeometryChanged, this, &Window::updateWindowPosition);
625     updateWindowPosition();
626 }
627 
~Window()628 Scene::Window::~Window()
629 {
630 }
631 
updateToplevel(Deleted * deleted)632 void Scene::Window::updateToplevel(Deleted *deleted)
633 {
634     toplevel = deleted;
635 }
636 
referencePreviousPixmap()637 void Scene::Window::referencePreviousPixmap()
638 {
639     if (surfaceItem()) {
640         referencePreviousPixmap_helper(surfaceItem());
641     }
642 }
643 
referencePreviousPixmap_helper(SurfaceItem * item)644 void Scene::Window::referencePreviousPixmap_helper(SurfaceItem *item)
645 {
646     item->referencePreviousPixmap();
647 
648     const QList<Item *> children = item->childItems();
649     for (Item *child : children) {
650         referencePreviousPixmap_helper(static_cast<SurfaceItem *>(child));
651     }
652 }
653 
unreferencePreviousPixmap()654 void Scene::Window::unreferencePreviousPixmap()
655 {
656     if (surfaceItem()) {
657         unreferencePreviousPixmap_helper(surfaceItem());
658     }
659 }
660 
unreferencePreviousPixmap_helper(SurfaceItem * item)661 void Scene::Window::unreferencePreviousPixmap_helper(SurfaceItem *item)
662 {
663     item->unreferencePreviousPixmap();
664 
665     const QList<Item *> children = item->childItems();
666     for (Item *child : children) {
667         unreferencePreviousPixmap_helper(static_cast<SurfaceItem *>(child));
668     }
669 }
670 
decorationShape() const671 QRegion Scene::Window::decorationShape() const
672 {
673     const QRect decorationInnerRect = toplevel->rect() - toplevel->frameMargins();
674     return QRegion(toplevel->rect()) - decorationInnerRect;
675 }
676 
isVisible() const677 bool Scene::Window::isVisible() const
678 {
679     if (toplevel->isDeleted())
680         return false;
681     if (!toplevel->isOnCurrentDesktop())
682         return false;
683     if (!toplevel->isOnCurrentActivity())
684         return false;
685     if (AbstractClient *c = dynamic_cast<AbstractClient*>(toplevel))
686         return c->isShown(true);
687     return true; // Unmanaged is always visible
688 }
689 
isOpaque() const690 bool Scene::Window::isOpaque() const
691 {
692     return toplevel->opacity() == 1.0 && !toplevel->hasAlpha();
693 }
694 
isPaintingEnabled() const695 bool Scene::Window::isPaintingEnabled() const
696 {
697     return !disable_painting;
698 }
699 
resetPaintingEnabled()700 void Scene::Window::resetPaintingEnabled()
701 {
702     disable_painting = 0;
703     if (toplevel->isDeleted())
704         disable_painting |= PAINT_DISABLED_BY_DELETE;
705     if (static_cast<EffectsHandlerImpl*>(effects)->isDesktopRendering()) {
706         if (!toplevel->isOnDesktop(static_cast<EffectsHandlerImpl*>(effects)->currentRenderedDesktop())) {
707             disable_painting |= PAINT_DISABLED_BY_DESKTOP;
708         }
709     } else {
710         if (!toplevel->isOnCurrentDesktop())
711             disable_painting |= PAINT_DISABLED_BY_DESKTOP;
712     }
713     if (!toplevel->isOnCurrentActivity())
714         disable_painting |= PAINT_DISABLED_BY_ACTIVITY;
715     if (AbstractClient *c = dynamic_cast<AbstractClient*>(toplevel)) {
716         if (c->isMinimized())
717             disable_painting |= PAINT_DISABLED_BY_MINIMIZE;
718         if (c->isHiddenInternal()) {
719             disable_painting |= PAINT_DISABLED;
720         }
721     }
722 }
723 
enablePainting(int reason)724 void Scene::Window::enablePainting(int reason)
725 {
726     disable_painting &= ~reason;
727 }
728 
disablePainting(int reason)729 void Scene::Window::disablePainting(int reason)
730 {
731     disable_painting |= reason;
732 }
733 
windowItem() const734 WindowItem *Scene::Window::windowItem() const
735 {
736     return m_windowItem.data();
737 }
738 
surfaceItem() const739 SurfaceItem *Scene::Window::surfaceItem() const
740 {
741     return m_windowItem->surfaceItem();
742 }
743 
shadowItem() const744 ShadowItem *Scene::Window::shadowItem() const
745 {
746     return m_windowItem->shadowItem();
747 }
748 
updateWindowPosition()749 void Scene::Window::updateWindowPosition()
750 {
751     m_windowItem->setPosition(pos());
752 }
753 
754 //****************************************
755 // Scene::EffectFrame
756 //****************************************
EffectFrame(EffectFrameImpl * frame)757 Scene::EffectFrame::EffectFrame(EffectFrameImpl* frame)
758     : m_effectFrame(frame)
759 {
760 }
761 
~EffectFrame()762 Scene::EffectFrame::~EffectFrame()
763 {
764 }
765 
SceneFactory(QObject * parent)766 SceneFactory::SceneFactory(QObject *parent)
767     : QObject(parent)
768 {
769 }
770 
~SceneFactory()771 SceneFactory::~SceneFactory()
772 {
773 }
774 
775 } // namespace
776