From 554865572184f486945918890b2eb88c0ade1352 Mon Sep 17 00:00:00 2001 From: Hongfei Shang Date: Mon, 20 Jun 2022 16:59:44 +0800 Subject: [PATCH 1/2] touch gesture: add touch gesture support --- autotests/CMakeLists.txt | 20 +- cmake/modules/FindXTest.cmake | 5 + debian/kwin-common.install | 1 + src/CMakeLists.txt | 11 + src/backends/fakeinput/fakeinputdevice.cpp | 2 + .../x11/standalone/xinputintegration.cpp | 74 +- src/gestures.cpp | 1018 ++++++++++++++--- src/gestures.h | 608 ++++++++-- src/globalshortcuts.cpp | 30 +- src/input.cpp | 430 ++++++- src/input.h | 53 + src/screenedge.cpp | 27 +- src/screenedge.h | 2 + src/touch_gesture_action_control.cpp | 601 ++++++++++ src/touch_gesture_action_control.h | 106 ++ src/touchgesture.xml | 262 +++++ src/workspace.cpp | 3 + src/xmlreader.cpp | 356 ++++++ src/xmlreader.h | 117 ++ 19 files changed, 3391 insertions(+), 335 deletions(-) create mode 100644 cmake/modules/FindXTest.cmake create mode 100644 src/touch_gesture_action_control.cpp create mode 100644 src/touch_gesture_action_control.h create mode 100644 src/touchgesture.xml create mode 100644 src/xmlreader.cpp create mode 100644 src/xmlreader.h diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 17800c028..98964a794 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -160,18 +160,18 @@ ecm_mark_as_test(testOnScreenNotification) ######################################################## # Test Gestures ######################################################## -set(testGestures_SRCS - ../src/gestures.cpp - test_gestures.cpp -) -add_executable(testGestures ${testGestures_SRCS}) +#set(testGestures_SRCS +# ../src/gestures.cpp +# test_gestures.cpp +#) +#add_executable(testGestures ${testGestures_SRCS}) -target_link_libraries(testGestures - Qt::Test -) +#target_link_libraries(testGestures +# Qt::Test +#) -add_test(NAME kwin-testGestures COMMAND testGestures) -ecm_mark_as_test(testGestures) +#add_test(NAME kwin-testGestures COMMAND testGestures) +#ecm_mark_as_test(testGestures) ######################################################## # Test X11 TimestampUpdate diff --git a/cmake/modules/FindXTest.cmake b/cmake/modules/FindXTest.cmake new file mode 100644 index 000000000..e39b74914 --- /dev/null +++ b/cmake/modules/FindXTest.cmake @@ -0,0 +1,5 @@ +find_package(PkgConfig) +pkg_check_modules(XTEST REQUIRED xtst) + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(XTEST DEFAULT_MSG XTEST_FOUND) diff --git a/debian/kwin-common.install b/debian/kwin-common.install index 972d076f3..1e8ffe1bb 100644 --- a/debian/kwin-common.install +++ b/debian/kwin-common.install @@ -13,3 +13,4 @@ usr/lib/*/qt5/plugins/kwin/ usr/lib/*/qt5/plugins/org.kde.kdecoration2/kwin5_aurorae.so usr/lib/*/qt5/plugins/org.kde.kdecoration2/libkwin-style-ukui.so usr/lib/*/qt5/qml/org/kde/kwin/ +usr/share/touchgesture/touchgesture.xml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1ade3cbbc..d4712fe8b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -148,6 +148,8 @@ set(kwin_SRCS xkb.cpp xwaylandclient.cpp xwl/xwayland_interface.cpp + xmlreader.cpp + touch_gesture_action_control.cpp ) qt_add_dbus_adaptor(kwin_SRCS scripting/org.kde.kwin.Script.xml scripting/scripting.h KWin::AbstractScript) @@ -228,6 +230,12 @@ target_link_libraries(kwin ${Qsettings_LIBRARIES} ) +find_package(XTest REQUIRED) +set_package_properties(XTest PROPERTIES TYPE REQUIRED) +target_link_libraries(kwin + ${XTEST_LIBRARIES}) + + add_subdirectory(backends) add_subdirectory(scenes) add_subdirectory(utils) @@ -342,3 +350,6 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kwin_export.h DESTINATION ${KDE_INSTAL # Install the KWin/Script service type install(FILES scripting/kwinscript.desktop DESTINATION ${KDE_INSTALL_KSERVICETYPES5DIR}) + +set(GESTURE_CONFIG_DIR "/usr/share/touchgesture") +install(FILES touchgesture.xml DESTINATION ${GESTURE_CONFIG_DIR}) diff --git a/src/backends/fakeinput/fakeinputdevice.cpp b/src/backends/fakeinput/fakeinputdevice.cpp index 75500b75c..6a8253538 100644 --- a/src/backends/fakeinput/fakeinputdevice.cpp +++ b/src/backends/fakeinput/fakeinputdevice.cpp @@ -8,6 +8,8 @@ #include +#include + namespace KWin { static int s_lastDeviceId = 0; diff --git a/src/backends/x11/standalone/xinputintegration.cpp b/src/backends/x11/standalone/xinputintegration.cpp index 0bc5b915c..dc0062637 100644 --- a/src/backends/x11/standalone/xinputintegration.cpp +++ b/src/backends/x11/standalone/xinputintegration.cpp @@ -106,43 +106,43 @@ public: m_x11Cursor->schedulePoll(); } break; - case XI_TouchBegin: { - auto e = reinterpret_cast(event); - m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y))); - break; - } - case XI_TouchUpdate: { - auto e = reinterpret_cast(event); - const QPointF touchPosition = QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)); - if (e->detail == m_trackingTouchId) { - const auto last = m_lastTouchPositions.value(e->detail); - ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(touchPosition.x() - last.x(), touchPosition.y() - last.y())); - } - m_lastTouchPositions.insert(e->detail, touchPosition); - break; - } - case XI_TouchEnd: { - auto e = reinterpret_cast(event); - if (e->detail == m_trackingTouchId) { - ScreenEdges::self()->gestureRecognizer()->endSwipeGesture(); - } - m_lastTouchPositions.remove(e->detail); - m_trackingTouchId = 0; - break; - } - case XI_TouchOwnership: { - auto e = reinterpret_cast(event); - auto it = m_lastTouchPositions.constFind(e->touchid); - if (it == m_lastTouchPositions.constEnd()) { - XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, XIRejectTouch); - } else { - if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(it.value()) > 0) { - m_trackingTouchId = e->touchid; - } - XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, m_trackingTouchId == e->touchid ? XIAcceptTouch : XIRejectTouch); - } - break; - } +// case XI_TouchBegin: { +// auto e = reinterpret_cast(event); +// m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y))); +// break; +// } +// case XI_TouchUpdate: { +// auto e = reinterpret_cast(event); +// const QPointF touchPosition = QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)); +// if (e->detail == m_trackingTouchId) { +// const auto last = m_lastTouchPositions.value(e->detail); +// ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(touchPosition.x() - last.x(), touchPosition.y() - last.y())); +// } +// m_lastTouchPositions.insert(e->detail, touchPosition); +// break; +// } +// case XI_TouchEnd: { +// auto e = reinterpret_cast(event); +// if (e->detail == m_trackingTouchId) { +// ScreenEdges::self()->gestureRecognizer()->endSwipeGesture(); +// } +// m_lastTouchPositions.remove(e->detail); +// m_trackingTouchId = 0; +// break; +// } +// case XI_TouchOwnership: { +// auto e = reinterpret_cast(event); +// auto it = m_lastTouchPositions.constFind(e->touchid); +// if (it == m_lastTouchPositions.constEnd()) { +// XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, XIRejectTouch); +// } else { +// if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(it.value()) > 0) { +// m_trackingTouchId = e->touchid; +// } +// XIAllowTouchEvents(display(), e->deviceid, e->sourceid, e->touchid, m_trackingTouchId == e->touchid ? XIAcceptTouch : XIRejectTouch); +// } +// break; +// } default: if (m_x11Cursor) { m_x11Cursor->schedulePoll(); diff --git a/src/gestures.cpp b/src/gestures.cpp index 72e2c619f..f4ffe0f30 100644 --- a/src/gestures.cpp +++ b/src/gestures.cpp @@ -1,67 +1,260 @@ -/* - KWin - the KDE window manager - This file is part of the KDE project. +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. - SPDX-FileCopyrightText: 2017 Martin Gräßlin +Copyright (C) 2017 Martin Gräßlin - SPDX-License-Identifier: GPL-2.0-or-later -*/ +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ #include "gestures.h" #include #include #include +#include +#include +#include + +#include +#include namespace KWin { Gesture::Gesture(QObject *parent) : QObject(parent) + , m_isStart(false) { + connect(this, &Gesture::started, this, [=](){ m_isStart = true; }); + connect(this, &Gesture::cancelled, this, [=](){ m_isStart = false; }); + connect(this, &Gesture::triggered, this, [=](){ m_isStart = false; }); } Gesture::~Gesture() = default; +/*! 滑动手势 */ SwipeGesture::SwipeGesture(QObject *parent) : Gesture(parent) { + // 设置手势配置默认值 + m_fingerCount = 0; + m_minimumR = MIN_SWIPE_DIST; + m_maximumR = INT32_MAX; + m_direction = GestureDirection::Down; + m_startGeometry = QRect(0, 0, 0, 0); } SwipeGesture::~SwipeGesture() = default; -void SwipeGesture::setStartGeometry(const QRect &geometry) -{ - setMinimumX(geometry.x()); - setMinimumY(geometry.y()); - setMaximumX(geometry.x() + geometry.width()); - setMaximumY(geometry.y() + geometry.height()); - - Q_ASSERT(m_maximumX >= m_minimumX); - Q_ASSERT(m_maximumY >= m_minimumY); +/*! Pinch手势 */ +PinchGesture::PinchGesture(QObject *parent) + : Gesture(parent){ + m_direction = GestureDirection::In; + m_fingerCount = 0; } -qreal SwipeGesture::minimumDeltaReachedProgress(const QSizeF &delta) const +PinchGesture::~PinchGesture() = default; + +/*! LongPress手势 */ +LongPressGesture::LongPressGesture(QObject *parent) + : Gesture(parent){ + m_timeout = LONGPRESS_DEFAULT_TIMEOUT; +} + +LongPressGesture::~LongPressGesture() = default; + +/*! Touchpad Swipe手势 */ +TouchpadSwipeGesture::TouchpadSwipeGesture(QObject *parent) + : Gesture(parent) { - if (!m_minimumDeltaRelevant || m_minimumDelta.isNull()) { - return 1.0; - } - switch (m_direction) { - case Direction::Up: - case Direction::Down: - return std::min(std::abs(delta.height()) / std::abs(m_minimumDelta.height()), 1.0); - case Direction::Left: - case Direction::Right: - return std::min(std::abs(delta.width()) / std::abs(m_minimumDelta.width()), 1.0); - default: - Q_UNREACHABLE(); + m_fingerCount = 0; + m_minSwipeDistance = MIN_TOUCHPAD_SWIPE_DIST; +} + +TouchpadSwipeGesture::~TouchpadSwipeGesture() = default; + +/*! Touchpad Tap手势 */ +TouchpadTapGesture::TouchpadTapGesture(QObject *parent) + : Gesture(parent) +{ + m_fingerCount = 0; +} + +TouchpadTapGesture::~TouchpadTapGesture() = default; + + +/*! Touchpad Swipe Sequence手势 */ +TouchpadSwipeSequenceGesture::TouchpadSwipeSequenceGesture(QObject *parent) + : Gesture(parent) +{ + m_fingerCount = 0; +} + +TouchpadSwipeSequenceGesture::~TouchpadSwipeSequenceGesture() = default; + + +/*! 保存触摸状态 */ +void TouchState::reset(){ + beginTime = QTime::currentTime(); + // 手指数据清空 + QVector points; + for(auto it = slotid.begin(); it != slotid.end(); ++it){ + int &id = it->second; + slotInit[id] = slotPoints[id]; + slotLast[id] = slotPoints[id]; + points.append(slotInit[id]); } + initRect = calculateRect(points); + curRect = lastRect = initRect; } -bool SwipeGesture::minimumDeltaReached(const QSizeF &delta) const +void TouchState::insertSlot(const int &detail, const QPointF &pos) { - return minimumDeltaReachedProgress(delta) >= 1.0; + int id = 0; + while(slotMask & (1 << id)) { + id++; + } + if (id > MAX_SLOT_NUM - 1) { + qDebug() << "Touching more than the maximum number of fingers, fingers:" << id << "see MAX_SLOT_NUM"; + return; + } + slotMask |= 1 << id; + slotPoints[id] = pos; + slotInit[id] = pos; + slotLast[id] = pos; + slotid[detail] = id; + // printf("当前的 detail = %d, ID = %d, 手指个数 = %d\n", detail, id, slotid.size()); + // 计算多指下手指的抽象中心 + QVector points; + for(auto it = slotid.begin(); it != slotid.end(); ++it){ + points.append(slotInit[it->second]); + } + initRect = calculateRect(points); + curRect = initRect; } +void TouchState::updateSlot(const int &detail, const QPointF &pos){ + //! 屏蔽触摸坐标的抖动 + QPointF cp = getCurPos(detail); + // std::cout << "防抖 = " << dist(cp.x(), cp.y(), pos.x(), pos.y()) << std::endl; + if(dist(cp.x(), cp.y(), pos.x(), pos.y()) > 4){ + int id = slotid[detail]; + slotLast[id] = slotPoints[id]; + slotPoints[id] = pos; + } + +// std::cout << slotPoints[slotid[detail]].x() << " " << slotPoints[slotid[detail]].y() << std::endl; + + //! 更新当前触摸状态的外接矩形 + QVector points; + for(auto it = slotid.begin(); it != slotid.end(); ++it){ + points.append(slotPoints[it->second]); + } + lastRect = curRect; + curRect = calculateRect(points); +} + +void TouchState::gestureDetector(const int &detail, const QPointF &pos) +{ + +} + +void TouchState::deleteSlot(const int &detail) +{ + // printf("删除 detail = %d, ID = %d\n", detail, slotid[detail]); + slotMask ^= 1 << slotid[detail]; + auto t = slotid.find(detail); + slotid.erase(t); +} + +int TouchState::getLastTime() +{ + int res = 0; + QTime curTime = QTime::currentTime(); + res = beginTime.msecsTo(curTime); + return res; +} + +ExternalCircle TouchState::calculateEC(QPointF p1, QPointF p2, QPointF p3){ + ExternalCircle res; + if(p1 == p2 && p1 == p3) return res; + if(p1 == p2 || p2 == p3){ + if(p1 == p2){ + QPointF t = p2; + p2 = p3; + p3 = t; + } + res.r = dist(p1.x(), p1.y(), p2.x(), p2.y()) / 2; + res.center.setX((p1.x() + p2.x()) / 2); + res.center.setY((p1.y() + p2.y()) / 2); + return res; + } + + double a, b, c, p, s, sinc; + double x1 = p1.x(), y1 = p1.y(); + double x2 = p2.x(), y2 = p2.y(); + double x3 = p3.x(), y3 = p3.y(); + a = dist(x1, y1, x2, y2);// 求出三角形三条边 + b = dist(x1, y1, x3, y3); + c = dist(x2, y2, x3, y3); + p = (a + b + c) / 2; //半周长 + s = sqrt(p * (p - a) * (p - b) * (p - c)); //海伦公式求面积 + std::cout << s << std::endl; + sinc = (2 * s) / (a * b); //求角c的正弦值,(1/2)*sinc*a*b = s; + if(!sinc){ // 处理三点共线 + QPointF lb = QPointF(fmin(p1.x(), p2.x() < p3.x() ? p2.x() : p3.x()), + fmin(p1.y(), p2.y() < p3.y() ? p2.y() : p3.y())); + QPointF rt = QPointF(fmax(p1.x(), p2.x() > p3.x() ? p2.x() : p3.x()), + fmax(p1.y(), p2.y() > p3.y() ? p2.y() : p3.y())); + res.r = dist(lb.x(), lb.y(), rt.x(), rt.y()) / 2; + res.center.setX((lb.x() + rt.x()) / 2); + res.center.setY((lb.y() + rt.y()) / 2); + return res; + } + res.r = c / (2 * sinc); //2*r = c/sinc = a/sina = b/sinb +// s1 = pai * r * r;//求圆的面积 + // 求外接圆圆心 + double x = ((y2-y1)*(y3*y3-y1*y1+x3*x3-x1*x1)-(y3-y1)*(y2*y2-y1*y1+x2*x2-x1*x1))/(2*(x3-x1)*(y2-y1)-2*((x2-x1)*(y3-y1))); + double y = ((x2-x1)*(x3*x3-x1*x1+y3*y3-y1*y1)-(x3-x1)*(x2*x2-x1*x1+y2*y2-y1*y1))/(2*(y3-y1)*(x2-x1)-2*((y2-y1)*(x3-x1))); + res.center = QPointF(x, y); + return res; +} + +ExternalRect TouchState::calculateRect(const QVector &points){ + ExternalRect rect; + if(!points.size()) return rect; + double minx = points[0].x(); + double miny = points[0].y(); + double maxx = points[0].x(); + double maxy = points[0].y(); + for(const QPointF &p : points){ + minx = fmin(minx, p.x()); + miny = fmin(miny, p.y()); + maxx = fmax(maxx, p.x()); + maxy = fmax(maxy, p.y()); + } + rect.x = minx; + rect.y = miny; + rect.w = maxx - minx; + rect.h = maxy - miny; + rect.center.setX((minx + maxx) / 2); + rect.center.setY((miny + maxy) / 2); + rect.diagL = dist(minx, miny, maxx, maxy); + return rect; +} + + GestureRecognizer::GestureRecognizer(QObject *parent) : QObject(parent) { @@ -85,135 +278,686 @@ void GestureRecognizer::unregisterGesture(KWin::Gesture* gesture) m_destroyConnections.erase(it); } m_gestures.removeAll(gesture); - if (m_activeSwipeGestures.removeOne(gesture)) { - Q_EMIT gesture->cancelled(); + if (m_activeGestures.removeOne(gesture)) { + if(gesture->isStart()) + Q_EMIT gesture->cancelled(); } } -int GestureRecognizer::startSwipeGesture(uint fingerCount, const QPointF &startPos, StartPositionBehavior startPosBehavior) +/*! + * \author Yunpeng Zhu. + * \brief 这是^(* ̄(oo) ̄)^写的,牛牪犇不! + */ +int GestureRecognizer::startGesture(int detail, const QPointF &pos) { - int count = 0; - // TODO: verify that no gesture is running + // 当新触摸手指加入时,重置当前的检测状态 + reset(); + + static int cnt = 0; + m_touchState.insertSlot(detail, pos); + int fingerCount = m_touchState.fingerCnt(); +// QPointF startPos = pos; + //! 判断手势类型 选择满足条件的手势进行激活 for (Gesture *gesture : qAsConst(m_gestures)) { - SwipeGesture *swipeGesture = qobject_cast(gesture); - if (!gesture) { - continue; - } - if (swipeGesture->minimumFingerCountIsRelevant()) { - if (swipeGesture->minimumFingerCount() > fingerCount) { + switch(gesture->gestureType()) + { + case Gesture::GestureType::Swipe:{ + SwipeGesture *swipeGesture = qobject_cast(gesture); + if(!detectSwpie(swipeGesture)) { continue; } + break; } - if (swipeGesture->maximumFingerCountIsRelevant()) { - if (swipeGesture->maximumFingerCount() < fingerCount) { - continue; - } + case Gesture::GestureType::Pinch:{ + PinchGesture *pg = dynamic_cast(gesture); + if(pg->fingerCount() != fingerCount) continue; + break; } - if (startPosBehavior == StartPositionBehavior::Relevant) { - if (swipeGesture->minimumXIsRelevant()) { - if (swipeGesture->minimumX() > startPos.x()) { - continue; + case Gesture::GestureType::LongPress:{ + LongPressGesture *lg = dynamic_cast(gesture); + if(lg->fingerCount() != fingerCount) continue; + //! 对于long-press-gesture开启定时器 + QTimer *timer = new QTimer; + timer->setInterval(lg->timeout()); + timer->start(); + m_longPressTimers.append(timer); + connect(timer, &QTimer::timeout, this, [=](){ + timer->stop(); + bool isCancel = false; + double delta = calculateFingerDelta(); + if(delta > 5 * m_touchState.fingerCnt()) isCancel = true; + if(!isCancel){ + Q_EMIT lg->started(); + //! 清空当前除 long-press-gesture的所有激活的手势 + for(auto it = m_activeGestures.begin(); it != m_activeGestures.end();){ + Gesture *g = static_cast(*it); + if(g->gestureType() == Gesture::GestureType::LongPress){ + ++it; + }else{ + it = m_activeGestures.erase(it); + } + } } - } - if (swipeGesture->maximumXIsRelevant()) { - if (swipeGesture->maximumX() < startPos.x()) { - continue; - } - } - if (swipeGesture->minimumYIsRelevant()) { - if (swipeGesture->minimumY() > startPos.y()) { - continue; - } - } - if (swipeGesture->maximumYIsRelevant()) { - if (swipeGesture->maximumY() < startPos.y()) { - continue; - } - } + }); + break; } - // direction doesn't matter yet - m_activeSwipeGestures << swipeGesture; - count++; - Q_EMIT swipeGesture->started(); + } + m_activeGestures << gesture; + // Q_EMIT gesture->started(); + ++cnt; } - return count; + + // std::cout << "参与竞争的手势个数是 " << cnt << std::endl; + cnt = 0; + + return 0; } -void GestureRecognizer::updateSwipeGesture(const QSizeF &delta) +void GestureRecognizer::updateGesture(int detail, const QPointF &pos) { - m_swipeUpdates << delta; - m_currentDelta += delta; - // with high resolution touch(pad) gestures can be cancelled without intention - // -> don't cancel movements if their accumulated values are too small but also still update the gesture for animations - if (std::abs(m_currentDelta.width()) > 1 || std::abs(m_currentDelta.height()) > 1) { - m_lastDelta = m_currentDelta; - m_currentDelta = QSizeF(0, 0); - } else if (std::abs(m_lastDelta.width()) < 1 && std::abs(m_lastDelta.height()) < 1) { - // no direction yet - return; - } + //! 计算当前手势的移动方向 + m_touchState.updateSlot(detail, pos); - // determine the direction of the swipe - if (m_lastDelta.width() == m_lastDelta.height()) { - // special case of diagonal, this is not yet supported, thus cancel all gestures - cancelActiveSwipeGestures(); - return; - } - SwipeGesture::Direction direction; - if (std::abs(m_lastDelta.width()) > std::abs(m_lastDelta.height())) { - // horizontal - direction = m_lastDelta.width() < 0 ? SwipeGesture::Direction::Left : SwipeGesture::Direction::Right; - } else { - // vertical - direction = m_lastDelta.height() < 0 ? SwipeGesture::Direction::Up : SwipeGesture::Direction::Down; - } - const QSizeF combinedDelta = std::accumulate(m_swipeUpdates.constBegin(), m_swipeUpdates.constEnd(), QSizeF(0, 0)); - for (auto it = m_activeSwipeGestures.begin(); it != m_activeSwipeGestures.end();) { - auto g = qobject_cast(*it); - if (g->direction() == direction) { - if (g->isMinimumDeltaRelevant()) { - Q_EMIT g->progress(g->minimumDeltaReachedProgress(combinedDelta)); + double distance = 0; + // 如果当前手势处于更新状态则去更新手势 + // not suppoort状态下,认为该手势已经不满足任何条件,不会触发任何动作 + if(m_direction != Gesture::GestureDirection::NotSupport){ + int fcnt = m_touchState.fingerCnt(); + if(fcnt == 1){ + m_direction = calculateSingleDirection(detail); + distance = calculateSingleDistance(detail); + if(m_direction != Gesture::GestureDirection::NoDirection + && m_direction != Gesture::GestureDirection::NotSupport) + m_gestureType = Gesture::GestureType::Swipe; + }else{ + //! 滑动手势和捏合手势同时去竞争,谁先进入手势识别状态,则认为当前手势是谁 + //! 判断当前手势是否进入捏合状态 + if(m_gestureType == Gesture::GestureType::Pinch || m_gestureType == Gesture::GestureType::Waiting){ + m_direction = calculatePinchDirection(); + if(m_direction != Gesture::GestureDirection::NoDirection && + m_direction != Gesture::GestureDirection::NotSupport) + m_gestureType = Gesture::GestureType::Pinch; } - it++; - } else { + //! 判断是否进入滑动状态 + if(m_gestureType == Gesture::GestureType::Swipe || m_gestureType == Gesture::GestureType::Waiting){ + m_direction = calculateSingleDirection(); + distance = calculateSingleDistance(); + if(m_direction != Gesture::GestureDirection::NoDirection + && m_direction != Gesture::GestureDirection::NotSupport) + m_gestureType = Gesture::GestureType::Swipe; + } + } + } +// std::cout << typeToQString(m_gestureType).toStdString().c_str() << " " +// << directionToQString(m_direction).toStdString().c_str() << std::endl; + + //! 更新已经激活的手势的状态,对于已经不满足条件的手势进行剔除 + for(auto it = m_activeGestures.begin(); it != m_activeGestures.end();){ + bool isDelete = false; + qreal progressDelta = 0; + Gesture *g = static_cast(*it); + // std::cout << "-==================" << typeToQString(g->gestureType()).toStdString() << std::endl; + + if(m_gestureType != Gesture::GestureType::Waiting && g->gestureType() != m_gestureType){ + isDelete = true; + }else{ + switch (g->gestureType()) { + case Gesture::GestureType::Swipe:{ + SwipeGesture *sg = dynamic_cast(g); + + if(m_direction != Gesture::GestureDirection::NoDirection + && sg->direction() != m_direction //! 检测当前的滑动方向是否还满足该手势的要求 + || distance > sg->maxSwipeDistance()){ //! 检测当前的滑动距离是否还满足该手势的要求 + + isDelete = true; + break; + } + + //! 检测当前手势是否要开始更新 + if(distance > sg->minSwipeDistance()){ + if(g->isStart()){ + if(distance / (SWIPE_REACH_DELTA + 1) > m_swipeReachCount){ + ++m_swipeReachCount; + Q_EMIT sg->reach(); + } + }else{ + m_swipeReachCount = distance / (SWIPE_REACH_DELTA + 1); + Q_EMIT g->started(); + } + } + break; + } + case Gesture::GestureType::Pinch:{ + PinchGesture *pg = dynamic_cast(g); + if(m_direction != Gesture::GestureDirection::NoDirection + && pg->direction() != m_direction){ + isDelete = true; + } + break; + } + case Gesture::GestureType::LongPress:{ + /*! 中间手势类型一旦被确定,其余手势将被清空, + * 包括LongPress手势, + * 所以不需要对LongPress手势特殊判断, + * 只需在手势结束时,进行一次判断即可 */ + break; + } + default: + break; + } + } + + if(isDelete){ + if(g->isStart()) + Q_EMIT g->cancelled(); + it = m_activeGestures.erase(it); + }else{ + ++it; + } + } +} + +void GestureRecognizer::cancelActiveGesture() +{ + for (auto g : qAsConst(m_activeGestures)) { + if(g->isStart()) Q_EMIT g->cancelled(); - it = m_activeSwipeGestures.erase(it); + } +} + +void GestureRecognizer::cancelGesture() +{ + cancelActiveGesture(); +} + +void GestureRecognizer::endGesture(int detail, const QPointF &pos) +{ + // std::cout << "结束时剩余激活手势的个数:" << m_activeGestures.size() << std::endl; + static int cnt = 0; + //! 当前手势的滑动距离 + double distance = calculateSingleDistance(m_touchState.fingerCnt() == 1 ? detail : -1); + + // 对于还在激活状态的手势,将手势激活信号发出 + for(Gesture* g : qAsConst(m_activeGestures)){ + bool isDelete = false; // 判断该手势是否满足触发条件 + switch (g->gestureType()) { + case Gesture::GestureType::Swipe:{ + SwipeGesture *sg = dynamic_cast(g); + if(distance < sg->minSwipeDistance()) isDelete = true; + else if(m_gestureType != Gesture::GestureType::Swipe) isDelete = true; + break; } - } -} - -void GestureRecognizer::cancelActiveSwipeGestures() -{ - for (auto g : qAsConst(m_activeSwipeGestures)) { - Q_EMIT g->cancelled(); - } - m_activeSwipeGestures.clear(); - m_currentDelta = QSizeF(0, 0); - m_lastDelta = QSizeF(0, 0); -} - -void GestureRecognizer::cancelSwipeGesture() -{ - cancelActiveSwipeGestures(); - m_swipeUpdates.clear(); - m_currentDelta = QSizeF(0, 0); - m_lastDelta = QSizeF(0, 0); -} - -void GestureRecognizer::endSwipeGesture() -{ - const QSizeF delta = std::accumulate(m_swipeUpdates.constBegin(), m_swipeUpdates.constEnd(), QSizeF(0, 0)); - for (auto g : qAsConst(m_activeSwipeGestures)) { - if (static_cast(g)->minimumDeltaReached(delta)) { + case Gesture::GestureType::Pinch:{ + if(m_gestureType != Gesture::GestureType::Pinch) isDelete = true; + break; + } + case Gesture::GestureType::LongPress: { + LongPressGesture *lg = dynamic_cast(g); + //! 判断手指有没有移动 + double delta = calculateFingerDelta(); + // std::cout << "********** delta = " << delta << std::endl; + if(delta > 5 * lg->fingerCount()){ isDelete = true; } + //! 判断是否到达手势要求的时间 + else if(m_touchState.getLastTime() < lg->timeout()) { isDelete = true; } + // std::cout << "手势的持续时间是:" << m_touchState.getLastTime() << std::endl; + break; + } + default:{ + isDelete = true; + break; + } + } + if(isDelete){ + if(g->isStart()) + Q_EMIT g->cancelled(); + }else{ + ++cnt; Q_EMIT g->triggered(); - } else { - Q_EMIT g->cancelled(); } } - m_activeSwipeGestures.clear(); - m_swipeUpdates.clear(); - m_currentDelta = QSizeF(0, 0); - m_lastDelta = QSizeF(0, 0); + // std::cout << "激活的手势个数是:" << cnt << std::endl; + cnt = 0; + + m_touchState.deleteSlot(detail); + m_activeGestures.clear(); + reset(); } +void GestureRecognizer::reset() +{ + m_swipeReachCount = 0; + m_direction = Gesture::GestureDirection::NoDirection; + m_gestureType = Gesture::GestureType::Waiting; + m_touchState.reset(); + m_activeGestures.clear(); + // 清空定时器 + for(QTimer *timer : m_longPressTimers){ + if(timer){ + timer->stop(); + timer->deleteLater(); + } + } + m_longPressTimers.clear(); +} + +Gesture::GestureDirection GestureRecognizer::calculateSingleDirection(const int detail){ + Gesture::GestureDirection res = Gesture::GestureDirection::NoDirection; + QPointF ori = detail == -1 ? m_touchState.initRect.center : m_touchState.getInitPos(detail); + QPointF tar = detail == -1 ? m_touchState.curRect.center : m_touchState.getCurPos(detail); + QPointF lastPoint = detail == -1 ? m_touchState.lastRect.center : m_touchState.getLastPos(detail); + // 当前方向与滑动方向保持一致,方向变化后,手势要清空 + int dx = tar.x() - ori.x(); + int dy = tar.y() - ori.y(); // dx,dy 当前点相对与起始点的偏移量 + int lx = tar.x() - lastPoint.x(); + int ly = tar.y() - lastPoint.y(); // lx, ly 当前点相对于上一坐标的偏移量 + int dis = dist(tar.x(), tar.y(), ori.x(), ori.y()); + if(dis > MIN_SWIPE_DIST){ + if(fabs(dx) > fabs(dy)){ + double angle = 2 * acos(fabs(dx) / dis) * 180 / M_PI; + // 确保手势移动在规定区域内 并且手势方向不发生改变 + if(angle < MAX_SWIPE_ANGLE && (dx >= 0 && lx >= 0 || dx <= 0 && lx <= 0)){ + // std::cout << (dx > 0 ? "RIGHT" : "LEFT") << std::endl; + res = dx > 0 ? Gesture::GestureDirection::Right : Gesture::GestureDirection::Left; + } else { + // std::cout << "不支持该手势" << std::endl; + res = Gesture::GestureDirection::NotSupport; + } + }else{ + double angle = 2 * acos(fabs(dy) / dis) * 180 / M_PI; + if(angle < MAX_SWIPE_ANGLE && (dy >= 0 && ly >= 0 || dy <= 0 && ly <= 0)){ + // std::cout << (dy > 0 ? "DOWN" : "UP") << std::endl; + res = dy > 0 ? Gesture::GestureDirection::Down : Gesture::GestureDirection::Up; + } + else{ + // std::cout << "不支持该手势" << std::endl; + res = Gesture::GestureDirection::NotSupport; + } + } + } + return res; +} + +double GestureRecognizer::calculateSingleDistance(const int detail){ + QPointF ori = detail == -1 ? m_touchState.initRect.center : m_touchState.getInitPos(detail); + QPointF tar = detail == -1 ? m_touchState.curRect.center : m_touchState.getCurPos(detail); + return dist(ori.x(), ori.y(), tar.x(), tar.y()); +} + +//! 计算Pinch手势的方向 +Gesture::GestureDirection GestureRecognizer::calculatePinchDirection() +{ + int fcnt = m_touchState.fingerCnt(); + if(fcnt <= 1) return Gesture::GestureDirection::NoDirection; + ExternalRect crect = m_touchState.curRect; + ExternalRect irect = m_touchState.initRect; + ExternalRect lrect = m_touchState.lastRect; + // std::cout << crect.toQString().toStdString() << std::endl; + //! 判断pinch手势的方向 + double delta = fmax(fmax(fabs(crect.diagL - irect.diagL), fabs(crect.h - irect.h)), fabs(crect.w - irect.w)); + if(delta > MIN_PINCH_DIST){ + double dci = crect.diagL - irect.diagL; + double dcl = crect.diagL - lrect.diagL; + if(dci < 0 && dcl > 0 || dci > 0 && dcl < 0) + return Gesture::GestureDirection::NotSupport; + return dci < 0 ? Gesture::GestureDirection::In : Gesture::GestureDirection::Out; + } + return Gesture::GestureDirection::NoDirection; +} + +double GestureRecognizer::calculateFingerDelta() +{ + double delta = 0; + for(auto it = m_touchState.slotid.begin(); it != m_touchState.slotid.end(); ++it){ + QPointF ori = m_touchState.slotInit[it->second]; + QPointF tar = m_touchState.slotPoints[it->second]; + delta += dist(ori.x(), ori.y(), tar.x(), tar.y()); + } + return delta; +} + +//! 开始滑动时检测滑动手势是否满足当前的滑动状态 +bool GestureRecognizer::detectSwpie(SwipeGesture *gesture){ + if (!gesture) { + return false; + } + uint fingerCount = m_touchState.fingerCnt(); + if (gesture->fingerCount() != fingerCount) { + return false; + } + + // 检测起始点是否是开始范围内 + for(auto it = m_touchState.slotid.begin(); it != m_touchState.slotid.end(); ++it){ + QPointF &p = m_touchState.slotInit[it->second]; + QRect rect = gesture->startGeometry(); + if(p.x() < rect.x() || p.x() > rect.x() + rect.width() || + p.y() < rect.y() || p.y() > rect.y() + rect.height()) + return false; + } + + // 检测滑动距离是否超出最大滑动距离 +// double sdis = 0; // 滑动距离 +// if(fingerCount == 1){ +// auto it = m_touchState.slotid.begin(); +// sdis = dist(m_touchState.slotInit[it->second].x(), +// m_touchState.slotInit[it->second].y(), +// m_touchState.slotPoints[it->second].x(), +// m_touchState.slotPoints[it->second].y()); +// }else{ +// sdis = dist(m_touchState.initRect.center.x(), +// m_touchState.initRect.center.y(), +// m_touchState.curRect.center.x(), +// m_touchState.curRect.center.y()); +// } +// if(sdis > gesture->maximumR()) +// return false; + return true; +} + +QString GestureRecognizer::directionToQString(Gesture::GestureDirection direction){ + switch (direction) { + case Gesture::GestureDirection::Down: return QString("DOWN"); + case Gesture::GestureDirection::In: return QString("IN"); + case Gesture::GestureDirection::Left: return QString("LEFT"); + case Gesture::GestureDirection::NotSupport: return QString("NOTSUPPORT"); + case Gesture::GestureDirection::Out: return QString("OUT"); + case Gesture::GestureDirection::Right: return QString("RIGHT"); + case Gesture::GestureDirection::Up: return QString("UP"); + case Gesture::GestureDirection::NoDirection: return QString("NoDirection"); + default: + break; + } + return QString("ERROR"); +} + +QString GestureRecognizer::typeToQString(Gesture::GestureType type){ + switch (type) { + case Gesture::GestureType::Swipe: return "Swipe"; + case Gesture::GestureType::Pinch: return "Pinch"; + case Gesture::GestureType::LongPress: return "LongPress"; + case Gesture::GestureType::Waiting: return "Waiting"; + } + return "Error"; +} + + +TouchpadGestureRecognizer::TouchpadGestureRecognizer(QObject *parent) + : QObject(parent) +{ + m_gestureState.init(); +} + +TouchpadGestureRecognizer::~TouchpadGestureRecognizer() = default; +void TouchpadGestureRecognizer::registerGesture(KWin::Gesture *gesture) +{ + Q_ASSERT(!m_gesture.contains(gesture)); + auto connection = connect(gesture,&QObject::destroyed,this,std::bind(&TouchpadGestureRecognizer::unregisterGesture,this,gesture)); + m_destroyConnections.insert(gesture,connection); + m_gesture << gesture; + +} +void TouchpadGestureRecognizer::unregisterGesture(KWin::Gesture *gesture) +{ + auto it = m_destroyConnections.find(gesture); + if(it != m_destroyConnections.end()){ + disconnect(it.value()); + m_destroyConnections.erase(it); + } + m_gesture.removeAll(gesture); + if(m_activeGesture.removeOne(gesture)){ + if(gesture->isStart()){ + Q_EMIT gesture->cancelled(); + } + } +} + +void TouchpadGestureRecognizer::startRecognizer(Gesture::GestureType flag,int fingerCount,quint32 time) +{ + // printf("Touchpad-StartRecognize::::::::::::::::::::::\n"); + clearDate(); + // 初始化手势状态 + m_gestureState.fingerCount = fingerCount; + m_gestureState.gestureType = flag; + + for(Gesture *gesture : qAsConst(m_gesture)){ + switch (gesture->gestureType()) { + case Gesture::GestureType::Swipe:{ + TouchpadSwipeGesture *swp = dynamic_cast(gesture); + if(flag != Gesture::GestureType::Swipe)continue; + if(swp->fingerCount() != m_gestureState.fingerCount) continue; + break; + } + case Gesture::GestureType::Tap:{ + TouchpadTapGesture *tap = dynamic_cast(gesture); + if(flag != Gesture::GestureType::Tap) continue; + if(tap->fingerCount() != m_gestureState.fingerCount) continue; + if(!tap->isStart()) + Q_EMIT tap->started(); + break; + } + case Gesture::GestureType::SwipeSequence:{ + TouchpadSwipeSequenceGesture *swipe2 = dynamic_cast(gesture); + if(swipe2->fingerCount() != m_gestureState.fingerCount) continue; + if(flag != Gesture::GestureType::Swipe) continue; + break; + } + default: + break; + } + m_activeGesture << gesture; + + } +} +void TouchpadGestureRecognizer::updateRecognizer(Gesture::GestureType flag,const QSizeF &delta,quint32 time) +{ + // 更新手势的状态 + m_gestureState.sumDeltaX += delta.width(); + m_gestureState.sumDeltaY += delta.height(); + m_gestureState.curDeltaX += delta.width(); + m_gestureState.curDeltaY += delta.height(); + + // 防抖处理 + if((fabs(delta.width()) < 0.4 || fabs(delta.height()) < 0.4) + && m_gestureState.curDeltaX < STATE_UPDATE_SPAN / 2.0 + && m_gestureState.curDeltaY < STATE_UPDATE_SPAN / 2.0) return; + + // 计算当前手势滑动的方向 + Gesture::GestureDirection direction = Gesture::GestureDirection::NoDirection; + + // 计算x、y轴单位(STATE_UPDATE_SPAN)方向的变化 + Gesture::GestureDirection horDirection = Gesture::GestureDirection::NoDirection; + Gesture::GestureDirection verDirection = Gesture::GestureDirection::NoDirection; + + //! 注意这里的flag是触摸板上报的事件类型 + switch (flag) { + case Gesture::GestureType::Swipe: + { + direction = calculateDirection(delta); + if(fabs(m_gestureState.curDeltaX) > STATE_UPDATE_SPAN){ + horDirection = m_gestureState.curDeltaX > 0 ? Gesture::GestureDirection::Right: + Gesture::GestureDirection::Left; + m_gestureState.lastDeltaX += m_gestureState.curDeltaX; + m_gestureState.curDeltaX = 0; + } + if(fabs(m_gestureState.curDeltaY) > STATE_UPDATE_SPAN){ + verDirection = m_gestureState.curDeltaY > 0 ? Gesture::GestureDirection::Down: + Gesture::GestureDirection::Up; + m_gestureState.lastDeltaY += m_gestureState.curDeltaY; + m_gestureState.curDeltaY = 0; + } + break; + } + case Gesture::GestureType::Tap: + { + break; + } + default: + break; + } + + if(m_gestureState.gestureDirection == Gesture::GestureDirection::NoDirection){ + m_gestureState.gestureDirection = direction; + }else if(m_gestureState.gestureDirection != direction){ + m_gestureState.gestureDirection = Gesture::GestureDirection::NotSupport; + } + + //! 决策当前激活手势的去留 + for(auto it = m_activeGesture.begin();it != m_activeGesture.end();){ + Gesture *gesture = static_cast(*it); + bool notSupport = false; + switch (gesture->gestureType()) { + case Gesture::GestureType::Swipe:{ + TouchpadSwipeGesture *swp = dynamic_cast(gesture); + if((swp->direction() != m_gestureState.gestureDirection)){ + notSupport = true; + break; + } + if(!swp->isStart()){ + //! 判断手势的滑动距离是否满足开始的条件 + //滑动距离 + bool isSendStart = false; + if(Gesture::GestureDirection::Left == swp->direction() || + Gesture::GestureDirection::Right == swp->direction()){ + isSendStart = fabs(m_gestureState.sumDeltaX) > swp->minSwipeDistance(); + }else{ + isSendStart = fabs(m_gestureState.sumDeltaY) > swp->minSwipeDistance(); + } + if(isSendStart) + Q_EMIT swp->started(); + } + break; + } + case Gesture::GestureType::Tap:{ + break; + } + case Gesture::GestureType::SwipeSequence: + { + TouchpadSwipeSequenceGesture *swipe2 = dynamic_cast(gesture); + // 当左右滑动时,开启该手势 + // printf("%lf\n", m_gestureState.sumDeltaX); + if(!swipe2->isStart() && + (m_gestureState.gestureDirection == Gesture::GestureDirection::Left + || m_gestureState.gestureDirection == Gesture::GestureDirection::Right) && + fabs(m_gestureState.sumDeltaX) > 15){ + Q_EMIT swipe2->started(); + } + + // 手势开始后,持续通知当前的手势方向 + if(swipe2->isStart()) + { + if(horDirection != Gesture::GestureDirection::NoDirection) + Q_EMIT swipe2->sigNotifyGestureDirection(horDirection); + if(verDirection != Gesture::GestureDirection::NoDirection) + Q_EMIT swipe2->sigNotifyGestureDirection(verDirection); + } + break; + } + default:{ + notSupport = true; + break; + } + } + + if(notSupport){ + if(gesture->isStart()){ + gesture->cancelled(); + } + it = m_activeGesture.erase(it); + }else{ + ++it; + } + } + + +} +void TouchpadGestureRecognizer::endRecognizer(Gesture::GestureType flag,quint32 time) +{ + // printf("Touchpad-endRecognize::::::::::::::::::::::::::\n"); + for(Gesture *gesture : qAsConst(m_activeGesture)){ + bool notSupport = false; + switch (gesture->gestureType()) { + case Gesture::GestureType::Swipe:{ + break; + } + case Gesture::GestureType::Tap:{ + break; + } + case Gesture::GestureType::SwipeSequence:{ + break; + } + default:{ + notSupport = true; + break; + } + } + if(gesture->isStart()){ + if(notSupport){ + Q_EMIT gesture->cancelled(); + }else{ + Q_EMIT gesture->triggered(); + } + } + } + clearDate(); + +} +void TouchpadGestureRecognizer::cancelRecognizer(Gesture::GestureType flag,quint32 time) +{ + /* + * 收到cancel事件之后 把当前发送的信号取消掉 + */// notSupport = true; + + for(Gesture *gesture : qAsConst(m_activeGesture)){ + if(gesture->isStart()){ + Q_EMIT gesture->cancelled(); + } + } +} + +/* + * 只有当向一个方向滑动超过一定的距离的时候才会认定是在该方向进行了滑动 + * 需要计算width和height + * 向左滑动:width<0 width > height + * 向右滑动:width>0 width > height + * 向上滑动:height > 0 height > width + * 向下滑动:height < 0 height > width +*/ +Gesture::GestureDirection TouchpadGestureRecognizer::calculateDirection(const QSizeF &delta) +{ + Gesture::GestureDirection direction = Gesture::GestureDirection::NoDirection; + if(std::abs(delta.height()) > std::abs(delta.width())){ + if(delta.height() > 0){ + direction = Gesture::GestureDirection::Down; + }else{ + direction = Gesture::GestureDirection::Up; + } + } + else if(std::abs(delta.width()) > std::abs(delta.height())){ + if(delta.width() > 0){ + direction = Gesture::GestureDirection::Right; + }else{ + direction = Gesture::GestureDirection::Left; + } + } + return direction; +} + +void TouchpadGestureRecognizer::clearDate() +{ + m_gestureState.init(); + m_activeGesture.clear(); +} + +// TouchpadGestureState +void TouchpadGestureState::init() +{ + gestureType = Gesture::GestureType::Waiting; + sumDeltaX = sumDeltaY = 0; + curDeltaX = curDeltaY = 0; + lastDeltaX = lastDeltaY = 0; + fingerCount = 0; + isGestureDirectionChange = false; + gestureDirection = Gesture::GestureDirection::NoDirection; +} + + } diff --git a/src/gestures.h b/src/gestures.h index eed5ced5b..3c2ae27b8 100644 --- a/src/gestures.h +++ b/src/gestures.h @@ -1,11 +1,37 @@ +/******************************************************************** + UKUI-KWin - the UKUI3.0 window manager + This file is part of the UKUI project + The ukui-kwin is forked from kwin + +Copyright (C) 2014-2020 kylinos.cn + + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2017 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ + /* - KWin - the KDE window manager - This file is part of the KDE project. - - SPDX-FileCopyrightText: 2017 Martin Gräßlin - - SPDX-License-Identifier: GPL-2.0-or-later -*/ + * KWin UKUI Global Gesture + * + * Copyright (C) 2021, KylinSoft Co., Ltd. + * + * Authors: Yunpeng Zhu + * + */ #ifndef KWIN_GESTURES_H #define KWIN_GESTURES_H @@ -16,6 +42,25 @@ #include #include #include +#include +#include +#include +#include + +#include + +// 触摸 +#define MAX_SWIPE_ANGLE (90) // 滑动过程中的容错角度 +#define MIN_SWIPE_DIST (20) // 最小的滑动距离px +#define SWIPE_REACH_DELTA (40) // 滑动过程中触发 +#define MIN_PINCH_DIST (40) // 进入滑动状态的最小变化量px +#define LONGPRESS_DEFAULT_TIMEOUT (200) // 长按手势的默认触发时间 + +#define dist(x1,y1,x2,y2) (double)(sqrt(pow(x1-x2,2)+pow(y1-y2,2))) + +// 触摸板 +#define MIN_TOUCHPAD_SWIPE_DIST (20) // 触摸板 Swipe 手势开始的执行的最小滑动距离 +#define STATE_UPDATE_SPAN (40) // 进行cur、last状态切换的间隔 namespace KWin { @@ -24,9 +69,46 @@ class Gesture : public QObject { Q_OBJECT public: - ~Gesture() override; + /*! + * \brief 手势枚举类型 + */ + enum class GestureType{ + Swipe + ,Pinch + ,LongPress + ,Tap + ,SwipeSequence + ,Waiting // 还未明确下来该手势 + }; + + //! 标记手势是哪个设备的手势 + enum class TouchDevice{ + TouchScreen, // 触摸屏 + TouchPad // 触摸板 + }; + + enum class GestureDirection{ + // Swipe + Down = 0 + ,Left + ,Up + ,Right + // Pinch + ,In = 4 + ,Out + // other + ,NoDirection // 手势还不明确 + ,NotSupport // 不支持该手势 + }; + virtual ~Gesture() = 0; + + virtual GestureType gestureType() = 0; + virtual TouchDevice touchDevice() = 0; + bool isStart(){ return m_isStart; } + protected: explicit Gesture(QObject *parent); + bool m_isStart; // 标记手势是否开始 Q_SIGNALS: /** @@ -49,125 +131,209 @@ class SwipeGesture : public Gesture { Q_OBJECT public: - enum class Direction { - Down, - Left, - Up, - Right - }; + + GestureType gestureType() override { return GestureType::Swipe; } + TouchDevice touchDevice() override { return TouchDevice::TouchScreen; } explicit SwipeGesture(QObject *parent = nullptr); ~SwipeGesture() override; - bool minimumFingerCountIsRelevant() const { - return m_minimumFingerCountRelevant; + void setFingerCount(uint count) { + if(count < 1 || count > 5) count = 0; + m_fingerCount = count; } - void setMinimumFingerCount(uint count) { - m_minimumFingerCount = count; - m_minimumFingerCountRelevant = true; - } - uint minimumFingerCount() const { - return m_minimumFingerCount; + uint fingerCount() const { + return m_fingerCount; } - bool maximumFingerCountIsRelevant() const { - return m_maximumFingerCountRelevant; - } - void setMaximumFingerCount(uint count) { - m_maximumFingerCount = count; - m_maximumFingerCountRelevant = true; - } - uint maximumFingerCount() const { - return m_maximumFingerCount; - } - - Direction direction() const { + GestureDirection direction() const { return m_direction; } - void setDirection(Direction direction) { + void setDirection(GestureDirection direction) { + if((int)direction < 0 || (int)direction > 3) direction = GestureDirection::Down; m_direction = direction; } - void setMinimumX(int x) { - m_minimumX = x; - m_minimumXRelevant = true; + void setMinSwipeDistance(int diatance) { + if(diatance < MIN_SWIPE_DIST) diatance = MIN_SWIPE_DIST; + m_minimumR = diatance; } - int minimumX() const { - return m_minimumX; - } - bool minimumXIsRelevant() const { - return m_minimumXRelevant; - } - void setMinimumY(int y) { - m_minimumY = y; - m_minimumYRelevant = true; - } - int minimumY() const { - return m_minimumY; - } - bool minimumYIsRelevant() const { - return m_minimumYRelevant; + int minSwipeDistance() const { + return m_minimumR; } - void setMaximumX(int x) { - m_maximumX = x; - m_maximumXRelevant = true; + void setMaxSwipeDistance(int diatance) { + if(diatance < MIN_SWIPE_DIST) diatance = MIN_SWIPE_DIST; + m_maximumR = diatance; } - int maximumX() const { - return m_maximumX; - } - bool maximumXIsRelevant() const { - return m_maximumXRelevant; - } - void setMaximumY(int y) { - m_maximumY = y; - m_maximumYRelevant = true; - } - int maximumY() const { - return m_maximumY; - } - bool maximumYIsRelevant() const { - return m_maximumYRelevant; - } - void setStartGeometry(const QRect &geometry); - - QSizeF minimumDelta() const { - return m_minimumDelta; - } - void setMinimumDelta(const QSizeF &delta) { - m_minimumDelta = delta; - m_minimumDeltaRelevant = true; - } - bool isMinimumDeltaRelevant() const { - return m_minimumDeltaRelevant; + int maxSwipeDistance() const { + return m_maximumR; } - qreal minimumDeltaReachedProgress(const QSizeF &delta) const; - bool minimumDeltaReached(const QSizeF &delta) const; + void setStartGeometry(const QRect &geometry){ + m_startGeometry = geometry; + } + + QRect startGeometry(){ + return m_startGeometry; + } Q_SIGNALS: - /** - * The progress of the gesture if a minimumDelta is set. - * The progress is reported in [0.0,1.0] + /*! + * 目前的手势支持中,并不会发出该信号 + * 接口的保留,为了edge的特效 */ void progress(qreal); + /*! + * 每当手势的滑动距离到达 SWIPE_REACH_DELTA 时触发 + */ + void reach(); + private: - bool m_minimumFingerCountRelevant = false; - uint m_minimumFingerCount = 0; - bool m_maximumFingerCountRelevant = false; - uint m_maximumFingerCount = 0; - Direction m_direction = Direction::Down; - bool m_minimumXRelevant = false; - int m_minimumX = 0; - bool m_minimumYRelevant = false; - int m_minimumY = 0; - bool m_maximumXRelevant = false; - int m_maximumX = 0; - bool m_maximumYRelevant = false; - int m_maximumY = 0; - bool m_minimumDeltaRelevant = false; - QSizeF m_minimumDelta; + uint m_fingerCount = 0; + GestureDirection m_direction; + int m_minimumR = 0; + int m_maximumR = 0; + QRect m_startGeometry; +}; +/*! + * \brief 捏合手势 + */ +class PinchGesture : public Gesture{ + Q_OBJECT +public: + + GestureType gestureType() override { return GestureType::Pinch; } + TouchDevice touchDevice() override { return TouchDevice::TouchScreen; } + + explicit PinchGesture(QObject *parent = nullptr); + ~PinchGesture() override; + + void setDirection(GestureDirection direction){ + if((int)direction < 4 || (int)direction > 5) direction = GestureDirection::In; + m_direction = direction; + } + + GestureDirection direction(){ + return m_direction; + } + + void setFingerCount(int count){ + if(count < 2 || count > 5) count = 0; + m_fingerCount = count; + } + + int fingerCount(){ + return m_fingerCount; + } + +private: + int m_fingerCount; + GestureDirection m_direction; +}; + +/*! + * \brief 长按手势 + */ +class LongPressGesture : public Gesture{ + Q_OBJECT +public: + GestureType gestureType() override { return GestureType::LongPress; } + TouchDevice touchDevice() override { return TouchDevice::TouchScreen; } + + explicit LongPressGesture(QObject *parent = nullptr); + ~LongPressGesture() override; + + void setTimeout(int timeout){ + if(timeout < 0) timeout = 0; + m_timeout = timeout; + } + + int timeout(){ + return m_timeout; + } + + void setFingerCount(int count){ + if(count < 1 || count > 5) count = 0; + m_fingerCnt = count; + } + + int fingerCount(){ + return m_fingerCnt; + } + +private: + int m_timeout; // 长按手势的触发时间 ms + int m_fingerCnt; // 手势触发要求的手指数量 +}; + +/*! + * \author Yunpeng Zhu. + * \brief 记录外接圆参数 + */ +typedef struct _external_circle{ + QPointF center; + double r; +}ExternalCircle; +/*! + * \brief 记录外接矩形参数 + */ +typedef struct _external_rect{ + double x, y, w, h; // 外接矩形的范围 + double diagL; // 对角线长度 + QPointF center; // 外接矩形的中心 + + QString toQString(){ + QString res = QString("QRect(%1, %2, %3, %4);" + "DiagL=%5;" + "Center(%6, %7)").arg(x).arg(y).arg(w).arg(h) + .arg(diagL) + .arg(center.x()).arg(center.y()); + return res; + } + +}ExternalRect; + +/*! + * \author Yunpeng Zhu. + * \brief 记录当前触摸手势状态 + */ +class TouchState{ +public: + static const int MAX_SLOT_NUM = 10; + + ExternalRect initRect, lastRect, curRect; // 多指抽象成单指的位置,目前采用外接矩形做匹配 + QPointF slotInit[MAX_SLOT_NUM]; // 开始各触摸点的位置 + QPointF slotLast[MAX_SLOT_NUM]; // 上次各触摸点的位置 + QPointF slotPoints[MAX_SLOT_NUM]; // 当前各触摸点的位置 + int slotMask; // 记录(0 ~ 9)的使用情况 + std::unordered_map slotid; // detail -> (0 ~ 9) + QTime beginTime; // 记录手势开始的时间 + + TouchState(): slotMask(0){} + //! 处理驱动上报的事件异常,事件不成对时的异常 + void insertSlot(const int &detail, const QPointF &pos); + void updateSlot(const int &detail, const QPointF &pos); + void deleteSlot(const int &detail); + void gestureDetector(const int &detail, const QPointF &pos); + int fingerCnt() { return slotid.size(); } + QPointF getInitPos(const int &detail){ return slotInit[slotid[detail]]; } // 得到触摸点的初始位置 + QPointF getCurPos(const int &detail){ return slotPoints[slotid[detail]]; } // 得到当前点的位置 + QPointF getLastPos(const int &detail){ return slotLast[slotid[detail]]; } // 得到上次的触摸位置 + int getLastTime(); // 获取手势的持续时间 + void reset(); // 重置触摸检测的状态 + + /*! + * \brief 计算三点的外接圆 + * \param p1, p2, p3 + * \return + */ + ExternalCircle calculateEC(QPointF p1, QPointF p2, QPointF p3); + /*! + * \brief 获取点集的外接矩形 + */ + ExternalRect calculateRect(const QVector &points); }; class KWIN_EXPORT GestureRecognizer : public QObject @@ -180,33 +346,225 @@ public: void registerGesture(Gesture *gesture); void unregisterGesture(Gesture *gesture); - int startSwipeGesture(uint fingerCount) { - return startSwipeGesture(fingerCount, QPointF(), StartPositionBehavior::Irrelevant); - } - int startSwipeGesture(const QPointF &startPos) { - return startSwipeGesture(1, startPos, StartPositionBehavior::Relevant); - } - void updateSwipeGesture(const QSizeF &delta); - void cancelSwipeGesture(); - void endSwipeGesture(); + /*! + * \author Yunpeng Zhu. + */ + int startGesture(int detail, const QPointF &pos); + void updateGesture(int detail, const QPointF &pos); + void cancelGesture(); + void cancelActiveGesture(); + void endGesture(int detail, const QPointF &pos); + + bool detectSwpie(SwipeGesture *gesture); private: - void cancelActiveSwipeGestures(); - enum class StartPositionBehavior { - Relevant, - Irrelevant - }; - int startSwipeGesture(uint fingerCount, const QPointF &startPos, StartPositionBehavior startPosBehavior); - QVector m_gestures; - QVector m_activeSwipeGestures; - QMap m_destroyConnections; - QVector m_swipeUpdates; - QSizeF m_lastDelta = QSizeF(0, 0); - QSizeF m_currentDelta = QSizeF(0, 0); + /*! + * \brief 重置手势检测状态 + */ + void reset(); + /*! + * \brief 计算单指的滑动方向 + * \param detail = -1 表示计算多指外接矩形的滑动方向 + */ + Gesture::GestureDirection calculateSingleDirection(const int detail = -1); + /*! + * \brief 计算detail表示的手指的滑动距离 + * \param detail = -1 时 表示计算多指外接矩形的滑动距离 + * \return + */ + double calculateSingleDistance(int detail = -1); + /*! + * \brief 计算多指捏合方向 + * \return 手势方向 + */ + Gesture::GestureDirection calculatePinchDirection(); + /*! + * \brief 计算手指间的偏移量 + * \return + */ + double calculateFingerDelta(); + + QString directionToQString(Gesture::GestureDirection direction); + QString typeToQString(Gesture::GestureType type); + + + QVector m_gestures; // 所有已经 + QVector m_activeGestures; // 记录当前已经激活的手势 + QVector m_longPressTimers; // 对于长按手势,开启定时器去发送started信号 + bool gestureDectorState; // 是否开始手势检测 + TouchState m_touchState; // 保存触摸点数据 + Gesture::GestureDirection m_direction; // 记录当前手势方向 + Gesture::GestureType m_gestureType; // 记录当前手势类型 + QMap m_destroyConnections; + + int m_swipeReachCount; // 保存当前滑动手势触发的进度 +}; + + +class TouchpadSwipeGesture : public Gesture +{ +public: + GestureType gestureType() override{ return GestureType::Swipe;} + TouchDevice touchDevice() override { return TouchDevice::TouchPad; } + + explicit TouchpadSwipeGesture(QObject *parent = nullptr); + ~TouchpadSwipeGesture(); + + void setFingersCount(int fingerCount){ + if(fingerCount<=2 || fingerCount >= 5){ + fingerCount = 0; + } + m_fingerCount= fingerCount; + } + int fingerCount(){ + return m_fingerCount; + } + + GestureDirection direction() const { + return m_direction; + } + void setDirection(GestureDirection direction) { + m_direction = direction; + } + + qreal minSwipeDistance(){ + return m_minSwipeDistance; + } + + void setMinSwipeDistance(qreal minSwipeDistance){ + m_minSwipeDistance = minSwipeDistance; + } + +private: + int m_fingerCount = 0; + GestureDirection m_direction = KWin::Gesture::GestureDirection::Down; + //! 手势触发的最小滑动距离 + qreal m_minSwipeDistance; +}; + +class TouchpadTapGesture : public Gesture +{ +public: + GestureType gestureType() override { return GestureType::Tap; } + TouchDevice touchDevice() override { return TouchDevice::TouchPad; } + + explicit TouchpadTapGesture(QObject *parent = nullptr); + ~TouchpadTapGesture() override; + + void setFingersCount(int fingerCount){ + if(fingerCount != 3 && fingerCount != 4){ + fingerCount = 0; + } + m_fingerCount = fingerCount; + } + int fingerCount(){ + return m_fingerCount; + } + void setIcount(int icount){ + m_icount = icount; + } + int icount(){ + return m_icount; + } + +private: + int m_fingerCount; + int m_icount; +}; + +/* 该手势只关心滑动的手指数量,当方向改变时,会发出Direction-Change的信号 */ +class TouchpadSwipeSequenceGesture : public Gesture{ + Q_OBJECT +public: + GestureType gestureType() override{ return GestureType::SwipeSequence; } + TouchDevice touchDevice() override { return TouchDevice::TouchPad; } + + explicit TouchpadSwipeSequenceGesture(QObject *parent = nullptr); + ~TouchpadSwipeSequenceGesture(); + + void setFingersCount(int fingerCount){ + if(fingerCount <= 2 || fingerCount >= 5){ + fingerCount = 0; + } + m_fingerCount = fingerCount; + } + int fingerCount(){ + return m_fingerCount; + } + +Q_SIGNALS: + void sigNotifyGestureDirection(KWin::Gesture::GestureDirection direction); + +private: + const int StepMinSpan = 5; // 触发一次direction changed的最小间隔 + int m_fingerCount = 0; + GestureDirection m_direction = KWin::Gesture::GestureDirection::Down; +}; + + +struct TouchpadGestureState{ + Gesture::GestureType gestureType; + //! X,Y 不要理解为二维坐标,按照各自一维坐标去理解 + //! 将sum,last,cur,理解为三种状态向量,则状态切换公式为 last + cur = sum + //! last 与 cur 发生状态切换的阈值为 STATE_UPDATE_SPAN + // 自手势开始,到当前状态的手势偏移量 + qreal sumDeltaX; + qreal sumDeltaY; + // 上次确定手势方向时,手势的总偏移量 + // 可以将手势状态确定理解为关键点,该值为当前值和关键点相对的偏移量 + qreal lastDeltaX; + qreal lastDeltaY; + // current delta 相对与last delta 的偏移量 + qreal curDeltaX; + qreal curDeltaY; + + // 手势的手指数量 + int fingerCount; + + // 手势的方向是否发生变化 + bool isGestureDirectionChange; + + // 手势的方向 + Gesture::GestureDirection gestureDirection; + + TouchpadGestureState(){ init(); } + // 初始化参数 + void init(); +}; + +class TouchpadGestureRecognizer : public QObject +{ + Q_OBJECT +public: + TouchpadGestureRecognizer(QObject *parent=nullptr); + + ~TouchpadGestureRecognizer() override; + bool threeFigerCount = false; + bool fourFigerCount = false; + + void registerGesture(Gesture *gesture); + void unregisterGesture(Gesture *gesture); + + void startRecognizer(Gesture::GestureType flag, int fingerCount,quint32 time); + void updateRecognizer(Gesture::GestureType flag,const QSizeF &delta,quint32 time); + void endRecognizer(Gesture::GestureType flag,quint32 time); + void cancelRecognizer(Gesture::GestureType flag,quint32 time); +private: + + QVector m_gesture; + QVector m_activeGesture; + QMap m_destroyConnections; + + TouchpadGestureState m_gestureState; + Gesture::GestureDirection calculateDirection(const QSizeF &delta); + void clearDate(); + }; } -Q_DECLARE_METATYPE(KWin::SwipeGesture::Direction) +Q_DECLARE_METATYPE(KWin::Gesture::GestureDirection) +Q_DECLARE_METATYPE(KWin::Gesture::GestureType) +Q_DECLARE_METATYPE(KWin::Gesture::TouchDevice) #endif diff --git a/src/globalshortcuts.cpp b/src/globalshortcuts.cpp index 01c546909..11998b328 100644 --- a/src/globalshortcuts.cpp +++ b/src/globalshortcuts.cpp @@ -28,24 +28,26 @@ GlobalShortcut::GlobalShortcut(Shortcut &&sc, QAction *action) : m_shortcut(sc) , m_action(action) { - static const QMap dirs = { - {SwipeDirection::Up, SwipeGesture::Direction::Up}, - {SwipeDirection::Down, SwipeGesture::Direction::Down}, - {SwipeDirection::Left, SwipeGesture::Direction::Left}, - {SwipeDirection::Right, SwipeGesture::Direction::Right}, + static const QMap dirs = { + {SwipeDirection::Up, Gesture::GestureDirection::Up}, + {SwipeDirection::Down, Gesture::GestureDirection::Down}, + {SwipeDirection::Left, Gesture::GestureDirection::Left}, + {SwipeDirection::Right, Gesture::GestureDirection::Right}, }; if (auto swipeGesture = std::get_if(&sc)) { m_gesture.reset(new SwipeGesture); m_gesture->setDirection(dirs[swipeGesture->swipeDirection]); - m_gesture->setMaximumFingerCount(4); - m_gesture->setMinimumFingerCount(4); +// m_gesture->setMaximumFingerCount(4); +// m_gesture->setMinimumFingerCount(4); + m_gesture->setFingerCount(4); QObject::connect(m_gesture.get(), &SwipeGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection); } else if (auto rtSwipeGesture = std::get_if(&sc)) { m_gesture.reset(new SwipeGesture); m_gesture->setDirection(dirs[rtSwipeGesture->swipeDirection]); - m_gesture->setMinimumDelta(QSizeF(200, 200)); - m_gesture->setMaximumFingerCount(4); - m_gesture->setMinimumFingerCount(4); +// m_gesture->setMinimumDelta(QSizeF(200, 200)); +// m_gesture->setMaximumFingerCount(4); +// m_gesture->setMinimumFingerCount(4); + m_gesture->setFingerCount(4); QObject::connect(m_gesture.get(), &SwipeGesture::triggered, m_action, &QAction::trigger, Qt::QueuedConnection); QObject::connect(m_gesture.get(), &SwipeGesture::cancelled, m_action, &QAction::trigger, Qt::QueuedConnection); QObject::connect(m_gesture.get(), &SwipeGesture::progress, [cb = rtSwipeGesture->progressCallback](qreal v) { @@ -214,22 +216,22 @@ bool GlobalShortcutsManager::processAxis(Qt::KeyboardModifiers mods, PointerAxis void GlobalShortcutsManager::processSwipeStart(uint fingerCount) { - m_gestureRecognizer->startSwipeGesture(fingerCount); +// m_gestureRecognizer->startSwipeGesture(fingerCount); } void GlobalShortcutsManager::processSwipeUpdate(const QSizeF &delta) { - m_gestureRecognizer->updateSwipeGesture(delta); +// m_gestureRecognizer->updateSwipeGesture(delta); } void GlobalShortcutsManager::processSwipeCancel() { - m_gestureRecognizer->cancelSwipeGesture(); +// m_gestureRecognizer->cancelSwipeGesture(); } void GlobalShortcutsManager::processSwipeEnd() { - m_gestureRecognizer->endSwipeGesture(); +// m_gestureRecognizer->endSwipeGesture(); // TODO: cancel on Wayland Seat if one triggered } diff --git a/src/input.cpp b/src/input.cpp index 47b9519c3..2dae60a5d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -41,6 +41,8 @@ #include "workspace.h" #include "xwl/xwayland_interface.h" #include "cursor.h" +#include "xmlreader.h" +#include "touch_gesture_action_control.h" #include #include #include @@ -60,10 +62,11 @@ #include #include #include +#include #include #include - +#include namespace KWin { @@ -916,6 +919,402 @@ private: QTimer* m_powerDown = nullptr; }; +/********************************************************** + * 手势管理器(触摸屏) + *********************************************************/ +KWIN_SINGLETON_FACTORY(GestureManager) + +GestureManager::GestureManager(QObject* parent) : QObject(parent), + InputEventFilter(), + m_gestureRecognizer(new GestureRecognizer()), + m_edges(ScreenEdges::self()->getEdgeGeometry()), + m_gestureConfigWatcher(nullptr) +{ + // 获取每个屏幕的位置 + for (auto screen : QGuiApplication::screens()) { + m_geometrys.append(screen->geometry()); + } + auto gestureConfig = XMLReader::getInstance()->getGestureConfig(); + auto gestureSpecifics = gestureConfig.keys(); + + for(QList& gestureSpecific : gestureSpecifics) + { + auto gestureAction = gestureConfig[gestureSpecific]; + gestureAndAction = new GestureAndActionBase(gestureSpecific, gestureAction); + m_gestureAndAction << gestureAndAction; + registerGesture(gestureAndAction); + } + QFile gestureConfigFile(XMLReader::getConfigFile()); + if(gestureConfigFile.exists()) { + m_gestureConfigWatcher = new QFileSystemWatcher(); + m_gestureConfigWatcher->addPath(XMLReader::getConfigFile()); + connect(m_gestureConfigWatcher, &QFileSystemWatcher::fileChanged, this, &GestureManager::updateGesture); + } + else { + qDebug() << "Fatal Error: gesture config is not exist !"; + } +} + +void GestureManager::registerGesture(GestureAndActionBase *gestureAndAction) +{ + // 注意:多个swipe手势可能共用一个GestureAction + // 当接多个显示器的时候,上边缘下滑手势就可能存在多个swipe手势,而且他们共用一个GestureAction + if(gestureAndAction->getDevice() == TouchDevice::TouchPad) + return; + + /*! + * \brief 有关Gesture中started,triggered,cancelled信号的使用 + * \details 这三个信号一定是成对出现的,started必对应一个triggered或者cancelled信号 + * 保证都和GestureAndActionBase中的槽链接,否则,当GestureAndActionBase中 + * 触发Modify类型的快捷键时,会导致Modify快捷键不释放! + */ + switch (gestureAndAction->getGestureType()) { + case GestureType::EDGESWIPE:{ // 边缘手势 + QRect m_edge_geometry; + switch (gestureAndAction->getGestureDirection()) { + case KWin::Gesture::GestureDirection::Down: + for(auto edge : m_edges){ + if(edge->isTop()){ + m_edge_geometry = edge->geometry(); + swipeGesture = new SwipeGesture(); + swipeGesture->setFingerCount(gestureAndAction->getFingers()); + swipeGesture->setStartGeometry(m_edge_geometry); + swipeGesture->setMinSwipeDistance(gestureAndAction->getMinSwipeDistance()); + swipeGesture->setDirection(gestureAndAction->getGestureDirection()); + m_gestureRecognizer->registerGesture(swipeGesture); + m_gesture << swipeGesture; + connect(swipeGesture, &Gesture::started, + gestureAndAction, &GestureAndActionBase::onGestureActionStart, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::triggered, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::cancelled, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + } + } + break; + case KWin::Gesture::GestureDirection::Up: + for(auto edge : m_edges){ + if(edge->isBottom()){ + m_edge_geometry = edge->geometry(); + swipeGesture = new SwipeGesture(); + swipeGesture->setFingerCount(gestureAndAction->getFingers()); + swipeGesture->setStartGeometry(m_edge_geometry); + swipeGesture->setMinSwipeDistance(gestureAndAction->getMinSwipeDistance()); + swipeGesture->setDirection(gestureAndAction->getGestureDirection()); + m_gestureRecognizer->registerGesture(swipeGesture); + m_gesture << swipeGesture; + connect(swipeGesture, &Gesture::started, + gestureAndAction, &GestureAndActionBase::onGestureActionStart, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::triggered, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::cancelled, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + } + } + break; + case KWin::Gesture::GestureDirection::Left: + for(auto edge : m_edges){ + if(edge->isRight()){ + m_edge_geometry = edge->geometry(); + swipeGesture = new SwipeGesture(); + swipeGesture->setFingerCount(gestureAndAction->getFingers()); + swipeGesture->setStartGeometry(m_edge_geometry); + swipeGesture->setMinSwipeDistance(gestureAndAction->getMinSwipeDistance()); + swipeGesture->setDirection(gestureAndAction->getGestureDirection()); + m_gestureRecognizer->registerGesture(swipeGesture); + m_gesture << swipeGesture; + connect(swipeGesture, &Gesture::started, + gestureAndAction, &GestureAndActionBase::onGestureActionStart, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::triggered, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::cancelled, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + } + } + break; + case KWin::Gesture::GestureDirection::Right: + for(auto edge : m_edges){ + if(edge->isLeft()){ + m_edge_geometry = edge->geometry(); + swipeGesture = new SwipeGesture(); + swipeGesture->setFingerCount(gestureAndAction->getFingers()); + swipeGesture->setStartGeometry(m_edge_geometry); + swipeGesture->setMinSwipeDistance(gestureAndAction->getMinSwipeDistance()); + swipeGesture->setDirection(gestureAndAction->getGestureDirection()); + m_gestureRecognizer->registerGesture(swipeGesture); + m_gesture << swipeGesture; + connect(swipeGesture, &Gesture::started, + gestureAndAction, &GestureAndActionBase::onGestureActionStart, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::triggered, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + connect(swipeGesture, &Gesture::cancelled, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered, + Qt::QueuedConnection); + } + } + break; + default: + break; + } + break; + } + case GestureType::SWIPE:{ // 全局手势 + for (auto m_geometry : m_geometrys) { + swipeGesture = new SwipeGesture(); + swipeGesture->setFingerCount(gestureAndAction->getFingers()); + swipeGesture->setStartGeometry(m_geometry.adjusted(5, 5, -5, -5)); + swipeGesture->setMinSwipeDistance(gestureAndAction->getMinSwipeDistance()); + swipeGesture->setDirection(gestureAndAction->getGestureDirection()); + m_gestureRecognizer->registerGesture(swipeGesture); + m_gesture << swipeGesture; + connect(swipeGesture, &Gesture::started, + gestureAndAction, &GestureAndActionBase::onGestureActionStart); + connect(swipeGesture, &SwipeGesture::reach, + gestureAndAction, &GestureAndActionBase::onGestureActionReach); + connect(swipeGesture, &Gesture::triggered, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered); + connect(swipeGesture, &Gesture::cancelled, + gestureAndAction, &GestureAndActionBase::onGestureActionTriggered); + connect(swipeGesture, &Gesture::cancelled, + gestureAndAction, [](){ + qDebug() << "\tGet cancel signal ! ! !"; + }); + } + break; + } + case GestureType::PINCH:{ // 缩放手势 + pinchGesture = new PinchGesture(); + pinchGesture->setFingerCount(gestureAndAction->getFingers()); + pinchGesture->setDirection(gestureAndAction->getGestureDirection()); + m_gestureRecognizer->registerGesture(pinchGesture); + m_gesture << pinchGesture; + connect(pinchGesture, &Gesture::triggered, + gestureAndAction, &GestureAndActionBase::onGestureActionStart, + Qt::QueuedConnection); + break; + } + case GestureType::LONGPRESS:{ // 长按手势 + longPressGesture = new LongPressGesture(); + longPressGesture->setFingerCount(gestureAndAction->getFingers()); + longPressGesture->setTimeout(gestureAndAction->getTimeOut()); + m_gestureRecognizer->registerGesture(longPressGesture); + m_gesture << longPressGesture; + // 达到长按条件后就出发右键 + connect(longPressGesture, &Gesture::started, + gestureAndAction, &GestureAndActionBase::onGestureActionStart, + Qt::QueuedConnection); + break; + } + default: + break; + } +} + +GestureManager::~GestureManager(){ + delete m_gestureRecognizer; + qDeleteAll(m_gesture); + qDeleteAll(m_gestureAndAction); + if(m_gestureConfigWatcher){ + delete m_gestureConfigWatcher; + m_gestureConfigWatcher = nullptr; + } + s_self = nullptr; +} + +void GestureManager::updateGesture() { + m_gestureConfigWatcher->addPath(XMLReader::getConfigFile()); + qDebug() << "\tConfig file changed ! ! !"; + + // 重新获取手势配置文件中激活的手势 + XMLReader::getInstance()->clearGestureConfig(); + XMLReader::getInstance()->phraseXml(); + auto gestureConfig = XMLReader::getInstance()->getGestureConfig(); + auto gestureSpecifics = gestureConfig.keys(); + + // 清空之前的所有连接 + for(int i = 0; i < m_gesture.size(); i++){ + if(m_gesture[i]->gestureType() == Gesture::GestureType::Swipe){ + swipeGesture = static_cast(m_gesture[i]); + m_gestureRecognizer->unregisterGesture(swipeGesture); + swipeGesture->disconnect(); + } + else if(m_gesture[i]->gestureType() == Gesture::GestureType::Pinch){ + pinchGesture = static_cast(m_gesture[i]); + m_gestureRecognizer->unregisterGesture(pinchGesture); + pinchGesture->disconnect(); + } + else if(m_gesture[i]->gestureType() == Gesture::GestureType::LongPress){ + longPressGesture = static_cast(m_gesture[i]); + m_gestureRecognizer->unregisterGesture(longPressGesture); + longPressGesture->disconnect(); + } + } + for(QVector::iterator it = m_gesture.begin(); it != m_gesture.end(); it++){ + Gesture *deleteGesture = (*it); + delete(deleteGesture); + } + for(QList::iterator it = m_gestureAndAction.begin(); it != m_gestureAndAction.end(); it++){ + GestureAndActionBase *deleteAction = (*it); + delete(deleteAction); + } + m_gesture.clear(); + m_gestureAndAction.clear(); + + // 重新注册所有手势 + for(QList& gestureSpecific : gestureSpecifics){ + auto gestureAction = gestureConfig[gestureSpecific]; + gestureAndAction = new GestureAndActionBase(gestureSpecific, gestureAction); + m_gestureAndAction << gestureAndAction; + registerGesture(gestureAndAction); + } + return; +} + +bool GestureManager::isGestureAlreadyRegisted(GestureAndActionBase *gesture) { + if(m_gestureAndAction.size() == 0) { + return false; + } + for(GestureAndActionBase *gestureAndAction : m_gestureAndAction) { + if((*gestureAndAction) == (*gesture)) { + return true; + } + } + return false; +} + +void GestureManager::recreateSwipeGesture(){ + qDebug() << endl + << "\tGestureManager::recreateSwipeGesture()" << m_gesture.size() << m_gestureAndAction.size(); + // 清空之前的所有连接 + for(int i = 0; i < m_gesture.size(); i++){ + if(m_gesture[i]->gestureType() == Gesture::GestureType::Swipe){ + swipeGesture = static_cast(m_gesture[i]); + m_gestureRecognizer->unregisterGesture(swipeGesture); + swipeGesture->disconnect(); + } + else if(m_gesture[i]->gestureType() == Gesture::GestureType::Pinch){ + pinchGesture = static_cast(m_gesture[i]); + m_gestureRecognizer->unregisterGesture(pinchGesture); + pinchGesture->disconnect(); + } + else if(m_gesture[i]->gestureType() == Gesture::GestureType::LongPress){ + longPressGesture = static_cast(m_gesture[i]); + m_gestureRecognizer->unregisterGesture(longPressGesture); + longPressGesture->disconnect(); + } + } + for(QVector::iterator it = m_gesture.begin(); it != m_gesture.end(); it++){ + Gesture *deleteGesture = (*it); + delete(deleteGesture); + } + for(QList::iterator it = m_gestureAndAction.begin(); it != m_gestureAndAction.end(); it++){ + GestureAndActionBase *deleteAction = (*it); + delete(deleteAction); + } + m_gesture.clear(); + m_gestureAndAction.clear(); + + m_edges.clear(); + m_edges = ScreenEdges::self()->getEdgeGeometry(); + + // 获取每个屏幕的位置 + m_geometrys.clear(); + for (auto screen : QGuiApplication::screens()) { + m_geometrys.append(screen->geometry()); + } + auto gestureConfig = XMLReader::getInstance()->getGestureConfig(); + auto gestureSpecifics = gestureConfig.keys(); + // 重新注册所有手势 + for(QList& gestureSpecific : gestureSpecifics){ + auto gestureAction = gestureConfig[gestureSpecific]; + gestureAndAction = new GestureAndActionBase(gestureSpecific, gestureAction); + m_gestureAndAction << gestureAndAction; + registerGesture(gestureAndAction); + } + return; +} + +bool GestureManager::isScreenEdge(const QPointF &pos) +{ + QRect geometry; + for (auto edge : m_edges) { + geometry = edge->geometry(); + if (geometry.contains(pos.toPoint())) + return true; + } + return false; +} + +bool GestureManager::pointerEvent(QMouseEvent *event, quint32 nativeButton) +{ + Q_UNUSED(nativeButton) + ScreenEdges::self()->isEntered(event); + // always forward + return false; +} + +bool GestureManager::touchDown(qint32 id, const QPointF &pos, quint32 time) { + Q_UNUSED(time) +// qDebug() << "GestureManager::touchDown(id, pos, time) override id =" << id << "pos =" << pos; +// GestureTest::self()->startGesture(id, pos); + + m_lastPos = pos; + m_gestureRecognizer->startGesture(id, pos); + + if (m_touchInProgress || waylandServer()->seat()->isTouchSequence()) { + m_touchInProgress = false; + m_id = 0; + return false; + } + + if (isScreenEdge(pos)) { + m_id = id; + m_touchInProgress = true; + return true; + } + return false; +} +bool GestureManager::touchMotion(qint32 id, const QPointF &pos, quint32 time) { + Q_UNUSED(time) +// qDebug() << "GestureManager::touchMotion(id, pos, time) override id =" << id << "pos =" << m_lastPos; + m_lastPos = pos; + +// GestureTest::self()->updateGesture(id, pos); + m_gestureRecognizer->updateGesture(id, pos); + + if (m_touchInProgress && m_id == id) { + return true; + } + return false; +} +bool GestureManager::touchUp(qint32 id, quint32 time) { + Q_UNUSED(time) +// qDebug() << "GestureManager::touchUp(id, pos, time) override id =" << id << "pos =" << m_lastPos; +// GestureTest::self()->endGesture(id, QPointF()); + m_gestureRecognizer->endGesture(id, QPointF()); + + if (m_touchInProgress && m_id == id) { + m_touchInProgress = false; + return true; + } + return false; +} + +QPointF GestureManager::currentTouchPoint() +{ + return m_lastPos; +} namespace { @@ -1400,12 +1799,14 @@ public: // TODO: better check whether a touch sequence is in progress if (m_touchInProgress || waylandServer()->seat()->isTouchSequence()) { // cancel existing touch - ScreenEdges::self()->gestureRecognizer()->cancelSwipeGesture(); +// ScreenEdges::self()->gestureRecognizer()->cancelSwipeGesture(); + ScreenEdges::self()->gestureRecognizer()->startGesture(id, pos); m_touchInProgress = false; m_id = 0; return false; } - if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(pos) > 0) { +// if (ScreenEdges::self()->gestureRecognizer()->startSwipeGesture(pos) > 0) { + if (ScreenEdges::self()->gestureRecognizer()->startGesture(id, pos) > 0) { m_touchInProgress = true; m_id = id; m_lastPos = pos; @@ -1416,7 +1817,8 @@ public: bool touchMotion(qint32 id, const QPointF &pos, quint32 time) override { Q_UNUSED(time) if (m_touchInProgress && m_id == id) { - ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(pos.x() - m_lastPos.x(), pos.y() - m_lastPos.y())); +// ScreenEdges::self()->gestureRecognizer()->updateSwipeGesture(QSizeF(pos.x() - m_lastPos.x(), pos.y() - m_lastPos.y())); + ScreenEdges::self()->gestureRecognizer()->updateGesture(id, pos); m_lastPos = pos; return true; } @@ -1425,7 +1827,8 @@ public: bool touchUp(qint32 id, quint32 time) override { Q_UNUSED(time) if (m_touchInProgress && m_id == id) { - ScreenEdges::self()->gestureRecognizer()->endSwipeGesture(); +// ScreenEdges::self()->gestureRecognizer()->endSwipeGesture(); + ScreenEdges::self()->gestureRecognizer()->endGesture(id, QPoint(0, 0)); m_touchInProgress = false; return true; } @@ -2467,6 +2870,8 @@ void InputRedirection::setupInputFilters() && hasGlobalShortcutSupport) { installInputEventFilter(new VirtualTerminalFilter); } + + installInputEventFilter(GestureManager::create()); installInputEventSpy(new HideCursorSpy); installInputEventSpy(new UserActivitySpy); if (hasGlobalShortcutSupport) { @@ -2897,6 +3302,21 @@ bool InputRedirection::isSelectingWindow() const return m_windowSelector ? m_windowSelector->isActive() : false; } +void InputRedirection::processKeyboardKey(uint32_t key, KeyboardKeyState state, uint32_t time) +{ + m_keyboard->processKey(key, state, time); +} + +void InputRedirection::processPointerButton(uint32_t button, InputRedirection::PointerButtonState state, uint32_t time) +{ + m_pointer->processButton(button, state, time); +} + +void InputRedirection::processPointerMotion(const QPointF &pos, uint32_t time) +{ + m_pointer->processMotionAbsolute(pos, time); +} + InputDeviceHandler::InputDeviceHandler(InputRedirection *input) : QObject(input) { diff --git a/src/input.h b/src/input.h index fb6efbdbc..44830a0ef 100644 --- a/src/input.h +++ b/src/input.h @@ -28,6 +28,7 @@ class QKeySequence; class QMouseEvent; class QKeyEvent; class QWheelEvent; +class QFileSystemWatcher; namespace KWin { @@ -238,6 +239,10 @@ public: void enableTouchpads(); void disableTouchpads(); + void processKeyboardKey(uint32_t key, KeyboardKeyState state, uint32_t time); + void processPointerButton(uint32_t button, InputRedirection::PointerButtonState state, uint32_t time); + void processPointerMotion(const QPointF &pos, uint32_t time); + Q_SIGNALS: void deviceAdded(InputDevice *device); void deviceRemoved(InputDevice *device); @@ -498,6 +503,54 @@ private: bool m_inited = false; }; +class Edge; +class Gesture; +class LongPressGesture; +class PinchGesture; +class SwipeGesture; +class GestureRecognizer; +class GestureAndActionBase; +class GestureManager : public QObject, public InputEventFilter +{ + Q_OBJECT +public: + ~GestureManager(); + + void registerGesture(GestureAndActionBase *gestureAndAction); + 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; + bool isGestureAlreadyRegisted(GestureAndActionBase *gesture); + bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override; + + QPointF currentTouchPoint(); + +public Q_SLOTS: + void recreateSwipeGesture(); + void updateGesture(); + +private: + bool isScreenEdge(const QPointF &pos); + + bool m_touchInProgress = false; + qint32 m_id = 0; + QPointF m_lastPos; + QVector m_gesture; + QList m_gestureAndAction; + GestureRecognizer* m_gestureRecognizer; + QVector m_geometrys; + QList m_edges; + + LongPressGesture* longPressGesture; + PinchGesture* pinchGesture; + SwipeGesture* swipeGesture; + + GestureAndActionBase *gestureAndAction; + QFileSystemWatcher *m_gestureConfigWatcher; + + KWIN_SINGLETON(GestureManager) +}; + inline InputRedirection *input() { diff --git a/src/screenedge.cpp b/src/screenedge.cpp index 569415ed1..47b9c8665 100644 --- a/src/screenedge.cpp +++ b/src/screenedge.cpp @@ -66,8 +66,9 @@ Edge::Edge(ScreenEdges *parent) , m_client(nullptr) , m_gesture(new SwipeGesture(this)) { - m_gesture->setMinimumFingerCount(1); - m_gesture->setMaximumFingerCount(1); +// m_gesture->setMinimumFingerCount(1); +// m_gesture->setMaximumFingerCount(1); + m_gesture->setFingerCount(1); connect(m_gesture, &Gesture::triggered, this, [this] { stopApproaching(); @@ -522,7 +523,7 @@ void Edge::setGeometry(const QRect &geometry) if (isScreenEdge()) { const AbstractOutput *output = kwinApp()->platform()->outputAt(m_geometry.center()); m_gesture->setStartGeometry(m_geometry); - m_gesture->setMinimumDelta(QSizeF(MINIMUM_DELTA, MINIMUM_DELTA) / output->scale()); +// m_gesture->setMinimumDelta(QSizeF(MINIMUM_DELTA, MINIMUM_DELTA) / output->scale()); } } @@ -667,16 +668,16 @@ void Edge::setBorder(ElectricBorder border) m_border = border; switch (m_border) { case ElectricTop: - m_gesture->setDirection(SwipeGesture::Direction::Down); + m_gesture->setDirection(Gesture::GestureDirection::Down); break; case ElectricRight: - m_gesture->setDirection(SwipeGesture::Direction::Left); + m_gesture->setDirection(Gesture::GestureDirection::Left); break; case ElectricBottom: - m_gesture->setDirection(SwipeGesture::Direction::Up); + m_gesture->setDirection(Gesture::GestureDirection::Up); break; case ElectricLeft: - m_gesture->setDirection(SwipeGesture::Direction::Right); + m_gesture->setDirection(Gesture::GestureDirection::Right); break; default: break; @@ -1515,4 +1516,16 @@ QVector< xcb_window_t > ScreenEdges::windows() const return wins; } +QList ScreenEdges::getEdgeGeometry(){ + QList edges; + qDebug() << "The number of edge is" << m_edges.size(); + for(auto& edge : m_edges){ + if(edge->isScreenEdge()){ + edges << edge; + qDebug() << edge->geometry(); + } + } + return edges; +} + } //namespace diff --git a/src/screenedge.h b/src/screenedge.h index 6db7b53de..c95cac915 100644 --- a/src/screenedge.h +++ b/src/screenedge.h @@ -337,6 +337,8 @@ public: bool handleDndNotify(xcb_window_t window, const QPoint &point); bool handleEnterNotifiy(xcb_window_t window, const QPoint &point, const QDateTime ×tamp); + QList getEdgeGeometry(); + public Q_SLOTS: void reconfigure(); /** diff --git a/src/touch_gesture_action_control.cpp b/src/touch_gesture_action_control.cpp new file mode 100644 index 000000000..d13af9c3a --- /dev/null +++ b/src/touch_gesture_action_control.cpp @@ -0,0 +1,601 @@ +#include "touch_gesture_action_control.h" + +// will error if include after xlib +#include + +#include "input.h" +#include "wayland_server.h" +#include "linux/input-event-codes.h" + +#include +#include + +#include +#include +#include +#include +#include + +const char* serviceName = "com.kylin.statusmanager.interface"; +const char* interfacePath = "/"; +const char* interfaceName = "com.kylin.statusmanager.interface"; +const char* signalName = "mode_change_signal"; +const char* getModeMethod = "get_current_tabletmode"; + +namespace KWin +{ + +// 键盘按键映射表 +static QMap keyCode { + {"a", KEY_A}, + {"b", KEY_B}, + {"c", KEY_C}, + {"d", KEY_D}, + {"e", KEY_E}, + {"f", KEY_F}, + {"g", KEY_G}, + {"h", KEY_H}, + {"i", KEY_I}, + {"g", KEY_G}, + {"k", KEY_K}, + {"l", KEY_L}, + {"m", KEY_M}, + {"n", KEY_N}, + {"o", KEY_O}, + {"p", KEY_P}, + {"q", KEY_Q}, + {"r", KEY_R}, + {"s", KEY_S}, + {"t", KEY_T}, + {"u", KEY_U}, + {"v", KEY_V}, + {"w", KEY_W}, + {"x", KEY_X}, + {"y", KEY_Y}, + {"z", KEY_Z}, + {"Tab",KEY_TAB}, + {"Super_L", KEY_LEFTMETA}, + {"Alt_L", KEY_LEFTALT}, + {"Left", KEY_LEFT}, + {"Right", KEY_RIGHT}, + {"Down", KEY_DOWN}, + {"Up", KEY_UP}, + {"Shift_L", KEY_LEFTSHIFT}, + {"Control_R", KEY_RIGHTCTRL}, + {"Control_L", KEY_LEFTCTRL}, + {"Alt_R", KEY_RIGHTALT}, + {"Super_R", KEY_RIGHTMETA}, + {"Shift_R", KEY_RIGHTSHIFT} +}; + +static QMap btnCode { + {1, BTN_LEFT}, + {2, BTN_MIDDLE}, + {3, BTN_RIGHT}, + {4, BTN_FORWARD}, + {5, BTN_BACK} +}; + +KylinStatusInterface* KylinStatusInterface::s_self = nullptr; +bool KylinStatusInterface::isInterfaceInstalled = true; + +KylinStatusInterface::KylinStatusInterface(const QDBusConnection &connection, QObject *parent = nullptr) : + QDBusAbstractInterface(serviceName, interfacePath, interfaceName, connection, parent) +{ + bool ret = QDBusConnection::sessionBus().connect(serviceName, interfacePath, + interfaceName, signalName, this, + SLOT(setTabletMode(bool))); + if(ret) { +// qDebug() << "\t信号连接成功!"; + } + else { +// qDebug() << "\t信号连接失败!"; + } +} + +KylinStatusInterface::~KylinStatusInterface() +{ + if(s_self){ + delete s_self; + s_self = nullptr; + } +} + +bool KylinStatusInterface::isTabletMode() +{ + if(s_self) + { + QDBusMessage reply = s_self->call(QLatin1String(getModeMethod)); + if(reply.type() == QDBusMessage::ReplyMessage) + return reply.arguments().takeFirst().toBool(); + else + return false; + } + else + return false; +} + +bool KylinStatusInterface::setTabletMode(bool isTablet) +{ + GestureAndActionBase::setTabletMode(isTablet); + return true; +} + +KylinStatusInterface* KylinStatusInterface::getInstance() +{ + if(s_self) + return s_self; + else if(isInterfaceInstalled){ + s_self = new KylinStatusInterface(QDBusConnection::sessionBus()); + if(s_self->isValid()) + return s_self; + else{ + isInterfaceInstalled = false; + return nullptr; + } + } + else + return nullptr; +} + + +KylinStatusInterface* GestureAndActionBase::s_interface = nullptr; +bool GestureAndActionBase::s_isTabletMode = false; + +GestureAndActionBase::GestureAndActionBase(QList gesture, QPair> gestureAction) : + m_gestureType(strToGestureType(gesture[0])), + m_touchDevice(strToTouchDevice(gesture[3])), + m_fingers(gesture[1].toUInt()), + m_gestureDirection(strToGestureDirection(gesture[2])), + m_actionType(gestureAction.first), + m_gestureAction(gestureAction.second), + m_isNeedX(isNeedConnectX11()) +// m_display(nullptr) +{ + +// if(m_isNeedX){ +// m_display = XOpenDisplay(nullptr); +// if (!m_display) { +// qDebug() << "\tOpening X11 display failed!"; +// } +// } + if(!s_interface){ + s_interface = KylinStatusInterface::getInstance(); + if(s_interface){ +// qDebug() << "dbus connect success!"; + s_isTabletMode = s_interface->isTabletMode(); + } + } +} + +GestureAndActionBase::~GestureAndActionBase(){ +// if (m_display != nullptr) { +// XCloseDisplay(m_display); +// } +} + +bool GestureAndActionBase::operator ==(GestureAndActionBase& value) { + if(this->m_gestureAction != value.m_gestureAction) { + return false; + } + if(this->m_fingers != value.m_fingers) { + return false; + } + if(this->m_gestureDirection != value.m_gestureDirection) { + return false; + } + if(this->m_actionType != value.m_actionType) { + return false; + } + if(this->m_gestureAction != value.m_gestureAction) + { + return false; + } + if(this->m_touchDevice != value.m_touchDevice){ + return false; + } + return true; +} + +bool GestureAndActionBase::isNeedEmulateKeyOrButton(){ + switch (getGestureActionType()) { + case ActionType::SEND_KEYS: + case ActionType::SEND_BUTTON: + return true; + default: + break; + } + return false; +} + +bool GestureAndActionBase::isNeedConnectX11(){ + if(!KWin::waylandServer() && isNeedEmulateKeyOrButton()) + return true; + else + return false; +} + +void GestureAndActionBase::setTabletMode(bool isTablet) +{ + s_isTabletMode = isTablet; +// qDebug() << "Current Mode is" << (s_isTabletMode ? "Tablet" : "PC"); +} + +void GestureAndActionBase::sendKeyEvent(const QStringList &keycodes, bool is_press) const { +// qDebug()<<"keycodes"<startDetached(command, QStringList()); + break; + } + case ActionType::SEND_BUTTON:{ + QString btn; + if (m_gestureAction.count("button") == 1) { + btn = m_gestureAction["button"]; + } + + sendButtonEvent(btn.toUInt(), true); + sendButtonEvent(btn.toUInt(), false); + break; + } + case ActionType::DBUS_ACTION:{ + //printf("dbus\n"); + // session,org.ukui.ScreenSaver,/,org.ukui.ScreenSaver,ShowScreensaver + QStringList dbus; + if (m_gestureAction.count("dbus") == 1) { + dbus = m_gestureAction["dbus"].split(','); + } +/* + * TODO: + * dbus调用对多参数的处理 +*/ +// QStringList args; +// if (actionSettings.count("arg") == 1) { +// args = actionSettings["arg"].split(','); +// } +// qDebug() << "\t" << dbus; + QString& bustype = dbus[0]; + QString& service = dbus[1]; + QString& path = dbus[2]; + QString& interface = dbus[3]; + QString& method = dbus[4]; + QDBusMessage message = QDBusMessage::createMethodCall(service, path, interface, method); +// qDebug() << message.arguments() << message.interface() << message.member(); +/* + * TODO: + * dbus调用对多参数的处理 +*/ +// QList arguments; +// if(args.size() > 0){ +// foreach (QString& arg, args) { +// arguments << QVariant(arg); +// message.setArguments(arguments); +// } +// } + if(bustype == "session") + QDBusConnection::sessionBus().call(message); + else if(bustype == "system") + QDBusConnection::systemBus().call(message); + break; + } + case ActionType::NOT_SUPPORTED:{ +// qDebug() << endl +// << "\t" << gestureTypeToStr(getGestureType()) << "\tEmit ActionType::NOT_SUPPORTED"; + break; + } + default: + break; + } +} + +void GestureAndActionBase::onGestureActionReach() +{ + if(!s_isTabletMode && isOnlyTablet()) + return; + if(!isRepeatable()) + return; + switch (m_actionType) { + case ActionType::SEND_KEYS:{ + QStringList keys; + if (m_gestureAction.count("keys") == 1) { + keys = m_gestureAction["keys"].split('+'); +// qDebug()<<"---------"; +// qDebug()<<"KEYS-"<startDetached(command, QStringList()); + break; + } + case ActionType::DBUS_ACTION:{ + // session,org.ukui.ScreenSaver,/,org.ukui.ScreenSaver,ShowScreensaver + QStringList dbus; + if (m_gestureAction.count("dbus") == 1) { + dbus = m_gestureAction["dbus"].split(','); + } +/* + * TODO: + * dbus调用对多参数的处理 +*/ +// QStringList args; +// if (actionSettings.count("arg") == 1) { +// args = actionSettings["arg"].split(','); +// } + QString& bustype = dbus[0]; + QString& service = dbus[1]; + QString& path = dbus[2]; + QString& interface = dbus[3]; + QString& method = dbus[4]; + QDBusMessage message = QDBusMessage::createMethodCall(service, path, interface, method); +// qDebug() << message.arguments() << message.interface() << message.member(); +/* + * TODO: + * dbus调用对多参数的处理 +*/ +// QList arguments; +// if(args.size() > 0){ +// foreach (QString& arg, args) { +// arguments << QVariant(arg); +// message.setArguments(arguments); +// } +// } + if(bustype == "session") + QDBusConnection::sessionBus().call(message); + else if(bustype == "system") + QDBusConnection::systemBus().call(message); + break; + } + case ActionType::NOT_SUPPORTED:{ +// qDebug() << endl +// << "\t" << gestureTypeToStr(m_gestureType) << "\tEmit ActionType::NOT_SUPPORTED"; + break; + } + default: + break; + } +} + +void GestureAndActionBase::onGestureActionTriggered() +{ + if(!s_isTabletMode && isOnlyTablet()) { + qDebug() << "It's not in tablet mode now, so this gesture don't work"; + return; + } + if(m_actionType != ActionType::SEND_KEYS){ + if(!getEventStage("trigger")) + return; + } + switch (m_actionType) { + case ActionType::SEND_KEYS:{ + QStringList modifiers; + QStringList keys; + if (m_gestureAction.count("modifiers") == 1) { + modifiers = m_gestureAction["modifiers"].split('+'); + } + if (m_gestureAction.count("keys") == 1) { + keys = m_gestureAction["keys"].split('+'); + } + if(getEventStage("start")){ + qDebug() << "Gesture SEND_KEYS" << "modifiers:" << modifiers; + sendKeyEvent(modifiers, false);//start阶段的时候按键在triggered阶段释放 + break; + }else{ + qDebug() << "Gesture SEND_KEYS" << "Modifiers:" + << modifiers << "Keys:" << keys; + sendKeyEvent(modifiers, true); + sendKeyEvent(keys, true); + sendKeyEvent(keys, false); + sendKeyEvent(modifiers,false); + //printf("send keys is ok\n"); + } + + break; + } + case ActionType::RUN_COMMAND:{ + QString command = m_gestureAction.value("command"); + qDebug() << "Gesture RUN_COMMAND" << "Command:" << command; + QProcess *myProcess = new QProcess(this); + myProcess->startDetached(command, QStringList()); + break; + } + case ActionType::SEND_BUTTON:{ + QString btn; + if (m_gestureAction.count("button") == 1) { + btn = m_gestureAction["button"]; + } + qDebug() << "Gesture SEND_BUTTON" << "Button:" << btn; + sendButtonEvent(btn.toInt(), true); + sendButtonEvent(btn.toInt(), false); + break; + } + case ActionType::DBUS_ACTION:{ + QStringList dbus; + if (m_gestureAction.count("dbus") == 1) { + dbus = m_gestureAction["dbus"].split(','); + } + + qDebug() << "Gesture DBUS_ACTION" << "DBus:" << dbus; + QString& bustype = dbus[0]; + QString& service = dbus[1]; + QString& path = dbus[2]; + QString& interface = dbus[3]; + QString& method = dbus[4]; + QDBusMessage message = QDBusMessage::createMethodCall(service, path, interface, method); + + if(bustype == "session") + QDBusConnection::sessionBus().call(message); + else if(bustype == "system") + QDBusConnection::systemBus().call(message); + break; + } + case ActionType::NOT_SUPPORTED:{ + qDebug() << "Gesture NOT_SUPPORTED"; + break; + } + default: + break; + } +} + +} diff --git a/src/touch_gesture_action_control.h b/src/touch_gesture_action_control.h new file mode 100644 index 000000000..6a0122530 --- /dev/null +++ b/src/touch_gesture_action_control.h @@ -0,0 +1,106 @@ +#ifndef GESTUREBASEACTION_H +#define GESTUREBASEACTION_H + +#include "xmlreader.h" + +#include +#include + +//class Display; +class QString; +namespace KWin +{ + +class KylinStatusInterface : public QDBusAbstractInterface +{ + Q_OBJECT +public: + ~KylinStatusInterface(); + static KylinStatusInterface* getInstance(); + bool isTabletMode(); + +public Q_SLOTS: + bool setTabletMode(bool isTablet); + +private: + static KylinStatusInterface* s_self; + static bool isInterfaceInstalled; + KylinStatusInterface(const QDBusConnection &connection, QObject *parent); +}; + +class KWIN_EXPORT GestureAndActionBase : public QObject +{ + Q_OBJECT +public: + GestureAndActionBase(QList gesture, QPair> gestureAction); + + ~GestureAndActionBase(); + friend class KylinStatusInterface; + + bool operator ==(GestureAndActionBase& value); + GestureType getGestureType() + { + return m_gestureType; + } + + TouchDevice getDevice() + { + return m_touchDevice; + } + + KWin::Gesture::GestureDirection getGestureDirection(){ + return m_gestureDirection; + } + int getFingers() const { + return m_fingers; + } + ActionType getGestureActionType(){ + return m_actionType; + } + QMap& getGestureActionSetting(){ + return m_gestureAction; + } + + quint32 getTimeOut(); + quint32 getMinSwipeDistance(); + bool getEventStage(QString str); + bool isRepeatable(); + bool isOnlyTablet(); + + + // 模拟键盘Key事件 + void sendKeyEvent(const QStringList &keycodes, bool is_press) const; + // 模拟鼠标Button事件 + void sendButtonEvent(quint32 button, bool is_press) const; + +private: + bool isNeedConnectX11(); + bool isNeedEmulateKeyOrButton(); + static void setTabletMode(bool isTablet); + +public Q_SLOTS: + void onGestureActionStart(); + void onGestureActionReach(); + void onGestureActionTriggered(); + void getSignal(bool mode); + void onAllDirectionKey(Gesture::GestureDirection direction); + +private: + GestureType m_gestureType; + TouchDevice m_touchDevice; + quint32 m_fingers; + KWin::Gesture::GestureDirection m_gestureDirection; + ActionType m_actionType; + QMap m_gestureAction; + bool m_isNeedX; + /** + * X11 connection. + */ +// Display* m_display; + static KylinStatusInterface* s_interface; + static bool s_isTabletMode; +}; + +} + +#endif // GESTUREBASEACTION_H diff --git a/src/touchgesture.xml b/src/touchgesture.xml new file mode 100644 index 000000000..f87646b68 --- /dev/null +++ b/src/touchgesture.xml @@ -0,0 +1,262 @@ + + + + + + true + start + false + + 800 + + + + + + + true + start + false + session,org.ukui.KWin,/KWin,org.ukui.KWin,alwaysShowDesktop + + + + + + + + true + start + 50 + ukui-sidebar -show + false + + + + + + + true + start + false + session,org.ukui.KWin,/MultitaskView,org.ukui.KWin.MultitaskView,show + + + + + + + + true + start + false + + + + + + + false + start + ukui-sidebar -show + false + + + + + + + false + start + ukui-sidebar -show + true + + + + + + + false + start + false + Alt_L + Left + + + + + + + false + start + false + Alt_L + Right + + + + + + + true + start + ukui-search -s + false + + + + + + + true + start + true + 80 + Alt_L+Shift_L + Tab + + + + + + true + start + true + 80 + Alt_L + Tab + + + + + + + false + start + false + Super_L + d + d + + + + + + + false + start + false + session,org.ukui.ScreenSaver,/,org.ukui.ScreenSaver,ShowScreensaver + + + + + + + + + true + trigger + false + session,com.ukui.search.service,/,org.ukui.search.service,showWindow + + + + + + + + true + start + false + session,org.ukui.KWin,/MultitaskView,org.ukui.KWin.MultitaskView,show + + + + + + + + + true + start + false + session,org.ukui.KWin,/KWin,org.ukui.KWin,alwaysShowDesktop + + + + + + + + true + start + false + Alt_L + Tab + Tab + + + + + + + true + trigger + false + Super_L + a + a + + + + + + + true + trigger + false + Control_L+Alt_L + Right + Right + + + + + + + true + trigger + false + Control_L+Alt_L + Left + Left + + + + + + + + true + trigger + false + Control_L+Alt_L + Up + Up + + + + + + + true + trigger + false + Control_L+Alt_L + Down + Down + + + + diff --git a/src/workspace.cpp b/src/workspace.cpp index adaf78c98..bf529c847 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -2149,6 +2149,9 @@ void Workspace::desktopResized() // TODO: emit a signal instead and remove the deep function calls into edges and effects ScreenEdges::self()->recreateEdges(); + if(GestureManager::self()) + GestureManager::self()->recreateSwipeGesture(); + if (m_geometry != oldGeometry) { Q_EMIT geometryChanged(); } diff --git a/src/xmlreader.cpp b/src/xmlreader.cpp new file mode 100644 index 000000000..daeba505d --- /dev/null +++ b/src/xmlreader.cpp @@ -0,0 +1,356 @@ +#include +#include "xmlreader.h" +const char *USR_SHARE_CONFIG_FILE = "/usr/share/touchgesture/touchgesture.xml"; +const char *HOME_CONFIG_DIR = ".config/touchgesture"; +const char *CONFIG_FILE = "touchgesture.xml"; + +GestureType strToGestureType(QString& gestureType) { + if (gestureType == "EDGESWIPE") { + return GestureType::EDGESWIPE; + } + else if (gestureType == "SWIPE") { + return GestureType::SWIPE; + } + else if (gestureType == "PINCH") { + return GestureType::PINCH; + } + else if (gestureType == "LONGPRESS") { + return GestureType::LONGPRESS; + } + else if(gestureType == "TAP"){ + return GestureType::TAP; + } + else if(gestureType == "SWIPESEQUENCE") + { + return GestureType::SWIPESEQUENCE; + } + return GestureType::NOT_SUPPORTED; +} +QString gestureTypeToStr(GestureType gestureType) { + switch (gestureType) { + case GestureType::EDGESWIPE: + return "EDGESWIPE"; + case GestureType::SWIPE: + return "SWIPE"; + case GestureType::PINCH: + return "PINCH"; + case GestureType::LONGPRESS: + return "LONGPRESS"; + case GestureType::SWIPESEQUENCE: + return "SWIPESEQUENCE"; + case GestureType::TAP: + return "TAP"; + default: + return "NOT_SUPPORTED"; + } +} +QString gestureDirectionToStr(KWin::Gesture::GestureDirection gestureDirection) { + switch (gestureDirection) { + case KWin::Gesture::GestureDirection::Up: + return "Up"; + case KWin::Gesture::GestureDirection::Down: + return "Down"; + case KWin::Gesture::GestureDirection::Left: + return "Left"; + case KWin::Gesture::GestureDirection::Right: + return "Right"; + case KWin::Gesture::GestureDirection::In: + return "In"; + case KWin::Gesture::GestureDirection::Out: + return "Out"; + case KWin::Gesture::GestureDirection::NoDirection: + return "NoDirection"; + default: + return "NoDirection"; + } +} +KWin::Gesture::GestureDirection strToGestureDirection(QString& direction) { + if (direction == "Up") { + return KWin::Gesture::GestureDirection::Up; + } + if (direction == "Down") { + return KWin::Gesture::GestureDirection::Down; + } + if (direction == "Left") { + return KWin::Gesture::GestureDirection::Left; + } + if (direction == "Right") { + return KWin::Gesture::GestureDirection::Right; + } + if (direction == "In") { + return KWin::Gesture::GestureDirection::In; + } + if (direction == "Out") { + return KWin::Gesture::GestureDirection::Out; + } + if (direction == "NoDirection") { + return KWin::Gesture::GestureDirection::NoDirection; + } + return KWin::Gesture::GestureDirection::NoDirection; +} +QString actionTypeToStr(ActionType actionType) { + switch (actionType) { + case ActionType::SEND_KEYS: + return "SEND_KEYS"; + case ActionType::RUN_COMMAND: + return "RUN_COMMAND"; + case ActionType::SEND_BUTTON: + return "SEND_BUTTON"; + case ActionType::DBUS_ACTION: + return "DBUS_ACTION"; + default: + return "NOT_SUPPORTED"; + } +} +ActionType strToActionType(QString& actionType) { + if (actionType == "SEND_KEYS") { + return ActionType::SEND_KEYS; + } + if (actionType == "RUN_COMMAND") { + return ActionType::RUN_COMMAND; + } + if (actionType == "SEND_BUTTON") { + return ActionType::SEND_BUTTON; + } + if (actionType == "DBUS_ACTION") { + return ActionType::DBUS_ACTION; + } + return ActionType::NOT_SUPPORTED; +} + +QString touchDeviceTostr(TouchDevice touchDevice){ + switch (touchDevice) { + case TouchDevice::TouchPad: + return "TOUCHPAD"; + case TouchDevice::TouchScreen: + return "TOUCHSCREEN"; + default: + return "NOT_SUPPORT"; + } +} + +TouchDevice strToTouchDevice(QString& touchdevice){ + + if(touchdevice == "TouchScreen"){ + return TouchDevice::TouchScreen; + } + if(touchdevice == "TouchPad"){ + return TouchDevice::TouchPad; + } + return TouchDevice::NOT_SUPPORT; +} + +XMLReader* XMLReader::s_self = nullptr; + +XMLReader::XMLReader() +{ + if(!copyConfigIfNotPresent()) + qDebug() << "config file create error!"; + + phraseXml(); +} + +QString XMLReader::errorString() const{ + return QString("Error:%1 Line:%2 Column:%3") + .arg(reader.errorString()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber()); +} + +XMLReader* XMLReader::getInstance() +{ + if(!s_self){ + s_self = new XMLReader(); + return s_self; + } + else + return s_self; +} + +QString XMLReader::getConfigPath() const +{ + return getHomePath() + QString("/%1").arg(HOME_CONFIG_DIR); +} + +QString XMLReader::getConfigFile() +{ + return getHomePath() + QString("/%1/%2").arg(HOME_CONFIG_DIR).arg(CONFIG_FILE); +} + +bool XMLReader::copyConfigIfNotPresent() +{ + QFile configFile(getConfigFile()); + if(!configFile.exists()){ + QDir configDir; + configDir.mkpath(getConfigPath()); + if(!QFile::exists(QString(USR_SHARE_CONFIG_FILE))){ + qDebug() << USR_SHARE_CONFIG_FILE << "is not exists"; + return false; + } + return QFile::copy(QString(USR_SHARE_CONFIG_FILE), getConfigFile()); + } + return true; +} + +bool XMLReader::read() +{ + if (reader.readNextStartElement()) { + QString strName = reader.name().toString(); // 获取根元素 + qDebug() << "根元素名字为:" << strName; + + QXmlStreamAttributes attributes = reader.attributes(); + if (attributes.hasAttribute("version")) { // 存在属性 version + QString strVersion = attributes.value("version").toString(); + if (strVersion == "1.0") { // 可以作为版本兼容性判断 + qDebug() << "Version : " << strVersion << endl + << "_____________________________"; + } else { + reader.raiseError("The file is not an XBEL version 1.0 file."); + } + } + } + + return !reader.error(); +} + +bool XMLReader::phraseXml() +{ + QString strFile(getConfigFile()); + file = new QFile(strFile); + if (!file->open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug() << QString("Cannot read file %1(%2).").arg(strFile).arg(file->errorString()); + return false; + } + reader.setDevice(file); + + QString gestureType; + QString fingers; + QString direction; + QString actionType; + QString isEnable; + QString touchdevice; + QMap actionSettings; + QString setName; + while(!reader.atEnd()) + { + reader.readNext(); + if(reader.isStartElement() && reader.name() != "touchgesture") + { + // 将属性读出 + QXmlStreamAttributes attributes = reader.attributes(); + if(attributes.hasAttribute("type")){ + gestureType = attributes.value("type").toString(); + } + if(attributes.hasAttribute("fingers")){ + fingers = attributes.value("fingers").toString(); + } + if(attributes.hasAttribute("direction")){ + direction = attributes.value("direction").toString(); + } + if(attributes.hasAttribute("touchdevice")){ + touchdevice = attributes.value("touchdevice").toString(); + } + + // 将action的设置读出 + if(reader.name() == "action" && reader.attributes().hasAttribute("actiontype")){ +// qDebug() << "action actiontype =" << reader.attributes().value("actiontype").toString(); + actionType = attributes.value("actiontype").toString(); + continue; + } + else if(reader.name() == "enable"){ + setName = "enable"; + isEnable = reader.readElementText(); + actionSettings[setName] = isEnable; + continue; + } + else if(reader.name() == "onlytablet"){ + setName = "onlytablet"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "distance"){ + setName = "distance"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "modifiers"){ + setName = "modifiers"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "keys"){ + setName = "keys"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "command"){ + setName = "command"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "button"){ + setName = "button"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "dbus"){ + setName = "dbus"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "arg"){ + setName = "arg"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "repeat"){ + setName = "repeat"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "timeout"){ + setName = "timeout"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else if(reader.name() == "eventstage"){ + setName = "eventstage"; + actionSettings[setName] = reader.readElementText(); + continue; + } + else{ +// qDebug() << "Unkown node ! ! !"; + } + } + else if(reader.isEndElement() && reader.name() == "gesture"){ + // 只有配置文件中被指定为enable=true的手势才会被注册到配置中去; + if(isEnable == "true"){ + saveGestureConfig(gestureType, fingers, direction,touchdevice, + strToActionType(actionType), actionSettings); + } + actionSettings.clear(); + isEnable = "false"; + } + } + + file->close(); + qDebug() << "The number of registed fingers is :" << gestureConfig.size(); + return true; +} + +QString XMLReader::arrangeGestureConfigToEnumStr(GestureType gestureType, QString &fingers, KWin::Gesture::GestureDirection gestureDirection) +{ + return QString::number(static_cast(gestureType)) + "_" + fingers + "_" + + QString::number(static_cast(gestureDirection)); +} + +void XMLReader::saveGestureConfig(QString gestureType, QString &fingers, + QString gestureDirection, QString touchDevice, ActionType actionType, + QMap &actionSettings) +{ + QList key; + key << gestureType << fingers << gestureDirection << touchDevice; +// = arrangeGestureConfigToEnumStr(gestureType, fingers, gestureDirection); +// GestureSpecific key(gestureType, fingers.toInt(), gestureDirection); + gestureConfig[key] = qMakePair(actionType, actionSettings); +} diff --git a/src/xmlreader.h b/src/xmlreader.h new file mode 100644 index 000000000..8d070d224 --- /dev/null +++ b/src/xmlreader.h @@ -0,0 +1,117 @@ +#ifndef XMLREADER_H +#define XMLREADER_H + +#include +#include +#include +#include +#include +#include + +enum class GestureType { + EDGESWIPE, + SWIPE, + PINCH, + LONGPRESS, + TAP, + SWIPESEQUENCE, + NOT_SUPPORTED, +}; +enum class TouchDevice +{ + TouchScreen, + TouchPad, + NOT_SUPPORT +}; +enum class ActionType { + SEND_KEYS, + RUN_COMMAND, + SEND_BUTTON, + DBUS_ACTION, + NOT_SUPPORTED +}; + +// 手势类型和枚举类型之间的转换 +GestureType strToGestureType(QString& gestureType); +QString gestureTypeToStr(GestureType gestureType); +// 手势方向和枚举类型之间的转换 +QString gestureDirectionToStr(KWin::Gesture::GestureDirection gestureDirection); +KWin::Gesture::GestureDirection strToGestureDirection(QString& direction); +// 手势触发动作和枚举类型之间的转换 +QString actionTypeToStr(ActionType actionType); +ActionType strToActionType(QString& actionType); + +QString touchDeviceTostr(TouchDevice touchDevice); +TouchDevice strToTouchDevice(QString& touchdevice); + + + + +class XMLReader +{ +public: + ~XMLReader(){ + if(file) + { + delete file; + file = nullptr; + } + } + static XMLReader* getInstance(); + + // not used! + bool read(); + // 解析手势的配置文件 + bool phraseXml(); + // 获取用户的家目录的绝对路径 + static QString getHomePath() { + return QDir::homePath(); + } + // 获取配置文件所在目录的绝对路径 + QString getConfigPath() const; + // 获取配置文件的绝对路径 + static QString getConfigFile(); + + // 如果家目录不存在配置文件,则从/usr/share下面拷贝一份到家目录 + bool copyConfigIfNotPresent(); + + // 将配置文件中被指定为enable=true的手势注册到数据成员中去; + void saveGestureConfig(QString gestureType, + QString &fingers, + QString gestureDirection, + QString touchDevice, + ActionType actionType, QMap &actionSettings); + + // 将配置文件中被指定为enable=true的手势注册到数据成员中去; + QMap, QPair>> getGestureConfig(){ + return gestureConfig; + } + + // 清空缓存中的数据 + bool clearGestureConfig() { + gestureConfig.clear(); + if(gestureConfig.size() == 0) { + return true; + } + else { + return false; + } + } + + // 将手势类型、手指数、手势方向的enum指拼接成字符串,便于存储; + QString arrangeGestureConfigToEnumStr(GestureType gestureType, + QString &fingers, + KWin::Gesture::GestureDirection gestureDirection); + + // 失败时获取错误信息 + QString errorString() const; + +private: + XMLReader(); + static XMLReader* s_self; + QXmlStreamReader reader; + QFile* file; + QMap, QPair>> gestureConfig; +}; + +#endif // XMLREADER_H From 5ce36f14ce1a7a5e351b6554fb2e8f40bb40e9d5 Mon Sep 17 00:00:00 2001 From: Hongfei Shang Date: Mon, 20 Jun 2022 18:14:35 +0800 Subject: [PATCH 2/2] kwinrc: default config --- data/CMakeLists.txt | 1 + data/kwinrc | 118 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 data/kwinrc diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index 690a113ca..eeead3148 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -12,3 +12,4 @@ install(TARGETS kwin5_update_default_rules DESTINATION ${KDE_INSTALL_LIBDIR}/kco ########### install files ############### install(FILES org_kde_kwin.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}) +install(FILES kwinrc DESTINATION /etc/xdg) diff --git a/data/kwinrc b/data/kwinrc new file mode 100644 index 000000000..b67a0e0cb --- /dev/null +++ b/data/kwinrc @@ -0,0 +1,118 @@ +[Compositing] +Enabled=false +OpenGLIsUnsafe=false +#UKUI for preview +HiddenPreviews=6 +#UKUI animation speed +AnimationSpeed=2 + +[Effect-PresentWindows] +#UKUI disable hotarea +BorderActivate=9 +BorderActivateAll=9 +BorderActivateClass=9 +TouchBorderActivate=7 +TouchBorderActivateAll=9 +TouchBorderActivateClass=9 + +[Desktops] +Number=4 +Rows=1 + + +[Effect-Cube] +TouchBorderActivate=9 +TouchBorderActivateCylinder=9 +TouchBorderActivateSphere=9 + +[Effect-DesktopGrid] +TouchBorderActivate=9 + +[Windows] +#UKUI placement +Placement=Random +#UKUI not hide dialog +HideUtilityWindowsForInactive=false +#UKUI touchpad 4 fingers gesture +RollOverDesktops=false + + +[Effect-Blur] +BlurStrength=9 +NoiseStrength=1 + +[Plugins] +blurEnabled=true +#UKUI true conflict with sogou input +presentwindowsEnabled=false +flipswitchEnabled=true +highlightwindowEnabled=true +startupfeedbackEnabled=false +zoomEnabled=false +touchmotionstreakEnabled=true +touchclickEnabled=true +#UKUI touchpad 4 fingers gesture +#Default not to virtual desktop switch animation +desktopchangeosdEnabled=false +#UKUI show desktop not aperture window +kwin4_effect_windowapertureEnabled=false +#UKUI disable window fade +kwin4_effect_fadeEnabled=false +kwin4_effect_morphingpopupsEnabled=false +kwin4_effect_translucencyEnabled=false +kwin4_effect_scaleEnabled=true +kwin4_effect_eyeonscreenEnabled=true + +[Effect-CoverSwitch] +TabBox=false +TabBoxAlternative=false + +[Effect-FlipSwitch] +TabBox=false +TabBoxAlternative=false + +[MouseBindings] +CommandAllKey=Meta +CommandAll1=Nothing +CommandAll2=Nothing +CommandAll3=Nothing + +[TabBox] +ActivitiesMode=1 +ApplicationsMode=0 +DesktopMode=1 +HighlightWindows=false +LayoutName=thumbnail_grid +MinimizedMode=0 +MultiScreenMode=0 +ShowDesktopMode=0 +ShowTabBox=true +SwitchingMode=0 +BorderActivate=9 +BorderAlternativeActivate=9 +TouchBorderActivate=9 +TouchBorderAlternativeActivate=9 + +[TouchEdges] +Bottom=None +Left=None +Right=None +Top=None + +[TabBoxAlternative] +ActivitiesMode=1 +ApplicationsMode=0 +DesktopMode=1 +HighlightWindows=true +LayoutName=org.kde.breeze.desktop +MinimizedMode=0 +MultiScreenMode=0 +ShowDesktopMode=0 +ShowTabBox=true +SwitchingMode=0 + +[org.kde.kdecoration2] +ButtonsOnLeft=M +ButtonsOnRight=IAX +library=UKUI +theme=UKUI