KWin
Loading...
Searching...
No Matches
magnifier.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2007 Lubos Lunak <l.lunak@kde.org>
6 SPDX-FileCopyrightText: 2007 Christian Nitschkowski <christian.nitschkowski@kdemail.net>
7 SPDX-FileCopyrightText: 2011 Martin Gräßlin <mgraesslin@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12#include "magnifier.h"
13// KConfigSkeleton
14#include "magnifierconfig.h"
15
16#include <QAction>
17#include <kstandardaction.h>
18
19#include "core/renderviewport.h"
21#include "opengl/glutils.h"
22#include <KGlobalAccel>
23
24namespace KWin
25{
26
27const int FRAME_WIDTH = 5;
28
30 : m_zoom(1)
31 , m_targetZoom(1)
32 , m_polling(false)
33 , m_lastPresentTime(std::chrono::milliseconds::zero())
34 , m_texture(nullptr)
35 , m_fbo(nullptr)
36{
37 MagnifierConfig::instance(effects->config());
38 QAction *a;
39 a = KStandardAction::zoomIn(this, &MagnifierEffect::zoomIn, this);
40 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Plus) << (Qt::META | Qt::Key_Equal));
41 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Plus) << (Qt::META | Qt::Key_Equal));
42
43 a = KStandardAction::zoomOut(this, &MagnifierEffect::zoomOut, this);
44 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Minus));
45 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_Minus));
46
47 a = KStandardAction::actualSize(this, &MagnifierEffect::toggle, this);
48 KGlobalAccel::self()->setDefaultShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_0));
49 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>() << (Qt::META | Qt::Key_0));
50
51 connect(effects, &EffectsHandler::mouseChanged, this, &MagnifierEffect::slotMouseChanged);
52 connect(effects, &EffectsHandler::windowAdded, this, &MagnifierEffect::slotWindowAdded);
53
54 const auto windows = effects->stackingOrder();
55 for (EffectWindow *window : windows) {
56 slotWindowAdded(window);
57 }
58
60}
61
63{
64 // Save the zoom value.
65 MagnifierConfig::setInitialZoom(m_targetZoom);
66 MagnifierConfig::self()->save();
67}
68
73
74void MagnifierEffect::reconfigure(ReconfigureFlags)
75{
76 MagnifierConfig::self()->read();
77 int width, height;
78 width = MagnifierConfig::width();
79 height = MagnifierConfig::height();
80 m_magnifierSize = QSize(width, height);
81 // Load the saved zoom value.
82 m_targetZoom = MagnifierConfig::initialZoom();
83 if (m_targetZoom != m_zoom) {
84 toggle();
85 }
86}
87
88void MagnifierEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
89{
90 const int time = m_lastPresentTime.count() ? (presentTime - m_lastPresentTime).count() : 0;
91
92 if (m_zoom != m_targetZoom) {
93 double diff = time / animationTime(500.0);
94 if (m_targetZoom > m_zoom) {
95 m_zoom = std::min(m_zoom * std::max(1 + diff, 1.2), m_targetZoom);
96 } else {
97 m_zoom = std::max(m_zoom * std::min(1 - diff, 0.8), m_targetZoom);
98 if (m_zoom == 1.0) {
99 // zoom ended - delete FBO and texture
100 m_fbo.reset();
101 m_texture.reset();
102 }
103 }
104 }
105
106 if (m_zoom != m_targetZoom) {
107 m_lastPresentTime = presentTime;
108 } else {
109 m_lastPresentTime = std::chrono::milliseconds::zero();
110 }
111
112 effects->prePaintScreen(data, presentTime);
113 if (m_zoom != 1.0) {
114 data.paint += magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH);
115 }
116}
117
118void MagnifierEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
119{
120 effects->paintScreen(renderTarget, viewport, mask, region, screen); // paint normal screen
121 if (m_zoom != 1.0) {
122 // get the right area from the current rendered screen
123 const QRect area = magnifierArea();
124 const QPointF cursor = cursorPos();
125 const auto scale = viewport.scale();
126
127 QRectF srcArea(cursor.x() - (double)area.width() / (m_zoom * 2),
128 cursor.y() - (double)area.height() / (m_zoom * 2),
129 (double)area.width() / m_zoom, (double)area.height() / m_zoom);
131 m_fbo->blitFromRenderTarget(renderTarget, viewport, srcArea.toRect(), QRect(QPoint(), m_fbo->size()));
132 // paint magnifier
134 QMatrix4x4 mvp = viewport.projectionMatrix();
135 mvp.translate(area.x() * scale, area.y() * scale);
137 m_texture->render(area.size() * scale);
139
141 vbo->reset();
142
143 QRectF areaF = scaledRect(area, scale);
144 const QRectF frame = scaledRect(area.adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH), scale);
145 QList<QVector2D> verts;
146 verts.reserve(4 * 6 * 2);
147 // top frame
148 verts.push_back(QVector2D(frame.right(), frame.top()));
149 verts.push_back(QVector2D(frame.left(), frame.top()));
150 verts.push_back(QVector2D(frame.left(), areaF.top()));
151 verts.push_back(QVector2D(frame.left(), areaF.top()));
152 verts.push_back(QVector2D(frame.right(), areaF.top()));
153 verts.push_back(QVector2D(frame.right(), frame.top()));
154 // left frame
155 verts.push_back(QVector2D(areaF.left(), frame.top()));
156 verts.push_back(QVector2D(frame.left(), frame.top()));
157 verts.push_back(QVector2D(frame.left(), frame.bottom()));
158 verts.push_back(QVector2D(frame.left(), frame.bottom()));
159 verts.push_back(QVector2D(areaF.left(), frame.bottom()));
160 verts.push_back(QVector2D(areaF.left(), frame.top()));
161 // right frame
162 verts.push_back(QVector2D(frame.right(), frame.top()));
163 verts.push_back(QVector2D(areaF.right(), frame.top()));
164 verts.push_back(QVector2D(areaF.right(), frame.bottom()));
165 verts.push_back(QVector2D(areaF.right(), frame.bottom()));
166 verts.push_back(QVector2D(frame.right(), frame.bottom()));
167 verts.push_back(QVector2D(frame.right(), frame.top()));
168 // bottom frame
169 verts.push_back(QVector2D(frame.right(), areaF.bottom()));
170 verts.push_back(QVector2D(frame.left(), areaF.bottom()));
171 verts.push_back(QVector2D(frame.left(), frame.bottom()));
172 verts.push_back(QVector2D(frame.left(), frame.bottom()));
173 verts.push_back(QVector2D(frame.right(), frame.bottom()));
174 verts.push_back(QVector2D(frame.right(), areaF.bottom()));
175 vbo->setVertices(verts);
176
179 binder.shader()->setUniform(GLShader::ColorUniform::Color, QColor(0, 0, 0));
180 vbo->render(GL_TRIANGLES);
181 }
182 }
183}
184
186{
187 if (m_zoom != m_targetZoom) {
188 QRect framedarea = magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH);
189 effects->addRepaint(framedarea);
190 }
192}
193
194QRect MagnifierEffect::magnifierArea(QPointF pos) const
195{
196 return QRect(pos.x() - m_magnifierSize.width() / 2, pos.y() - m_magnifierSize.height() / 2,
197 m_magnifierSize.width(), m_magnifierSize.height());
198}
199
200void MagnifierEffect::zoomIn()
201{
202 m_targetZoom *= 1.2;
203 if (!m_polling) {
204 m_polling = true;
206 }
207 if (effects->isOpenGLCompositing() && !m_texture) {
209 m_texture = GLTexture::allocate(GL_RGBA16F, m_magnifierSize);
210 if (!m_texture) {
211 return;
212 }
213 m_texture->setContentTransform(OutputTransform());
214 m_fbo = std::make_unique<GLFramebuffer>(m_texture.get());
215 }
216 effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH));
217}
218
219void MagnifierEffect::zoomOut()
220{
221 m_targetZoom /= 1.2;
222 if (m_targetZoom <= 1) {
223 m_targetZoom = 1;
224 if (m_polling) {
225 m_polling = false;
227 }
228 if (m_zoom == m_targetZoom) {
230 m_fbo.reset();
231 m_texture.reset();
232 }
233 }
234 effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH));
235}
236
237void MagnifierEffect::toggle()
238{
239 if (m_zoom == 1.0) {
240 if (m_targetZoom == 1.0) {
241 m_targetZoom = 2;
242 }
243 if (!m_polling) {
244 m_polling = true;
246 }
247 if (effects->isOpenGLCompositing() && !m_texture) {
249 m_texture = GLTexture::allocate(GL_RGBA16F, m_magnifierSize);
250 if (!m_texture) {
251 return;
252 }
253 m_texture->setContentTransform(OutputTransform());
254 m_fbo = std::make_unique<GLFramebuffer>(m_texture.get());
255 }
256 } else {
257 m_targetZoom = 1;
258 if (m_polling) {
259 m_polling = false;
261 }
262 }
263 effects->addRepaint(magnifierArea().adjusted(-FRAME_WIDTH, -FRAME_WIDTH, FRAME_WIDTH, FRAME_WIDTH));
264}
265
266void MagnifierEffect::slotMouseChanged(const QPointF &pos, const QPointF &old,
267 Qt::MouseButtons, Qt::MouseButtons, Qt::KeyboardModifiers, Qt::KeyboardModifiers)
268{
269 if (pos != old && m_zoom != 1) {
270 // need full repaint as we might lose some change events on fast mouse movements
271 // see Bug 187658
273 }
274}
275
276void MagnifierEffect::slotWindowAdded(EffectWindow *w)
277{
278 connect(w, &EffectWindow::windowDamaged, this, &MagnifierEffect::slotWindowDamaged);
279}
280
281void MagnifierEffect::slotWindowDamaged()
282{
283 if (isActive()) {
284 effects->addRepaint(magnifierArea());
285 }
286}
287
289{
290 return m_zoom != 1.0 || m_zoom != m_targetZoom;
291}
292
294{
295 return m_magnifierSize;
296}
297
299{
300 return m_targetZoom;
301}
302
303} // namespace
304
305#include "moc_magnifier.cpp"
Representation of a window used by/for Effect classes.
void windowDamaged(KWin::EffectWindow *w)
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
QList< EffectWindow * > stackingOrder
Q_SCRIPTABLE void addRepaint(const QRectF &r)
bool makeOpenGLContextCurrent()
Makes the OpenGL compositing context current.
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
void mouseChanged(const QPointF &pos, const QPointF &oldpos, Qt::MouseButtons buttons, Qt::MouseButtons oldbuttons, Qt::KeyboardModifiers modifiers, Qt::KeyboardModifiers oldmodifiers)
KSharedConfigPtr config() const
void windowAdded(KWin::EffectWindow *w)
Q_SCRIPTABLE void addRepaintFull()
static bool blitSupported()
bool setUniform(const char *name, float value)
Definition glshader.cpp:301
static std::unique_ptr< GLTexture > allocate(GLenum internalFormat, const QSize &size, int levels=1)
Vertex Buffer Object.
static GLVertexBuffer * streamingBuffer()
void render(GLenum primitiveMode)
void setVertices(const T &range)
bool isActive() const override
void reconfigure(ReconfigureFlags) override
Definition magnifier.cpp:74
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
Definition magnifier.cpp:88
void postPaintScreen() override
static bool supported()
Definition magnifier.cpp:69
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen) override
~MagnifierEffect() override
Definition magnifier.cpp:62
QMatrix4x4 projectionMatrix() const
static ShaderManager * instance()
GLShader * pushShader(ShaderTraits traits)
static QPointF cursorPos()
Definition effect.cpp:478
static double animationTime(const KConfigGroup &cfg, const QString &key, int defaultTime)
Definition effect.cpp:483
@ ReconfigureAll
Definition effect.h:601
const int FRAME_WIDTH
Definition magnifier.cpp:27
KWIN_EXPORT QRectF scaledRect(const QRectF &rect, qreal scale)
Definition globals.h:243
EffectsHandler * effects