File indexing completed on 2024-11-10 04:57:13

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org>
0006     SPDX-FileCopyrightText: 2010 Sebastian Sauer <sebsauer@kdab.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "zoom.h"
0012 // KConfigSkeleton
0013 #include "zoomconfig.h"
0014 
0015 #if HAVE_ACCESSIBILITY
0016 #include "accessibilityintegration.h"
0017 #endif
0018 
0019 #include <KConfigGroup>
0020 #include <KGlobalAccel>
0021 #include <KLocalizedString>
0022 #include <QAction>
0023 #include <QStyle>
0024 #include <QVector2D>
0025 #include <kstandardaction.h>
0026 
0027 #include "core/rendertarget.h"
0028 #include "core/renderviewport.h"
0029 #include "effect/effecthandler.h"
0030 #include "opengl/glutils.h"
0031 
0032 namespace KWin
0033 {
0034 
0035 ZoomEffect::ZoomEffect()
0036     : Effect()
0037     , zoom(1)
0038     , target_zoom(1)
0039     , polling(false)
0040     , zoomFactor(1.25)
0041     , mouseTracking(MouseTrackingProportional)
0042     , mousePointer(MousePointerScale)
0043     , focusDelay(350) // in milliseconds
0044     , isMouseHidden(false)
0045     , xMove(0)
0046     , yMove(0)
0047     , moveFactor(20.0)
0048     , lastPresentTime(std::chrono::milliseconds::zero())
0049 {
0050     ZoomConfig::instance(effects->config());
0051     QAction *a = nullptr;
0052     a = KStandardAction::zoomIn(this, SLOT(zoomIn()), this);
0053     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Plus) << (Qt::META | Qt::Key_Equal));
0054     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Plus) << (Qt::META | Qt::Key_Equal));
0055     effects->registerAxisShortcut(Qt::ControlModifier | Qt::MetaModifier, PointerAxisDown, a);
0056 
0057     a = KStandardAction::zoomOut(this, SLOT(zoomOut()), this);
0058     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Minus));
0059     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Minus));
0060     effects->registerAxisShortcut(Qt::ControlModifier | Qt::MetaModifier, PointerAxisUp, a);
0061 
0062     a = KStandardAction::actualSize(this, SLOT(actualSize()), this);
0063     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_0));
0064     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_0));
0065 
0066     a = new QAction(this);
0067     a->setObjectName(QStringLiteral("MoveZoomLeft"));
0068     a->setText(i18n("Move Zoomed Area to Left"));
0069     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>());
0070     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>());
0071     connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomLeft);
0072 
0073     a = new QAction(this);
0074     a->setObjectName(QStringLiteral("MoveZoomRight"));
0075     a->setText(i18n("Move Zoomed Area to Right"));
0076     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>());
0077     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>());
0078     connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomRight);
0079 
0080     a = new QAction(this);
0081     a->setObjectName(QStringLiteral("MoveZoomUp"));
0082     a->setText(i18n("Move Zoomed Area Upwards"));
0083     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>());
0084     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>());
0085     connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomUp);
0086 
0087     a = new QAction(this);
0088     a->setObjectName(QStringLiteral("MoveZoomDown"));
0089     a->setText(i18n("Move Zoomed Area Downwards"));
0090     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>());
0091     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>());
0092     connect(a, &QAction::triggered, this, &ZoomEffect::moveZoomDown);
0093 
0094     // TODO: these two actions don't belong into the effect. They need to be moved into KWin core
0095     a = new QAction(this);
0096     a->setObjectName(QStringLiteral("MoveMouseToFocus"));
0097     a->setText(i18n("Move Mouse to Focus"));
0098     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_F5));
0099     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_F5));
0100     connect(a, &QAction::triggered, this, &ZoomEffect::moveMouseToFocus);
0101 
0102     a = new QAction(this);
0103     a->setObjectName(QStringLiteral("MoveMouseToCenter"));
0104     a->setText(i18n("Move Mouse to Center"));
0105     KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_F6));
0106     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_F6));
0107     connect(a, &QAction::triggered, this, &ZoomEffect::moveMouseToCenter);
0108 
0109     timeline.setDuration(350);
0110     timeline.setFrameRange(0, 100);
0111     connect(&timeline, &QTimeLine::frameChanged, this, &ZoomEffect::timelineFrameChanged);
0112     connect(effects, &EffectsHandler::mouseChanged, this, &ZoomEffect::slotMouseChanged);
0113     connect(effects, &EffectsHandler::windowAdded, this, &ZoomEffect::slotWindowAdded);
0114     connect(effects, &EffectsHandler::screenRemoved, this, &ZoomEffect::slotScreenRemoved);
0115 
0116 #if HAVE_ACCESSIBILITY
0117     if (!effects->waylandDisplay()) {
0118         // on Wayland, the accessibility integration can cause KWin to hang
0119         m_accessibilityIntegration = new ZoomAccessibilityIntegration(this);
0120         connect(m_accessibilityIntegration, &ZoomAccessibilityIntegration::focusPointChanged, this, &ZoomEffect::moveFocus);
0121     }
0122 #endif
0123 
0124     const auto windows = effects->stackingOrder();
0125     for (EffectWindow *w : windows) {
0126         slotWindowAdded(w);
0127     }
0128 
0129     source_zoom = -1; // used to trigger initialZoom reading
0130     reconfigure(ReconfigureAll);
0131 }
0132 
0133 ZoomEffect::~ZoomEffect()
0134 {
0135     // switch off and free resources
0136     showCursor();
0137     // Save the zoom value.
0138     ZoomConfig::setInitialZoom(target_zoom);
0139     ZoomConfig::self()->save();
0140 }
0141 
0142 bool ZoomEffect::isFocusTrackingEnabled() const
0143 {
0144 #if HAVE_ACCESSIBILITY
0145     return m_accessibilityIntegration && m_accessibilityIntegration->isFocusTrackingEnabled();
0146 #else
0147     return false;
0148 #endif
0149 }
0150 
0151 bool ZoomEffect::isTextCaretTrackingEnabled() const
0152 {
0153 #if HAVE_ACCESSIBILITY
0154     return m_accessibilityIntegration && m_accessibilityIntegration->isTextCaretTrackingEnabled();
0155 #else
0156     return false;
0157 #endif
0158 }
0159 
0160 GLTexture *ZoomEffect::ensureCursorTexture()
0161 {
0162     if (!m_cursorTexture || m_cursorTextureDirty) {
0163         m_cursorTexture.reset();
0164         m_cursorTextureDirty = false;
0165         const auto cursor = effects->cursorImage();
0166         if (!cursor.image().isNull()) {
0167             m_cursorTexture = GLTexture::upload(cursor.image());
0168             if (!m_cursorTexture) {
0169                 return nullptr;
0170             }
0171             m_cursorTexture->setWrapMode(GL_CLAMP_TO_EDGE);
0172         }
0173     }
0174     return m_cursorTexture.get();
0175 }
0176 
0177 void ZoomEffect::markCursorTextureDirty()
0178 {
0179     m_cursorTextureDirty = true;
0180 }
0181 
0182 void ZoomEffect::showCursor()
0183 {
0184     if (isMouseHidden) {
0185         disconnect(effects, &EffectsHandler::cursorShapeChanged, this, &ZoomEffect::markCursorTextureDirty);
0186         // show the previously hidden mouse-pointer again and free the loaded texture/picture.
0187         effects->showCursor();
0188         m_cursorTexture.reset();
0189         isMouseHidden = false;
0190     }
0191 }
0192 
0193 void ZoomEffect::hideCursor()
0194 {
0195     if (mouseTracking == MouseTrackingProportional && mousePointer == MousePointerKeep) {
0196         return; // don't replace the actual cursor by a static image for no reason.
0197     }
0198     if (!isMouseHidden) {
0199         // try to load the cursor-theme into a OpenGL texture and if successful then hide the mouse-pointer
0200         GLTexture *texture = nullptr;
0201         if (effects->isOpenGLCompositing()) {
0202             texture = ensureCursorTexture();
0203         }
0204         if (texture) {
0205             effects->hideCursor();
0206             connect(effects, &EffectsHandler::cursorShapeChanged, this, &ZoomEffect::markCursorTextureDirty);
0207             isMouseHidden = true;
0208         }
0209     }
0210 }
0211 
0212 void ZoomEffect::reconfigure(ReconfigureFlags)
0213 {
0214     ZoomConfig::self()->read();
0215     // On zoom-in and zoom-out change the zoom by the defined zoom-factor.
0216     zoomFactor = std::max(0.1, ZoomConfig::zoomFactor());
0217     // Visibility of the mouse-pointer.
0218     mousePointer = MousePointerType(ZoomConfig::mousePointer());
0219     // Track moving of the mouse.
0220     mouseTracking = MouseTrackingType(ZoomConfig::mouseTracking());
0221 #if HAVE_ACCESSIBILITY
0222     if (m_accessibilityIntegration) {
0223         // Enable tracking of the focused location.
0224         m_accessibilityIntegration->setFocusTrackingEnabled(ZoomConfig::enableFocusTracking());
0225         // Enable tracking of the text caret.
0226         m_accessibilityIntegration->setTextCaretTrackingEnabled(ZoomConfig::enableTextCaretTracking());
0227     }
0228 #endif
0229     // The time in milliseconds to wait before a focus-event takes away a mouse-move.
0230     focusDelay = std::max(uint(0), ZoomConfig::focusDelay());
0231     // The factor the zoom-area will be moved on touching an edge on push-mode or using the navigation KAction's.
0232     moveFactor = std::max(0.1, ZoomConfig::moveFactor());
0233     if (source_zoom < 0) {
0234         // Load the saved zoom value.
0235         source_zoom = 1.0;
0236         target_zoom = ZoomConfig::initialZoom();
0237         if (target_zoom > 1.0) {
0238             zoomIn(target_zoom);
0239         }
0240     } else {
0241         source_zoom = 1.0;
0242     }
0243 }
0244 
0245 void ZoomEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0246 {
0247     if (zoom != target_zoom) {
0248         int time = 0;
0249         if (lastPresentTime.count()) {
0250             time = (presentTime - lastPresentTime).count();
0251         }
0252         lastPresentTime = presentTime;
0253 
0254         const float zoomDist = std::abs(target_zoom - source_zoom);
0255         if (target_zoom > zoom) {
0256             zoom = std::min(zoom + ((zoomDist * time) / animationTime(150 * zoomFactor)), target_zoom);
0257         } else {
0258             zoom = std::max(zoom - ((zoomDist * time) / animationTime(150 * zoomFactor)), target_zoom);
0259         }
0260     }
0261 
0262     if (zoom == 1.0) {
0263         showCursor();
0264     } else {
0265         hideCursor();
0266     }
0267 
0268     effects->prePaintScreen(data, presentTime);
0269 }
0270 
0271 ZoomEffect::OffscreenData *ZoomEffect::ensureOffscreenData(const RenderTarget &renderTarget, const RenderViewport &viewport, Output *screen)
0272 {
0273     const QRect rect = viewport.renderRect().toRect();
0274     const qreal devicePixelRatio = viewport.scale();
0275     const QSize nativeSize = (viewport.renderRect().size() * devicePixelRatio).toSize();
0276 
0277     OffscreenData &data = m_offscreenData[effects->waylandDisplay() ? screen : nullptr];
0278     data.viewport = rect;
0279 
0280     const GLenum textureFormat = renderTarget.colorDescription() == ColorDescription::sRGB ? GL_RGBA8 : GL_RGBA16F;
0281     if (!data.texture || data.texture->size() != nativeSize || data.texture->internalFormat() != textureFormat) {
0282         data.texture = GLTexture::allocate(textureFormat, nativeSize);
0283         if (!data.texture) {
0284             return nullptr;
0285         }
0286         data.texture->setFilter(GL_LINEAR);
0287         data.texture->setWrapMode(GL_CLAMP_TO_EDGE);
0288         data.framebuffer = std::make_unique<GLFramebuffer>(data.texture.get());
0289     }
0290 
0291     return &data;
0292 }
0293 
0294 void ZoomEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
0295 {
0296     OffscreenData *offscreenData = ensureOffscreenData(renderTarget, viewport, screen);
0297     if (!offscreenData) {
0298         return;
0299     }
0300 
0301     // Render the scene in an offscreen texture and then upscale it.
0302     RenderTarget offscreenRenderTarget(offscreenData->framebuffer.get(), renderTarget.colorDescription());
0303     RenderViewport offscreenViewport(viewport.renderRect(), viewport.scale(), offscreenRenderTarget);
0304     GLFramebuffer::pushFramebuffer(offscreenData->framebuffer.get());
0305     effects->paintScreen(offscreenRenderTarget, offscreenViewport, mask, region, screen);
0306     GLFramebuffer::popFramebuffer();
0307 
0308     const QSize screenSize = effects->virtualScreenSize();
0309     const auto scale = viewport.scale();
0310 
0311     // mouse-tracking allows navigation of the zoom-area using the mouse.
0312     qreal xTranslation = 0;
0313     qreal yTranslation = 0;
0314     switch (mouseTracking) {
0315     case MouseTrackingProportional:
0316         xTranslation = -int(cursorPoint.x() * (zoom - 1.0));
0317         yTranslation = -int(cursorPoint.y() * (zoom - 1.0));
0318         prevPoint = cursorPoint;
0319         break;
0320     case MouseTrackingCentred:
0321         prevPoint = cursorPoint;
0322         // fall through
0323     case MouseTrackingDisabled:
0324         xTranslation = std::min(0, std::max(int(screenSize.width() - screenSize.width() * zoom), int(screenSize.width() / 2 - prevPoint.x() * zoom)));
0325         yTranslation = std::min(0, std::max(int(screenSize.height() - screenSize.height() * zoom), int(screenSize.height() / 2 - prevPoint.y() * zoom)));
0326         break;
0327     case MouseTrackingPush: {
0328         // touching an edge of the screen moves the zoom-area in that direction.
0329         int x = cursorPoint.x() * zoom - prevPoint.x() * (zoom - 1.0);
0330         int y = cursorPoint.y() * zoom - prevPoint.y() * (zoom - 1.0);
0331         int threshold = 4;
0332         xMove = yMove = 0;
0333         if (x < threshold) {
0334             xMove = (x - threshold) / zoom;
0335         } else if (x + threshold > screenSize.width()) {
0336             xMove = (x + threshold - screenSize.width()) / zoom;
0337         }
0338         if (y < threshold) {
0339             yMove = (y - threshold) / zoom;
0340         } else if (y + threshold > screenSize.height()) {
0341             yMove = (y + threshold - screenSize.height()) / zoom;
0342         }
0343         if (xMove) {
0344             prevPoint.setX(std::max(0, std::min(screenSize.width(), prevPoint.x() + xMove)));
0345         }
0346         if (yMove) {
0347             prevPoint.setY(std::max(0, std::min(screenSize.height(), prevPoint.y() + yMove)));
0348         }
0349         xTranslation = -int(prevPoint.x() * (zoom - 1.0));
0350         yTranslation = -int(prevPoint.y() * (zoom - 1.0));
0351         break;
0352     }
0353     }
0354 
0355     // use the focusPoint if focus tracking is enabled
0356     if (isFocusTrackingEnabled() || isTextCaretTrackingEnabled()) {
0357         bool acceptFocus = true;
0358         if (mouseTracking != MouseTrackingDisabled && focusDelay > 0) {
0359             // Wait some time for the mouse before doing the switch. This serves as threshold
0360             // to prevent the focus from jumping around to much while working with the mouse.
0361             const int msecs = lastMouseEvent.msecsTo(lastFocusEvent);
0362             acceptFocus = msecs > focusDelay;
0363         }
0364         if (acceptFocus) {
0365             xTranslation = -int(focusPoint.x() * (zoom - 1.0));
0366             yTranslation = -int(focusPoint.y() * (zoom - 1.0));
0367             prevPoint = focusPoint;
0368         }
0369     }
0370 
0371     // Render transformed offscreen texture.
0372     glClearColor(0.0, 0.0, 0.0, 0.0);
0373     glClear(GL_COLOR_BUFFER_BIT);
0374 
0375     auto shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture);
0376     for (auto &[screen, offscreen] : m_offscreenData) {
0377         QMatrix4x4 matrix;
0378         matrix.translate(xTranslation * scale, yTranslation * scale);
0379         matrix.scale(zoom, zoom);
0380         matrix.translate(offscreen.viewport.x() * scale, offscreen.viewport.y() * scale);
0381 
0382         shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, viewport.projectionMatrix() * matrix);
0383 
0384         offscreen.texture->render(offscreen.viewport.size() * scale);
0385     }
0386     ShaderManager::instance()->popShader();
0387 
0388     if (mousePointer != MousePointerHide) {
0389         // Draw the mouse-texture at the position matching to zoomed-in image of the desktop. Hiding the
0390         // previous mouse-cursor and drawing our own fake mouse-cursor is needed to be able to scale the
0391         // mouse-cursor up and to re-position those mouse-cursor to match to the chosen zoom-level.
0392 
0393         GLTexture *cursorTexture = ensureCursorTexture();
0394         if (cursorTexture) {
0395             const auto cursor = effects->cursorImage();
0396             QSizeF cursorSize = QSizeF(cursor.image().size()) / cursor.image().devicePixelRatio();
0397             if (mousePointer == MousePointerScale) {
0398                 cursorSize *= zoom;
0399             }
0400 
0401             const QPointF p = (effects->cursorPos() - cursor.hotSpot()) * zoom + QPoint(xTranslation, yTranslation);
0402 
0403             glEnable(GL_BLEND);
0404             glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
0405             auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
0406             s->setColorspaceUniformsFromSRGB(renderTarget.colorDescription());
0407             QMatrix4x4 mvp = viewport.projectionMatrix();
0408             mvp.translate(p.x() * scale, p.y() * scale);
0409             s->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp);
0410             cursorTexture->render(cursorSize * scale);
0411             ShaderManager::instance()->popShader();
0412             glDisable(GL_BLEND);
0413         }
0414     }
0415 }
0416 
0417 void ZoomEffect::postPaintScreen()
0418 {
0419     if (zoom == target_zoom) {
0420         lastPresentTime = std::chrono::milliseconds::zero();
0421     }
0422 
0423     if (zoom == 1.0 || zoom != target_zoom) {
0424         // Either animation is running or the zoom effect has stopped.
0425         effects->addRepaintFull();
0426     }
0427 
0428     effects->postPaintScreen();
0429 }
0430 
0431 void ZoomEffect::zoomIn(double to)
0432 {
0433     source_zoom = zoom;
0434     if (to < 0.0) {
0435         target_zoom *= zoomFactor;
0436     } else {
0437         target_zoom = to;
0438     }
0439     if (!polling) {
0440         polling = true;
0441         effects->startMousePolling();
0442     }
0443     cursorPoint = effects->cursorPos().toPoint();
0444     if (mouseTracking == MouseTrackingDisabled) {
0445         prevPoint = cursorPoint;
0446     }
0447     effects->addRepaintFull();
0448 }
0449 
0450 void ZoomEffect::zoomOut()
0451 {
0452     source_zoom = zoom;
0453     target_zoom /= zoomFactor;
0454     if ((zoomFactor > 1 && target_zoom < 1.01) || (zoomFactor < 1 && target_zoom > 0.99)) {
0455         target_zoom = 1;
0456         if (polling) {
0457             polling = false;
0458             effects->stopMousePolling();
0459         }
0460     }
0461     if (mouseTracking == MouseTrackingDisabled) {
0462         prevPoint = effects->cursorPos().toPoint();
0463     }
0464     effects->addRepaintFull();
0465 }
0466 
0467 void ZoomEffect::actualSize()
0468 {
0469     source_zoom = zoom;
0470     target_zoom = 1;
0471     if (polling) {
0472         polling = false;
0473         effects->stopMousePolling();
0474     }
0475     effects->addRepaintFull();
0476 }
0477 
0478 void ZoomEffect::timelineFrameChanged(int /* frame */)
0479 {
0480     const QSize screenSize = effects->virtualScreenSize();
0481     prevPoint.setX(std::max(0, std::min(screenSize.width(), prevPoint.x() + xMove)));
0482     prevPoint.setY(std::max(0, std::min(screenSize.height(), prevPoint.y() + yMove)));
0483     cursorPoint = prevPoint;
0484     effects->addRepaintFull();
0485 }
0486 
0487 void ZoomEffect::moveZoom(int x, int y)
0488 {
0489     if (timeline.state() == QTimeLine::Running) {
0490         timeline.stop();
0491     }
0492 
0493     const QSize screenSize = effects->virtualScreenSize();
0494     if (x < 0) {
0495         xMove = -std::max(1.0, screenSize.width() / zoom / moveFactor);
0496     } else if (x > 0) {
0497         xMove = std::max(1.0, screenSize.width() / zoom / moveFactor);
0498     } else {
0499         xMove = 0;
0500     }
0501 
0502     if (y < 0) {
0503         yMove = -std::max(1.0, screenSize.height() / zoom / moveFactor);
0504     } else if (y > 0) {
0505         yMove = std::max(1.0, screenSize.height() / zoom / moveFactor);
0506     } else {
0507         yMove = 0;
0508     }
0509 
0510     timeline.start();
0511 }
0512 
0513 void ZoomEffect::moveZoomLeft()
0514 {
0515     moveZoom(-1, 0);
0516 }
0517 
0518 void ZoomEffect::moveZoomRight()
0519 {
0520     moveZoom(1, 0);
0521 }
0522 
0523 void ZoomEffect::moveZoomUp()
0524 {
0525     moveZoom(0, -1);
0526 }
0527 
0528 void ZoomEffect::moveZoomDown()
0529 {
0530     moveZoom(0, 1);
0531 }
0532 
0533 void ZoomEffect::moveMouseToFocus()
0534 {
0535     QCursor::setPos(focusPoint.x(), focusPoint.y());
0536 }
0537 
0538 void ZoomEffect::moveMouseToCenter()
0539 {
0540     const QRect r = effects->virtualScreenGeometry();
0541     QCursor::setPos(r.x() + r.width() / 2, r.y() + r.height() / 2);
0542 }
0543 
0544 void ZoomEffect::slotMouseChanged(const QPointF &pos, const QPointF &old, Qt::MouseButtons,
0545                                   Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers)
0546 {
0547     if (zoom == 1.0) {
0548         return;
0549     }
0550     cursorPoint = pos.toPoint();
0551     if (pos != old) {
0552         lastMouseEvent = QTime::currentTime();
0553         effects->addRepaintFull();
0554     }
0555 }
0556 
0557 void ZoomEffect::slotWindowAdded(EffectWindow *w)
0558 {
0559     connect(w, &EffectWindow::windowDamaged, this, &ZoomEffect::slotWindowDamaged);
0560 }
0561 
0562 void ZoomEffect::slotWindowDamaged()
0563 {
0564     if (zoom != 1.0) {
0565         effects->addRepaintFull();
0566     }
0567 }
0568 
0569 void ZoomEffect::slotScreenRemoved(Output *screen)
0570 {
0571     if (auto it = m_offscreenData.find(screen); it != m_offscreenData.end()) {
0572         effects->makeOpenGLContextCurrent();
0573         m_offscreenData.erase(it);
0574     }
0575 }
0576 
0577 void ZoomEffect::moveFocus(const QPoint &point)
0578 {
0579     if (zoom == 1.0) {
0580         return;
0581     }
0582     focusPoint = point;
0583     lastFocusEvent = QTime::currentTime();
0584     effects->addRepaintFull();
0585 }
0586 
0587 bool ZoomEffect::isActive() const
0588 {
0589     return zoom != 1.0 || zoom != target_zoom;
0590 }
0591 
0592 int ZoomEffect::requestedEffectChainPosition() const
0593 {
0594     return 10;
0595 }
0596 
0597 qreal ZoomEffect::configuredZoomFactor() const
0598 {
0599     return zoomFactor;
0600 }
0601 
0602 int ZoomEffect::configuredMousePointer() const
0603 {
0604     return mousePointer;
0605 }
0606 
0607 int ZoomEffect::configuredMouseTracking() const
0608 {
0609     return mouseTracking;
0610 }
0611 
0612 int ZoomEffect::configuredFocusDelay() const
0613 {
0614     return focusDelay;
0615 }
0616 
0617 qreal ZoomEffect::configuredMoveFactor() const
0618 {
0619     return moveFactor;
0620 }
0621 
0622 qreal ZoomEffect::targetZoom() const
0623 {
0624     return target_zoom;
0625 }
0626 
0627 } // namespace
0628 
0629 #include "moc_zoom.cpp"