From 8e3c5e20f4c39a5c1d331da94212e11783556e3b Mon Sep 17 00:00:00 2001 From: Hongfei Shang Date: Sun, 5 Jun 2022 22:41:23 +0800 Subject: [PATCH] touchclick: add touch trail effect Signed-off-by: Hongfei Shang --- src/effects/CMakeLists.txt | 1 + src/effects/touchtrail/CMakeLists.txt | 14 + src/effects/touchtrail/data/trailing.png | Bin 0 -> 6096 bytes src/effects/touchtrail/main.cpp | 18 ++ src/effects/touchtrail/metadata.json | 61 ++++ src/effects/touchtrail/touchtrail.cpp | 340 +++++++++++++++++++++++ src/effects/touchtrail/touchtrail.h | 81 ++++++ 7 files changed, 515 insertions(+) create mode 100644 src/effects/touchtrail/CMakeLists.txt create mode 100644 src/effects/touchtrail/data/trailing.png create mode 100644 src/effects/touchtrail/main.cpp create mode 100644 src/effects/touchtrail/metadata.json create mode 100644 src/effects/touchtrail/touchtrail.cpp create mode 100644 src/effects/touchtrail/touchtrail.h diff --git a/src/effects/CMakeLists.txt b/src/effects/CMakeLists.txt index dde8e09c5..ec4c75ed9 100644 --- a/src/effects/CMakeLists.txt +++ b/src/effects/CMakeLists.txt @@ -161,6 +161,7 @@ add_subdirectory(snaphelper) add_subdirectory(startupfeedback) add_subdirectory(trackmouse) add_subdirectory(touchclick) +add_subdirectory(touchtrail) add_subdirectory(wobblywindows) ############################################################################### diff --git a/src/effects/touchtrail/CMakeLists.txt b/src/effects/touchtrail/CMakeLists.txt new file mode 100644 index 000000000..153592beb --- /dev/null +++ b/src/effects/touchtrail/CMakeLists.txt @@ -0,0 +1,14 @@ +####################################### +# Effect + +set(touchtrail_SOURCES + touchtrail.cpp + main.cpp +) + +kwin4_add_effect_module(kwin4_effect_touchtrail ${touchtrail_SOURCES}) + + +# Data files +install(FILES data/trailing.png DESTINATION ${KDE_INSTALL_DATADIR}/${KWIN_NAME}) + diff --git a/src/effects/touchtrail/data/trailing.png b/src/effects/touchtrail/data/trailing.png new file mode 100644 index 0000000000000000000000000000000000000000..03a14094350cfa7f3a1b7c15bf7a96a6383bfe5e GIT binary patch literal 6096 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 W=&|y_$3|oT0000k literal 0 HcmV?d00001 diff --git a/src/effects/touchtrail/main.cpp b/src/effects/touchtrail/main.cpp new file mode 100644 index 000000000..02d702138 --- /dev/null +++ b/src/effects/touchtrail/main.cpp @@ -0,0 +1,18 @@ +/* + SPDX-FileCopyrightText: 2022 Hongfei Shang + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "touchtrail.h" + +namespace KWin +{ + +KWIN_EFFECT_FACTORY_SUPPORTED(TouchTrailEffect, + "metadata.json.stripped", + return TouchTrailEffect::supported();) + +} // namespace KWin + +#include "main.moc" diff --git a/src/effects/touchtrail/metadata.json b/src/effects/touchtrail/metadata.json new file mode 100644 index 000000000..073d01526 --- /dev/null +++ b/src/effects/touchtrail/metadata.json @@ -0,0 +1,61 @@ +{ + "KPlugin": { + "Category": "Appearance", + "Description": "Visualize touch points", + "Description[ar]": "تصور نقاط اللمس", + "Description[az]": "Ekranda toxunan nöqtələri canlandırır", + "Description[ca@valencia]": "Visualitza els punts de contacte", + "Description[ca]": "Visualitza els punts de contacte", + "Description[cs]": "Vizualizovat dotekové body", + "Description[en_GB]": "Visualise touch points", + "Description[es]": "Visualizar los puntos de contacto", + "Description[fi]": "Korosta kosketuspisteet", + "Description[fr]": "Afficher les points tactiles", + "Description[hu]": "Vizualizálja az érintési pontokat", + "Description[ia]": "Visualisa punctos de contacto", + "Description[id]": "Memvisualkan titik sentuh", + "Description[it]": "Visualizza i punti di tocco", + "Description[ko]": "터치 지점 표시", + "Description[nl]": "Aanraakpunten visualiseren", + "Description[nn]": "Visualiser kontaktpunkt", + "Description[pl]": "Uwidacznia punkty dotyku", + "Description[pt_BR]": "Visualizar pontos de toque", + "Description[ru]": "Визуализация событий касания экрана", + "Description[sk]": "Vizualizovať dotykové body", + "Description[sl]": "Vizualiziraj točke dotika", + "Description[sv]": "Åskådliggör beröringspunkter", + "Description[tr]": "Dokunma noktalarını görselleştir", + "Description[uk]": "Візуалізувати точки дотику", + "Description[x-test]": "xxVisualize touch pointsxx", + "Description[zh_CN]": "高亮显示在触控屏幕上的触摸点", + "EnabledByDefault": true, + "Id": "touchtrail", + "License": "GPL", + "Name": "Touch Points", + "Name[ar]": "نقاط اللمس", + "Name[az]": "Toxunma nöqtələri", + "Name[ca@valencia]": "Punts de contacte", + "Name[ca]": "Punts de contacte", + "Name[cs]": "Dotekové body", + "Name[en_GB]": "Touch Points", + "Name[es]": "Puntos de contacto", + "Name[fi]": "Kosketuspisteet", + "Name[fr]": "Points tactiles", + "Name[hu]": "Érintési pontok", + "Name[ia]": "Punctos de contacto", + "Name[it]": "Punti di tocco", + "Name[ko]": "터치 지점", + "Name[nl]": "Aanraakpunten", + "Name[nn]": "Kontaktpunkt", + "Name[pl]": "Punkty dotyku", + "Name[pt_BR]": "Pontos de toque", + "Name[ru]": "Точки прикосновения", + "Name[sk]": "Dotykové body", + "Name[sl]": "Točke dotika", + "Name[sv]": "Beröringspunkter", + "Name[tr]": "Dokunma Noktaları", + "Name[uk]": "Точки дотику", + "Name[x-test]": "xxTouch Pointsxx", + "Name[zh_CN]": "触摸点高亮" + } +} diff --git a/src/effects/touchtrail/touchtrail.cpp b/src/effects/touchtrail/touchtrail.cpp new file mode 100644 index 000000000..2d07374f8 --- /dev/null +++ b/src/effects/touchtrail/touchtrail.cpp @@ -0,0 +1,340 @@ +#include "touchtrail.h" + +#include + +#include +#include + +#include + +namespace KWin{ + +TouchTrailEffect::TouchTrailEffect() + : Effect() + , m_texture(nullptr) +{ + loadTexture(); + loadShader(); +} + +TouchTrailEffect::~TouchTrailEffect() +{ + if (m_texture) + delete m_texture; + m_texture = nullptr; +} + +bool TouchTrailEffect::supported() +{ + return effects->isOpenGLCompositing(); +} + +void TouchTrailEffect::prePaintScreen(ScreenPrePaintData &data, std::chrono::milliseconds presentTime) +{ + int time = 0; + if (m_lastPresentTime.count()) { + time = (presentTime - m_lastPresentTime).count(); + } + + for (int i = 0; i < m_touchPoints.size(); ++i) { + QVector::iterator it = m_touchPoints[i].begin(); + while (it != m_touchPoints[i].end()) { + it->life += time; + ++it; + } + + while (m_touchPoints[i].size() && m_touchPoints[i].first().life > MAX_LIFE) { + if (m_touchPoints[i].size() == 1) { + int detail = m_touchPoints[i].first().detail; + if (!m_idIsUse[i]) { + m_detail2ID.erase(detail); + } + } + m_touchPoints[i].removeFirst(); + } + } + calTriPoints(); + + if (0 == m_detail2ID.size()) { + m_lastPresentTime = std::chrono::milliseconds::zero(); + } else { + m_lastPresentTime = presentTime; + } + + effects->prePaintScreen(data, presentTime); +} + +void TouchTrailEffect::paintScreen(int mask, const QRegion ®ion, ScreenPaintData &data) +{ + // printf("adasdasds\n"); + effects->paintScreen(mask, region, data); + if (effects->isOpenGLCompositing()) { + paintScreenGL(data); + } +} + +void TouchTrailEffect::paintScreenGL(ScreenPaintData &data) +{ + GLShader *shader = ShaderManager::instance()->pushShader(ShaderTrait::MapTexture); + shader->setUniform(GLShader::ModelViewProjectionMatrix, data.projectionMatrix()); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + GLVertexBuffer *vbo = GLVertexBuffer::streamingBuffer(); + + for (int i = 0; i < m_triPoints.size(); ++i) + { + QVector vert, texCoord; + const int &n = m_triPoints[i].size(); + for (int j = 0; j < n; ++j) { + vert << m_triPoints[i][j].x() << m_triPoints[i][j].y(); + texCoord << ((j & 1) ? 1.0f : 0.0f) << (j >> 1) * 1.0 / (n >> 1); + } + vbo->reset(); + m_texture->bind(); + vbo->setData(vert.size() / 2, 2, vert.data(), texCoord.data()); + vbo->render(GL_TRIANGLE_STRIP); + m_texture->unbind(); + } + + + glDisable(GL_BLEND); + ShaderManager::instance()->popShader(); +} + +void TouchTrailEffect::postPaintScreen() +{ + effects->postPaintScreen(); + repaint(); +} + +bool TouchTrailEffect::isActive() const +{ + return m_detail2ID.size(); +} + +bool TouchTrailEffect::touchDown(qint32 id, const QPointF &pos, quint32 time) +{ + // printf("当前手指ID=%d\n", id); + insertTouchPoint(id, pos); + repaint(); + return false; +} + +bool TouchTrailEffect::touchMotion(qint32 id, const QPointF &pos, quint32 time) +{ + updateTouchPoint(id, pos); + repaint(); + return false; +} + +bool TouchTrailEffect::touchUp(qint32 id, quint32 time) +{ + // printf("离开的手指ID=%d\n", id); + removeTouchPoint(id); + // 各触摸点开始快速衰减 + // TODO 加上滑动距离作为衰减因子 + for (int i = 0; i < m_touchPoints.size(); ++i) { + int sub; + for (int j = 0; j < m_touchPoints[i].size(); j++) { + if (!j) { + sub = MAX_LIFE - m_touchPoints[i][j].life; + m_touchPoints[i][j].life = MAX_LIFE; + + } else { + m_touchPoints[i][j].life += sub; + } + } + } + return false; +} + +void TouchTrailEffect::insertTouchPoint(qint32 detail, QPointF pos) +{ + int id = -1; + do { + ++id; + if (id == m_touchPoints.size()) { + m_touchPoints.append(QVector()); + m_triPoints.append(QVector()); + m_idIsUse.append(false); + } + } while (m_idIsUse[id] || m_touchPoints[id].size()); + + m_detail2ID[detail] = id; + m_idIsUse[id] = true; + m_touchPoints[id].append({detail, pos, 0}); + + // printf("当前分配的ID为 %d\n", id); +} + +void TouchTrailEffect::updateTouchPoint(qint32 detail, QPointF pos) +{ + // 开关混成有可能导致事件不成对 + if (m_detail2ID.find(detail) == m_detail2ID.end()) { + return; + } + int id = m_detail2ID[detail]; + m_touchPoints[id].append({detail, pos, 0}); +} + +void TouchTrailEffect::removeTouchPoint(qint32 detail) +{ + // 开关混成有可能导致事件不成对 + if (m_detail2ID.find(detail) == m_detail2ID.end()) { + return; + } + int id = m_detail2ID[detail]; + m_idIsUse[id] = false; + if (!m_touchPoints[id].size()) { + m_detail2ID.erase(detail); + } +} + +void TouchTrailEffect::loadTexture() +{ +#if defined(QT_NO_DEBUG) + QString file_path = {QStandardPaths::locate(QStandardPaths::DataLocation, QStringLiteral(DEFAULT_TEXTURE_IMAGE))}; +#else + QString file_path = QCoreApplication::applicationDirPath() + QStringLiteral("/../../src/effects/touchtrail/data/") + QStringLiteral(DEFAULT_TEXTURE_IMAGE); +#endif + QImage img(file_path); + if (img.isNull()) { + qWarning() << "load image [" << file_path << "] failed"; + return; + } + + //! TODO 使用KWin中的纹理融合算法 + //! 没有找到kwin里,opengl的纹理融合 + //! 先手写个简单的纹理和颜色的融合,简单支持 + qreal Rd = 139.0 / 255.0; + qreal Gd = 134.0 / 255.0; + qreal Bd = 130.0 / 255.0; + qreal Ad = 30.0 / 255.0; + + for (int i = 0; i < img.width(); ++i) { + for (int j = 0; j < img.height(); ++j) { + QRgb p = img.pixel(i, j); + qreal Rs = qRed(p) / 255.0; + qreal Gs = qGreen(p) / 255.0; + qreal Bs = qBlue(p) / 255.0; + qreal As = qAlpha(p) / 255.0; + // printf("%d\n", As); + //! 纹理和颜色融合 +// img.setPixel(i, j, qRgba((Rs * As + Rd * (1 - As)) * 255, +// (Gs * As + Gd * (1 - As)) * 255, +// (Bs * As + Bd * (1 - As)) * 255, +// (fmin(As, Ad)) * 255)); + //! 纹理仅控制形状 + img.setPixel(i, j, qRgba(Rd * 255, + Gd * 255, + Bd * 255, + (fmin(As, Ad)) * 255)); + } + } + if (!m_texture) + m_texture = new GLTexture(img); +} + +void TouchTrailEffect::loadShader() +{ + // m_shader.reset(ShaderManager::instance()->generateShaderFromResources(ShaderTrait::MapTexture, QString(), QStringLiteral("coverswitch-reflection.glsl"))); +} + +void TouchTrailEffect::calTriPoints() +{ + for (int i = 0; i < m_touchPoints.size(); ++i) + { + m_triPoints[i].clear(); + if (m_touchPoints[i].size() < 3) continue; + QPointF last(m_touchPoints[i].first().pos); + QVector::iterator it = m_touchPoints[i].begin(); + for (++it; it != m_touchPoints[i].end(); ++it) { + QPointF &cur = it->pos; + qreal d = dist(cur.x(), cur.y(), last.x(), last.y()); + if (d < MIN_POINT_SEG) continue; + if (cur.x() == last.x() || cur.y() == last.y()) { + continue; + } + // 方向为 last->cur 的向量 + QPointF vec_l2c(cur.x() - last.x(), cur.y() - last.y()); + // cur last线段上的中点 + QPointF mid((cur.x() + last.x()) / 2, (cur.y() + last.y()) / 2); + d /= 2; + // 向量的斜率k + qreal k = (cur.y() - last.y()) / (cur.x() - last.x()); + qreal vlk = 1.0 / k; + qreal alpha = atan(fabs(vlk)); + qreal dx = DEFAULT_STROKE_WIDTH * cos(alpha); + qreal dy = DEFAULT_STROKE_WIDTH * sin(alpha); + QPointF p1, p2; + + if (k > 0 && vec_l2c.x() > 0) { + p1.setX(mid.x() - dx); p1.setY(mid.y() + dy); + p2.setX(mid.x() + dx); p2.setY(mid.y() - dy); + } else if (k > 0 && vec_l2c.x() < 0) { + p1.setX(mid.x() + dx); p1.setY(mid.y() - dy); + p2.setX(mid.x() - dx); p2.setY(mid.y() + dy);; + } else if (k < 0 && vec_l2c.x() > 0) { + p1.setX(mid.x() + dx); p1.setY(mid.y() + dy); + p2.setX(mid.x() - dx); p2.setY(mid.y() - dy); + } else { + p1.setX(mid.x() - dx); p1.setY(mid.y() - dy); + p2.setX(mid.x() + dx); p2.setY(mid.y() + dy); + } + m_triPoints[i].append(p1); + m_triPoints[i].append(p2); + last = cur; + + if (it + 1 == m_touchPoints[i].end()) { + mid = it->pos; + if (k > 0 && vec_l2c.x() > 0) { + p1.setX(mid.x() - dx); p1.setY(mid.y() + dy); + p2.setX(mid.x() + dx); p2.setY(mid.y() - dy); + } else if (k > 0 && vec_l2c.x() < 0) { + p1.setX(mid.x() + dx); p1.setY(mid.y() - dy); + p2.setX(mid.x() - dx); p2.setY(mid.y() + dy);; + } else if (k < 0 && vec_l2c.x() > 0) { + p1.setX(mid.x() + dx); p1.setY(mid.y() + dy); + p2.setX(mid.x() - dx); p2.setY(mid.y() - dy); + } else { + p1.setX(mid.x() - dx); p1.setY(mid.y() - dy); + p2.setX(mid.x() + dx); p2.setY(mid.y() + dy); + } + m_triPoints[i].append(p1); + m_triPoints[i].append(p2); + } + } + } + +} + +void TouchTrailEffect::repaint() +{ + // effects->addRepaintFull(); + + if (m_triPoints.size()) { + QRect dirtyRect; + QPoint topLeft(INT32_MAX, INT32_MAX), bottomRight(INT32_MIN, INT32_MIN); + for (int i = 0; i < m_triPoints.size(); ++i) { + for (int j = 0; j < m_triPoints[i].size(); ++j) { + QPointF &p = m_triPoints[i][j]; + topLeft.setX(fmin(topLeft.x(), p.x())); + topLeft.setY(fmin(topLeft.y(), p.y())); + bottomRight.setX(fmax(bottomRight.x(), p.x())); + bottomRight.setY(fmax(bottomRight.y(), p.y())); + } + } + dirtyRect.setTopLeft(topLeft); + dirtyRect.setBottomRight(bottomRight); + + m_dirtyRect = dirtyRect; + effects->addRepaint(m_dirtyRect); + } else { + effects->addRepaintFull(); + } + return; +} + +} diff --git a/src/effects/touchtrail/touchtrail.h b/src/effects/touchtrail/touchtrail.h new file mode 100644 index 000000000..eba27a4d1 --- /dev/null +++ b/src/effects/touchtrail/touchtrail.h @@ -0,0 +1,81 @@ +#ifndef KWIN_TOUCHMOTIONSTREAK_H +#define KWIN_TOUCHMOTIONSTREAK_H + +#include +#include + +#include + +#include + +namespace KWin { + +#define MAX_FINGER_COUNT 10 // 触摸屏支持最大触摸点的个数 +#define MIN_POINT_SEG 2/*px*/ // 触摸点之间允许的最小欧氏距离 +#define DEFAULT_STROKE_WIDTH 3/*px*/ // 条带的宽度 +#define MAX_POINT_NUMBER 8 // 保存的触摸点的最大数量 +#define DEFAULT_TEXTURE_IMAGE "trailing.png" // 默认纹理 +#define MAX_LIFE 200 // 每个触摸点存活的最长时间 + +#define dist(x1,y1,x2,y2) sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)) + +/*! + * \brief UKUI Touch Motion Streak Effect + * \author Yunpeng Zhu. + */ +class TouchTrailEffect + : public Effect +{ + Q_OBJECT + typedef struct _touch_point{ + int detail; + QPointF pos; + int life; + }TouchPoint; +public: + TouchTrailEffect(); + virtual ~TouchTrailEffect(); + + void prePaintScreen(ScreenPrePaintData& data, std::chrono::milliseconds presentTime) override; + void paintScreen(int mask, const QRegion ®ion, ScreenPaintData& data) override; + void postPaintScreen() override; + + bool isActive() const override; + bool touchDown(qint32 id, const QPointF &pos, quint32 time) override; + bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override; + bool touchUp(qint32 id, quint32 time) override; + + static bool supported(); + +private: + void paintScreenGL(ScreenPaintData &data); + void insertTouchPoint(qint32 detail, QPointF pos); + void updateTouchPoint(qint32 detail, QPointF pos); + void removeTouchPoint(qint32 detail); + void loadTexture(); + void loadShader(); + /*! + * \brief 计算三角形条带顶点的位置 + */ + void calTriPoints(); + void repaint(); + + //! 保存触摸点坐标 + QVector> m_touchPoints; + //! 保存所需要绘制的三角形条带的坐标 + QVector> m_triPoints; + //! 标记id是否被使用 + QVector m_idIsUse; + //! 将detail映射到0~MAX_FINGER_COUNT-1 + std::unordered_map m_detail2ID; + //! 绘制 + GLTexture *m_texture; + //! 记录需要重绘的区域 + QRect m_dirtyRect; + std::chrono::milliseconds m_lastPresentTime = std::chrono::milliseconds::zero(); + +}; + +} + +#endif // KWIN_TOUCHMOTIONSTREAK_H