File indexing completed on 2024-11-10 04:56:43
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 ®ion, 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 ®ion, 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 ®ion, 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 ®ion, 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 ¶meter) 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"