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 ®ion)
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 ®ion, 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 ®ion)
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 ®ion, 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 ®ion, 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 ®ion, 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 ®ion, 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