KWin
Loading...
Searching...
No Matches
touchpoints.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: 2012 Filip Wieladek <wattos@gmail.com>
6 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include "touchpoints.h"
12
13#include "core/rendertarget.h"
14#include "core/renderviewport.h"
16#include "opengl/glutils.h"
17#include <QAction>
18
19#include <KConfigGroup>
20#include <KGlobalAccel>
21
22#include <QPainter>
23
24#include <cmath>
25
26namespace KWin
27{
28
33
35
36static const Qt::GlobalColor s_colors[] = {
37 Qt::blue,
38 Qt::red,
39 Qt::green,
40 Qt::cyan,
41 Qt::magenta,
42 Qt::yellow,
43 Qt::gray,
44 Qt::darkBlue,
45 Qt::darkRed,
46 Qt::darkGreen};
47
48Qt::GlobalColor TouchPointsEffect::colorForId(quint32 id)
49{
50 auto it = m_colors.constFind(id);
51 if (it != m_colors.constEnd()) {
52 return it.value();
53 }
54 static int s_colorIndex = -1;
55 s_colorIndex = (s_colorIndex + 1) % 10;
56 m_colors.insert(id, s_colors[s_colorIndex]);
57 return s_colors[s_colorIndex];
58}
59
60bool TouchPointsEffect::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
61{
62 TouchPoint point;
63 point.pos = pos;
64 point.press = true;
65 point.color = colorForId(id);
66 m_points << point;
67 m_latestPositions.insert(id, pos);
68 repaint();
69 return false;
70}
71
72bool TouchPointsEffect::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
73{
74 TouchPoint point;
75 point.pos = pos;
76 point.press = true;
77 point.color = colorForId(id);
78 m_points << point;
79 m_latestPositions.insert(id, pos);
80 repaint();
81 return false;
82}
83
84bool TouchPointsEffect::touchUp(qint32 id, std::chrono::microseconds time)
85{
86 auto it = m_latestPositions.constFind(id);
87 if (it != m_latestPositions.constEnd()) {
88 TouchPoint point;
89 point.pos = it.value();
90 point.press = false;
91 point.color = colorForId(id);
92 m_points << point;
93 }
94 return false;
95}
96
97void TouchPointsEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
98{
99 int time = 0;
100 if (m_lastPresentTime.count()) {
101 time = (presentTime - m_lastPresentTime).count();
102 }
103
104 auto it = m_points.begin();
105 while (it != m_points.end()) {
106 it->time += time;
107 if (it->time > m_ringLife) {
108 it = m_points.erase(it);
109 } else {
110 it++;
111 }
112 }
113
114 if (m_points.isEmpty()) {
115 m_lastPresentTime = std::chrono::milliseconds::zero();
116 } else {
117 m_lastPresentTime = presentTime;
118 }
119
120 effects->prePaintScreen(data, presentTime);
121}
122
123void TouchPointsEffect::paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
124{
125 effects->paintScreen(renderTarget, viewport, mask, region, screen);
126
128 paintScreenSetupGl(renderTarget, viewport.projectionMatrix());
129 }
130 for (auto it = m_points.constBegin(), end = m_points.constEnd(); it != end; ++it) {
131 for (int i = 0; i < m_ringCount; ++i) {
132 float alpha = computeAlpha(it->time, i);
133 float size = computeRadius(it->time, it->press, i);
134 if (size > 0 && alpha > 0) {
135 QColor color = it->color;
136 color.setAlphaF(alpha);
137 drawCircle(viewport, color, it->pos.x(), it->pos.y(), size);
138 }
139 }
140 }
142 paintScreenFinishGl();
143 }
144}
145
147{
149 repaint();
150}
151
152float TouchPointsEffect::computeRadius(int time, bool press, int ring)
153{
154 float ringDistance = m_ringLife / (m_ringCount * 3);
155 if (press) {
156 return ((time - ringDistance * ring) / m_ringLife) * m_ringMaxSize;
157 }
158 return ((m_ringLife - time - ringDistance * ring) / m_ringLife) * m_ringMaxSize;
159}
160
161float TouchPointsEffect::computeAlpha(int time, int ring)
162{
163 float ringDistance = m_ringLife / (m_ringCount * 3);
164 return (m_ringLife - (float)time - ringDistance * (ring)) / m_ringLife;
165}
166
167void TouchPointsEffect::repaint()
168{
169 if (!m_points.isEmpty()) {
170 QRegion dirtyRegion;
171 const int radius = m_ringMaxSize + m_lineWidth;
172 for (auto it = m_points.constBegin(), end = m_points.constEnd(); it != end; ++it) {
173 dirtyRegion += QRect(it->pos.x() - radius, it->pos.y() - radius, 2 * radius, 2 * radius);
174 }
175 effects->addRepaint(dirtyRegion);
176 }
177}
178
180{
181 return !m_points.isEmpty();
182}
183
184void TouchPointsEffect::drawCircle(const RenderViewport &viewport, const QColor &color, float cx, float cy, float r)
185{
187 drawCircleGl(viewport, color, cx, cy, r);
189 drawCircleQPainter(color, cx, cy, r);
190 }
191}
192
193void TouchPointsEffect::drawCircleGl(const RenderViewport &viewport, const QColor &color, float cx, float cy, float r)
194{
195 static const int num_segments = 80;
196 static const float theta = 2 * 3.1415926 / float(num_segments);
197 static const float c = cosf(theta); // precalculate the sine and cosine
198 static const float s = sinf(theta);
199 const auto scale = viewport.scale();
200 float t;
201
202 float x = r; // we start at angle = 0
203 float y = 0;
204
205 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer();
206 vbo->reset();
208 QList<QVector2D> verts;
209 verts.reserve(num_segments);
210
211 for (int ii = 0; ii < num_segments; ++ii) {
212 verts.push_back(QVector2D((x + cx) * scale, (y + cy) * scale));
213 // apply the rotation matrix
214 t = x;
215 x = c * x - s * y;
216 y = s * t + c * y;
217 }
218 vbo->setVertices(verts);
219 vbo->render(GL_LINE_LOOP);
220}
221
222void TouchPointsEffect::drawCircleQPainter(const QColor &color, float cx, float cy, float r)
223{
224 QPainter *painter = effects->scenePainter();
225 painter->save();
226 painter->setPen(color);
227 painter->drawArc(cx - r, cy - r, r * 2, r * 2, 0, 5760);
228 painter->restore();
229}
230
231void TouchPointsEffect::paintScreenSetupGl(const RenderTarget &renderTarget, const QMatrix4x4 &projectionMatrix)
232{
235 shader->setColorspaceUniformsFromSRGB(renderTarget.colorDescription());
236
237 glLineWidth(m_lineWidth);
238 glEnable(GL_BLEND);
239 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
240}
241
242void TouchPointsEffect::paintScreenFinishGl()
243{
244 glDisable(GL_BLEND);
245
247}
248
249} // namespace
250
251#include "moc_touchpoints.cpp"
Base class for all KWin effects.
Definition effect.h:535
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen)
Q_SCRIPTABLE void addRepaint(const QRectF &r)
CompositingType compositingType
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime)
bool isOpenGLCompositing() const
Whether the Compositor is OpenGL based (either GL 1 or 2).
QPainter * scenePainter()
Provides access to the QPainter which is rendering to the back buffer.
bool setUniform(const char *name, float value)
Definition glshader.cpp:301
static GLVertexBuffer * streamingBuffer()
QMatrix4x4 projectionMatrix() const
GLShader * getBoundShader() const
static ShaderManager * instance()
GLShader * pushShader(ShaderTraits traits)
void postPaintScreen() override
void paintScreen(const RenderTarget &renderTarget, const RenderViewport &viewport, int mask, const QRegion &region, Output *screen) override
bool touchUp(qint32 id, std::chrono::microseconds time) override
~TouchPointsEffect() override
bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
bool isActive() const override
void prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) override
bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
@ QPainterCompositing
Definition globals.h:39
EffectsHandler * effects