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