File indexing completed on 2024-04-28 16:48:45

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0006     SPDX-FileCopyrightText: 2010, 2011 Martin Gräßlin <mgraesslin@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "effects.h"
0012 
0013 #include <config-kwin.h>
0014 
0015 #include "core/output.h"
0016 #include "effectloader.h"
0017 #include "effectsadaptor.h"
0018 #if KWIN_BUILD_ACTIVITIES
0019 #include "activities.h"
0020 #endif
0021 #include "core/renderbackend.h"
0022 #include "core/renderlayer.h"
0023 #include "cursor.h"
0024 #include "deleted.h"
0025 #include "group.h"
0026 #include "input_event.h"
0027 #include "internalwindow.h"
0028 #include "osd.h"
0029 #include "pointer_input.h"
0030 #include "scene/itemrenderer.h"
0031 #include "unmanaged.h"
0032 #include "x11window.h"
0033 #if KWIN_BUILD_TABBOX
0034 #include "tabbox.h"
0035 #endif
0036 #include "screenedge.h"
0037 #include "scripting/scriptedeffect.h"
0038 #if KWIN_BUILD_SCREENLOCKER
0039 #include "screenlockerwatcher.h"
0040 #endif
0041 #include "composite.h"
0042 #include "decorations/decorationbridge.h"
0043 #include "inputmethod.h"
0044 #include "inputpanelv1window.h"
0045 #include "kwinglutils.h"
0046 #include "scene/windowitem.h"
0047 #include "utils/xcbutils.h"
0048 #include "virtualdesktops.h"
0049 #include "wayland_server.h"
0050 #include "waylandwindow.h"
0051 #include "window_property_notify_x11_filter.h"
0052 #include "workspace.h"
0053 
0054 #include <KDecoration2/Decoration>
0055 #include <KDecoration2/DecorationSettings>
0056 
0057 #include <QDebug>
0058 #include <QMouseEvent>
0059 #include <QPainter>
0060 #include <QQmlEngine>
0061 #include <QQuickItem>
0062 #include <QQuickWindow>
0063 #include <QStandardPaths>
0064 #include <QWheelEvent>
0065 
0066 namespace KWin
0067 {
0068 //---------------------
0069 // Static
0070 
0071 static QByteArray readWindowProperty(xcb_window_t win, xcb_atom_t atom, xcb_atom_t type, int format)
0072 {
0073     if (win == XCB_WINDOW_NONE) {
0074         return QByteArray();
0075     }
0076     uint32_t len = 32768;
0077     for (;;) {
0078         Xcb::Property prop(false, win, atom, XCB_ATOM_ANY, 0, len);
0079         if (prop.isNull()) {
0080             // get property failed
0081             return QByteArray();
0082         }
0083         if (prop->bytes_after > 0) {
0084             len *= 2;
0085             continue;
0086         }
0087         return prop.toByteArray(format, type);
0088     }
0089 }
0090 
0091 static void deleteWindowProperty(xcb_window_t win, long int atom)
0092 {
0093     if (win == XCB_WINDOW_NONE) {
0094         return;
0095     }
0096     xcb_delete_property(kwinApp()->x11Connection(), win, atom);
0097 }
0098 
0099 static xcb_atom_t registerSupportProperty(const QByteArray &propertyName)
0100 {
0101     auto c = kwinApp()->x11Connection();
0102     if (!c) {
0103         return XCB_ATOM_NONE;
0104     }
0105     // get the atom for the propertyName
0106     UniqueCPtr<xcb_intern_atom_reply_t> atomReply(xcb_intern_atom_reply(c,
0107                                                                         xcb_intern_atom_unchecked(c, false, propertyName.size(), propertyName.constData()),
0108                                                                         nullptr));
0109     if (!atomReply) {
0110         return XCB_ATOM_NONE;
0111     }
0112     // announce property on root window
0113     unsigned char dummy = 0;
0114     xcb_change_property(c, XCB_PROP_MODE_REPLACE, kwinApp()->x11RootWindow(), atomReply->atom, atomReply->atom, 8, 1, &dummy);
0115     // TODO: add to _NET_SUPPORTED
0116     return atomReply->atom;
0117 }
0118 
0119 //---------------------
0120 
0121 EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, WorkspaceScene *scene)
0122     : EffectsHandler(Compositor::self()->backend()->compositingType())
0123     , keyboard_grab_effect(nullptr)
0124     , fullscreen_effect(nullptr)
0125     , m_compositor(compositor)
0126     , m_scene(scene)
0127     , m_effectLoader(new EffectLoader(this))
0128     , m_trackingCursorChanges(0)
0129 {
0130     qRegisterMetaType<QVector<KWin::EffectWindow *>>();
0131     qRegisterMetaType<KWin::SessionState>();
0132     connect(m_effectLoader, &AbstractEffectLoader::effectLoaded, this, [this](Effect *effect, const QString &name) {
0133         effect_order.insert(effect->requestedEffectChainPosition(), EffectPair(name, effect));
0134         loaded_effects << EffectPair(name, effect);
0135         effectsChanged();
0136     });
0137     m_effectLoader->setConfig(kwinApp()->config());
0138     new EffectsAdaptor(this);
0139     QDBusConnection dbus = QDBusConnection::sessionBus();
0140     dbus.registerObject(QStringLiteral("/Effects"), this);
0141 
0142     Workspace *ws = Workspace::self();
0143     VirtualDesktopManager *vds = VirtualDesktopManager::self();
0144     connect(ws, &Workspace::showingDesktopChanged, this, [this](bool showing, bool animated) {
0145         if (animated) {
0146             Q_EMIT showingDesktopChanged(showing);
0147         }
0148     });
0149     connect(ws, &Workspace::currentDesktopChanged, this, [this](int old, Window *window) {
0150         const int newDesktop = VirtualDesktopManager::self()->current();
0151         if (old != 0 && newDesktop != old) {
0152             Q_EMIT desktopChanged(old, newDesktop, window ? window->effectWindow() : nullptr);
0153             // TODO: remove in 4.10
0154             Q_EMIT desktopChanged(old, newDesktop);
0155         }
0156     });
0157     connect(ws, &Workspace::currentDesktopChanging, this, [this](uint currentDesktop, QPointF offset, KWin::Window *window) {
0158         Q_EMIT desktopChanging(currentDesktop, offset, window ? window->effectWindow() : nullptr);
0159     });
0160     connect(ws, &Workspace::currentDesktopChangingCancelled, this, [this]() {
0161         Q_EMIT desktopChangingCancelled();
0162     });
0163     connect(ws, &Workspace::desktopPresenceChanged, this, [this](Window *window, int old) {
0164         if (!window->effectWindow()) {
0165             return;
0166         }
0167         Q_EMIT desktopPresenceChanged(window->effectWindow(), old, window->desktop());
0168     });
0169     connect(ws, &Workspace::windowAdded, this, [this](Window *window) {
0170         if (window->readyForPainting()) {
0171             slotWindowShown(window);
0172         } else {
0173             connect(window, &Window::windowShown, this, &EffectsHandlerImpl::slotWindowShown);
0174         }
0175     });
0176     connect(ws, &Workspace::unmanagedAdded, this, [this](Unmanaged *u) {
0177         // it's never initially ready but has synthetic 50ms delay
0178         connect(u, &Window::windowShown, this, &EffectsHandlerImpl::slotUnmanagedShown);
0179     });
0180     connect(ws, &Workspace::internalWindowAdded, this, [this](InternalWindow *window) {
0181         setupWindowConnections(window);
0182         Q_EMIT windowAdded(window->effectWindow());
0183     });
0184     connect(ws, &Workspace::windowActivated, this, [this](Window *window) {
0185         Q_EMIT windowActivated(window ? window->effectWindow() : nullptr);
0186     });
0187     connect(ws, &Workspace::deletedRemoved, this, [this](KWin::Deleted *d) {
0188         Q_EMIT windowDeleted(d->effectWindow());
0189         elevated_windows.removeAll(d->effectWindow());
0190     });
0191     connect(ws->sessionManager(), &SessionManager::stateChanged, this, &KWin::EffectsHandler::sessionStateChanged);
0192     connect(vds, &VirtualDesktopManager::countChanged, this, &EffectsHandler::numberDesktopsChanged);
0193     connect(vds, &VirtualDesktopManager::layoutChanged, this, [this](int width, int height) {
0194         Q_EMIT desktopGridSizeChanged(QSize(width, height));
0195         Q_EMIT desktopGridWidthChanged(width);
0196         Q_EMIT desktopGridHeightChanged(height);
0197     });
0198     connect(Cursors::self()->mouse(), &Cursor::mouseChanged, this, &EffectsHandler::mouseChanged);
0199     connect(ws, &Workspace::geometryChanged, this, &EffectsHandler::virtualScreenSizeChanged);
0200     connect(ws, &Workspace::geometryChanged, this, &EffectsHandler::virtualScreenGeometryChanged);
0201 #if KWIN_BUILD_ACTIVITIES
0202     if (Activities *activities = Workspace::self()->activities()) {
0203         connect(activities, &Activities::added, this, &EffectsHandler::activityAdded);
0204         connect(activities, &Activities::removed, this, &EffectsHandler::activityRemoved);
0205         connect(activities, &Activities::currentChanged, this, &EffectsHandler::currentActivityChanged);
0206     }
0207 #endif
0208     connect(ws, &Workspace::stackingOrderChanged, this, &EffectsHandler::stackingOrderChanged);
0209 #if KWIN_BUILD_TABBOX
0210     TabBox::TabBox *tabBox = workspace()->tabbox();
0211     connect(tabBox, &TabBox::TabBox::tabBoxAdded, this, &EffectsHandler::tabBoxAdded);
0212     connect(tabBox, &TabBox::TabBox::tabBoxUpdated, this, &EffectsHandler::tabBoxUpdated);
0213     connect(tabBox, &TabBox::TabBox::tabBoxClosed, this, &EffectsHandler::tabBoxClosed);
0214     connect(tabBox, &TabBox::TabBox::tabBoxKeyEvent, this, &EffectsHandler::tabBoxKeyEvent);
0215 #endif
0216     connect(workspace()->screenEdges(), &ScreenEdges::approaching, this, &EffectsHandler::screenEdgeApproaching);
0217 #if KWIN_BUILD_SCREENLOCKER
0218     connect(kwinApp()->screenLockerWatcher(), &ScreenLockerWatcher::locked, this, &EffectsHandler::screenLockingChanged);
0219     connect(kwinApp()->screenLockerWatcher(), &ScreenLockerWatcher::aboutToLock, this, &EffectsHandler::screenAboutToLock);
0220 #endif
0221 
0222     connect(kwinApp(), &Application::x11ConnectionChanged, this, [this]() {
0223         registered_atoms.clear();
0224         for (auto it = m_propertiesForEffects.keyBegin(); it != m_propertiesForEffects.keyEnd(); it++) {
0225             const auto atom = registerSupportProperty(*it);
0226             if (atom == XCB_ATOM_NONE) {
0227                 continue;
0228             }
0229             m_compositor->keepSupportProperty(atom);
0230             m_managedProperties.insert(*it, atom);
0231             registerPropertyType(atom, true);
0232         }
0233         if (kwinApp()->x11Connection()) {
0234             m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
0235         } else {
0236             m_x11WindowPropertyNotify.reset();
0237         }
0238         Q_EMIT xcbConnectionChanged();
0239     });
0240 
0241     if (kwinApp()->x11Connection()) {
0242         m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
0243     }
0244 
0245     // connect all clients
0246     for (Window *window : ws->allClientList()) {
0247         if (window->readyForPainting()) {
0248             setupWindowConnections(window);
0249         } else {
0250             connect(window, &Window::windowShown, this, &EffectsHandlerImpl::slotWindowShown);
0251         }
0252     }
0253     for (Unmanaged *u : ws->unmanagedList()) {
0254         setupUnmanagedConnections(u);
0255     }
0256     for (InternalWindow *window : ws->internalWindows()) {
0257         setupWindowConnections(window);
0258     }
0259 
0260     connect(ws, &Workspace::outputAdded, this, &EffectsHandlerImpl::slotOutputAdded);
0261     connect(ws, &Workspace::outputRemoved, this, &EffectsHandlerImpl::slotOutputRemoved);
0262 
0263     const QList<Output *> outputs = ws->outputs();
0264     for (Output *output : outputs) {
0265         slotOutputAdded(output);
0266     }
0267 
0268     if (auto inputMethod = kwinApp()->inputMethod()) {
0269         connect(inputMethod, &InputMethod::panelChanged, this, &EffectsHandlerImpl::inputPanelChanged);
0270     }
0271 
0272     reconfigure();
0273 }
0274 
0275 EffectsHandlerImpl::~EffectsHandlerImpl()
0276 {
0277     unloadAllEffects();
0278 }
0279 
0280 void EffectsHandlerImpl::unloadAllEffects()
0281 {
0282     for (const EffectPair &pair : std::as_const(loaded_effects)) {
0283         destroyEffect(pair.second);
0284     }
0285 
0286     effect_order.clear();
0287     m_effectLoader->clear();
0288 
0289     effectsChanged();
0290 }
0291 
0292 void EffectsHandlerImpl::setupWindowConnections(Window *window)
0293 {
0294     connect(window, &Window::windowClosed, this, &EffectsHandlerImpl::slotWindowClosed);
0295     connect(window, static_cast<void (Window::*)(KWin::Window *, MaximizeMode)>(&Window::clientMaximizedStateChanged),
0296             this, &EffectsHandlerImpl::slotClientMaximized);
0297     connect(window, static_cast<void (Window::*)(KWin::Window *, MaximizeMode)>(&Window::clientMaximizedStateAboutToChange),
0298             this, [this](KWin::Window *window, MaximizeMode m) {
0299                 if (EffectWindowImpl *w = window->effectWindow()) {
0300                     Q_EMIT windowMaximizedStateAboutToChange(w, m & MaximizeHorizontal, m & MaximizeVertical);
0301                 }
0302             });
0303     connect(window, &Window::frameGeometryAboutToChange,
0304             this, [this](KWin::Window *window) {
0305                 if (EffectWindowImpl *w = window->effectWindow()) {
0306                     Q_EMIT windowFrameGeometryAboutToChange(w);
0307                 }
0308             });
0309     connect(window, &Window::clientStartUserMovedResized, this, [this](Window *window) {
0310         Q_EMIT windowStartUserMovedResized(window->effectWindow());
0311     });
0312     connect(window, &Window::clientStepUserMovedResized, this, [this](Window *window, const QRectF &geometry) {
0313         Q_EMIT windowStepUserMovedResized(window->effectWindow(), geometry);
0314     });
0315     connect(window, &Window::clientFinishUserMovedResized, this, [this](Window *window) {
0316         Q_EMIT windowFinishUserMovedResized(window->effectWindow());
0317     });
0318     connect(window, &Window::opacityChanged, this, &EffectsHandlerImpl::slotOpacityChanged);
0319     connect(window, &Window::clientMinimized, this, [this](Window *window, bool animate) {
0320         // TODO: notify effects even if it should not animate?
0321         if (animate) {
0322             Q_EMIT windowMinimized(window->effectWindow());
0323         }
0324     });
0325     connect(window, &Window::clientUnminimized, this, [this](Window *window, bool animate) {
0326         // TODO: notify effects even if it should not animate?
0327         if (animate) {
0328             Q_EMIT windowUnminimized(window->effectWindow());
0329         }
0330     });
0331     connect(window, &Window::modalChanged, this, &EffectsHandlerImpl::slotClientModalityChanged);
0332     connect(window, &Window::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
0333     connect(window, &Window::frameGeometryChanged, this, &EffectsHandlerImpl::slotFrameGeometryChanged);
0334     connect(window, &Window::damaged, this, &EffectsHandlerImpl::slotWindowDamaged);
0335     connect(window, &Window::unresponsiveChanged, this, [this, window](bool unresponsive) {
0336         Q_EMIT windowUnresponsiveChanged(window->effectWindow(), unresponsive);
0337     });
0338     connect(window, &Window::windowShown, this, [this](Window *window) {
0339         Q_EMIT windowShown(window->effectWindow());
0340     });
0341     connect(window, &Window::windowHidden, this, [this](Window *window) {
0342         Q_EMIT windowHidden(window->effectWindow());
0343     });
0344     connect(window, &Window::keepAboveChanged, this, [this, window](bool above) {
0345         Q_EMIT windowKeepAboveChanged(window->effectWindow());
0346     });
0347     connect(window, &Window::keepBelowChanged, this, [this, window](bool below) {
0348         Q_EMIT windowKeepBelowChanged(window->effectWindow());
0349     });
0350     connect(window, &Window::fullScreenChanged, this, [this, window]() {
0351         Q_EMIT windowFullScreenChanged(window->effectWindow());
0352     });
0353     connect(window, &Window::visibleGeometryChanged, this, [this, window]() {
0354         Q_EMIT windowExpandedGeometryChanged(window->effectWindow());
0355     });
0356     connect(window, &Window::decorationChanged, this, [this, window]() {
0357         Q_EMIT windowDecorationChanged(window->effectWindow());
0358     });
0359 }
0360 
0361 void EffectsHandlerImpl::setupUnmanagedConnections(Unmanaged *u)
0362 {
0363     connect(u, &Unmanaged::windowClosed, this, &EffectsHandlerImpl::slotWindowClosed);
0364     connect(u, &Unmanaged::opacityChanged, this, &EffectsHandlerImpl::slotOpacityChanged);
0365     connect(u, &Unmanaged::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged);
0366     connect(u, &Unmanaged::frameGeometryChanged, this, &EffectsHandlerImpl::slotFrameGeometryChanged);
0367     connect(u, &Unmanaged::damaged, this, &EffectsHandlerImpl::slotWindowDamaged);
0368     connect(u, &Unmanaged::visibleGeometryChanged, this, [this, u]() {
0369         Q_EMIT windowExpandedGeometryChanged(u->effectWindow());
0370     });
0371     connect(u, &Unmanaged::frameGeometryAboutToChange, this, [this](Window *window) {
0372         if (EffectWindowImpl *w = window->effectWindow()) {
0373             Q_EMIT windowFrameGeometryAboutToChange(w);
0374         }
0375     });
0376 }
0377 
0378 void EffectsHandlerImpl::reconfigure()
0379 {
0380     m_effectLoader->queryAndLoadAll();
0381 }
0382 
0383 // the idea is that effects call this function again which calls the next one
0384 void EffectsHandlerImpl::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0385 {
0386     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
0387         (*m_currentPaintScreenIterator++)->prePaintScreen(data, presentTime);
0388         --m_currentPaintScreenIterator;
0389     }
0390     // no special final code
0391 }
0392 
0393 void EffectsHandlerImpl::paintScreen(int mask, const QRegion &region, ScreenPaintData &data)
0394 {
0395     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
0396         (*m_currentPaintScreenIterator++)->paintScreen(mask, region, data);
0397         --m_currentPaintScreenIterator;
0398     } else {
0399         m_scene->finalPaintScreen(mask, region, data);
0400     }
0401 }
0402 
0403 void EffectsHandlerImpl::postPaintScreen()
0404 {
0405     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
0406         (*m_currentPaintScreenIterator++)->postPaintScreen();
0407         --m_currentPaintScreenIterator;
0408     }
0409     // no special final code
0410 }
0411 
0412 void EffectsHandlerImpl::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
0413 {
0414     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
0415         (*m_currentPaintWindowIterator++)->prePaintWindow(w, data, presentTime);
0416         --m_currentPaintWindowIterator;
0417     }
0418     // no special final code
0419 }
0420 
0421 void EffectsHandlerImpl::paintWindow(EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
0422 {
0423     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
0424         (*m_currentPaintWindowIterator++)->paintWindow(w, mask, region, data);
0425         --m_currentPaintWindowIterator;
0426     } else {
0427         m_scene->finalPaintWindow(static_cast<EffectWindowImpl *>(w), mask, region, data);
0428     }
0429 }
0430 
0431 void EffectsHandlerImpl::postPaintWindow(EffectWindow *w)
0432 {
0433     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
0434         (*m_currentPaintWindowIterator++)->postPaintWindow(w);
0435         --m_currentPaintWindowIterator;
0436     }
0437     // no special final code
0438 }
0439 
0440 Effect *EffectsHandlerImpl::provides(Effect::Feature ef)
0441 {
0442     for (int i = 0; i < loaded_effects.size(); ++i) {
0443         if (loaded_effects.at(i).second->provides(ef)) {
0444             return loaded_effects.at(i).second;
0445         }
0446     }
0447     return nullptr;
0448 }
0449 
0450 void EffectsHandlerImpl::drawWindow(EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
0451 {
0452     if (m_currentDrawWindowIterator != m_activeEffects.constEnd()) {
0453         (*m_currentDrawWindowIterator++)->drawWindow(w, mask, region, data);
0454         --m_currentDrawWindowIterator;
0455     } else {
0456         m_scene->finalDrawWindow(static_cast<EffectWindowImpl *>(w), mask, region, data);
0457     }
0458 }
0459 
0460 void EffectsHandlerImpl::renderWindow(EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
0461 {
0462     m_scene->finalDrawWindow(static_cast<EffectWindowImpl *>(w), mask, region, data);
0463 }
0464 
0465 bool EffectsHandlerImpl::hasDecorationShadows() const
0466 {
0467     return false;
0468 }
0469 
0470 bool EffectsHandlerImpl::decorationsHaveAlpha() const
0471 {
0472     return true;
0473 }
0474 
0475 // start another painting pass
0476 void EffectsHandlerImpl::startPaint()
0477 {
0478     m_activeEffects.clear();
0479     m_activeEffects.reserve(loaded_effects.count());
0480     for (QVector<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0481         if (it->second->isActive()) {
0482             m_activeEffects << it->second;
0483         }
0484     }
0485     m_currentDrawWindowIterator = m_activeEffects.constBegin();
0486     m_currentPaintWindowIterator = m_activeEffects.constBegin();
0487     m_currentPaintScreenIterator = m_activeEffects.constBegin();
0488 }
0489 
0490 void EffectsHandlerImpl::slotClientMaximized(Window *window, MaximizeMode maxMode)
0491 {
0492     bool horizontal = false;
0493     bool vertical = false;
0494     switch (maxMode) {
0495     case MaximizeHorizontal:
0496         horizontal = true;
0497         break;
0498     case MaximizeVertical:
0499         vertical = true;
0500         break;
0501     case MaximizeFull:
0502         horizontal = true;
0503         vertical = true;
0504         break;
0505     case MaximizeRestore: // fall through
0506     default:
0507         // default - nothing to do
0508         break;
0509     }
0510     if (EffectWindowImpl *w = window->effectWindow()) {
0511         Q_EMIT windowMaximizedStateChanged(w, horizontal, vertical);
0512     }
0513 }
0514 
0515 void EffectsHandlerImpl::slotOpacityChanged(Window *window, qreal oldOpacity)
0516 {
0517     if (window->opacity() == oldOpacity || !window->effectWindow()) {
0518         return;
0519     }
0520     Q_EMIT windowOpacityChanged(window->effectWindow(), oldOpacity, (qreal)window->opacity());
0521 }
0522 
0523 void EffectsHandlerImpl::slotWindowShown(Window *window)
0524 {
0525     Q_ASSERT(window->isClient());
0526     disconnect(window, &Window::windowShown, this, &EffectsHandlerImpl::slotWindowShown);
0527     setupWindowConnections(window);
0528     Q_EMIT windowAdded(window->effectWindow());
0529 }
0530 
0531 void EffectsHandlerImpl::slotUnmanagedShown(Window *window)
0532 { // regardless, unmanaged windows are -yet?- not synced anyway
0533     Q_ASSERT(qobject_cast<Unmanaged *>(window));
0534     Unmanaged *u = static_cast<Unmanaged *>(window);
0535     setupUnmanagedConnections(u);
0536     Q_EMIT windowAdded(u->effectWindow());
0537 }
0538 
0539 void EffectsHandlerImpl::slotWindowClosed(Window *original, Deleted *d)
0540 {
0541     original->disconnect(this);
0542     if (d) {
0543         Q_EMIT windowClosed(d->effectWindow());
0544     }
0545 }
0546 
0547 void EffectsHandlerImpl::slotClientModalityChanged()
0548 {
0549     Q_EMIT windowModalityChanged(static_cast<X11Window *>(sender())->effectWindow());
0550 }
0551 
0552 void EffectsHandlerImpl::slotCurrentTabAboutToChange(EffectWindow *from, EffectWindow *to)
0553 {
0554     Q_EMIT currentTabAboutToChange(from, to);
0555 }
0556 
0557 void EffectsHandlerImpl::slotTabAdded(EffectWindow *w, EffectWindow *to)
0558 {
0559     Q_EMIT tabAdded(w, to);
0560 }
0561 
0562 void EffectsHandlerImpl::slotTabRemoved(EffectWindow *w, EffectWindow *leaderOfFormerGroup)
0563 {
0564     Q_EMIT tabRemoved(w, leaderOfFormerGroup);
0565 }
0566 
0567 void EffectsHandlerImpl::slotWindowDamaged(Window *window)
0568 {
0569     if (!window->effectWindow()) {
0570         // can happen during tear down of window
0571         return;
0572     }
0573     Q_EMIT windowDamaged(window->effectWindow());
0574 }
0575 
0576 void EffectsHandlerImpl::slotGeometryShapeChanged(Window *window, const QRectF &old)
0577 {
0578     // during late cleanup effectWindow() may be already NULL
0579     // in some functions that may still call this
0580     if (window == nullptr || window->effectWindow() == nullptr) {
0581         return;
0582     }
0583     Q_EMIT windowGeometryShapeChanged(window->effectWindow(), old);
0584 }
0585 
0586 void EffectsHandlerImpl::slotFrameGeometryChanged(Window *window, const QRectF &oldGeometry)
0587 {
0588     // effectWindow() might be nullptr during tear down of the client.
0589     if (window->effectWindow()) {
0590         Q_EMIT windowFrameGeometryChanged(window->effectWindow(), oldGeometry);
0591     }
0592 }
0593 
0594 void EffectsHandlerImpl::setActiveFullScreenEffect(Effect *e)
0595 {
0596     if (fullscreen_effect == e) {
0597         return;
0598     }
0599     const bool activeChanged = (e == nullptr || fullscreen_effect == nullptr);
0600     fullscreen_effect = e;
0601     Q_EMIT activeFullScreenEffectChanged();
0602     if (activeChanged) {
0603         const auto delegates = m_scene->delegates();
0604         for (SceneDelegate *delegate : delegates) {
0605             RenderLoop *loop = delegate->layer()->loop();
0606             if (fullscreen_effect) {
0607                 loop->setLatencyPolicy(LatencyPolicy::LatencyExtremelyHigh);
0608             } else {
0609                 loop->resetLatencyPolicy();
0610             }
0611         }
0612         Q_EMIT hasActiveFullScreenEffectChanged();
0613         workspace()->screenEdges()->checkBlocking();
0614     }
0615 }
0616 
0617 Effect *EffectsHandlerImpl::activeFullScreenEffect() const
0618 {
0619     return fullscreen_effect;
0620 }
0621 
0622 bool EffectsHandlerImpl::hasActiveFullScreenEffect() const
0623 {
0624     return fullscreen_effect;
0625 }
0626 
0627 bool EffectsHandlerImpl::grabKeyboard(Effect *effect)
0628 {
0629     if (keyboard_grab_effect != nullptr) {
0630         return false;
0631     }
0632     if (!doGrabKeyboard()) {
0633         return false;
0634     }
0635     keyboard_grab_effect = effect;
0636     return true;
0637 }
0638 
0639 bool EffectsHandlerImpl::doGrabKeyboard()
0640 {
0641     return true;
0642 }
0643 
0644 void EffectsHandlerImpl::ungrabKeyboard()
0645 {
0646     Q_ASSERT(keyboard_grab_effect != nullptr);
0647     doUngrabKeyboard();
0648     keyboard_grab_effect = nullptr;
0649 }
0650 
0651 void EffectsHandlerImpl::doUngrabKeyboard()
0652 {
0653 }
0654 
0655 void EffectsHandlerImpl::grabbedKeyboardEvent(QKeyEvent *e)
0656 {
0657     if (keyboard_grab_effect != nullptr) {
0658         keyboard_grab_effect->grabbedKeyboardEvent(e);
0659     }
0660 }
0661 
0662 void EffectsHandlerImpl::startMouseInterception(Effect *effect, Qt::CursorShape shape)
0663 {
0664     if (m_grabbedMouseEffects.contains(effect)) {
0665         return;
0666     }
0667     m_grabbedMouseEffects.append(effect);
0668     if (m_grabbedMouseEffects.size() != 1) {
0669         return;
0670     }
0671     doStartMouseInterception(shape);
0672 }
0673 
0674 void EffectsHandlerImpl::doStartMouseInterception(Qt::CursorShape shape)
0675 {
0676     input()->pointer()->setEffectsOverrideCursor(shape);
0677 
0678     // We want to allow global shortcuts to be triggered when moving a
0679     // window so it is possible to pick up a window and then move it to a
0680     // different desktop by using the global shortcut to switch desktop.
0681     // However, that means that some other things can also be triggered. If
0682     // an effect that fill the screen gets triggered that way, we end up in a
0683     // weird state where the move will restart after the effect closes. So to
0684     // avoid that, abort move/resize if a full screen effect starts.
0685     if (workspace()->moveResizeWindow()) {
0686         workspace()->moveResizeWindow()->endInteractiveMoveResize();
0687     }
0688 }
0689 
0690 void EffectsHandlerImpl::stopMouseInterception(Effect *effect)
0691 {
0692     if (!m_grabbedMouseEffects.contains(effect)) {
0693         return;
0694     }
0695     m_grabbedMouseEffects.removeAll(effect);
0696     if (m_grabbedMouseEffects.isEmpty()) {
0697         doStopMouseInterception();
0698     }
0699 }
0700 
0701 void EffectsHandlerImpl::doStopMouseInterception()
0702 {
0703     input()->pointer()->removeEffectsOverrideCursor();
0704 }
0705 
0706 bool EffectsHandlerImpl::isMouseInterception() const
0707 {
0708     return m_grabbedMouseEffects.count() > 0;
0709 }
0710 
0711 bool EffectsHandlerImpl::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0712 {
0713     // TODO: reverse call order?
0714     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0715         if (it->second->touchDown(id, pos, time)) {
0716             return true;
0717         }
0718     }
0719     return false;
0720 }
0721 
0722 bool EffectsHandlerImpl::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0723 {
0724     // TODO: reverse call order?
0725     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0726         if (it->second->touchMotion(id, pos, time)) {
0727             return true;
0728         }
0729     }
0730     return false;
0731 }
0732 
0733 bool EffectsHandlerImpl::touchUp(qint32 id, std::chrono::microseconds time)
0734 {
0735     // TODO: reverse call order?
0736     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0737         if (it->second->touchUp(id, time)) {
0738             return true;
0739         }
0740     }
0741     return false;
0742 }
0743 
0744 bool EffectsHandlerImpl::tabletToolEvent(TabletEvent *event)
0745 {
0746     // TODO: reverse call order?
0747     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0748         if (it->second->tabletToolEvent(event)) {
0749             return true;
0750         }
0751     }
0752     return false;
0753 }
0754 
0755 bool EffectsHandlerImpl::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time)
0756 {
0757     // TODO: reverse call order?
0758     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0759         if (it->second->tabletToolButtonEvent(button, pressed, tabletToolId.m_uniqueId)) {
0760             return true;
0761         }
0762     }
0763     return false;
0764 }
0765 
0766 bool EffectsHandlerImpl::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0767 {
0768     // TODO: reverse call order?
0769     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0770         if (it->second->tabletPadButtonEvent(button, pressed, tabletPadId.data)) {
0771             return true;
0772         }
0773     }
0774     return false;
0775 }
0776 
0777 bool EffectsHandlerImpl::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0778 {
0779     // TODO: reverse call order?
0780     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0781         if (it->second->tabletPadStripEvent(number, position, isFinger, tabletPadId.data)) {
0782             return true;
0783         }
0784     }
0785     return false;
0786 }
0787 
0788 bool EffectsHandlerImpl::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0789 {
0790     // TODO: reverse call order?
0791     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0792         if (it->second->tabletPadRingEvent(number, position, isFinger, tabletPadId.data)) {
0793             return true;
0794         }
0795     }
0796     return false;
0797 }
0798 
0799 void EffectsHandlerImpl::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
0800 {
0801     input()->registerPointerShortcut(modifiers, pointerButtons, action);
0802 }
0803 
0804 void EffectsHandlerImpl::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
0805 {
0806     input()->registerAxisShortcut(modifiers, axis, action);
0807 }
0808 
0809 void EffectsHandlerImpl::registerRealtimeTouchpadSwipeShortcut(SwipeDirection dir, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
0810 {
0811     input()->registerRealtimeTouchpadSwipeShortcut(dir, fingerCount, onUp, progressCallback);
0812 }
0813 
0814 void EffectsHandlerImpl::registerTouchpadSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action)
0815 {
0816     input()->registerTouchpadSwipeShortcut(direction, fingerCount, action);
0817 }
0818 
0819 void EffectsHandlerImpl::registerRealtimeTouchpadPinchShortcut(PinchDirection dir, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
0820 {
0821     input()->registerRealtimeTouchpadPinchShortcut(dir, fingerCount, onUp, progressCallback);
0822 }
0823 
0824 void EffectsHandlerImpl::registerTouchpadPinchShortcut(PinchDirection direction, uint fingerCount, QAction *action)
0825 {
0826     input()->registerTouchpadPinchShortcut(direction, fingerCount, action);
0827 }
0828 
0829 void EffectsHandlerImpl::registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
0830 {
0831     input()->registerTouchscreenSwipeShortcut(direction, fingerCount, action, progressCallback);
0832 }
0833 
0834 void *EffectsHandlerImpl::getProxy(QString name)
0835 {
0836     for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0837         if ((*it).first == name) {
0838             return (*it).second->proxy();
0839         }
0840     }
0841 
0842     return nullptr;
0843 }
0844 
0845 void EffectsHandlerImpl::startMousePolling()
0846 {
0847     if (Cursors::self()->mouse()) {
0848         Cursors::self()->mouse()->startMousePolling();
0849     }
0850 }
0851 
0852 void EffectsHandlerImpl::stopMousePolling()
0853 {
0854     if (Cursors::self()->mouse()) {
0855         Cursors::self()->mouse()->stopMousePolling();
0856     }
0857 }
0858 
0859 bool EffectsHandlerImpl::hasKeyboardGrab() const
0860 {
0861     return keyboard_grab_effect != nullptr;
0862 }
0863 
0864 void EffectsHandlerImpl::registerPropertyType(long atom, bool reg)
0865 {
0866     if (reg) {
0867         ++registered_atoms[atom]; // initialized to 0 if not present yet
0868     } else {
0869         if (--registered_atoms[atom] == 0) {
0870             registered_atoms.remove(atom);
0871         }
0872     }
0873 }
0874 
0875 xcb_atom_t EffectsHandlerImpl::announceSupportProperty(const QByteArray &propertyName, Effect *effect)
0876 {
0877     PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
0878     if (it != m_propertiesForEffects.end()) {
0879         // property has already been registered for an effect
0880         // just append Effect and return the atom stored in m_managedProperties
0881         if (!it.value().contains(effect)) {
0882             it.value().append(effect);
0883         }
0884         return m_managedProperties.value(propertyName, XCB_ATOM_NONE);
0885     }
0886     m_propertiesForEffects.insert(propertyName, QList<Effect *>() << effect);
0887     const auto atom = registerSupportProperty(propertyName);
0888     if (atom == XCB_ATOM_NONE) {
0889         return atom;
0890     }
0891     m_compositor->keepSupportProperty(atom);
0892     m_managedProperties.insert(propertyName, atom);
0893     registerPropertyType(atom, true);
0894     return atom;
0895 }
0896 
0897 void EffectsHandlerImpl::removeSupportProperty(const QByteArray &propertyName, Effect *effect)
0898 {
0899     PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
0900     if (it == m_propertiesForEffects.end()) {
0901         // property is not registered - nothing to do
0902         return;
0903     }
0904     if (!it.value().contains(effect)) {
0905         // property is not registered for given effect - nothing to do
0906         return;
0907     }
0908     it.value().removeAll(effect);
0909     if (!it.value().isEmpty()) {
0910         // property still registered for another effect - nothing further to do
0911         return;
0912     }
0913     const xcb_atom_t atom = m_managedProperties.take(propertyName);
0914     registerPropertyType(atom, false);
0915     m_propertiesForEffects.remove(propertyName);
0916     m_compositor->removeSupportProperty(atom); // delayed removal
0917 }
0918 
0919 QByteArray EffectsHandlerImpl::readRootProperty(long atom, long type, int format) const
0920 {
0921     if (!kwinApp()->x11Connection()) {
0922         return QByteArray();
0923     }
0924     return readWindowProperty(kwinApp()->x11RootWindow(), atom, type, format);
0925 }
0926 
0927 void EffectsHandlerImpl::activateWindow(EffectWindow *effectWindow)
0928 {
0929     auto window = static_cast<EffectWindowImpl *>(effectWindow)->window();
0930     if (window->isClient()) {
0931         Workspace::self()->activateWindow(window, true);
0932     }
0933 }
0934 
0935 EffectWindow *EffectsHandlerImpl::activeWindow() const
0936 {
0937     return Workspace::self()->activeWindow() ? Workspace::self()->activeWindow()->effectWindow() : nullptr;
0938 }
0939 
0940 void EffectsHandlerImpl::moveWindow(EffectWindow *w, const QPoint &pos, bool snap, double snapAdjust)
0941 {
0942     auto window = static_cast<EffectWindowImpl *>(w)->window();
0943     if (!window->isClient() || !window->isMovable()) {
0944         return;
0945     }
0946 
0947     if (snap) {
0948         window->move(Workspace::self()->adjustWindowPosition(window, pos, true, snapAdjust));
0949     } else {
0950         window->move(pos);
0951     }
0952 }
0953 
0954 void EffectsHandlerImpl::windowToDesktop(EffectWindow *w, int desktop)
0955 {
0956     auto window = static_cast<EffectWindowImpl *>(w)->window();
0957     if (window->isClient() && !window->isDesktop() && !window->isDock()) {
0958         Workspace::self()->sendWindowToDesktop(window, desktop, true);
0959     }
0960 }
0961 
0962 void EffectsHandlerImpl::windowToDesktops(EffectWindow *w, const QVector<uint> &desktopIds)
0963 {
0964     auto window = static_cast<EffectWindowImpl *>(w)->window();
0965     if (!window->isClient() || window->isDesktop() || window->isDock()) {
0966         return;
0967     }
0968     QVector<VirtualDesktop *> desktops;
0969     desktops.reserve(desktopIds.count());
0970     for (uint x11Id : desktopIds) {
0971         if (x11Id > VirtualDesktopManager::self()->count()) {
0972             continue;
0973         }
0974         VirtualDesktop *d = VirtualDesktopManager::self()->desktopForX11Id(x11Id);
0975         Q_ASSERT(d);
0976         if (desktops.contains(d)) {
0977             continue;
0978         }
0979         desktops << d;
0980     }
0981     window->setDesktops(desktops);
0982 }
0983 
0984 void EffectsHandlerImpl::windowToScreen(EffectWindow *w, EffectScreen *screen)
0985 {
0986     auto window = static_cast<EffectWindowImpl *>(w)->window();
0987     if (window->isClient() && !window->isDesktop() && !window->isDock()) {
0988         EffectScreenImpl *screenImpl = static_cast<EffectScreenImpl *>(screen);
0989         Workspace::self()->sendWindowToOutput(window, screenImpl->platformOutput());
0990     }
0991 }
0992 
0993 void EffectsHandlerImpl::setShowingDesktop(bool showing)
0994 {
0995     Workspace::self()->setShowingDesktop(showing);
0996 }
0997 
0998 QString EffectsHandlerImpl::currentActivity() const
0999 {
1000 #if KWIN_BUILD_ACTIVITIES
1001     if (!Workspace::self()->activities()) {
1002         return QString();
1003     }
1004     return Workspace::self()->activities()->current();
1005 #else
1006     return QString();
1007 #endif
1008 }
1009 
1010 int EffectsHandlerImpl::currentDesktop() const
1011 {
1012     return VirtualDesktopManager::self()->current();
1013 }
1014 
1015 int EffectsHandlerImpl::numberOfDesktops() const
1016 {
1017     return VirtualDesktopManager::self()->count();
1018 }
1019 
1020 void EffectsHandlerImpl::setCurrentDesktop(int desktop)
1021 {
1022     VirtualDesktopManager::self()->setCurrent(desktop);
1023 }
1024 
1025 void EffectsHandlerImpl::setNumberOfDesktops(int desktops)
1026 {
1027     VirtualDesktopManager::self()->setCount(desktops);
1028 }
1029 
1030 QSize EffectsHandlerImpl::desktopGridSize() const
1031 {
1032     return VirtualDesktopManager::self()->grid().size();
1033 }
1034 
1035 int EffectsHandlerImpl::desktopGridWidth() const
1036 {
1037     return desktopGridSize().width();
1038 }
1039 
1040 int EffectsHandlerImpl::desktopGridHeight() const
1041 {
1042     return desktopGridSize().height();
1043 }
1044 
1045 int EffectsHandlerImpl::workspaceWidth() const
1046 {
1047     return desktopGridWidth() * Workspace::self()->geometry().width();
1048 }
1049 
1050 int EffectsHandlerImpl::workspaceHeight() const
1051 {
1052     return desktopGridHeight() * Workspace::self()->geometry().height();
1053 }
1054 
1055 int EffectsHandlerImpl::desktopAtCoords(QPoint coords) const
1056 {
1057     if (auto vd = VirtualDesktopManager::self()->grid().at(coords)) {
1058         return vd->x11DesktopNumber();
1059     }
1060     return 0;
1061 }
1062 
1063 QPoint EffectsHandlerImpl::desktopGridCoords(int id) const
1064 {
1065     return VirtualDesktopManager::self()->grid().gridCoords(id);
1066 }
1067 
1068 QPoint EffectsHandlerImpl::desktopCoords(int id) const
1069 {
1070     QPoint coords = VirtualDesktopManager::self()->grid().gridCoords(id);
1071     if (coords.x() == -1) {
1072         return QPoint(-1, -1);
1073     }
1074     const QSize displaySize = Workspace::self()->geometry().size();
1075     return QPoint(coords.x() * displaySize.width(), coords.y() * displaySize.height());
1076 }
1077 
1078 int EffectsHandlerImpl::desktopAbove(int desktop, bool wrap) const
1079 {
1080     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Up, wrap);
1081 }
1082 
1083 int EffectsHandlerImpl::desktopToRight(int desktop, bool wrap) const
1084 {
1085     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Right, wrap);
1086 }
1087 
1088 int EffectsHandlerImpl::desktopBelow(int desktop, bool wrap) const
1089 {
1090     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Down, wrap);
1091 }
1092 
1093 int EffectsHandlerImpl::desktopToLeft(int desktop, bool wrap) const
1094 {
1095     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Left, wrap);
1096 }
1097 
1098 QString EffectsHandlerImpl::desktopName(int desktop) const
1099 {
1100     const VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForX11Id(desktop);
1101     return vd ? vd->name() : QString();
1102 }
1103 
1104 bool EffectsHandlerImpl::optionRollOverDesktops() const
1105 {
1106     return options->isRollOverDesktops();
1107 }
1108 
1109 double EffectsHandlerImpl::animationTimeFactor() const
1110 {
1111     return options->animationTimeFactor();
1112 }
1113 
1114 EffectWindow *EffectsHandlerImpl::findWindow(WId id) const
1115 {
1116     if (X11Window *w = Workspace::self()->findClient(Predicate::WindowMatch, id)) {
1117         return w->effectWindow();
1118     }
1119     if (Unmanaged *w = Workspace::self()->findUnmanaged(id)) {
1120         return w->effectWindow();
1121     }
1122     return nullptr;
1123 }
1124 
1125 EffectWindow *EffectsHandlerImpl::findWindow(KWaylandServer::SurfaceInterface *surf) const
1126 {
1127     if (waylandServer()) {
1128         if (Window *w = waylandServer()->findWindow(surf)) {
1129             return w->effectWindow();
1130         }
1131     }
1132     return nullptr;
1133 }
1134 
1135 EffectWindow *EffectsHandlerImpl::findWindow(QWindow *w) const
1136 {
1137     if (Window *window = workspace()->findInternal(w)) {
1138         return window->effectWindow();
1139     }
1140     return nullptr;
1141 }
1142 
1143 EffectWindow *EffectsHandlerImpl::findWindow(const QUuid &id) const
1144 {
1145     if (Window *window = workspace()->findToplevel(id)) {
1146         return window->effectWindow();
1147     }
1148     return nullptr;
1149 }
1150 
1151 EffectWindowList EffectsHandlerImpl::stackingOrder() const
1152 {
1153     QList<Window *> list = workspace()->stackingOrder();
1154     EffectWindowList ret;
1155     for (Window *t : list) {
1156         if (EffectWindow *w = t->effectWindow()) {
1157             ret.append(w);
1158         }
1159     }
1160     return ret;
1161 }
1162 
1163 void EffectsHandlerImpl::setElevatedWindow(KWin::EffectWindow *w, bool set)
1164 {
1165     elevated_windows.removeAll(w);
1166     if (set) {
1167         elevated_windows.append(w);
1168     }
1169 }
1170 
1171 void EffectsHandlerImpl::setTabBoxWindow(EffectWindow *w)
1172 {
1173 #if KWIN_BUILD_TABBOX
1174     auto window = static_cast<EffectWindowImpl *>(w)->window();
1175     if (window->isClient()) {
1176         workspace()->tabbox()->setCurrentClient(window);
1177     }
1178 #endif
1179 }
1180 
1181 void EffectsHandlerImpl::setTabBoxDesktop(int desktop)
1182 {
1183 #if KWIN_BUILD_TABBOX
1184     workspace()->tabbox()->setCurrentDesktop(desktop);
1185 #endif
1186 }
1187 
1188 EffectWindowList EffectsHandlerImpl::currentTabBoxWindowList() const
1189 {
1190 #if KWIN_BUILD_TABBOX
1191     const auto clients = workspace()->tabbox()->currentClientList();
1192     EffectWindowList ret;
1193     ret.reserve(clients.size());
1194     std::transform(std::cbegin(clients), std::cend(clients),
1195                    std::back_inserter(ret),
1196                    [](auto client) {
1197                        return client->effectWindow();
1198                    });
1199     return ret;
1200 #else
1201     return EffectWindowList();
1202 #endif
1203 }
1204 
1205 void EffectsHandlerImpl::refTabBox()
1206 {
1207 #if KWIN_BUILD_TABBOX
1208     workspace()->tabbox()->reference();
1209 #endif
1210 }
1211 
1212 void EffectsHandlerImpl::unrefTabBox()
1213 {
1214 #if KWIN_BUILD_TABBOX
1215     workspace()->tabbox()->unreference();
1216 #endif
1217 }
1218 
1219 void EffectsHandlerImpl::closeTabBox()
1220 {
1221 #if KWIN_BUILD_TABBOX
1222     workspace()->tabbox()->close();
1223 #endif
1224 }
1225 
1226 QList<int> EffectsHandlerImpl::currentTabBoxDesktopList() const
1227 {
1228 #if KWIN_BUILD_TABBOX
1229     return workspace()->tabbox()->currentDesktopList();
1230 #else
1231     return QList<int>();
1232 #endif
1233 }
1234 
1235 int EffectsHandlerImpl::currentTabBoxDesktop() const
1236 {
1237 #if KWIN_BUILD_TABBOX
1238     return workspace()->tabbox()->currentDesktop();
1239 #else
1240     return -1;
1241 #endif
1242 }
1243 
1244 EffectWindow *EffectsHandlerImpl::currentTabBoxWindow() const
1245 {
1246 #if KWIN_BUILD_TABBOX
1247     if (auto c = workspace()->tabbox()->currentClient()) {
1248         return c->effectWindow();
1249     }
1250 #endif
1251     return nullptr;
1252 }
1253 
1254 void EffectsHandlerImpl::addRepaintFull()
1255 {
1256     m_compositor->scene()->addRepaintFull();
1257 }
1258 
1259 void EffectsHandlerImpl::addRepaint(const QRect &r)
1260 {
1261     m_compositor->scene()->addRepaint(r);
1262 }
1263 
1264 void EffectsHandlerImpl::addRepaint(const QRectF &r)
1265 {
1266     m_compositor->scene()->addRepaint(r.toAlignedRect());
1267 }
1268 
1269 void EffectsHandlerImpl::addRepaint(const QRegion &r)
1270 {
1271     m_compositor->scene()->addRepaint(r);
1272 }
1273 
1274 void EffectsHandlerImpl::addRepaint(int x, int y, int w, int h)
1275 {
1276     m_compositor->scene()->addRepaint(x, y, w, h);
1277 }
1278 
1279 EffectScreen *EffectsHandlerImpl::activeScreen() const
1280 {
1281     return EffectScreenImpl::get(workspace()->activeOutput());
1282 }
1283 
1284 static VirtualDesktop *resolveVirtualDesktop(int desktopId)
1285 {
1286     if (desktopId == 0 || desktopId == -1) {
1287         return VirtualDesktopManager::self()->currentDesktop();
1288     } else {
1289         return VirtualDesktopManager::self()->desktopForX11Id(desktopId);
1290     }
1291 }
1292 
1293 QRectF EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectScreen *screen, int desktop) const
1294 {
1295     const EffectScreenImpl *screenImpl = static_cast<const EffectScreenImpl *>(screen);
1296     return Workspace::self()->clientArea(opt, screenImpl->platformOutput(), resolveVirtualDesktop(desktop));
1297 }
1298 
1299 QRectF EffectsHandlerImpl::clientArea(clientAreaOption opt, const EffectWindow *effectWindow) const
1300 {
1301     const Window *window = static_cast<const EffectWindowImpl *>(effectWindow)->window();
1302     return Workspace::self()->clientArea(opt, window);
1303 }
1304 
1305 QRectF EffectsHandlerImpl::clientArea(clientAreaOption opt, const QPoint &p, int desktop) const
1306 {
1307     const Output *output = Workspace::self()->outputAt(p);
1308     const VirtualDesktop *virtualDesktop = resolveVirtualDesktop(desktop);
1309     return Workspace::self()->clientArea(opt, output, virtualDesktop);
1310 }
1311 
1312 QRect EffectsHandlerImpl::virtualScreenGeometry() const
1313 {
1314     return Workspace::self()->geometry();
1315 }
1316 
1317 QSize EffectsHandlerImpl::virtualScreenSize() const
1318 {
1319     return Workspace::self()->geometry().size();
1320 }
1321 
1322 void EffectsHandlerImpl::defineCursor(Qt::CursorShape shape)
1323 {
1324     input()->pointer()->setEffectsOverrideCursor(shape);
1325 }
1326 
1327 bool EffectsHandlerImpl::checkInputWindowEvent(QMouseEvent *e)
1328 {
1329     if (m_grabbedMouseEffects.isEmpty()) {
1330         return false;
1331     }
1332     for (Effect *effect : std::as_const(m_grabbedMouseEffects)) {
1333         effect->windowInputMouseEvent(e);
1334     }
1335     return true;
1336 }
1337 
1338 bool EffectsHandlerImpl::checkInputWindowEvent(QWheelEvent *e)
1339 {
1340     if (m_grabbedMouseEffects.isEmpty()) {
1341         return false;
1342     }
1343     for (Effect *effect : std::as_const(m_grabbedMouseEffects)) {
1344         effect->windowInputMouseEvent(e);
1345     }
1346     return true;
1347 }
1348 
1349 void EffectsHandlerImpl::connectNotify(const QMetaMethod &signal)
1350 {
1351     if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
1352         if (!m_trackingCursorChanges) {
1353             connect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
1354             Cursors::self()->mouse()->startCursorTracking();
1355         }
1356         ++m_trackingCursorChanges;
1357     }
1358     EffectsHandler::connectNotify(signal);
1359 }
1360 
1361 void EffectsHandlerImpl::disconnectNotify(const QMetaMethod &signal)
1362 {
1363     if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
1364         Q_ASSERT(m_trackingCursorChanges > 0);
1365         if (!--m_trackingCursorChanges) {
1366             Cursors::self()->mouse()->stopCursorTracking();
1367             disconnect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
1368         }
1369     }
1370     EffectsHandler::disconnectNotify(signal);
1371 }
1372 
1373 void EffectsHandlerImpl::checkInputWindowStacking()
1374 {
1375     if (m_grabbedMouseEffects.isEmpty()) {
1376         return;
1377     }
1378     doCheckInputWindowStacking();
1379 }
1380 
1381 void EffectsHandlerImpl::doCheckInputWindowStacking()
1382 {
1383 }
1384 
1385 QPoint EffectsHandlerImpl::cursorPos() const
1386 {
1387     return Cursors::self()->mouse()->pos();
1388 }
1389 
1390 void EffectsHandlerImpl::reserveElectricBorder(ElectricBorder border, Effect *effect)
1391 {
1392     workspace()->screenEdges()->reserve(border, effect, "borderActivated");
1393 }
1394 
1395 void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border, Effect *effect)
1396 {
1397     workspace()->screenEdges()->unreserve(border, effect);
1398 }
1399 
1400 void EffectsHandlerImpl::registerTouchBorder(ElectricBorder border, QAction *action)
1401 {
1402     workspace()->screenEdges()->reserveTouch(border, action);
1403 }
1404 
1405 void EffectsHandlerImpl::registerRealtimeTouchBorder(ElectricBorder border, QAction *action, EffectsHandler::TouchBorderCallback progressCallback)
1406 {
1407     workspace()->screenEdges()->reserveTouch(border, action, [progressCallback](ElectricBorder border, const QPointF &deltaProgress, Output *output) {
1408         progressCallback(border, deltaProgress, EffectScreenImpl::get(output));
1409     });
1410 }
1411 
1412 void EffectsHandlerImpl::unregisterTouchBorder(ElectricBorder border, QAction *action)
1413 {
1414     workspace()->screenEdges()->unreserveTouch(border, action);
1415 }
1416 
1417 QPainter *EffectsHandlerImpl::scenePainter()
1418 {
1419     return m_scene->renderer()->painter();
1420 }
1421 
1422 void EffectsHandlerImpl::toggleEffect(const QString &name)
1423 {
1424     if (isEffectLoaded(name)) {
1425         unloadEffect(name);
1426     } else {
1427         loadEffect(name);
1428     }
1429 }
1430 
1431 QStringList EffectsHandlerImpl::loadedEffects() const
1432 {
1433     QStringList listModules;
1434     listModules.reserve(loaded_effects.count());
1435     std::transform(loaded_effects.constBegin(), loaded_effects.constEnd(),
1436                    std::back_inserter(listModules),
1437                    [](const EffectPair &pair) {
1438                        return pair.first;
1439                    });
1440     return listModules;
1441 }
1442 
1443 QStringList EffectsHandlerImpl::listOfEffects() const
1444 {
1445     return m_effectLoader->listOfKnownEffects();
1446 }
1447 
1448 bool EffectsHandlerImpl::loadEffect(const QString &name)
1449 {
1450     makeOpenGLContextCurrent();
1451     m_compositor->scene()->addRepaintFull();
1452 
1453     return m_effectLoader->loadEffect(name);
1454 }
1455 
1456 void EffectsHandlerImpl::unloadEffect(const QString &name)
1457 {
1458     auto it = std::find_if(effect_order.begin(), effect_order.end(),
1459                            [name](EffectPair &pair) {
1460                                return pair.first == name;
1461                            });
1462     if (it == effect_order.end()) {
1463         qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Effect not loaded :" << name;
1464         return;
1465     }
1466 
1467     qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Unloading Effect :" << name;
1468     destroyEffect((*it).second);
1469     effect_order.erase(it);
1470     effectsChanged();
1471 
1472     m_compositor->scene()->addRepaintFull();
1473 }
1474 
1475 void EffectsHandlerImpl::destroyEffect(Effect *effect)
1476 {
1477     makeOpenGLContextCurrent();
1478 
1479     if (fullscreen_effect == effect) {
1480         setActiveFullScreenEffect(nullptr);
1481     }
1482 
1483     if (keyboard_grab_effect == effect) {
1484         ungrabKeyboard();
1485     }
1486 
1487     stopMouseInterception(effect);
1488 
1489     const QList<QByteArray> properties = m_propertiesForEffects.keys();
1490     for (const QByteArray &property : properties) {
1491         removeSupportProperty(property, effect);
1492     }
1493 
1494     delete effect;
1495 }
1496 
1497 void EffectsHandlerImpl::reconfigureEffect(const QString &name)
1498 {
1499     for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1500         if ((*it).first == name) {
1501             kwinApp()->config()->reparseConfiguration();
1502             makeOpenGLContextCurrent();
1503             (*it).second->reconfigure(Effect::ReconfigureAll);
1504             return;
1505         }
1506     }
1507 }
1508 
1509 bool EffectsHandlerImpl::isEffectLoaded(const QString &name) const
1510 {
1511     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1512                            [&name](const EffectPair &pair) {
1513                                return pair.first == name;
1514                            });
1515     return it != loaded_effects.constEnd();
1516 }
1517 
1518 bool EffectsHandlerImpl::isEffectSupported(const QString &name)
1519 {
1520     // If the effect is loaded, it is obviously supported.
1521     if (isEffectLoaded(name)) {
1522         return true;
1523     }
1524 
1525     // next checks might require a context
1526     makeOpenGLContextCurrent();
1527 
1528     return m_effectLoader->isEffectSupported(name);
1529 }
1530 
1531 QList<bool> EffectsHandlerImpl::areEffectsSupported(const QStringList &names)
1532 {
1533     QList<bool> retList;
1534     retList.reserve(names.count());
1535     std::transform(names.constBegin(), names.constEnd(),
1536                    std::back_inserter(retList),
1537                    [this](const QString &name) {
1538                        return isEffectSupported(name);
1539                    });
1540     return retList;
1541 }
1542 
1543 void EffectsHandlerImpl::reloadEffect(Effect *effect)
1544 {
1545     QString effectName;
1546     for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1547         if ((*it).second == effect) {
1548             effectName = (*it).first;
1549             break;
1550         }
1551     }
1552     if (!effectName.isNull()) {
1553         unloadEffect(effectName);
1554         m_effectLoader->loadEffect(effectName);
1555     }
1556 }
1557 
1558 void EffectsHandlerImpl::effectsChanged()
1559 {
1560     loaded_effects.clear();
1561     m_activeEffects.clear(); // it's possible to have a reconfigure and a quad rebuild between two paint cycles - bug #308201
1562 
1563     loaded_effects.reserve(effect_order.count());
1564     std::copy(effect_order.constBegin(), effect_order.constEnd(),
1565               std::back_inserter(loaded_effects));
1566 
1567     m_activeEffects.reserve(loaded_effects.count());
1568 
1569     m_currentPaintScreenIterator = m_activeEffects.constBegin();
1570     m_currentPaintWindowIterator = m_activeEffects.constBegin();
1571     m_currentDrawWindowIterator = m_activeEffects.constBegin();
1572 }
1573 
1574 QStringList EffectsHandlerImpl::activeEffects() const
1575 {
1576     QStringList ret;
1577     for (QVector<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(),
1578                                                    end = loaded_effects.constEnd();
1579          it != end; ++it) {
1580         if (it->second->isActive()) {
1581             ret << it->first;
1582         }
1583     }
1584     return ret;
1585 }
1586 
1587 bool EffectsHandlerImpl::blocksDirectScanout() const
1588 {
1589     return std::any_of(m_activeEffects.constBegin(), m_activeEffects.constEnd(), [](const Effect *effect) {
1590         return effect->blocksDirectScanout();
1591     });
1592 }
1593 
1594 KWaylandServer::Display *EffectsHandlerImpl::waylandDisplay() const
1595 {
1596     if (waylandServer()) {
1597         return waylandServer()->display();
1598     }
1599     return nullptr;
1600 }
1601 
1602 std::unique_ptr<EffectFrame> EffectsHandlerImpl::effectFrame(EffectFrameStyle style, bool staticSize, const QPoint &position, Qt::Alignment alignment) const
1603 {
1604     return std::make_unique<EffectFrameImpl>(style, staticSize, position, alignment);
1605 }
1606 
1607 QVariant EffectsHandlerImpl::kwinOption(KWinOption kwopt)
1608 {
1609     switch (kwopt) {
1610     case CloseButtonCorner: {
1611         // TODO: this could become per window and be derived from the actual position in the deco
1612         const auto settings = Workspace::self()->decorationBridge()->settings();
1613         return settings && settings->decorationButtonsLeft().contains(KDecoration2::DecorationButtonType::Close) ? Qt::TopLeftCorner : Qt::TopRightCorner;
1614     }
1615     case SwitchDesktopOnScreenEdge:
1616         return workspace()->screenEdges()->isDesktopSwitching();
1617     case SwitchDesktopOnScreenEdgeMovingWindows:
1618         return workspace()->screenEdges()->isDesktopSwitchingMovingClients();
1619     default:
1620         return QVariant(); // an invalid one
1621     }
1622 }
1623 
1624 QString EffectsHandlerImpl::supportInformation(const QString &name) const
1625 {
1626     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1627                            [name](const EffectPair &pair) {
1628                                return pair.first == name;
1629                            });
1630     if (it == loaded_effects.constEnd()) {
1631         return QString();
1632     }
1633 
1634     QString support((*it).first + QLatin1String(":\n"));
1635     const QMetaObject *metaOptions = (*it).second->metaObject();
1636     for (int i = 0; i < metaOptions->propertyCount(); ++i) {
1637         const QMetaProperty property = metaOptions->property(i);
1638         if (qstrcmp(property.name(), "objectName") == 0) {
1639             continue;
1640         }
1641         support += QString::fromUtf8(property.name()) + QLatin1String(": ") + (*it).second->property(property.name()).toString() + QLatin1Char('\n');
1642     }
1643 
1644     return support;
1645 }
1646 
1647 bool EffectsHandlerImpl::isScreenLocked() const
1648 {
1649 #if KWIN_BUILD_SCREENLOCKER
1650     return kwinApp()->screenLockerWatcher()->isLocked();
1651 #else
1652     return false;
1653 #endif
1654 }
1655 
1656 QString EffectsHandlerImpl::debug(const QString &name, const QString &parameter) const
1657 {
1658     QString internalName = name.toLower();
1659     for (QVector<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1660         if ((*it).first == internalName) {
1661             return it->second->debug(parameter);
1662         }
1663     }
1664     return QString();
1665 }
1666 
1667 bool EffectsHandlerImpl::makeOpenGLContextCurrent()
1668 {
1669     return m_scene->makeOpenGLContextCurrent();
1670 }
1671 
1672 void EffectsHandlerImpl::doneOpenGLContextCurrent()
1673 {
1674     m_scene->doneOpenGLContextCurrent();
1675 }
1676 
1677 bool EffectsHandlerImpl::animationsSupported() const
1678 {
1679     static const QByteArray forceEnvVar = qgetenv("KWIN_EFFECTS_FORCE_ANIMATIONS");
1680     if (!forceEnvVar.isEmpty()) {
1681         static const int forceValue = forceEnvVar.toInt();
1682         return forceValue == 1;
1683     }
1684     return m_scene->animationsSupported();
1685 }
1686 
1687 void EffectsHandlerImpl::highlightWindows(const QVector<EffectWindow *> &windows)
1688 {
1689     Effect *e = provides(Effect::HighlightWindows);
1690     if (!e) {
1691         return;
1692     }
1693     e->perform(Effect::HighlightWindows, QVariantList{QVariant::fromValue(windows)});
1694 }
1695 
1696 PlatformCursorImage EffectsHandlerImpl::cursorImage() const
1697 {
1698     return kwinApp()->cursorImage();
1699 }
1700 
1701 void EffectsHandlerImpl::hideCursor()
1702 {
1703     Cursors::self()->hideCursor();
1704 }
1705 
1706 void EffectsHandlerImpl::showCursor()
1707 {
1708     Cursors::self()->showCursor();
1709 }
1710 
1711 void EffectsHandlerImpl::startInteractiveWindowSelection(std::function<void(KWin::EffectWindow *)> callback)
1712 {
1713     kwinApp()->startInteractiveWindowSelection([callback](KWin::Window *window) {
1714         if (window && window->effectWindow()) {
1715             callback(window->effectWindow());
1716         } else {
1717             callback(nullptr);
1718         }
1719     });
1720 }
1721 
1722 void EffectsHandlerImpl::startInteractivePositionSelection(std::function<void(const QPoint &)> callback)
1723 {
1724     kwinApp()->startInteractivePositionSelection(callback);
1725 }
1726 
1727 void EffectsHandlerImpl::showOnScreenMessage(const QString &message, const QString &iconName)
1728 {
1729     OSD::show(message, iconName);
1730 }
1731 
1732 void EffectsHandlerImpl::hideOnScreenMessage(OnScreenMessageHideFlags flags)
1733 {
1734     OSD::HideFlags osdFlags;
1735     if (flags.testFlag(OnScreenMessageHideFlag::SkipsCloseAnimation)) {
1736         osdFlags |= OSD::HideFlag::SkipCloseAnimation;
1737     }
1738     OSD::hide(osdFlags);
1739 }
1740 
1741 KSharedConfigPtr EffectsHandlerImpl::config() const
1742 {
1743     return kwinApp()->config();
1744 }
1745 
1746 KSharedConfigPtr EffectsHandlerImpl::inputConfig() const
1747 {
1748     return InputConfig::self()->inputConfig();
1749 }
1750 
1751 Effect *EffectsHandlerImpl::findEffect(const QString &name) const
1752 {
1753     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(), [name](const EffectPair &pair) {
1754         return pair.first == name;
1755     });
1756     if (it == loaded_effects.constEnd()) {
1757         return nullptr;
1758     }
1759     return (*it).second;
1760 }
1761 
1762 void EffectsHandlerImpl::renderOffscreenQuickView(OffscreenQuickView *w) const
1763 {
1764     if (!w->isVisible()) {
1765         return;
1766     }
1767     if (compositingType() == OpenGLCompositing) {
1768         GLTexture *t = w->bufferAsTexture();
1769         if (!t) {
1770             return;
1771         }
1772 
1773         ShaderTraits traits = ShaderTrait::MapTexture;
1774         const qreal a = w->opacity();
1775         if (a != 1.0) {
1776             traits |= ShaderTrait::Modulate;
1777         }
1778 
1779         GLShader *shader = ShaderManager::instance()->pushShader(traits);
1780         const QRectF rect = scaledRect(w->geometry(), m_scene->renderer()->renderTargetScale());
1781 
1782         QMatrix4x4 mvp(m_scene->renderer()->renderTargetProjectionMatrix());
1783         mvp.translate(rect.x(), rect.y());
1784         shader->setUniform(GLShader::ModelViewProjectionMatrix, mvp);
1785 
1786         if (a != 1.0) {
1787             shader->setUniform(GLShader::ModulationConstant, QVector4D(a, a, a, a));
1788         }
1789 
1790         glEnable(GL_BLEND);
1791         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1792         t->bind();
1793         t->render(w->geometry(), m_scene->renderer()->renderTargetScale());
1794         t->unbind();
1795         glDisable(GL_BLEND);
1796 
1797         ShaderManager::instance()->popShader();
1798     } else if (compositingType() == QPainterCompositing) {
1799         QPainter *painter = effects->scenePainter();
1800         const QImage buffer = w->bufferAsImage();
1801         if (buffer.isNull()) {
1802             return;
1803         }
1804         painter->save();
1805         painter->setOpacity(w->opacity());
1806         painter->drawImage(w->geometry(), buffer);
1807         painter->restore();
1808     }
1809 }
1810 
1811 SessionState EffectsHandlerImpl::sessionState() const
1812 {
1813     return Workspace::self()->sessionManager()->state();
1814 }
1815 
1816 QList<EffectScreen *> EffectsHandlerImpl::screens() const
1817 {
1818     return m_effectScreens;
1819 }
1820 
1821 EffectScreen *EffectsHandlerImpl::screenAt(const QPoint &point) const
1822 {
1823     return EffectScreenImpl::get(Workspace::self()->outputAt(point));
1824 }
1825 
1826 EffectScreen *EffectsHandlerImpl::findScreen(const QString &name) const
1827 {
1828     for (EffectScreen *screen : std::as_const(m_effectScreens)) {
1829         if (screen->name() == name) {
1830             return screen;
1831         }
1832     }
1833     return nullptr;
1834 }
1835 
1836 EffectScreen *EffectsHandlerImpl::findScreen(int screenId) const
1837 {
1838     return m_effectScreens.value(screenId);
1839 }
1840 
1841 void EffectsHandlerImpl::slotOutputAdded(Output *output)
1842 {
1843     EffectScreen *screen = new EffectScreenImpl(output, this);
1844     m_effectScreens.append(screen);
1845     Q_EMIT screenAdded(screen);
1846 }
1847 
1848 void EffectsHandlerImpl::slotOutputRemoved(Output *output)
1849 {
1850     EffectScreen *screen = EffectScreenImpl::get(output);
1851     m_effectScreens.removeOne(screen);
1852     Q_EMIT screenRemoved(screen);
1853     delete screen;
1854 }
1855 
1856 void EffectsHandlerImpl::renderScreen(EffectScreen *screen)
1857 {
1858     RenderTarget renderTarget(GLFramebuffer::currentFramebuffer());
1859     renderTarget.setDevicePixelRatio(screen->devicePixelRatio());
1860 
1861     auto output = static_cast<EffectScreenImpl *>(screen)->platformOutput();
1862 
1863     RenderLayer layer(output->renderLoop());
1864     SceneDelegate delegate(m_scene, output);
1865     delegate.setLayer(&layer);
1866 
1867     m_scene->prePaint(&delegate);
1868     m_scene->paint(&renderTarget, output->geometry());
1869     m_scene->postPaint();
1870 }
1871 
1872 bool EffectsHandlerImpl::isCursorHidden() const
1873 {
1874     return Cursors::self()->isCursorHidden();
1875 }
1876 
1877 QRect EffectsHandlerImpl::renderTargetRect() const
1878 {
1879     return m_scene->renderer()->renderTargetRect();
1880 }
1881 
1882 qreal EffectsHandlerImpl::renderTargetScale() const
1883 {
1884     return m_scene->renderer()->renderTargetScale();
1885 }
1886 
1887 KWin::EffectWindow *EffectsHandlerImpl::inputPanel() const
1888 {
1889     if (!kwinApp()->inputMethod() || !kwinApp()->inputMethod()->isEnabled()) {
1890         return nullptr;
1891     }
1892 
1893     auto panel = kwinApp()->inputMethod()->panel();
1894     if (panel) {
1895         return panel->effectWindow();
1896     }
1897     return nullptr;
1898 }
1899 
1900 bool EffectsHandlerImpl::isInputPanelOverlay() const
1901 {
1902     if (!kwinApp()->inputMethod() || !kwinApp()->inputMethod()->isEnabled()) {
1903         return true;
1904     }
1905 
1906     auto panel = kwinApp()->inputMethod()->panel();
1907     if (panel) {
1908         return panel->mode() == InputPanelV1Window::Mode::Overlay;
1909     }
1910     return true;
1911 }
1912 
1913 //****************************************
1914 // EffectScreenImpl
1915 //****************************************
1916 
1917 EffectScreenImpl::EffectScreenImpl(Output *output, QObject *parent)
1918     : EffectScreen(parent)
1919     , m_platformOutput(output)
1920 {
1921     m_platformOutput->m_effectScreen = this;
1922 
1923     connect(output, &Output::aboutToChange, this, &EffectScreen::aboutToChange);
1924     connect(output, &Output::changed, this, &EffectScreen::changed);
1925     connect(output, &Output::wakeUp, this, &EffectScreen::wakeUp);
1926     connect(output, &Output::aboutToTurnOff, this, &EffectScreen::aboutToTurnOff);
1927     connect(output, &Output::scaleChanged, this, &EffectScreen::devicePixelRatioChanged);
1928     connect(output, &Output::geometryChanged, this, &EffectScreen::geometryChanged);
1929 }
1930 
1931 EffectScreenImpl::~EffectScreenImpl()
1932 {
1933     if (m_platformOutput) {
1934         m_platformOutput->m_effectScreen = nullptr;
1935     }
1936 }
1937 
1938 EffectScreenImpl *EffectScreenImpl::get(Output *output)
1939 {
1940     return output->m_effectScreen;
1941 }
1942 
1943 Output *EffectScreenImpl::platformOutput() const
1944 {
1945     return m_platformOutput;
1946 }
1947 
1948 QString EffectScreenImpl::name() const
1949 {
1950     return m_platformOutput->name();
1951 }
1952 
1953 QString EffectScreenImpl::manufacturer() const
1954 {
1955     return m_platformOutput->manufacturer();
1956 }
1957 
1958 QString EffectScreenImpl::model() const
1959 {
1960     return m_platformOutput->model();
1961 }
1962 
1963 QString EffectScreenImpl::serialNumber() const
1964 {
1965     return m_platformOutput->serialNumber();
1966 }
1967 
1968 qreal EffectScreenImpl::devicePixelRatio() const
1969 {
1970     return m_platformOutput->scale();
1971 }
1972 
1973 QRect EffectScreenImpl::geometry() const
1974 {
1975     return m_platformOutput->geometry();
1976 }
1977 
1978 int EffectScreenImpl::refreshRate() const
1979 {
1980     return m_platformOutput->refreshRate();
1981 }
1982 
1983 EffectScreen::Transform EffectScreenImpl::transform() const
1984 {
1985     return EffectScreen::Transform(m_platformOutput->transform());
1986 }
1987 
1988 //****************************************
1989 // EffectWindowImpl
1990 //****************************************
1991 
1992 EffectWindowImpl::EffectWindowImpl(Window *window)
1993     : m_window(window)
1994     , m_windowItem(nullptr)
1995 {
1996     // Deleted windows are not managed. So, when windowClosed signal is
1997     // emitted, effects can't distinguish managed windows from unmanaged
1998     // windows(e.g. combo box popups, popup menus, etc). Save value of the
1999     // managed property during construction of EffectWindow. At that time,
2000     // parent can be Client, XdgShellClient, or Unmanaged. So, later on, when
2001     // an instance of Deleted becomes parent of the EffectWindow, effects
2002     // can still figure out whether it is/was a managed window.
2003     managed = window->isClient();
2004 
2005     m_waylandWindow = qobject_cast<KWin::WaylandWindow *>(window) != nullptr;
2006     m_x11Window = qobject_cast<KWin::X11Window *>(window) != nullptr || qobject_cast<KWin::Unmanaged *>(window) != nullptr;
2007 }
2008 
2009 EffectWindowImpl::~EffectWindowImpl()
2010 {
2011 }
2012 
2013 void EffectWindowImpl::refVisible(const EffectWindowVisibleRef *holder)
2014 {
2015     m_windowItem->refVisible(holder->reason());
2016 }
2017 
2018 void EffectWindowImpl::unrefVisible(const EffectWindowVisibleRef *holder)
2019 {
2020     m_windowItem->unrefVisible(holder->reason());
2021 }
2022 
2023 void EffectWindowImpl::addRepaint(const QRect &r)
2024 {
2025     m_windowItem->scheduleRepaint(QRegion(r));
2026 }
2027 
2028 void EffectWindowImpl::addRepaintFull()
2029 {
2030     m_windowItem->scheduleRepaint(m_windowItem->boundingRect());
2031 }
2032 
2033 void EffectWindowImpl::addLayerRepaint(const QRect &r)
2034 {
2035     m_windowItem->scheduleRepaint(m_windowItem->mapFromGlobal(r));
2036 }
2037 
2038 const EffectWindowGroup *EffectWindowImpl::group() const
2039 {
2040     if (auto c = qobject_cast<X11Window *>(m_window)) {
2041         return c->group()->effectGroup();
2042     }
2043     return nullptr; // TODO
2044 }
2045 
2046 void EffectWindowImpl::refWindow()
2047 {
2048     if (auto d = static_cast<Deleted *>(m_window->isDeleted() ? m_window : nullptr)) {
2049         return d->refWindow();
2050     }
2051     Q_UNREACHABLE(); // TODO
2052 }
2053 
2054 void EffectWindowImpl::unrefWindow()
2055 {
2056     if (auto d = static_cast<Deleted *>(m_window->isDeleted() ? m_window : nullptr)) {
2057         return d->unrefWindow(); // delays deletion in case
2058     }
2059     Q_UNREACHABLE(); // TODO
2060 }
2061 
2062 EffectScreen *EffectWindowImpl::screen() const
2063 {
2064     return EffectScreenImpl::get(m_window->output());
2065 }
2066 
2067 #define WINDOW_HELPER(rettype, prototype, toplevelPrototype) \
2068     rettype EffectWindowImpl::prototype() const              \
2069     {                                                        \
2070         return m_window->toplevelPrototype();                \
2071     }
2072 
2073 WINDOW_HELPER(double, opacity, opacity)
2074 WINDOW_HELPER(qreal, x, x)
2075 WINDOW_HELPER(qreal, y, y)
2076 WINDOW_HELPER(qreal, width, width)
2077 WINDOW_HELPER(qreal, height, height)
2078 WINDOW_HELPER(QPointF, pos, pos)
2079 WINDOW_HELPER(QSizeF, size, size)
2080 WINDOW_HELPER(QRectF, geometry, frameGeometry)
2081 WINDOW_HELPER(QRectF, frameGeometry, frameGeometry)
2082 WINDOW_HELPER(QRectF, bufferGeometry, bufferGeometry)
2083 WINDOW_HELPER(QRectF, clientGeometry, clientGeometry)
2084 WINDOW_HELPER(QRectF, expandedGeometry, visibleGeometry)
2085 WINDOW_HELPER(QRectF, rect, rect)
2086 WINDOW_HELPER(int, desktop, desktop)
2087 WINDOW_HELPER(bool, isDesktop, isDesktop)
2088 WINDOW_HELPER(bool, isDock, isDock)
2089 WINDOW_HELPER(bool, isToolbar, isToolbar)
2090 WINDOW_HELPER(bool, isMenu, isMenu)
2091 WINDOW_HELPER(bool, isNormalWindow, isNormalWindow)
2092 WINDOW_HELPER(bool, isDialog, isDialog)
2093 WINDOW_HELPER(bool, isSplash, isSplash)
2094 WINDOW_HELPER(bool, isUtility, isUtility)
2095 WINDOW_HELPER(bool, isDropdownMenu, isDropdownMenu)
2096 WINDOW_HELPER(bool, isPopupMenu, isPopupMenu)
2097 WINDOW_HELPER(bool, isTooltip, isTooltip)
2098 WINDOW_HELPER(bool, isNotification, isNotification)
2099 WINDOW_HELPER(bool, isCriticalNotification, isCriticalNotification)
2100 WINDOW_HELPER(bool, isAppletPopup, isAppletPopup)
2101 WINDOW_HELPER(bool, isOnScreenDisplay, isOnScreenDisplay)
2102 WINDOW_HELPER(bool, isComboBox, isComboBox)
2103 WINDOW_HELPER(bool, isDNDIcon, isDNDIcon)
2104 WINDOW_HELPER(bool, isDeleted, isDeleted)
2105 WINDOW_HELPER(QString, windowRole, windowRole)
2106 WINDOW_HELPER(QStringList, activities, activities)
2107 WINDOW_HELPER(bool, skipsCloseAnimation, skipsCloseAnimation)
2108 WINDOW_HELPER(KWaylandServer::SurfaceInterface *, surface, surface)
2109 WINDOW_HELPER(bool, isPopupWindow, isPopupWindow)
2110 WINDOW_HELPER(bool, isOutline, isOutline)
2111 WINDOW_HELPER(bool, isLockScreen, isLockScreen)
2112 WINDOW_HELPER(pid_t, pid, pid)
2113 WINDOW_HELPER(qlonglong, windowId, window)
2114 WINDOW_HELPER(QUuid, internalId, internalId)
2115 WINDOW_HELPER(bool, isHidden, isHiddenInternal)
2116 
2117 #undef WINDOW_HELPER
2118 
2119 // TODO: Merge Window and Deleted.
2120 #define MANAGED_HELPER(rettype, prototype, propertyname, defaultValue)                     \
2121     rettype EffectWindowImpl::prototype() const                                            \
2122     {                                                                                      \
2123         auto client = static_cast<Window *>(m_window->isClient() ? m_window : nullptr);    \
2124         if (client) {                                                                      \
2125             return client->propertyname();                                                 \
2126         }                                                                                  \
2127         auto deleted = static_cast<Deleted *>(m_window->isDeleted() ? m_window : nullptr); \
2128         if (deleted) {                                                                     \
2129             return deleted->propertyname();                                                \
2130         }                                                                                  \
2131         return defaultValue;                                                               \
2132     }
2133 
2134 MANAGED_HELPER(bool, isMinimized, isMinimized, false)
2135 MANAGED_HELPER(bool, isModal, isModal, false)
2136 MANAGED_HELPER(bool, isFullScreen, isFullScreen, false)
2137 MANAGED_HELPER(bool, keepAbove, keepAbove, false)
2138 MANAGED_HELPER(bool, keepBelow, keepBelow, false)
2139 MANAGED_HELPER(QString, caption, caption, QString());
2140 MANAGED_HELPER(QVector<uint>, desktops, x11DesktopIds, QVector<uint>());
2141 MANAGED_HELPER(bool, isMovable, isMovable, false)
2142 MANAGED_HELPER(bool, isMovableAcrossScreens, isMovableAcrossScreens, false)
2143 MANAGED_HELPER(bool, isUserMove, isInteractiveMove, false)
2144 MANAGED_HELPER(bool, isUserResize, isInteractiveResize, false)
2145 MANAGED_HELPER(QRectF, iconGeometry, iconGeometry, QRectF())
2146 MANAGED_HELPER(bool, isSpecialWindow, isSpecialWindow, true)
2147 MANAGED_HELPER(bool, acceptsFocus, wantsInput, true) // We don't actually know...
2148 MANAGED_HELPER(QIcon, icon, icon, QIcon())
2149 MANAGED_HELPER(bool, isSkipSwitcher, skipSwitcher, false)
2150 MANAGED_HELPER(bool, decorationHasAlpha, decorationHasAlpha, false)
2151 MANAGED_HELPER(bool, isUnresponsive, unresponsive, false)
2152 
2153 #undef MANAGED_HELPER
2154 
2155 // legacy from tab groups, can be removed when no effects use this any more.
2156 bool EffectWindowImpl::isCurrentTab() const
2157 {
2158     return true;
2159 }
2160 
2161 QString EffectWindowImpl::windowClass() const
2162 {
2163     return m_window->resourceName() + QLatin1Char(' ') + m_window->resourceClass();
2164 }
2165 
2166 QRectF EffectWindowImpl::contentsRect() const
2167 {
2168     return QRectF(m_window->clientPos(), m_window->clientSize());
2169 }
2170 
2171 NET::WindowType EffectWindowImpl::windowType() const
2172 {
2173     return m_window->windowType();
2174 }
2175 
2176 QSizeF EffectWindowImpl::basicUnit() const
2177 {
2178     if (auto window = qobject_cast<X11Window *>(m_window)) {
2179         return window->basicUnit();
2180     }
2181     return QSize(1, 1);
2182 }
2183 
2184 void EffectWindowImpl::setWindow(Window *w)
2185 {
2186     m_window = w;
2187     setParent(w);
2188 }
2189 
2190 void EffectWindowImpl::setWindowItem(WindowItem *item)
2191 {
2192     m_windowItem = item;
2193 }
2194 
2195 QRectF EffectWindowImpl::decorationInnerRect() const
2196 {
2197     return m_window->rect() - m_window->frameMargins();
2198 }
2199 
2200 KDecoration2::Decoration *EffectWindowImpl::decoration() const
2201 {
2202     return m_window->decoration();
2203 }
2204 
2205 QByteArray EffectWindowImpl::readProperty(long atom, long type, int format) const
2206 {
2207     if (!kwinApp()->x11Connection()) {
2208         return QByteArray();
2209     }
2210     return readWindowProperty(window()->window(), atom, type, format);
2211 }
2212 
2213 void EffectWindowImpl::deleteProperty(long int atom) const
2214 {
2215     if (kwinApp()->x11Connection()) {
2216         deleteWindowProperty(window()->window(), atom);
2217     }
2218 }
2219 
2220 EffectWindow *EffectWindowImpl::findModal()
2221 {
2222     Window *modal = m_window->findModal();
2223     if (modal) {
2224         return modal->effectWindow();
2225     }
2226 
2227     return nullptr;
2228 }
2229 
2230 EffectWindow *EffectWindowImpl::transientFor()
2231 {
2232     Window *transientFor = m_window->transientFor();
2233     if (transientFor) {
2234         return transientFor->effectWindow();
2235     }
2236 
2237     return nullptr;
2238 }
2239 
2240 QWindow *EffectWindowImpl::internalWindow() const
2241 {
2242     if (auto window = qobject_cast<InternalWindow *>(m_window)) {
2243         return window->handle();
2244     }
2245     return nullptr;
2246 }
2247 
2248 template<typename T>
2249 EffectWindowList getMainWindows(T *c)
2250 {
2251     const auto mainwindows = c->mainWindows();
2252     EffectWindowList ret;
2253     ret.reserve(mainwindows.size());
2254     std::transform(std::cbegin(mainwindows), std::cend(mainwindows),
2255                    std::back_inserter(ret),
2256                    [](auto window) {
2257                        return window->effectWindow();
2258                    });
2259     return ret;
2260 }
2261 
2262 EffectWindowList EffectWindowImpl::mainWindows() const
2263 {
2264     if (auto client = static_cast<Window *>(m_window->isClient() ? m_window : nullptr)) {
2265         return getMainWindows(client);
2266     }
2267 
2268     if (auto deleted = static_cast<Deleted *>(m_window->isDeleted() ? m_window : nullptr)) {
2269         return getMainWindows(deleted);
2270     }
2271 
2272     return {};
2273 }
2274 
2275 void EffectWindowImpl::setData(int role, const QVariant &data)
2276 {
2277     if (!data.isNull()) {
2278         dataMap[role] = data;
2279     } else {
2280         dataMap.remove(role);
2281     }
2282     Q_EMIT effects->windowDataChanged(this, role);
2283 }
2284 
2285 QVariant EffectWindowImpl::data(int role) const
2286 {
2287     return dataMap.value(role);
2288 }
2289 
2290 void EffectWindowImpl::elevate(bool elevate)
2291 {
2292     effects->setElevatedWindow(this, elevate);
2293 }
2294 
2295 void EffectWindowImpl::minimize()
2296 {
2297     if (m_window->isClient()) {
2298         m_window->minimize();
2299     }
2300 }
2301 
2302 void EffectWindowImpl::unminimize()
2303 {
2304     if (m_window->isClient()) {
2305         m_window->unminimize();
2306     }
2307 }
2308 
2309 void EffectWindowImpl::closeWindow()
2310 {
2311     if (m_window->isClient()) {
2312         m_window->closeWindow();
2313     }
2314 }
2315 
2316 void EffectWindowImpl::referencePreviousWindowPixmap()
2317 {
2318     // TODO: Implement.
2319 }
2320 
2321 void EffectWindowImpl::unreferencePreviousWindowPixmap()
2322 {
2323     // TODO: Implement.
2324 }
2325 
2326 bool EffectWindowImpl::isManaged() const
2327 {
2328     return managed;
2329 }
2330 
2331 bool EffectWindowImpl::isWaylandClient() const
2332 {
2333     return m_waylandWindow;
2334 }
2335 
2336 bool EffectWindowImpl::isX11Client() const
2337 {
2338     return m_x11Window;
2339 }
2340 
2341 //****************************************
2342 // EffectWindowGroupImpl
2343 //****************************************
2344 
2345 EffectWindowList EffectWindowGroupImpl::members() const
2346 {
2347     const auto memberList = group->members();
2348     EffectWindowList ret;
2349     ret.reserve(memberList.size());
2350     std::transform(std::cbegin(memberList), std::cend(memberList), std::back_inserter(ret), [](auto window) {
2351         return window->effectWindow();
2352     });
2353     return ret;
2354 }
2355 
2356 //****************************************
2357 // EffectFrameImpl
2358 //****************************************
2359 
2360 EffectFrameQuickScene::EffectFrameQuickScene(EffectFrameStyle style, bool staticSize, QPoint position,
2361                                              Qt::Alignment alignment, QObject *parent)
2362     : OffscreenQuickScene(parent)
2363     , m_style(style)
2364     , m_static(staticSize)
2365     , m_point(position)
2366     , m_alignment(alignment)
2367 {
2368 
2369     QString name;
2370     switch (style) {
2371     case EffectFrameNone:
2372         name = QStringLiteral("none");
2373         break;
2374     case EffectFrameUnstyled:
2375         name = QStringLiteral("unstyled");
2376         break;
2377     case EffectFrameStyled:
2378         name = QStringLiteral("styled");
2379         break;
2380     }
2381 
2382     const QString defaultPath = QStringLiteral("kwin/frames/plasma/frame_%1.qml").arg(name);
2383     // TODO read from kwinApp()->config() "QmlPath" like Outline/OnScreenNotification
2384     // *if* someone really needs this to be configurable.
2385     const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, defaultPath);
2386 
2387     setSource(QUrl::fromLocalFile(path), QVariantMap{{QStringLiteral("effectFrame"), QVariant::fromValue(this)}});
2388 
2389     if (rootItem()) {
2390         connect(rootItem(), &QQuickItem::implicitWidthChanged, this, &EffectFrameQuickScene::reposition);
2391         connect(rootItem(), &QQuickItem::implicitHeightChanged, this, &EffectFrameQuickScene::reposition);
2392     }
2393 }
2394 
2395 EffectFrameQuickScene::~EffectFrameQuickScene() = default;
2396 
2397 EffectFrameStyle EffectFrameQuickScene::style() const
2398 {
2399     return m_style;
2400 }
2401 
2402 bool EffectFrameQuickScene::isStatic() const
2403 {
2404     return m_static;
2405 }
2406 
2407 const QFont &EffectFrameQuickScene::font() const
2408 {
2409     return m_font;
2410 }
2411 
2412 void EffectFrameQuickScene::setFont(const QFont &font)
2413 {
2414     if (m_font == font) {
2415         return;
2416     }
2417 
2418     m_font = font;
2419     Q_EMIT fontChanged(font);
2420     reposition();
2421 }
2422 
2423 const QIcon &EffectFrameQuickScene::icon() const
2424 {
2425     return m_icon;
2426 }
2427 
2428 void EffectFrameQuickScene::setIcon(const QIcon &icon)
2429 {
2430     m_icon = icon;
2431     Q_EMIT iconChanged(icon);
2432     reposition();
2433 }
2434 
2435 const QSize &EffectFrameQuickScene::iconSize() const
2436 {
2437     return m_iconSize;
2438 }
2439 
2440 void EffectFrameQuickScene::setIconSize(const QSize &iconSize)
2441 {
2442     if (m_iconSize == iconSize) {
2443         return;
2444     }
2445 
2446     m_iconSize = iconSize;
2447     Q_EMIT iconSizeChanged(iconSize);
2448     reposition();
2449 }
2450 
2451 const QString &EffectFrameQuickScene::text() const
2452 {
2453     return m_text;
2454 }
2455 
2456 void EffectFrameQuickScene::setText(const QString &text)
2457 {
2458     if (m_text == text) {
2459         return;
2460     }
2461 
2462     m_text = text;
2463     Q_EMIT textChanged(text);
2464     reposition();
2465 }
2466 
2467 qreal EffectFrameQuickScene::frameOpacity() const
2468 {
2469     return m_frameOpacity;
2470 }
2471 
2472 void EffectFrameQuickScene::setFrameOpacity(qreal frameOpacity)
2473 {
2474     if (m_frameOpacity != frameOpacity) {
2475         m_frameOpacity = frameOpacity;
2476         Q_EMIT frameOpacityChanged(frameOpacity);
2477     }
2478 }
2479 
2480 bool EffectFrameQuickScene::crossFadeEnabled() const
2481 {
2482     return m_crossFadeEnabled;
2483 }
2484 
2485 void EffectFrameQuickScene::setCrossFadeEnabled(bool enabled)
2486 {
2487     if (m_crossFadeEnabled != enabled) {
2488         m_crossFadeEnabled = enabled;
2489         Q_EMIT crossFadeEnabledChanged(enabled);
2490     }
2491 }
2492 
2493 qreal EffectFrameQuickScene::crossFadeProgress() const
2494 {
2495     return m_crossFadeProgress;
2496 }
2497 
2498 void EffectFrameQuickScene::setCrossFadeProgress(qreal progress)
2499 {
2500     if (m_crossFadeProgress != progress) {
2501         m_crossFadeProgress = progress;
2502         Q_EMIT crossFadeProgressChanged(progress);
2503     }
2504 }
2505 
2506 Qt::Alignment EffectFrameQuickScene::alignment() const
2507 {
2508     return m_alignment;
2509 }
2510 
2511 void EffectFrameQuickScene::setAlignment(Qt::Alignment alignment)
2512 {
2513     if (m_alignment == alignment) {
2514         return;
2515     }
2516 
2517     m_alignment = alignment;
2518     reposition();
2519 }
2520 
2521 QPoint EffectFrameQuickScene::position() const
2522 {
2523     return m_point;
2524 }
2525 
2526 void EffectFrameQuickScene::setPosition(const QPoint &point)
2527 {
2528     if (m_point == point) {
2529         return;
2530     }
2531 
2532     m_point = point;
2533     reposition();
2534 }
2535 
2536 void EffectFrameQuickScene::reposition()
2537 {
2538     if (!rootItem() || m_point.x() < 0 || m_point.y() < 0) {
2539         return;
2540     }
2541 
2542     QSizeF size;
2543     if (m_static) {
2544         size = rootItem()->size();
2545     } else {
2546         size = QSizeF(rootItem()->implicitWidth(), rootItem()->implicitHeight());
2547     }
2548 
2549     QRect geometry(QPoint(), size.toSize());
2550 
2551     if (m_alignment & Qt::AlignLeft)
2552         geometry.moveLeft(m_point.x());
2553     else if (m_alignment & Qt::AlignRight)
2554         geometry.moveLeft(m_point.x() - geometry.width());
2555     else
2556         geometry.moveLeft(m_point.x() - geometry.width() / 2);
2557     if (m_alignment & Qt::AlignTop)
2558         geometry.moveTop(m_point.y());
2559     else if (m_alignment & Qt::AlignBottom)
2560         geometry.moveTop(m_point.y() - geometry.height());
2561     else
2562         geometry.moveTop(m_point.y() - geometry.height() / 2);
2563 
2564     if (geometry == this->geometry()) {
2565         return;
2566     }
2567 
2568     setGeometry(geometry);
2569 }
2570 
2571 EffectFrameImpl::EffectFrameImpl(EffectFrameStyle style, bool staticSize, QPoint position, Qt::Alignment alignment)
2572     : QObject(nullptr)
2573     , EffectFrame()
2574     , m_view(new EffectFrameQuickScene(style, staticSize, position, alignment, nullptr))
2575 {
2576     connect(m_view, &OffscreenQuickScene::repaintNeeded, this, [this] {
2577         effects->addRepaint(geometry());
2578     });
2579     connect(m_view, &OffscreenQuickScene::geometryChanged, this, [this](const QRect &oldGeometry, const QRect &newGeometry) {
2580         effects->addRepaint(oldGeometry);
2581         m_geometry = newGeometry;
2582         effects->addRepaint(newGeometry);
2583     });
2584 }
2585 
2586 EffectFrameImpl::~EffectFrameImpl()
2587 {
2588     // Effects often destroy their cached TextFrames in pre/postPaintScreen.
2589     // Destroying an OffscreenQuickView changes GL context, which we
2590     // must not do during effect rendering.
2591     // Delay destruction of the view until after the rendering.
2592     m_view->deleteLater();
2593 }
2594 
2595 Qt::Alignment EffectFrameImpl::alignment() const
2596 {
2597     return m_view->alignment();
2598 }
2599 
2600 void EffectFrameImpl::setAlignment(Qt::Alignment alignment)
2601 {
2602     m_view->setAlignment(alignment);
2603 }
2604 
2605 const QFont &EffectFrameImpl::font() const
2606 {
2607     return m_view->font();
2608 }
2609 
2610 void EffectFrameImpl::setFont(const QFont &font)
2611 {
2612     m_view->setFont(font);
2613 }
2614 
2615 void EffectFrameImpl::free()
2616 {
2617     m_view->hide();
2618 }
2619 
2620 const QRect &EffectFrameImpl::geometry() const
2621 {
2622     // Can't forward to OffscreenQuickScene::geometry() because we return a reference.
2623     return m_geometry;
2624 }
2625 
2626 void EffectFrameImpl::setGeometry(const QRect &geometry, bool force)
2627 {
2628     m_view->setGeometry(geometry);
2629 }
2630 
2631 const QIcon &EffectFrameImpl::icon() const
2632 {
2633     return m_view->icon();
2634 }
2635 
2636 void EffectFrameImpl::setIcon(const QIcon &icon)
2637 {
2638     m_view->setIcon(icon);
2639 
2640     if (m_view->iconSize().isEmpty() && !icon.availableSizes().isEmpty()) { // Set a size if we don't already have one
2641         setIconSize(icon.availableSizes().constFirst());
2642     }
2643 }
2644 
2645 const QSize &EffectFrameImpl::iconSize() const
2646 {
2647     return m_view->iconSize();
2648 }
2649 
2650 void EffectFrameImpl::setIconSize(const QSize &size)
2651 {
2652     m_view->setIconSize(size);
2653 }
2654 
2655 void EffectFrameImpl::setPosition(const QPoint &point)
2656 {
2657     m_view->setPosition(point);
2658 }
2659 
2660 void EffectFrameImpl::render(const QRegion &region, double opacity, double frameOpacity)
2661 {
2662     if (!m_view->rootItem()) {
2663         return;
2664     }
2665 
2666     m_view->show();
2667 
2668     m_view->setOpacity(opacity);
2669     m_view->setFrameOpacity(frameOpacity);
2670 
2671     effects->renderOffscreenQuickView(m_view);
2672 }
2673 
2674 const QString &EffectFrameImpl::text() const
2675 {
2676     return m_view->text();
2677 }
2678 
2679 void EffectFrameImpl::setText(const QString &text)
2680 {
2681     m_view->setText(text);
2682 }
2683 
2684 EffectFrameStyle EffectFrameImpl::style() const
2685 {
2686     return m_view->style();
2687 }
2688 
2689 bool EffectFrameImpl::isCrossFade() const
2690 {
2691     return m_view->crossFadeEnabled();
2692 }
2693 
2694 void EffectFrameImpl::enableCrossFade(bool enable)
2695 {
2696     m_view->setCrossFadeEnabled(enable);
2697 }
2698 
2699 qreal EffectFrameImpl::crossFadeProgress() const
2700 {
2701     return m_view->crossFadeProgress();
2702 }
2703 
2704 void EffectFrameImpl::setCrossFadeProgress(qreal progress)
2705 {
2706     m_view->setCrossFadeProgress(progress);
2707 }
2708 
2709 } // namespace