File indexing completed on 2025-04-27 11:33:15
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2012 Filip Wieladek <wattos@gmail.com> 0006 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "touchpoints.h" 0012 0013 #include <QAction> 0014 #include <kwinglutils.h> 0015 0016 #include <KConfigGroup> 0017 #include <KGlobalAccel> 0018 0019 #include <QPainter> 0020 0021 #include <cmath> 0022 0023 namespace KWin 0024 { 0025 0026 TouchPointsEffect::TouchPointsEffect() 0027 : Effect() 0028 { 0029 } 0030 0031 TouchPointsEffect::~TouchPointsEffect() = default; 0032 0033 static const Qt::GlobalColor s_colors[] = { 0034 Qt::blue, 0035 Qt::red, 0036 Qt::green, 0037 Qt::cyan, 0038 Qt::magenta, 0039 Qt::yellow, 0040 Qt::gray, 0041 Qt::darkBlue, 0042 Qt::darkRed, 0043 Qt::darkGreen}; 0044 0045 Qt::GlobalColor TouchPointsEffect::colorForId(quint32 id) 0046 { 0047 auto it = m_colors.constFind(id); 0048 if (it != m_colors.constEnd()) { 0049 return it.value(); 0050 } 0051 static int s_colorIndex = -1; 0052 s_colorIndex = (s_colorIndex + 1) % 10; 0053 m_colors.insert(id, s_colors[s_colorIndex]); 0054 return s_colors[s_colorIndex]; 0055 } 0056 0057 bool TouchPointsEffect::touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) 0058 { 0059 TouchPoint point; 0060 point.pos = pos; 0061 point.press = true; 0062 point.color = colorForId(id); 0063 m_points << point; 0064 m_latestPositions.insert(id, pos); 0065 repaint(); 0066 return false; 0067 } 0068 0069 bool TouchPointsEffect::touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) 0070 { 0071 TouchPoint point; 0072 point.pos = pos; 0073 point.press = true; 0074 point.color = colorForId(id); 0075 m_points << point; 0076 m_latestPositions.insert(id, pos); 0077 repaint(); 0078 return false; 0079 } 0080 0081 bool TouchPointsEffect::touchUp(qint32 id, std::chrono::microseconds time) 0082 { 0083 auto it = m_latestPositions.constFind(id); 0084 if (it != m_latestPositions.constEnd()) { 0085 TouchPoint point; 0086 point.pos = it.value(); 0087 point.press = false; 0088 point.color = colorForId(id); 0089 m_points << point; 0090 } 0091 return false; 0092 } 0093 0094 void TouchPointsEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) 0095 { 0096 int time = 0; 0097 if (m_lastPresentTime.count()) { 0098 time = (presentTime - m_lastPresentTime).count(); 0099 } 0100 0101 auto it = m_points.begin(); 0102 while (it != m_points.end()) { 0103 it->time += time; 0104 if (it->time > m_ringLife) { 0105 it = m_points.erase(it); 0106 } else { 0107 it++; 0108 } 0109 } 0110 0111 if (m_points.isEmpty()) { 0112 m_lastPresentTime = std::chrono::milliseconds::zero(); 0113 } else { 0114 m_lastPresentTime = presentTime; 0115 } 0116 0117 effects->prePaintScreen(data, presentTime); 0118 } 0119 0120 void TouchPointsEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data) 0121 { 0122 effects->paintScreen(mask, region, data); 0123 0124 paintScreenSetup(mask, region, data); 0125 for (auto it = m_points.constBegin(), end = m_points.constEnd(); it != end; ++it) { 0126 for (int i = 0; i < m_ringCount; ++i) { 0127 float alpha = computeAlpha(it->time, i); 0128 float size = computeRadius(it->time, it->press, i); 0129 if (size > 0 && alpha > 0) { 0130 QColor color = it->color; 0131 color.setAlphaF(alpha); 0132 drawCircle(color, it->pos.x(), it->pos.y(), size); 0133 } 0134 } 0135 } 0136 paintScreenFinish(mask, region, data); 0137 } 0138 0139 void TouchPointsEffect::postPaintScreen() 0140 { 0141 effects->postPaintScreen(); 0142 repaint(); 0143 } 0144 0145 float TouchPointsEffect::computeRadius(int time, bool press, int ring) 0146 { 0147 float ringDistance = m_ringLife / (m_ringCount * 3); 0148 if (press) { 0149 return ((time - ringDistance * ring) / m_ringLife) * m_ringMaxSize; 0150 } 0151 return ((m_ringLife - time - ringDistance * ring) / m_ringLife) * m_ringMaxSize; 0152 } 0153 0154 float TouchPointsEffect::computeAlpha(int time, int ring) 0155 { 0156 float ringDistance = m_ringLife / (m_ringCount * 3); 0157 return (m_ringLife - (float)time - ringDistance * (ring)) / m_ringLife; 0158 } 0159 0160 void TouchPointsEffect::repaint() 0161 { 0162 if (!m_points.isEmpty()) { 0163 QRegion dirtyRegion; 0164 const int radius = m_ringMaxSize + m_lineWidth; 0165 for (auto it = m_points.constBegin(), end = m_points.constEnd(); it != end; ++it) { 0166 dirtyRegion |= QRect(it->pos.x() - radius, it->pos.y() - radius, 2 * radius, 2 * radius); 0167 } 0168 effects->addRepaint(dirtyRegion); 0169 } 0170 } 0171 0172 bool TouchPointsEffect::isActive() const 0173 { 0174 return !m_points.isEmpty(); 0175 } 0176 0177 void TouchPointsEffect::drawCircle(const QColor &color, float cx, float cy, float r) 0178 { 0179 if (effects->isOpenGLCompositing()) { 0180 drawCircleGl(color, cx, cy, r); 0181 } else if (effects->compositingType() == QPainterCompositing) { 0182 drawCircleQPainter(color, cx, cy, r); 0183 } 0184 } 0185 0186 void TouchPointsEffect::paintScreenSetup(int mask, QRegion region, ScreenPaintData &data) 0187 { 0188 if (effects->isOpenGLCompositing()) { 0189 paintScreenSetupGl(mask, region, data); 0190 } 0191 } 0192 0193 void TouchPointsEffect::paintScreenFinish(int mask, QRegion region, ScreenPaintData &data) 0194 { 0195 if (effects->isOpenGLCompositing()) { 0196 paintScreenFinishGl(mask, region, data); 0197 } 0198 } 0199 0200 void TouchPointsEffect::drawCircleGl(const QColor &color, float cx, float cy, float r) 0201 { 0202 static const int num_segments = 80; 0203 static const float theta = 2 * 3.1415926 / float(num_segments); 0204 static const float c = cosf(theta); // precalculate the sine and cosine 0205 static const float s = sinf(theta); 0206 const auto scale = effects->renderTargetScale(); 0207 float t; 0208 0209 float x = r; // we start at angle = 0 0210 float y = 0; 0211 0212 GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); 0213 vbo->reset(); 0214 vbo->setUseColor(true); 0215 vbo->setColor(color); 0216 QVector<float> verts; 0217 verts.reserve(num_segments * 2); 0218 0219 for (int ii = 0; ii < num_segments; ++ii) { 0220 verts << (x + cx) * scale << (y + cy) * scale; // output vertex 0221 // apply the rotation matrix 0222 t = x; 0223 x = c * x - s * y; 0224 y = s * t + c * y; 0225 } 0226 vbo->setData(verts.size() / 2, 2, verts.data(), nullptr); 0227 vbo->render(GL_LINE_LOOP); 0228 } 0229 0230 void TouchPointsEffect::drawCircleQPainter(const QColor &color, float cx, float cy, float r) 0231 { 0232 QPainter *painter = effects->scenePainter(); 0233 painter->save(); 0234 painter->setPen(color); 0235 painter->drawArc(cx - r, cy - r, r * 2, r * 2, 0, 5760); 0236 painter->restore(); 0237 } 0238 0239 void TouchPointsEffect::paintScreenSetupGl(int, QRegion, ScreenPaintData &data) 0240 { 0241 GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::UniformColor); 0242 shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); 0243 0244 glLineWidth(m_lineWidth); 0245 glEnable(GL_BLEND); 0246 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 0247 } 0248 0249 void TouchPointsEffect::paintScreenFinishGl(int, QRegion, ScreenPaintData &) 0250 { 0251 glDisable(GL_BLEND); 0252 0253 ShaderManager::instance()->popShader(); 0254 } 0255 0256 } // namespace