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 ®ion, 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"