File indexing completed on 2024-11-10 04:57:02
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org> 0006 SPDX-FileCopyrightText: 2007 Christian Nitschkowski <christian.nitschkowski@kdemail.net> 0007 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 #include "magnifier.h" 0013 // KConfigSkeleton 0014 #include "magnifierconfig.h" 0015 0016 #include <QAction> 0017 #include <kstandardaction.h> 0018 0019 #include "core/renderviewport.h" 0020 #include "effect/effecthandler.h" 0021 #include "opengl/glutils.h" 0022 #include <KGlobalAccel> 0023 0024 namespace KWin 0025 { 0026 0027 const int FRAME_WIDTH = 5; 0028 0029 MagnifierEffect::MagnifierEffect() 0030 : m_zoom(1) 0031 , m_targetZoom(1) 0032 , m_polling(false) 0033 , m_lastPresentTime(std::chrono::milliseconds::zero()) 0034 , m_texture(nullptr) 0035 , m_fbo(nullptr) 0036 { 0037 MagnifierConfig::instance(effects->config()); 0038 QAction *a; 0039 a = KStandardAction::zoomIn(this, &MagnifierEffect::zoomIn, this); 0040 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Plus) << (Qt::META | Qt::Key_Equal)); 0041 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Plus) << (Qt::META | Qt::Key_Equal)); 0042 0043 a = KStandardAction::zoomOut(this, &MagnifierEffect::zoomOut, this); 0044 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Minus)); 0045 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Minus)); 0046 0047 a = KStandardAction::actualSize(this, &MagnifierEffect::toggle, this); 0048 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_0)); 0049 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_0)); 0050 0051 connect(effects, &EffectsHandler::mouseChanged, this, &MagnifierEffect::slotMouseChanged); 0052 connect(effects, &EffectsHandler::windowAdded, this, &MagnifierEffect::slotWindowAdded); 0053 0054 const auto windows = effects->stackingOrder(); 0055 for (EffectWindow *window : windows) { 0056 slotWindowAdded(window); 0057 } 0058 0059 reconfigure(ReconfigureAll); 0060 } 0061 0062 MagnifierEffect::~MagnifierEffect() 0063 { 0064 // Save the zoom value. 0065 MagnifierConfig::setInitialZoom(m_targetZoom); 0066 MagnifierConfig::self()->save(); 0067 } 0068 0069 bool MagnifierEffect::supported() 0070 { 0071 return effects->isOpenGLCompositing() && GLFramebuffer::blitSupported(); 0072 } 0073 0074 void MagnifierEffect::reconfigure(ReconfigureFlags) 0075 { 0076 MagnifierConfig::self()->read(); 0077 int width, height; 0078 width = MagnifierConfig::width(); 0079 height = MagnifierConfig::height(); 0080 m_magnifierSize = QSize(width, height); 0081 // Load the saved zoom value. 0082 m_targetZoom = MagnifierConfig::initialZoom(); 0083 if (m_targetZoom != m_zoom) { 0084 toggle(); 0085 } 0086 } 0087 0088 void MagnifierEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) 0089 { 0090 const int time = m_lastPresentTime.count() ? (presentTime - m_lastPresentTime).count() : 0; 0091 0092 if (m_zoom != m_targetZoom) { 0093 double diff = time / animationTime(500.0); 0094 if (m_targetZoom > m_zoom) { 0095 m_zoom = std::min(m_zoom * std::max(1 + diff, 1.2), m_targetZoom); 0096 } else { 0097 m_zoom = std::max(m_zoom * std::min(1 - diff, 0.8), m_targetZoom); 0098 if (m_zoom == 1.0) { 0099 // zoom ended - delete FBO and texture 0100 m_fbo.reset(); 0101 m_texture.reset(); 0102 } 0103 } 0104 } 0105 0106 if (m_zoom != m_targetZoom) { 0107 m_lastPresentTime = presentTime; 0108 } else { 0109 m_lastPresentTime = std::chrono::milliseconds::zero(); 0110 } 0111 0112 effects->prePaintScreen(data, presentTime); 0113 if (m_zoom != 1.0) { 0114 data.paint += magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH); 0115 } 0116 } 0117 0118 void MagnifierEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion ®ion, Output *screen) 0119 { 0120 effects->paintScreen(renderTarget, viewport, mask, region, screen); // paint normal screen 0121 if (m_zoom != 1.0) { 0122 // get the right area from the current rendered screen 0123 const QRect area = magnifierArea(); 0124 const QPointF cursor = cursorPos(); 0125 const auto scale = viewport.scale(); 0126 0127 QRectF srcArea(cursor.x() - (double)area.width() / (m_zoom * 2), 0128 cursor.y() - (double)area.height() / (m_zoom * 2), 0129 (double)area.width() / m_zoom, (double)area.height() / m_zoom); 0130 if (effects->isOpenGLCompositing()) { 0131 m_fbo->blitFromRenderTarget(renderTarget, viewport, srcArea.toRect(), QRect(QPoint(), m_fbo->size())); 0132 // paint magnifier 0133 auto s = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); 0134 QMatrix4x4 mvp = viewport.projectionMatrix(); 0135 mvp.translate(area.x() * scale, area.y() * scale); 0136 s->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, mvp); 0137 m_texture->render(area.size() * scale); 0138 ShaderManager::instance()->popShader(); 0139 0140 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); 0141 vbo->reset(); 0142 0143 QRectF areaF = scaledRect(area, scale); 0144 const QRectF frame = scaledRect(area.adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH), scale); 0145 QList<QVector2D> verts; 0146 verts.reserve(4 * 6 * 2); 0147 // top frame 0148 verts.push_back(QVector2D(frame.right(), frame.top())); 0149 verts.push_back(QVector2D(frame.left(), frame.top())); 0150 verts.push_back(QVector2D(frame.left(), areaF.top())); 0151 verts.push_back(QVector2D(frame.left(), areaF.top())); 0152 verts.push_back(QVector2D(frame.right(), areaF.top())); 0153 verts.push_back(QVector2D(frame.right(), frame.top())); 0154 // left frame 0155 verts.push_back(QVector2D(areaF.left(), frame.top())); 0156 verts.push_back(QVector2D(frame.left(), frame.top())); 0157 verts.push_back(QVector2D(frame.left(), frame.bottom())); 0158 verts.push_back(QVector2D(frame.left(), frame.bottom())); 0159 verts.push_back(QVector2D(areaF.left(), frame.bottom())); 0160 verts.push_back(QVector2D(areaF.left(), frame.top())); 0161 // right frame 0162 verts.push_back(QVector2D(frame.right(), frame.top())); 0163 verts.push_back(QVector2D(areaF.right(), frame.top())); 0164 verts.push_back(QVector2D(areaF.right(), frame.bottom())); 0165 verts.push_back(QVector2D(areaF.right(), frame.bottom())); 0166 verts.push_back(QVector2D(frame.right(), frame.bottom())); 0167 verts.push_back(QVector2D(frame.right(), frame.top())); 0168 // bottom frame 0169 verts.push_back(QVector2D(frame.right(), areaF.bottom())); 0170 verts.push_back(QVector2D(frame.left(), areaF.bottom())); 0171 verts.push_back(QVector2D(frame.left(), frame.bottom())); 0172 verts.push_back(QVector2D(frame.left(), frame.bottom())); 0173 verts.push_back(QVector2D(frame.right(), frame.bottom())); 0174 verts.push_back(QVector2D(frame.right(), areaF.bottom())); 0175 vbo->setVertices(verts); 0176 0177 ShaderBinder binder(ShaderTrait::UniformColor); 0178 binder.shader()->setUniform(GLShader::Mat4Uniform::ModelViewProjectionMatrix, viewport.projectionMatrix()); 0179 binder.shader()->setUniform(GLShader::ColorUniform::Color, QColor(0, 0, 0)); 0180 vbo->render(GL_TRIANGLES); 0181 } 0182 } 0183 } 0184 0185 void MagnifierEffect::postPaintScreen() 0186 { 0187 if (m_zoom != m_targetZoom) { 0188 QRect framedarea = magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH); 0189 effects->addRepaint(framedarea); 0190 } 0191 effects->postPaintScreen(); 0192 } 0193 0194 QRect MagnifierEffect::magnifierArea(QPointF pos) const 0195 { 0196 return QRect(pos.x() - m_magnifierSize.width() / 2, pos.y() - m_magnifierSize.height() / 2, 0197 m_magnifierSize.width(), m_magnifierSize.height()); 0198 } 0199 0200 void MagnifierEffect::zoomIn() 0201 { 0202 m_targetZoom *= 1.2; 0203 if (!m_polling) { 0204 m_polling = true; 0205 effects->startMousePolling(); 0206 } 0207 if (effects->isOpenGLCompositing() && !m_texture) { 0208 effects->makeOpenGLContextCurrent(); 0209 m_texture = GLTexture::allocate(GL_RGBA16F, m_magnifierSize); 0210 if (!m_texture) { 0211 return; 0212 } 0213 m_texture->setContentTransform(OutputTransform()); 0214 m_fbo = std::make_unique<GLFramebuffer>(m_texture.get()); 0215 } 0216 effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH)); 0217 } 0218 0219 void MagnifierEffect::zoomOut() 0220 { 0221 m_targetZoom /= 1.2; 0222 if (m_targetZoom <= 1) { 0223 m_targetZoom = 1; 0224 if (m_polling) { 0225 m_polling = false; 0226 effects->stopMousePolling(); 0227 } 0228 if (m_zoom == m_targetZoom) { 0229 effects->makeOpenGLContextCurrent(); 0230 m_fbo.reset(); 0231 m_texture.reset(); 0232 } 0233 } 0234 effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH)); 0235 } 0236 0237 void MagnifierEffect::toggle() 0238 { 0239 if (m_zoom == 1.0) { 0240 if (m_targetZoom == 1.0) { 0241 m_targetZoom = 2; 0242 } 0243 if (!m_polling) { 0244 m_polling = true; 0245 effects->startMousePolling(); 0246 } 0247 if (effects->isOpenGLCompositing() && !m_texture) { 0248 effects->makeOpenGLContextCurrent(); 0249 m_texture = GLTexture::allocate(GL_RGBA16F, m_magnifierSize); 0250 if (!m_texture) { 0251 return; 0252 } 0253 m_texture->setContentTransform(OutputTransform()); 0254 m_fbo = std::make_unique<GLFramebuffer>(m_texture.get()); 0255 } 0256 } else { 0257 m_targetZoom = 1; 0258 if (m_polling) { 0259 m_polling = false; 0260 effects->stopMousePolling(); 0261 } 0262 } 0263 effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH)); 0264 } 0265 0266 void MagnifierEffect::slotMouseChanged(const QPointF &pos, const QPointF &old, 0267 Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers) 0268 { 0269 if (pos != old && m_zoom != 1) { 0270 // need full repaint as we might lose some change events on fast mouse movements 0271 // see Bug 187658 0272 effects->addRepaintFull(); 0273 } 0274 } 0275 0276 void MagnifierEffect::slotWindowAdded(EffectWindow *w) 0277 { 0278 connect(w, &EffectWindow::windowDamaged, this, &MagnifierEffect::slotWindowDamaged); 0279 } 0280 0281 void MagnifierEffect::slotWindowDamaged() 0282 { 0283 if (isActive()) { 0284 effects->addRepaint(magnifierArea()); 0285 } 0286 } 0287 0288 bool MagnifierEffect::isActive() const 0289 { 0290 return m_zoom != 1.0 || m_zoom != m_targetZoom; 0291 } 0292 0293 QSize MagnifierEffect::magnifierSize() const 0294 { 0295 return m_magnifierSize; 0296 } 0297 0298 qreal MagnifierEffect::targetZoom() const 0299 { 0300 return m_targetZoom; 0301 } 0302 0303 } // namespace 0304 0305 #include "moc_magnifier.cpp"