File indexing completed on 2024-05-19 05:31:41

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: 2009 Lucas Murray <lmurray@undefinedfire.com>
0007     SPDX-FileCopyrightText: 2018 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "effect/effecthandler.h"
0013 
0014 #include "config-kwin.h"
0015 
0016 #include "compositor.h"
0017 #include "core/output.h"
0018 #include "core/renderbackend.h"
0019 #include "core/rendertarget.h"
0020 #include "core/renderviewport.h"
0021 #include "decorations/decorationbridge.h"
0022 #include "effect/effectloader.h"
0023 #include "effect/offscreenquickview.h"
0024 #include "effectsadaptor.h"
0025 #include "input.h"
0026 #include "input_event.h"
0027 #include "inputmethod.h"
0028 #include "inputpanelv1window.h"
0029 #include "keyboard_input.h"
0030 #include "opengl/glshader.h"
0031 #include "opengl/glshadermanager.h"
0032 #include "opengl/gltexture.h"
0033 #include "osd.h"
0034 #include "pointer_input.h"
0035 #include "scene/itemrenderer.h"
0036 #include "scene/windowitem.h"
0037 #include "scene/workspacescene.h"
0038 #include "screenedge.h"
0039 #include "scripting/scripting.h"
0040 #include "sm.h"
0041 #include "virtualdesktops.h"
0042 #include "wayland_server.h"
0043 #include "window_property_notify_x11_filter.h"
0044 #include "workspace.h"
0045 #include "x11window.h"
0046 
0047 #if KWIN_BUILD_ACTIVITIES
0048 #include "activities.h"
0049 #endif
0050 #if KWIN_BUILD_TABBOX
0051 #include "tabbox/tabbox.h"
0052 #endif
0053 #if KWIN_BUILD_SCREENLOCKER
0054 #include "screenlockerwatcher.h"
0055 #endif
0056 
0057 #include <KDecoration2/Decoration>
0058 #include <KDecoration2/DecorationSettings>
0059 
0060 #include <QFontMetrics>
0061 #include <QMatrix4x4>
0062 #include <QPainter>
0063 #include <QPixmap>
0064 #include <QTimeLine>
0065 #include <QVariant>
0066 #include <QWindow>
0067 #include <QtMath>
0068 
0069 namespace KWin
0070 {
0071 
0072 static QByteArray readWindowProperty(xcb_window_t win, xcb_atom_t atom, xcb_atom_t type, int format)
0073 {
0074     if (win == XCB_WINDOW_NONE) {
0075         return QByteArray();
0076     }
0077     uint32_t len = 32768;
0078     for (;;) {
0079         Xcb::Property prop(false, win, atom, XCB_ATOM_ANY, 0, len);
0080         if (prop.isNull()) {
0081             // get property failed
0082             return QByteArray();
0083         }
0084         if (prop->bytes_after > 0) {
0085             len *= 2;
0086             continue;
0087         }
0088         return prop.toByteArray(format, type);
0089     }
0090 }
0091 
0092 static xcb_atom_t registerSupportProperty(const QByteArray &propertyName)
0093 {
0094     auto c = kwinApp()->x11Connection();
0095     if (!c) {
0096         return XCB_ATOM_NONE;
0097     }
0098     // get the atom for the propertyName
0099     UniqueCPtr<xcb_intern_atom_reply_t> atomReply(xcb_intern_atom_reply(c,
0100                                                                         xcb_intern_atom_unchecked(c, false, propertyName.size(), propertyName.constData()),
0101                                                                         nullptr));
0102     if (!atomReply) {
0103         return XCB_ATOM_NONE;
0104     }
0105     // announce property on root window
0106     unsigned char dummy = 0;
0107     xcb_change_property(c, XCB_PROP_MODE_REPLACE, kwinApp()->x11RootWindow(), atomReply->atom, atomReply->atom, 8, 1, &dummy);
0108     // TODO: add to _NET_SUPPORTED
0109     return atomReply->atom;
0110 }
0111 
0112 //****************************************
0113 // EffectsHandler
0114 //****************************************
0115 
0116 EffectsHandler::EffectsHandler(Compositor *compositor, WorkspaceScene *scene)
0117     : keyboard_grab_effect(nullptr)
0118     , fullscreen_effect(nullptr)
0119     , compositing_type(compositor->backend()->compositingType())
0120     , m_compositor(compositor)
0121     , m_scene(scene)
0122     , m_effectLoader(new EffectLoader(this))
0123     , m_trackingCursorChanges(0)
0124 {
0125     if (compositing_type == NoCompositing) {
0126         return;
0127     }
0128     KWin::effects = this;
0129 
0130     qRegisterMetaType<QList<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     connect(options, &Options::animationSpeedChanged, this, &EffectsHandler::reconfigureEffects);
0143 
0144     Workspace *ws = Workspace::self();
0145     VirtualDesktopManager *vds = VirtualDesktopManager::self();
0146     connect(ws, &Workspace::showingDesktopChanged, this, [this](bool showing, bool animated) {
0147         if (animated) {
0148             Q_EMIT showingDesktopChanged(showing);
0149         }
0150     });
0151     connect(ws, &Workspace::currentDesktopChanged, this, [this](VirtualDesktop *old, Window *window) {
0152         VirtualDesktop *newDesktop = VirtualDesktopManager::self()->currentDesktop();
0153         Q_EMIT desktopChanged(old, newDesktop, window ? window->effectWindow() : nullptr);
0154     });
0155     connect(ws, &Workspace::currentDesktopChanging, this, [this](VirtualDesktop *currentDesktop, QPointF offset, KWin::Window *window) {
0156         Q_EMIT desktopChanging(currentDesktop, offset, window ? window->effectWindow() : nullptr);
0157     });
0158     connect(ws, &Workspace::currentDesktopChangingCancelled, this, [this]() {
0159         Q_EMIT desktopChangingCancelled();
0160     });
0161     connect(ws, &Workspace::windowAdded, this, [this](Window *window) {
0162         setupWindowConnections(window);
0163         Q_EMIT windowAdded(window->effectWindow());
0164     });
0165     connect(ws, &Workspace::windowActivated, this, [this](Window *window) {
0166         Q_EMIT windowActivated(window ? window->effectWindow() : nullptr);
0167     });
0168     connect(ws, &Workspace::deletedRemoved, this, [this](KWin::Window *d) {
0169         Q_EMIT windowDeleted(d->effectWindow());
0170     });
0171     connect(ws->sessionManager(), &SessionManager::stateChanged, this, &KWin::EffectsHandler::sessionStateChanged);
0172     connect(vds, &VirtualDesktopManager::layoutChanged, this, [this](int width, int height) {
0173         Q_EMIT desktopGridSizeChanged(QSize(width, height));
0174         Q_EMIT desktopGridWidthChanged(width);
0175         Q_EMIT desktopGridHeightChanged(height);
0176     });
0177     connect(vds, &VirtualDesktopManager::desktopAdded, this, &EffectsHandler::desktopAdded);
0178     connect(vds, &VirtualDesktopManager::desktopRemoved, this, &EffectsHandler::desktopRemoved);
0179     connect(Cursors::self()->mouse(), &Cursor::mouseChanged, this, &EffectsHandler::mouseChanged);
0180     connect(ws, &Workspace::geometryChanged, this, &EffectsHandler::virtualScreenSizeChanged);
0181     connect(ws, &Workspace::geometryChanged, this, &EffectsHandler::virtualScreenGeometryChanged);
0182 #if KWIN_BUILD_ACTIVITIES
0183     if (Activities *activities = Workspace::self()->activities()) {
0184         connect(activities, &Activities::added, this, &EffectsHandler::activityAdded);
0185         connect(activities, &Activities::removed, this, &EffectsHandler::activityRemoved);
0186         connect(activities, &Activities::currentChanged, this, &EffectsHandler::currentActivityChanged);
0187     }
0188 #endif
0189     connect(ws, &Workspace::stackingOrderChanged, this, &EffectsHandler::stackingOrderChanged);
0190 #if KWIN_BUILD_TABBOX
0191     TabBox::TabBox *tabBox = workspace()->tabbox();
0192     connect(tabBox, &TabBox::TabBox::tabBoxAdded, this, &EffectsHandler::tabBoxAdded);
0193     connect(tabBox, &TabBox::TabBox::tabBoxUpdated, this, &EffectsHandler::tabBoxUpdated);
0194     connect(tabBox, &TabBox::TabBox::tabBoxClosed, this, &EffectsHandler::tabBoxClosed);
0195     connect(tabBox, &TabBox::TabBox::tabBoxKeyEvent, this, &EffectsHandler::tabBoxKeyEvent);
0196 #endif
0197     connect(workspace()->screenEdges(), &ScreenEdges::approaching, this, &EffectsHandler::screenEdgeApproaching);
0198 #if KWIN_BUILD_SCREENLOCKER
0199     connect(kwinApp()->screenLockerWatcher(), &ScreenLockerWatcher::locked, this, &EffectsHandler::screenLockingChanged);
0200     connect(kwinApp()->screenLockerWatcher(), &ScreenLockerWatcher::aboutToLock, this, &EffectsHandler::screenAboutToLock);
0201 #endif
0202 
0203     connect(kwinApp(), &Application::x11ConnectionChanged, this, [this]() {
0204         registered_atoms.clear();
0205         for (auto it = m_propertiesForEffects.keyBegin(); it != m_propertiesForEffects.keyEnd(); it++) {
0206             const auto atom = registerSupportProperty(*it);
0207             if (atom == XCB_ATOM_NONE) {
0208                 continue;
0209             }
0210             m_compositor->keepSupportProperty(atom);
0211             m_managedProperties.insert(*it, atom);
0212             registerPropertyType(atom, true);
0213         }
0214         if (kwinApp()->x11Connection()) {
0215             m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
0216         } else {
0217             m_x11WindowPropertyNotify.reset();
0218         }
0219         Q_EMIT xcbConnectionChanged();
0220     });
0221 
0222     if (kwinApp()->x11Connection()) {
0223         m_x11WindowPropertyNotify = std::make_unique<WindowPropertyNotifyX11Filter>(this);
0224     }
0225 
0226     // connect all clients
0227     for (Window *window : ws->windows()) {
0228         setupWindowConnections(window);
0229     }
0230 
0231     connect(ws, &Workspace::outputAdded, this, &EffectsHandler::screenAdded);
0232     connect(ws, &Workspace::outputRemoved, this, &EffectsHandler::screenRemoved);
0233 
0234     if (auto inputMethod = kwinApp()->inputMethod()) {
0235         connect(inputMethod, &InputMethod::panelChanged, this, &EffectsHandler::inputPanelChanged);
0236     }
0237 
0238     reconfigure();
0239 }
0240 
0241 EffectsHandler::~EffectsHandler()
0242 {
0243     unloadAllEffects();
0244     KWin::effects = nullptr;
0245 }
0246 
0247 xcb_window_t EffectsHandler::x11RootWindow() const
0248 {
0249     return kwinApp()->x11RootWindow();
0250 }
0251 
0252 xcb_connection_t *EffectsHandler::xcbConnection() const
0253 {
0254     return kwinApp()->x11Connection();
0255 }
0256 
0257 CompositingType EffectsHandler::compositingType() const
0258 {
0259     return compositing_type;
0260 }
0261 
0262 bool EffectsHandler::isOpenGLCompositing() const
0263 {
0264     return compositing_type & OpenGLCompositing;
0265 }
0266 
0267 void EffectsHandler::unloadAllEffects()
0268 {
0269     for (const EffectPair &pair : std::as_const(loaded_effects)) {
0270         destroyEffect(pair.second);
0271     }
0272 
0273     effect_order.clear();
0274     m_effectLoader->clear();
0275 
0276     effectsChanged();
0277 }
0278 
0279 void EffectsHandler::setupWindowConnections(Window *window)
0280 {
0281     connect(window, &Window::closed, this, [this, window]() {
0282         if (window->effectWindow()) {
0283             Q_EMIT windowClosed(window->effectWindow());
0284         }
0285     });
0286 }
0287 
0288 void EffectsHandler::reconfigure()
0289 {
0290     m_effectLoader->queryAndLoadAll();
0291 }
0292 
0293 // the idea is that effects call this function again which calls the next one
0294 void EffectsHandler::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0295 {
0296     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
0297         (*m_currentPaintScreenIterator++)->prePaintScreen(data, presentTime);
0298         --m_currentPaintScreenIterator;
0299     }
0300     // no special final code
0301 }
0302 
0303 void EffectsHandler::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
0304 {
0305     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
0306         (*m_currentPaintScreenIterator++)->paintScreen(renderTarget, viewport, mask, region, screen);
0307         --m_currentPaintScreenIterator;
0308     } else {
0309         m_scene->finalPaintScreen(renderTarget, viewport, mask, region, screen);
0310     }
0311 }
0312 
0313 void EffectsHandler::postPaintScreen()
0314 {
0315     if (m_currentPaintScreenIterator != m_activeEffects.constEnd()) {
0316         (*m_currentPaintScreenIterator++)->postPaintScreen();
0317         --m_currentPaintScreenIterator;
0318     }
0319     // no special final code
0320 }
0321 
0322 void EffectsHandler::prePaintWindow(EffectWindow *w, WindowPrePaintData &data, std::chrono::milliseconds presentTime)
0323 {
0324     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
0325         (*m_currentPaintWindowIterator++)->prePaintWindow(w, data, presentTime);
0326         --m_currentPaintWindowIterator;
0327     }
0328     // no special final code
0329 }
0330 
0331 void EffectsHandler::paintWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
0332 {
0333     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
0334         (*m_currentPaintWindowIterator++)->paintWindow(renderTarget, viewport, w, mask, region, data);
0335         --m_currentPaintWindowIterator;
0336     } else {
0337         m_scene->finalPaintWindow(renderTarget, viewport, w, mask, region, data);
0338     }
0339 }
0340 
0341 void EffectsHandler::postPaintWindow(EffectWindow *w)
0342 {
0343     if (m_currentPaintWindowIterator != m_activeEffects.constEnd()) {
0344         (*m_currentPaintWindowIterator++)->postPaintWindow(w);
0345         --m_currentPaintWindowIterator;
0346     }
0347     // no special final code
0348 }
0349 
0350 Effect *EffectsHandler::provides(Effect::Feature ef)
0351 {
0352     for (int i = 0; i < loaded_effects.size(); ++i) {
0353         if (loaded_effects.at(i).second->provides(ef)) {
0354             return loaded_effects.at(i).second;
0355         }
0356     }
0357     return nullptr;
0358 }
0359 
0360 void EffectsHandler::drawWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
0361 {
0362     if (m_currentDrawWindowIterator != m_activeEffects.constEnd()) {
0363         (*m_currentDrawWindowIterator++)->drawWindow(renderTarget, viewport, w, mask, region, data);
0364         --m_currentDrawWindowIterator;
0365     } else {
0366         m_scene->finalDrawWindow(renderTarget, viewport, w, mask, region, data);
0367     }
0368 }
0369 
0370 void EffectsHandler::renderWindow(const RenderTarget &renderTarget, const RenderViewport &viewport, EffectWindow *w, int mask, const QRegion &region, WindowPaintData &data)
0371 {
0372     m_scene->finalDrawWindow(renderTarget, viewport, w, mask, region, data);
0373 }
0374 
0375 bool EffectsHandler::hasDecorationShadows() const
0376 {
0377     return false;
0378 }
0379 
0380 bool EffectsHandler::decorationsHaveAlpha() const
0381 {
0382     return true;
0383 }
0384 
0385 // start another painting pass
0386 void EffectsHandler::startPaint()
0387 {
0388     m_activeEffects.clear();
0389     m_activeEffects.reserve(loaded_effects.count());
0390     for (QList<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0391         if (it->second->isActive()) {
0392             m_activeEffects << it->second;
0393         }
0394     }
0395     m_currentDrawWindowIterator = m_activeEffects.constBegin();
0396     m_currentPaintWindowIterator = m_activeEffects.constBegin();
0397     m_currentPaintScreenIterator = m_activeEffects.constBegin();
0398 }
0399 
0400 void EffectsHandler::setActiveFullScreenEffect(Effect *e)
0401 {
0402     if (fullscreen_effect == e) {
0403         return;
0404     }
0405     const bool activeChanged = (e == nullptr || fullscreen_effect == nullptr);
0406     fullscreen_effect = e;
0407     Q_EMIT activeFullScreenEffectChanged();
0408     if (activeChanged) {
0409         Q_EMIT hasActiveFullScreenEffectChanged();
0410         workspace()->screenEdges()->checkBlocking();
0411     }
0412 }
0413 
0414 Effect *EffectsHandler::activeFullScreenEffect() const
0415 {
0416     return fullscreen_effect;
0417 }
0418 
0419 bool EffectsHandler::hasActiveFullScreenEffect() const
0420 {
0421     return fullscreen_effect;
0422 }
0423 
0424 bool EffectsHandler::grabKeyboard(Effect *effect)
0425 {
0426     if (keyboard_grab_effect != nullptr) {
0427         return false;
0428     }
0429     if (!doGrabKeyboard()) {
0430         return false;
0431     }
0432     keyboard_grab_effect = effect;
0433     return true;
0434 }
0435 
0436 bool EffectsHandler::doGrabKeyboard()
0437 {
0438     return true;
0439 }
0440 
0441 void EffectsHandler::ungrabKeyboard()
0442 {
0443     Q_ASSERT(keyboard_grab_effect != nullptr);
0444     doUngrabKeyboard();
0445     keyboard_grab_effect = nullptr;
0446     input()->keyboard()->update();
0447 }
0448 
0449 void EffectsHandler::doUngrabKeyboard()
0450 {
0451 }
0452 
0453 void EffectsHandler::grabbedKeyboardEvent(QKeyEvent *e)
0454 {
0455     if (keyboard_grab_effect != nullptr) {
0456         keyboard_grab_effect->grabbedKeyboardEvent(e);
0457     }
0458 }
0459 
0460 void EffectsHandler::startMouseInterception(Effect *effect, Qt::CursorShape shape)
0461 {
0462     if (m_grabbedMouseEffects.contains(effect)) {
0463         return;
0464     }
0465     m_grabbedMouseEffects.append(effect);
0466     if (m_grabbedMouseEffects.size() != 1) {
0467         return;
0468     }
0469     doStartMouseInterception(shape);
0470 }
0471 
0472 void EffectsHandler::doStartMouseInterception(Qt::CursorShape shape)
0473 {
0474     input()->pointer()->setEffectsOverrideCursor(shape);
0475 
0476     // We want to allow global shortcuts to be triggered when moving a
0477     // window so it is possible to pick up a window and then move it to a
0478     // different desktop by using the global shortcut to switch desktop.
0479     // However, that means that some other things can also be triggered. If
0480     // an effect that fill the screen gets triggered that way, we end up in a
0481     // weird state where the move will restart after the effect closes. So to
0482     // avoid that, abort move/resize if a full screen effect starts.
0483     if (workspace()->moveResizeWindow()) {
0484         workspace()->moveResizeWindow()->endInteractiveMoveResize();
0485     }
0486 }
0487 
0488 void EffectsHandler::stopMouseInterception(Effect *effect)
0489 {
0490     if (!m_grabbedMouseEffects.contains(effect)) {
0491         return;
0492     }
0493     m_grabbedMouseEffects.removeAll(effect);
0494     if (m_grabbedMouseEffects.isEmpty()) {
0495         doStopMouseInterception();
0496     }
0497 }
0498 
0499 void EffectsHandler::doStopMouseInterception()
0500 {
0501     input()->pointer()->removeEffectsOverrideCursor();
0502 }
0503 
0504 bool EffectsHandler::isMouseInterception() const
0505 {
0506     return m_grabbedMouseEffects.count() > 0;
0507 }
0508 
0509 bool EffectsHandler::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0510 {
0511     // TODO: reverse call order?
0512     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0513         if (it->second->touchDown(id, pos, time)) {
0514             return true;
0515         }
0516     }
0517     return false;
0518 }
0519 
0520 bool EffectsHandler::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0521 {
0522     // TODO: reverse call order?
0523     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0524         if (it->second->touchMotion(id, pos, time)) {
0525             return true;
0526         }
0527     }
0528     return false;
0529 }
0530 
0531 bool EffectsHandler::touchUp(qint32 id, std::chrono::microseconds time)
0532 {
0533     // TODO: reverse call order?
0534     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0535         if (it->second->touchUp(id, time)) {
0536             return true;
0537         }
0538     }
0539     return false;
0540 }
0541 
0542 bool EffectsHandler::tabletToolEvent(TabletEvent *event)
0543 {
0544     // TODO: reverse call order?
0545     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0546         if (it->second->tabletToolEvent(event)) {
0547             return true;
0548         }
0549     }
0550     return false;
0551 }
0552 
0553 bool EffectsHandler::tabletToolButtonEvent(uint button, bool pressed, const TabletToolId &tabletToolId, std::chrono::microseconds time)
0554 {
0555     // TODO: reverse call order?
0556     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0557         if (it->second->tabletToolButtonEvent(button, pressed, tabletToolId.m_uniqueId)) {
0558             return true;
0559         }
0560     }
0561     return false;
0562 }
0563 
0564 bool EffectsHandler::tabletPadButtonEvent(uint button, bool pressed, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0565 {
0566     // TODO: reverse call order?
0567     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0568         if (it->second->tabletPadButtonEvent(button, pressed, tabletPadId.data)) {
0569             return true;
0570         }
0571     }
0572     return false;
0573 }
0574 
0575 bool EffectsHandler::tabletPadStripEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0576 {
0577     // TODO: reverse call order?
0578     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0579         if (it->second->tabletPadStripEvent(number, position, isFinger, tabletPadId.data)) {
0580             return true;
0581         }
0582     }
0583     return false;
0584 }
0585 
0586 bool EffectsHandler::tabletPadRingEvent(int number, int position, bool isFinger, const TabletPadId &tabletPadId, std::chrono::microseconds time)
0587 {
0588     // TODO: reverse call order?
0589     for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
0590         if (it->second->tabletPadRingEvent(number, position, isFinger, tabletPadId.data)) {
0591             return true;
0592         }
0593     }
0594     return false;
0595 }
0596 
0597 void EffectsHandler::registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
0598 {
0599     input()->registerPointerShortcut(modifiers, pointerButtons, action);
0600 }
0601 
0602 void EffectsHandler::registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
0603 {
0604     input()->registerAxisShortcut(modifiers, axis, action);
0605 }
0606 
0607 void EffectsHandler::registerTouchpadSwipeShortcut(SwipeDirection dir, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
0608 {
0609     input()->registerTouchpadSwipeShortcut(dir, fingerCount, onUp, progressCallback);
0610 }
0611 
0612 void EffectsHandler::registerTouchpadPinchShortcut(PinchDirection dir, uint fingerCount, QAction *onUp, std::function<void(qreal)> progressCallback)
0613 {
0614     input()->registerTouchpadPinchShortcut(dir, fingerCount, onUp, progressCallback);
0615 }
0616 
0617 void EffectsHandler::registerTouchscreenSwipeShortcut(SwipeDirection direction, uint fingerCount, QAction *action, std::function<void(qreal)> progressCallback)
0618 {
0619     input()->registerTouchscreenSwipeShortcut(direction, fingerCount, action, progressCallback);
0620 }
0621 
0622 void EffectsHandler::startMousePolling()
0623 {
0624     if (Cursors::self()->mouse()) {
0625         Cursors::self()->mouse()->startMousePolling();
0626     }
0627 }
0628 
0629 void EffectsHandler::stopMousePolling()
0630 {
0631     if (Cursors::self()->mouse()) {
0632         Cursors::self()->mouse()->stopMousePolling();
0633     }
0634 }
0635 
0636 bool EffectsHandler::hasKeyboardGrab() const
0637 {
0638     return keyboard_grab_effect != nullptr;
0639 }
0640 
0641 void EffectsHandler::registerPropertyType(long atom, bool reg)
0642 {
0643     if (reg) {
0644         ++registered_atoms[atom]; // initialized to 0 if not present yet
0645     } else {
0646         if (--registered_atoms[atom] == 0) {
0647             registered_atoms.remove(atom);
0648         }
0649     }
0650 }
0651 
0652 xcb_atom_t EffectsHandler::announceSupportProperty(const QByteArray &propertyName, Effect *effect)
0653 {
0654     PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
0655     if (it != m_propertiesForEffects.end()) {
0656         // property has already been registered for an effect
0657         // just append Effect and return the atom stored in m_managedProperties
0658         if (!it.value().contains(effect)) {
0659             it.value().append(effect);
0660         }
0661         return m_managedProperties.value(propertyName, XCB_ATOM_NONE);
0662     }
0663     m_propertiesForEffects.insert(propertyName, QList<Effect *>() << effect);
0664     const auto atom = registerSupportProperty(propertyName);
0665     if (atom == XCB_ATOM_NONE) {
0666         return atom;
0667     }
0668     m_compositor->keepSupportProperty(atom);
0669     m_managedProperties.insert(propertyName, atom);
0670     registerPropertyType(atom, true);
0671     return atom;
0672 }
0673 
0674 void EffectsHandler::removeSupportProperty(const QByteArray &propertyName, Effect *effect)
0675 {
0676     PropertyEffectMap::iterator it = m_propertiesForEffects.find(propertyName);
0677     if (it == m_propertiesForEffects.end()) {
0678         // property is not registered - nothing to do
0679         return;
0680     }
0681     if (!it.value().contains(effect)) {
0682         // property is not registered for given effect - nothing to do
0683         return;
0684     }
0685     it.value().removeAll(effect);
0686     if (!it.value().isEmpty()) {
0687         // property still registered for another effect - nothing further to do
0688         return;
0689     }
0690     const xcb_atom_t atom = m_managedProperties.take(propertyName);
0691     registerPropertyType(atom, false);
0692     m_propertiesForEffects.remove(propertyName);
0693     m_compositor->removeSupportProperty(atom); // delayed removal
0694 }
0695 
0696 QByteArray EffectsHandler::readRootProperty(long atom, long type, int format) const
0697 {
0698     if (!kwinApp()->x11Connection()) {
0699         return QByteArray();
0700     }
0701     return readWindowProperty(kwinApp()->x11RootWindow(), atom, type, format);
0702 }
0703 
0704 void EffectsHandler::activateWindow(EffectWindow *effectWindow)
0705 {
0706     auto window = effectWindow->window();
0707     if (window->isClient()) {
0708         Workspace::self()->activateWindow(window, true);
0709     }
0710 }
0711 
0712 EffectWindow *EffectsHandler::activeWindow() const
0713 {
0714     return Workspace::self()->activeWindow() ? Workspace::self()->activeWindow()->effectWindow() : nullptr;
0715 }
0716 
0717 void EffectsHandler::moveWindow(EffectWindow *w, const QPoint &pos, bool snap, double snapAdjust)
0718 {
0719     auto window = w->window();
0720     if (!window->isClient() || !window->isMovable()) {
0721         return;
0722     }
0723 
0724     if (snap) {
0725         window->move(Workspace::self()->adjustWindowPosition(window, pos, true, snapAdjust));
0726     } else {
0727         window->move(pos);
0728     }
0729 }
0730 
0731 void EffectsHandler::windowToDesktops(EffectWindow *w, const QList<VirtualDesktop *> &desktops)
0732 {
0733     auto window = w->window();
0734     if (!window->isClient() || window->isDesktop() || window->isDock()) {
0735         return;
0736     }
0737     window->setDesktops(desktops);
0738 }
0739 
0740 void EffectsHandler::windowToScreen(EffectWindow *w, Output *screen)
0741 {
0742     auto window = w->window();
0743     if (window->isClient() && !window->isDesktop() && !window->isDock()) {
0744         Workspace::self()->sendWindowToOutput(window, screen);
0745     }
0746 }
0747 
0748 void EffectsHandler::setShowingDesktop(bool showing)
0749 {
0750     Workspace::self()->setShowingDesktop(showing);
0751 }
0752 
0753 QString EffectsHandler::currentActivity() const
0754 {
0755 #if KWIN_BUILD_ACTIVITIES
0756     if (!Workspace::self()->activities()) {
0757         return QString();
0758     }
0759     return Workspace::self()->activities()->current();
0760 #else
0761     return QString();
0762 #endif
0763 }
0764 
0765 VirtualDesktop *EffectsHandler::currentDesktop() const
0766 {
0767     return VirtualDesktopManager::self()->currentDesktop();
0768 }
0769 
0770 QList<VirtualDesktop *> EffectsHandler::desktops() const
0771 {
0772     return VirtualDesktopManager::self()->desktops();
0773 }
0774 
0775 void EffectsHandler::setCurrentDesktop(VirtualDesktop *desktop)
0776 {
0777     VirtualDesktopManager::self()->setCurrent(desktop);
0778 }
0779 
0780 QSize EffectsHandler::desktopGridSize() const
0781 {
0782     return VirtualDesktopManager::self()->grid().size();
0783 }
0784 
0785 int EffectsHandler::desktopGridWidth() const
0786 {
0787     return desktopGridSize().width();
0788 }
0789 
0790 int EffectsHandler::desktopGridHeight() const
0791 {
0792     return desktopGridSize().height();
0793 }
0794 
0795 int EffectsHandler::workspaceWidth() const
0796 {
0797     return desktopGridWidth() * Workspace::self()->geometry().width();
0798 }
0799 
0800 int EffectsHandler::workspaceHeight() const
0801 {
0802     return desktopGridHeight() * Workspace::self()->geometry().height();
0803 }
0804 
0805 VirtualDesktop *EffectsHandler::desktopAtCoords(QPoint coords) const
0806 {
0807     return VirtualDesktopManager::self()->grid().at(coords);
0808 }
0809 
0810 QPoint EffectsHandler::desktopGridCoords(VirtualDesktop *desktop) const
0811 {
0812     return VirtualDesktopManager::self()->grid().gridCoords(desktop);
0813 }
0814 
0815 QPoint EffectsHandler::desktopCoords(VirtualDesktop *desktop) const
0816 {
0817     QPoint coords = VirtualDesktopManager::self()->grid().gridCoords(desktop);
0818     if (coords.x() == -1) {
0819         return QPoint(-1, -1);
0820     }
0821     const QSize displaySize = Workspace::self()->geometry().size();
0822     return QPoint(coords.x() * displaySize.width(), coords.y() * displaySize.height());
0823 }
0824 
0825 VirtualDesktop *EffectsHandler::desktopAbove(VirtualDesktop *desktop, bool wrap) const
0826 {
0827     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Up, wrap);
0828 }
0829 
0830 VirtualDesktop *EffectsHandler::desktopToRight(VirtualDesktop *desktop, bool wrap) const
0831 {
0832     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Right, wrap);
0833 }
0834 
0835 VirtualDesktop *EffectsHandler::desktopBelow(VirtualDesktop *desktop, bool wrap) const
0836 {
0837     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Down, wrap);
0838 }
0839 
0840 VirtualDesktop *EffectsHandler::desktopToLeft(VirtualDesktop *desktop, bool wrap) const
0841 {
0842     return VirtualDesktopManager::self()->inDirection(desktop, VirtualDesktopManager::Direction::Left, wrap);
0843 }
0844 
0845 QString EffectsHandler::desktopName(VirtualDesktop *desktop) const
0846 {
0847     return desktop->name();
0848 }
0849 
0850 bool EffectsHandler::optionRollOverDesktops() const
0851 {
0852     return options->isRollOverDesktops();
0853 }
0854 
0855 double EffectsHandler::animationTimeFactor() const
0856 {
0857     return options->animationTimeFactor();
0858 }
0859 
0860 EffectWindow *EffectsHandler::findWindow(WId id) const
0861 {
0862     if (X11Window *w = Workspace::self()->findClient(Predicate::WindowMatch, id)) {
0863         return w->effectWindow();
0864     }
0865     if (X11Window *w = Workspace::self()->findUnmanaged(id)) {
0866         return w->effectWindow();
0867     }
0868     return nullptr;
0869 }
0870 
0871 EffectWindow *EffectsHandler::findWindow(SurfaceInterface *surf) const
0872 {
0873     if (waylandServer()) {
0874         if (Window *w = waylandServer()->findWindow(surf)) {
0875             return w->effectWindow();
0876         }
0877     }
0878     return nullptr;
0879 }
0880 
0881 EffectWindow *EffectsHandler::findWindow(QWindow *w) const
0882 {
0883     if (Window *window = workspace()->findInternal(w)) {
0884         return window->effectWindow();
0885     }
0886     return nullptr;
0887 }
0888 
0889 EffectWindow *EffectsHandler::findWindow(const QUuid &id) const
0890 {
0891     if (Window *window = workspace()->findWindow(id)) {
0892         return window->effectWindow();
0893     }
0894     return nullptr;
0895 }
0896 
0897 QList<EffectWindow *> EffectsHandler::stackingOrder() const
0898 {
0899     QList<Window *> list = workspace()->stackingOrder();
0900     QList<EffectWindow *> ret;
0901     for (Window *t : list) {
0902         if (EffectWindow *w = t->effectWindow()) {
0903             ret.append(w);
0904         }
0905     }
0906     return ret;
0907 }
0908 
0909 void EffectsHandler::setElevatedWindow(KWin::EffectWindow *w, bool set)
0910 {
0911     WindowItem *item = w->windowItem();
0912 
0913     if (set) {
0914         item->elevate();
0915     } else {
0916         item->deelevate();
0917     }
0918 }
0919 
0920 void EffectsHandler::setTabBoxWindow(EffectWindow *w)
0921 {
0922 #if KWIN_BUILD_TABBOX
0923     auto window = w->window();
0924     if (window->isClient()) {
0925         workspace()->tabbox()->setCurrentClient(window);
0926     }
0927 #endif
0928 }
0929 
0930 QList<EffectWindow *> EffectsHandler::currentTabBoxWindowList() const
0931 {
0932 #if KWIN_BUILD_TABBOX
0933     const auto clients = workspace()->tabbox()->currentClientList();
0934     QList<EffectWindow *> ret;
0935     ret.reserve(clients.size());
0936     std::transform(std::cbegin(clients), std::cend(clients),
0937                    std::back_inserter(ret),
0938                    [](auto client) {
0939                        return client->effectWindow();
0940                    });
0941     return ret;
0942 #else
0943     return QList<EffectWindow *>();
0944 #endif
0945 }
0946 
0947 void EffectsHandler::refTabBox()
0948 {
0949 #if KWIN_BUILD_TABBOX
0950     workspace()->tabbox()->reference();
0951 #endif
0952 }
0953 
0954 void EffectsHandler::unrefTabBox()
0955 {
0956 #if KWIN_BUILD_TABBOX
0957     workspace()->tabbox()->unreference();
0958 #endif
0959 }
0960 
0961 void EffectsHandler::closeTabBox()
0962 {
0963 #if KWIN_BUILD_TABBOX
0964     workspace()->tabbox()->close();
0965 #endif
0966 }
0967 
0968 EffectWindow *EffectsHandler::currentTabBoxWindow() const
0969 {
0970 #if KWIN_BUILD_TABBOX
0971     if (auto c = workspace()->tabbox()->currentClient()) {
0972         return c->effectWindow();
0973     }
0974 #endif
0975     return nullptr;
0976 }
0977 
0978 void EffectsHandler::addRepaintFull()
0979 {
0980     m_compositor->scene()->addRepaintFull();
0981 }
0982 
0983 void EffectsHandler::addRepaint(const QRect &r)
0984 {
0985     m_compositor->scene()->addRepaint(r);
0986 }
0987 
0988 void EffectsHandler::addRepaint(const QRectF &r)
0989 {
0990     m_compositor->scene()->addRepaint(r.toAlignedRect());
0991 }
0992 
0993 void EffectsHandler::addRepaint(const QRegion &r)
0994 {
0995     m_compositor->scene()->addRepaint(r);
0996 }
0997 
0998 void EffectsHandler::addRepaint(int x, int y, int w, int h)
0999 {
1000     m_compositor->scene()->addRepaint(x, y, w, h);
1001 }
1002 
1003 Output *EffectsHandler::activeScreen() const
1004 {
1005     return workspace()->activeOutput();
1006 }
1007 
1008 QRectF EffectsHandler::clientArea(clientAreaOption opt, const Output *screen, const VirtualDesktop *desktop) const
1009 {
1010     return Workspace::self()->clientArea(opt, screen, desktop);
1011 }
1012 
1013 QRectF EffectsHandler::clientArea(clientAreaOption opt, const EffectWindow *effectWindow) const
1014 {
1015     const Window *window = effectWindow->window();
1016     return Workspace::self()->clientArea(opt, window);
1017 }
1018 
1019 QRectF EffectsHandler::clientArea(clientAreaOption opt, const QPoint &p, const VirtualDesktop *desktop) const
1020 {
1021     const Output *output = Workspace::self()->outputAt(p);
1022     return Workspace::self()->clientArea(opt, output, desktop);
1023 }
1024 
1025 QRect EffectsHandler::virtualScreenGeometry() const
1026 {
1027     return Workspace::self()->geometry();
1028 }
1029 
1030 QSize EffectsHandler::virtualScreenSize() const
1031 {
1032     return Workspace::self()->geometry().size();
1033 }
1034 
1035 void EffectsHandler::defineCursor(Qt::CursorShape shape)
1036 {
1037     input()->pointer()->setEffectsOverrideCursor(shape);
1038 }
1039 
1040 bool EffectsHandler::checkInputWindowEvent(QMouseEvent *e)
1041 {
1042     if (m_grabbedMouseEffects.isEmpty()) {
1043         return false;
1044     }
1045     for (Effect *effect : std::as_const(m_grabbedMouseEffects)) {
1046         effect->windowInputMouseEvent(e);
1047     }
1048     return true;
1049 }
1050 
1051 bool EffectsHandler::checkInputWindowEvent(QWheelEvent *e)
1052 {
1053     if (m_grabbedMouseEffects.isEmpty()) {
1054         return false;
1055     }
1056     for (Effect *effect : std::as_const(m_grabbedMouseEffects)) {
1057         effect->windowInputMouseEvent(e);
1058     }
1059     return true;
1060 }
1061 
1062 void EffectsHandler::connectNotify(const QMetaMethod &signal)
1063 {
1064     if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
1065         if (!m_trackingCursorChanges) {
1066             connect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
1067             Cursors::self()->mouse()->startCursorTracking();
1068         }
1069         ++m_trackingCursorChanges;
1070     }
1071     QObject::connectNotify(signal);
1072 }
1073 
1074 void EffectsHandler::disconnectNotify(const QMetaMethod &signal)
1075 {
1076     if (signal == QMetaMethod::fromSignal(&EffectsHandler::cursorShapeChanged)) {
1077         Q_ASSERT(m_trackingCursorChanges > 0);
1078         if (!--m_trackingCursorChanges) {
1079             Cursors::self()->mouse()->stopCursorTracking();
1080             disconnect(Cursors::self()->mouse(), &Cursor::cursorChanged, this, &EffectsHandler::cursorShapeChanged);
1081         }
1082     }
1083     QObject::disconnectNotify(signal);
1084 }
1085 
1086 void EffectsHandler::checkInputWindowStacking()
1087 {
1088     if (m_grabbedMouseEffects.isEmpty()) {
1089         return;
1090     }
1091     doCheckInputWindowStacking();
1092 }
1093 
1094 void EffectsHandler::doCheckInputWindowStacking()
1095 {
1096 }
1097 
1098 QPointF EffectsHandler::cursorPos() const
1099 {
1100     return Cursors::self()->mouse()->pos();
1101 }
1102 
1103 void EffectsHandler::reserveElectricBorder(ElectricBorder border, Effect *effect)
1104 {
1105     workspace()->screenEdges()->reserve(border, effect, "borderActivated");
1106 }
1107 
1108 void EffectsHandler::unreserveElectricBorder(ElectricBorder border, Effect *effect)
1109 {
1110     workspace()->screenEdges()->unreserve(border, effect);
1111 }
1112 
1113 void EffectsHandler::registerTouchBorder(ElectricBorder border, QAction *action)
1114 {
1115     workspace()->screenEdges()->reserveTouch(border, action);
1116 }
1117 
1118 void EffectsHandler::registerRealtimeTouchBorder(ElectricBorder border, QAction *action, EffectsHandler::TouchBorderCallback progressCallback)
1119 {
1120     workspace()->screenEdges()->reserveTouch(border, action, progressCallback);
1121 }
1122 
1123 void EffectsHandler::unregisterTouchBorder(ElectricBorder border, QAction *action)
1124 {
1125     workspace()->screenEdges()->unreserveTouch(border, action);
1126 }
1127 
1128 QPainter *EffectsHandler::scenePainter()
1129 {
1130     return m_scene->renderer()->painter();
1131 }
1132 
1133 void EffectsHandler::toggleEffect(const QString &name)
1134 {
1135     if (isEffectLoaded(name)) {
1136         unloadEffect(name);
1137     } else {
1138         loadEffect(name);
1139     }
1140 }
1141 
1142 QStringList EffectsHandler::loadedEffects() const
1143 {
1144     QStringList listModules;
1145     listModules.reserve(loaded_effects.count());
1146     std::transform(loaded_effects.constBegin(), loaded_effects.constEnd(),
1147                    std::back_inserter(listModules),
1148                    [](const EffectPair &pair) {
1149                        return pair.first;
1150                    });
1151     return listModules;
1152 }
1153 
1154 QStringList EffectsHandler::listOfEffects() const
1155 {
1156     return m_effectLoader->listOfKnownEffects();
1157 }
1158 
1159 bool EffectsHandler::loadEffect(const QString &name)
1160 {
1161     makeOpenGLContextCurrent();
1162     m_compositor->scene()->addRepaintFull();
1163 
1164     return m_effectLoader->loadEffect(name);
1165 }
1166 
1167 void EffectsHandler::unloadEffect(const QString &name)
1168 {
1169     auto it = std::find_if(effect_order.begin(), effect_order.end(),
1170                            [name](EffectPair &pair) {
1171                                return pair.first == name;
1172                            });
1173     if (it == effect_order.end()) {
1174         qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Effect not loaded :" << name;
1175         return;
1176     }
1177 
1178     qCDebug(KWIN_CORE) << "EffectsHandler::unloadEffect : Unloading Effect :" << name;
1179     destroyEffect((*it).second);
1180     effect_order.erase(it);
1181     effectsChanged();
1182 
1183     m_compositor->scene()->addRepaintFull();
1184 }
1185 
1186 void EffectsHandler::destroyEffect(Effect *effect)
1187 {
1188     makeOpenGLContextCurrent();
1189 
1190     if (fullscreen_effect == effect) {
1191         setActiveFullScreenEffect(nullptr);
1192     }
1193 
1194     if (keyboard_grab_effect == effect) {
1195         ungrabKeyboard();
1196     }
1197 
1198     stopMouseInterception(effect);
1199 
1200     const QList<QByteArray> properties = m_propertiesForEffects.keys();
1201     for (const QByteArray &property : properties) {
1202         removeSupportProperty(property, effect);
1203     }
1204 
1205     delete effect;
1206 }
1207 
1208 void EffectsHandler::reconfigureEffects()
1209 {
1210     makeOpenGLContextCurrent();
1211     for (const EffectPair &pair : loaded_effects) {
1212         pair.second->reconfigure(Effect::ReconfigureAll);
1213     }
1214 }
1215 
1216 void EffectsHandler::reconfigureEffect(const QString &name)
1217 {
1218     for (QList<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1219         if ((*it).first == name) {
1220             kwinApp()->config()->reparseConfiguration();
1221             makeOpenGLContextCurrent();
1222             (*it).second->reconfigure(Effect::ReconfigureAll);
1223             return;
1224         }
1225     }
1226 }
1227 
1228 bool EffectsHandler::isEffectLoaded(const QString &name) const
1229 {
1230     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1231                            [&name](const EffectPair &pair) {
1232                                return pair.first == name;
1233                            });
1234     return it != loaded_effects.constEnd();
1235 }
1236 
1237 bool EffectsHandler::isEffectSupported(const QString &name)
1238 {
1239     // If the effect is loaded, it is obviously supported.
1240     if (isEffectLoaded(name)) {
1241         return true;
1242     }
1243 
1244     // next checks might require a context
1245     makeOpenGLContextCurrent();
1246 
1247     return m_effectLoader->isEffectSupported(name);
1248 }
1249 
1250 QList<bool> EffectsHandler::areEffectsSupported(const QStringList &names)
1251 {
1252     QList<bool> retList;
1253     retList.reserve(names.count());
1254     std::transform(names.constBegin(), names.constEnd(),
1255                    std::back_inserter(retList),
1256                    [this](const QString &name) {
1257                        return isEffectSupported(name);
1258                    });
1259     return retList;
1260 }
1261 
1262 void EffectsHandler::reloadEffect(Effect *effect)
1263 {
1264     QString effectName;
1265     for (QList<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1266         if ((*it).second == effect) {
1267             effectName = (*it).first;
1268             break;
1269         }
1270     }
1271     if (!effectName.isNull()) {
1272         unloadEffect(effectName);
1273         m_effectLoader->loadEffect(effectName);
1274     }
1275 }
1276 
1277 void EffectsHandler::effectsChanged()
1278 {
1279     loaded_effects.clear();
1280     m_activeEffects.clear(); // it's possible to have a reconfigure and a quad rebuild between two paint cycles - bug #308201
1281 
1282     loaded_effects.reserve(effect_order.count());
1283     std::copy(effect_order.constBegin(), effect_order.constEnd(),
1284               std::back_inserter(loaded_effects));
1285 
1286     m_activeEffects.reserve(loaded_effects.count());
1287 
1288     m_currentPaintScreenIterator = m_activeEffects.constBegin();
1289     m_currentPaintWindowIterator = m_activeEffects.constBegin();
1290     m_currentDrawWindowIterator = m_activeEffects.constBegin();
1291 }
1292 
1293 QStringList EffectsHandler::activeEffects() const
1294 {
1295     QStringList ret;
1296     for (QList<KWin::EffectPair>::const_iterator it = loaded_effects.constBegin(),
1297                                                  end = loaded_effects.constEnd();
1298          it != end; ++it) {
1299         if (it->second->isActive()) {
1300             ret << it->first;
1301         }
1302     }
1303     return ret;
1304 }
1305 
1306 bool EffectsHandler::isEffectActive(const QString &pluginId) const
1307 {
1308     auto it = std::find_if(loaded_effects.cbegin(), loaded_effects.cend(), [&pluginId](const EffectPair &p) {
1309         return p.first == pluginId;
1310     });
1311     if (it == loaded_effects.cend()) {
1312         return false;
1313     }
1314     return it->second->isActive();
1315 }
1316 
1317 bool EffectsHandler::blocksDirectScanout() const
1318 {
1319     return std::any_of(m_activeEffects.constBegin(), m_activeEffects.constEnd(), [](const Effect *effect) {
1320         return effect->blocksDirectScanout();
1321     });
1322 }
1323 
1324 Display *EffectsHandler::waylandDisplay() const
1325 {
1326     if (waylandServer()) {
1327         return waylandServer()->display();
1328     }
1329     return nullptr;
1330 }
1331 
1332 QVariant EffectsHandler::kwinOption(KWinOption kwopt)
1333 {
1334     switch (kwopt) {
1335     case CloseButtonCorner: {
1336         // TODO: this could become per window and be derived from the actual position in the deco
1337         const auto settings = Workspace::self()->decorationBridge()->settings();
1338         return settings && settings->decorationButtonsLeft().contains(KDecoration2::DecorationButtonType::Close) ? Qt::TopLeftCorner : Qt::TopRightCorner;
1339     }
1340     case SwitchDesktopOnScreenEdge:
1341         return workspace()->screenEdges()->isDesktopSwitching();
1342     case SwitchDesktopOnScreenEdgeMovingWindows:
1343         return workspace()->screenEdges()->isDesktopSwitchingMovingClients();
1344     default:
1345         return QVariant(); // an invalid one
1346     }
1347 }
1348 
1349 QString EffectsHandler::supportInformation(const QString &name) const
1350 {
1351     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(),
1352                            [name](const EffectPair &pair) {
1353                                return pair.first == name;
1354                            });
1355     if (it == loaded_effects.constEnd()) {
1356         return QString();
1357     }
1358 
1359     QString support((*it).first + QLatin1String(":\n"));
1360     const QMetaObject *metaOptions = (*it).second->metaObject();
1361     for (int i = 0; i < metaOptions->propertyCount(); ++i) {
1362         const QMetaProperty property = metaOptions->property(i);
1363         if (qstrcmp(property.name(), "objectName") == 0) {
1364             continue;
1365         }
1366         support += QString::fromUtf8(property.name()) + QLatin1String(": ") + (*it).second->property(property.name()).toString() + QLatin1Char('\n');
1367     }
1368 
1369     return support;
1370 }
1371 
1372 bool EffectsHandler::isScreenLocked() const
1373 {
1374 #if KWIN_BUILD_SCREENLOCKER
1375     return kwinApp()->screenLockerWatcher()->isLocked();
1376 #else
1377     return false;
1378 #endif
1379 }
1380 
1381 QString EffectsHandler::debug(const QString &name, const QString &parameter) const
1382 {
1383     QString internalName = name.toLower();
1384     for (QList<EffectPair>::const_iterator it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) {
1385         if ((*it).first == internalName) {
1386             return it->second->debug(parameter);
1387         }
1388     }
1389     return QString();
1390 }
1391 
1392 bool EffectsHandler::makeOpenGLContextCurrent()
1393 {
1394     return m_scene->makeOpenGLContextCurrent();
1395 }
1396 
1397 void EffectsHandler::doneOpenGLContextCurrent()
1398 {
1399     m_scene->doneOpenGLContextCurrent();
1400 }
1401 
1402 bool EffectsHandler::animationsSupported() const
1403 {
1404     static const QByteArray forceEnvVar = qgetenv("KWIN_EFFECTS_FORCE_ANIMATIONS");
1405     if (!forceEnvVar.isEmpty()) {
1406         static const int forceValue = forceEnvVar.toInt();
1407         return forceValue == 1;
1408     }
1409     return m_scene->animationsSupported();
1410 }
1411 
1412 void EffectsHandler::highlightWindows(const QList<EffectWindow *> &windows)
1413 {
1414     Effect *e = provides(Effect::HighlightWindows);
1415     if (!e) {
1416         return;
1417     }
1418     e->perform(Effect::HighlightWindows, QVariantList{QVariant::fromValue(windows)});
1419 }
1420 
1421 PlatformCursorImage EffectsHandler::cursorImage() const
1422 {
1423     return kwinApp()->cursorImage();
1424 }
1425 
1426 void EffectsHandler::hideCursor()
1427 {
1428     Cursors::self()->hideCursor();
1429 }
1430 
1431 void EffectsHandler::showCursor()
1432 {
1433     Cursors::self()->showCursor();
1434 }
1435 
1436 void EffectsHandler::startInteractiveWindowSelection(std::function<void(KWin::EffectWindow *)> callback)
1437 {
1438     kwinApp()->startInteractiveWindowSelection([callback](KWin::Window *window) {
1439         if (window && window->effectWindow()) {
1440             callback(window->effectWindow());
1441         } else {
1442             callback(nullptr);
1443         }
1444     });
1445 }
1446 
1447 void EffectsHandler::startInteractivePositionSelection(std::function<void(const QPointF &)> callback)
1448 {
1449     kwinApp()->startInteractivePositionSelection(callback);
1450 }
1451 
1452 void EffectsHandler::showOnScreenMessage(const QString &message, const QString &iconName)
1453 {
1454     OSD::show(message, iconName);
1455 }
1456 
1457 void EffectsHandler::hideOnScreenMessage(OnScreenMessageHideFlags flags)
1458 {
1459     OSD::HideFlags osdFlags;
1460     if (flags.testFlag(OnScreenMessageHideFlag::SkipsCloseAnimation)) {
1461         osdFlags |= OSD::HideFlag::SkipCloseAnimation;
1462     }
1463     OSD::hide(osdFlags);
1464 }
1465 
1466 KSharedConfigPtr EffectsHandler::config() const
1467 {
1468     return kwinApp()->config();
1469 }
1470 
1471 KSharedConfigPtr EffectsHandler::inputConfig() const
1472 {
1473     return kwinApp()->inputConfig();
1474 }
1475 
1476 Effect *EffectsHandler::findEffect(const QString &name) const
1477 {
1478     auto it = std::find_if(loaded_effects.constBegin(), loaded_effects.constEnd(), [name](const EffectPair &pair) {
1479         return pair.first == name;
1480     });
1481     if (it == loaded_effects.constEnd()) {
1482         return nullptr;
1483     }
1484     return (*it).second;
1485 }
1486 
1487 void EffectsHandler::renderOffscreenQuickView(const RenderTarget &renderTarget, const RenderViewport &viewport, OffscreenQuickView *w) const
1488 {
1489     if (!w->isVisible()) {
1490         return;
1491     }
1492     if (compositingType() == OpenGLCompositing) {
1493         GLTexture *t = w->bufferAsTexture();
1494         if (!t) {
1495             return;
1496         }
1497 
1498         ShaderTraits traits = ShaderTrait::MapTexture | ShaderTrait::TransformColorspace;
1499         const qreal a = w->opacity();
1500         if (a != 1.0) {
1501             traits |= ShaderTrait::Modulate;
1502         }
1503 
1504         GLShader *shader = ShaderManager::instance()->pushShader(traits);
1505         const QRectF rect = scaledRect(w->geometry(), viewport.scale());
1506 
1507         QMatrix4x4 mvp(viewport.projectionMatrix());
1508         mvp.translate(rect.x(), rect.y());
1509         shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp);
1510 
1511         if (a != 1.0) {
1512             shader->setUniform(GLShader::Vec4Uniform::ModulationConstant, QVector4D(a, a, a, a));
1513         }
1514         shader->setColorspaceUniformsFromSRGB(renderTarget.colorDescription());
1515 
1516         const bool alphaBlending = w->hasAlphaChannel() || (a != 1.0);
1517         if (alphaBlending) {
1518             glEnable(GL_BLEND);
1519             glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
1520         }
1521 
1522         t->render(rect.size());
1523 
1524         if (alphaBlending) {
1525             glDisable(GL_BLEND);
1526         }
1527 
1528         ShaderManager::instance()->popShader();
1529     } else if (compositingType() == QPainterCompositing) {
1530         QPainter *painter = effects->scenePainter();
1531         const QImage buffer = w->bufferAsImage();
1532         if (buffer.isNull()) {
1533             return;
1534         }
1535         painter->save();
1536         painter->setOpacity(w->opacity());
1537         painter->drawImage(w->geometry(), buffer);
1538         painter->restore();
1539     }
1540 }
1541 
1542 SessionState EffectsHandler::sessionState() const
1543 {
1544     return Workspace::self()->sessionManager()->state();
1545 }
1546 
1547 QList<Output *> EffectsHandler::screens() const
1548 {
1549     return Workspace::self()->outputs();
1550 }
1551 
1552 Output *EffectsHandler::screenAt(const QPoint &point) const
1553 {
1554     return Workspace::self()->outputAt(point);
1555 }
1556 
1557 Output *EffectsHandler::findScreen(const QString &name) const
1558 {
1559     const auto outputs = Workspace::self()->outputs();
1560     for (Output *screen : outputs) {
1561         if (screen->name() == name) {
1562             return screen;
1563         }
1564     }
1565     return nullptr;
1566 }
1567 
1568 Output *EffectsHandler::findScreen(int screenId) const
1569 {
1570     return Workspace::self()->outputs().value(screenId);
1571 }
1572 
1573 bool EffectsHandler::isCursorHidden() const
1574 {
1575     return Cursors::self()->isCursorHidden();
1576 }
1577 
1578 KWin::EffectWindow *EffectsHandler::inputPanel() const
1579 {
1580     if (!kwinApp()->inputMethod() || !kwinApp()->inputMethod()->isEnabled()) {
1581         return nullptr;
1582     }
1583 
1584     auto panel = kwinApp()->inputMethod()->panel();
1585     if (panel) {
1586         return panel->effectWindow();
1587     }
1588     return nullptr;
1589 }
1590 
1591 bool EffectsHandler::isInputPanelOverlay() const
1592 {
1593     if (!kwinApp()->inputMethod() || !kwinApp()->inputMethod()->isEnabled()) {
1594         return true;
1595     }
1596 
1597     auto panel = kwinApp()->inputMethod()->panel();
1598     if (panel) {
1599         return panel->mode() == InputPanelV1Window::Mode::Overlay;
1600     }
1601     return true;
1602 }
1603 
1604 QQmlEngine *EffectsHandler::qmlEngine() const
1605 {
1606     return Scripting::self()->qmlEngine();
1607 }
1608 
1609 EffectsHandler *effects = nullptr;
1610 
1611 } // namespace
1612 
1613 #include "moc_effecthandler.cpp"
1614 #include "moc_globals.cpp"