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 ®ion, 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"