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

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 Jorge Mata <matamax123@gmail.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 "trackmouse.h"
0013 
0014 // KConfigSkeleton
0015 #include "trackmouseconfig.h"
0016 
0017 #include <QAction>
0018 #include <QMatrix4x4>
0019 #include <QPainter>
0020 #include <QTime>
0021 
0022 #include "core/rendertarget.h"
0023 #include "core/renderviewport.h"
0024 #include "effect/effecthandler.h"
0025 #include "opengl/glutils.h"
0026 
0027 #include <KGlobalAccel>
0028 #include <KLocalizedString>
0029 
0030 #include <cmath>
0031 
0032 namespace KWin
0033 {
0034 
0035 TrackMouseEffect::TrackMouseEffect()
0036     : m_angle(0)
0037 {
0038     TrackMouseConfig::instance(effects->config());
0039     if (effects->isOpenGLCompositing() || effects->compositingType() == QPainterCompositing) {
0040         m_angleBase = 90.0;
0041     }
0042     m_mousePolling = false;
0043 
0044     m_action = new QAction(this);
0045     m_action->setObjectName(QStringLiteral("TrackMouse"));
0046     m_action->setText(i18n("Track mouse"));
0047     KGlobalAccel::self()->setDefaultShortcut(m_action, QList<QKeySequence>());
0048     KGlobalAccel::self()->setShortcut(m_action, QList<QKeySequence>());
0049 
0050     connect(m_action, &QAction::triggered, this, &TrackMouseEffect::toggle);
0051 
0052     connect(effects, &EffectsHandler::mouseChanged, this, &TrackMouseEffect::slotMouseChanged);
0053     reconfigure(ReconfigureAll);
0054 }
0055 
0056 TrackMouseEffect::~TrackMouseEffect()
0057 {
0058     if (m_mousePolling) {
0059         effects->stopMousePolling();
0060     }
0061 }
0062 
0063 void TrackMouseEffect::reconfigure(ReconfigureFlags)
0064 {
0065     m_modifiers = Qt::KeyboardModifiers();
0066     TrackMouseConfig::self()->read();
0067     if (TrackMouseConfig::shift()) {
0068         m_modifiers |= Qt::ShiftModifier;
0069     }
0070     if (TrackMouseConfig::alt()) {
0071         m_modifiers |= Qt::AltModifier;
0072     }
0073     if (TrackMouseConfig::control()) {
0074         m_modifiers |= Qt::ControlModifier;
0075     }
0076     if (TrackMouseConfig::meta()) {
0077         m_modifiers |= Qt::MetaModifier;
0078     }
0079 
0080     if (m_modifiers) {
0081         if (!m_mousePolling) {
0082             effects->startMousePolling();
0083         }
0084         m_mousePolling = true;
0085     } else if (m_mousePolling) {
0086         effects->stopMousePolling();
0087         m_mousePolling = false;
0088     }
0089 }
0090 
0091 void TrackMouseEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
0092 {
0093     QTime t = QTime::currentTime();
0094     m_angle = ((t.second() % 4) * m_angleBase) + (t.msec() / 1000.0 * m_angleBase);
0095     m_lastRect[0].moveCenter(cursorPos().toPoint());
0096     m_lastRect[1].moveCenter(cursorPos().toPoint());
0097     data.paint += m_lastRect[0].adjusted(-1, -1, 1, 1);
0098 
0099     effects->prePaintScreen(data, presentTime);
0100 }
0101 
0102 void TrackMouseEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
0103 {
0104     effects->paintScreen(renderTarget, viewport, mask, region, screen); // paint normal screen
0105 
0106     if (effects->isOpenGLCompositing() && m_texture[0] && m_texture[1]) {
0107         ShaderBinder binder(ShaderTrait::MapTexture | ShaderTrait::TransformColorspace);
0108         GLShader *shader(binder.shader());
0109         if (!shader) {
0110             return;
0111         }
0112         shader->setColorspaceUniformsFromSRGB(renderTarget.colorDescription());
0113         glEnable(GL_BLEND);
0114         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
0115         QMatrix4x4 matrix(viewport.projectionMatrix());
0116         const QPointF p = m_lastRect[0].topLeft() + QPoint(m_lastRect[0].width() / 2.0, m_lastRect[0].height() / 2.0);
0117         const float x = p.x();
0118         const float y = p.y();
0119         const auto scale = viewport.scale();
0120         for (int i = 0; i < 2; ++i) {
0121             matrix.translate(x * scale, y * scale, 0.0);
0122             matrix.rotate(i ? -2 * m_angle : m_angle, 0, 0, 1.0);
0123             matrix.translate(-x * scale, -y * scale, 0.0);
0124             QMatrix4x4 mvp(matrix);
0125             mvp.translate(m_lastRect[i].x() * scale, m_lastRect[i].y() * scale);
0126             shader->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp);
0127             m_texture[i]->render(m_lastRect[i].size() * scale);
0128         }
0129         glDisable(GL_BLEND);
0130     } else if (effects->compositingType() == QPainterCompositing && !m_image[0].isNull() && !m_image[1].isNull()) {
0131         QPainter *painter = effects->scenePainter();
0132         const QPointF p = m_lastRect[0].topLeft() + QPoint(m_lastRect[0].width() / 2.0, m_lastRect[0].height() / 2.0);
0133         for (int i = 0; i < 2; ++i) {
0134             painter->save();
0135             painter->translate(p.x(), p.y());
0136             painter->rotate(i ? -2 * m_angle : m_angle);
0137             painter->translate(-p.x(), -p.y());
0138             painter->drawImage(m_lastRect[i], m_image[i]);
0139             painter->restore();
0140         }
0141     }
0142 }
0143 
0144 void TrackMouseEffect::postPaintScreen()
0145 {
0146     effects->addRepaint(m_lastRect[0].adjusted(-1, -1, 1, 1));
0147     effects->postPaintScreen();
0148 }
0149 
0150 bool TrackMouseEffect::init()
0151 {
0152     effects->makeOpenGLContextCurrent();
0153     if (!m_texture[0] && m_image[0].isNull()) {
0154         loadTexture();
0155         if (!m_texture[0] && m_image[0].isNull()) {
0156             return false;
0157         }
0158     }
0159     m_lastRect[0].moveCenter(cursorPos().toPoint());
0160     m_lastRect[1].moveCenter(cursorPos().toPoint());
0161     m_angle = 0;
0162     return true;
0163 }
0164 
0165 void TrackMouseEffect::toggle()
0166 {
0167     switch (m_state) {
0168     case State::ActivatedByModifiers:
0169         m_state = State::ActivatedByShortcut;
0170         break;
0171 
0172     case State::ActivatedByShortcut:
0173         m_state = State::Inactive;
0174         break;
0175 
0176     case State::Inactive:
0177         if (!init()) {
0178             return;
0179         }
0180         m_state = State::ActivatedByShortcut;
0181         break;
0182 
0183     default:
0184         Q_UNREACHABLE();
0185         break;
0186     }
0187 
0188     effects->addRepaint(m_lastRect[0].adjusted(-1, -1, 1, 1));
0189 }
0190 
0191 void TrackMouseEffect::slotMouseChanged(const QPointF &, const QPointF &,
0192                                         Qt::MouseButtons, Qt::MouseButtons,
0193                                         Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers)
0194 {
0195     if (!m_mousePolling) { // we didn't ask for it but maybe someone else did...
0196         return;
0197     }
0198 
0199     switch (m_state) {
0200     case State::ActivatedByModifiers:
0201         if (modifiers == m_modifiers) {
0202             return;
0203         }
0204         m_state = State::Inactive;
0205         break;
0206 
0207     case State::ActivatedByShortcut:
0208         return;
0209 
0210     case State::Inactive:
0211         if (modifiers != m_modifiers) {
0212             return;
0213         }
0214         if (!init()) {
0215             return;
0216         }
0217         m_state = State::ActivatedByModifiers;
0218         break;
0219 
0220     default:
0221         Q_UNREACHABLE();
0222         break;
0223     }
0224 
0225     effects->addRepaint(m_lastRect[0].adjusted(-1, -1, 1, 1));
0226 }
0227 
0228 void TrackMouseEffect::loadTexture()
0229 {
0230     QString f[2] = {QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("tm_outer.png")),
0231                     QStandardPaths::locate(QStandardPaths::AppDataLocation, QStringLiteral("tm_inner.png"))};
0232     if (f[0].isEmpty() || f[1].isEmpty()) {
0233         return;
0234     }
0235 
0236     for (int i = 0; i < 2; ++i) {
0237         if (effects->isOpenGLCompositing()) {
0238             QImage img(f[i]);
0239             m_texture[i] = GLTexture::upload(img);
0240             m_lastRect[i].setSize(img.size());
0241         }
0242         if (effects->compositingType() == QPainterCompositing) {
0243             m_image[i] = QImage(f[i]);
0244             m_lastRect[i].setSize(m_image[i].size());
0245         }
0246     }
0247 }
0248 
0249 bool TrackMouseEffect::isActive() const
0250 {
0251     return m_state != State::Inactive;
0252 }
0253 
0254 } // namespace
0255 
0256 #include "moc_trackmouse.cpp"