!2 touch gesture: add touch gesture support

This commit is contained in:
Hongfei Shang 2022-06-20 09:40:30 +00:00 committed by handsome_feng
parent ff520180b0
commit 2539101fd0
19 changed files with 3391 additions and 335 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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})

View File

@ -8,6 +8,8 @@
#include <KWaylandServer/fakeinput_interface.h>
#include <QDebug>
namespace KWin
{
static int s_lastDeviceId = 0;

View File

@ -106,43 +106,43 @@ public:
m_x11Cursor->schedulePoll();
}
break;
case XI_TouchBegin: {
auto e = reinterpret_cast<xXIDeviceEvent*>(event);
m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)));
break;
}
case XI_TouchUpdate: {
auto e = reinterpret_cast<xXIDeviceEvent*>(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<xXIDeviceEvent*>(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<xXITouchOwnershipEvent*>(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<xXIDeviceEvent*>(event);
// m_lastTouchPositions.insert(e->detail, QPointF(fixed1616ToReal(e->event_x), fixed1616ToReal(e->event_y)));
// break;
// }
// case XI_TouchUpdate: {
// auto e = reinterpret_cast<xXIDeviceEvent*>(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<xXIDeviceEvent*>(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<xXITouchOwnershipEvent*>(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();

File diff suppressed because it is too large Load Diff

View File

@ -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 <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 Martin Gräßlin <mgraesslin@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
* KWin UKUI Global Gesture
*
* Copyright (C) 2021, KylinSoft Co., Ltd.
*
* Authors: Yunpeng Zhu <zhuyunpeng@kylinos.cn>
*
*/
#ifndef KWIN_GESTURES_H
#define KWIN_GESTURES_H
@ -16,6 +42,25 @@
#include <QSizeF>
#include <QMap>
#include <QVector>
#include <QString>
#include <QRect>
#include <QTime>
#include <QTimer>
#include <unordered_map>
// 触摸
#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<int, int> 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<QPointF> &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<Gesture*> m_gestures;
QVector<Gesture*> m_activeSwipeGestures;
QMap<Gesture*, QMetaObject::Connection> m_destroyConnections;
QVector<QSizeF> 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<Gesture*> m_gestures; // 所有已经
QVector<Gesture*> m_activeGestures; // 记录当前已经激活的手势
QVector<QTimer*> m_longPressTimers; // 对于长按手势开启定时器去发送started信号
bool gestureDectorState; // 是否开始手势检测
TouchState m_touchState; // 保存触摸点数据
Gesture::GestureDirection m_direction; // 记录当前手势方向
Gesture::GestureType m_gestureType; // 记录当前手势类型
QMap<Gesture*, QMetaObject::Connection> 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 不要理解为二维坐标,按照各自一维坐标去理解
//! 将sumlastcur理解为三种状态向量则状态切换公式为 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<Gesture*> m_gesture;
QVector<Gesture*> m_activeGesture;
QMap<Gesture*,QMetaObject::Connection> 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

View File

@ -28,24 +28,26 @@ GlobalShortcut::GlobalShortcut(Shortcut &&sc, QAction *action)
: m_shortcut(sc)
, m_action(action)
{
static const QMap<SwipeDirection, SwipeGesture::Direction> dirs = {
{SwipeDirection::Up, SwipeGesture::Direction::Up},
{SwipeDirection::Down, SwipeGesture::Direction::Down},
{SwipeDirection::Left, SwipeGesture::Direction::Left},
{SwipeDirection::Right, SwipeGesture::Direction::Right},
static const QMap<SwipeDirection, Gesture::GestureDirection> 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<FourFingerSwipeShortcut>(&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<FourFingerRealtimeFeedbackSwipeShortcut>(&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
}

View File

@ -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 <KDecoration2/Decoration>
#include <KGlobalAccel>
#include <KLocalizedString>
@ -60,10 +62,11 @@
#include <QDBusPendingCall>
#include <QKeyEvent>
#include <QThread>
#include <QFileSystemWatcher>
#include <qpa/qwindowsysteminterface.h>
#include <xkbcommon/xkbcommon.h>
#include <iostream>
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<QString>& 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中startedtriggeredcancelled信号的使用
* \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<SwipeGesture*>(m_gesture[i]);
m_gestureRecognizer->unregisterGesture(swipeGesture);
swipeGesture->disconnect();
}
else if(m_gesture[i]->gestureType() == Gesture::GestureType::Pinch){
pinchGesture = static_cast<PinchGesture*>(m_gesture[i]);
m_gestureRecognizer->unregisterGesture(pinchGesture);
pinchGesture->disconnect();
}
else if(m_gesture[i]->gestureType() == Gesture::GestureType::LongPress){
longPressGesture = static_cast<LongPressGesture*>(m_gesture[i]);
m_gestureRecognizer->unregisterGesture(longPressGesture);
longPressGesture->disconnect();
}
}
for(QVector<Gesture*>::iterator it = m_gesture.begin(); it != m_gesture.end(); it++){
Gesture *deleteGesture = (*it);
delete(deleteGesture);
}
for(QList<GestureAndActionBase*>::iterator it = m_gestureAndAction.begin(); it != m_gestureAndAction.end(); it++){
GestureAndActionBase *deleteAction = (*it);
delete(deleteAction);
}
m_gesture.clear();
m_gestureAndAction.clear();
// 重新注册所有手势
for(QList<QString>& 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<SwipeGesture*>(m_gesture[i]);
m_gestureRecognizer->unregisterGesture(swipeGesture);
swipeGesture->disconnect();
}
else if(m_gesture[i]->gestureType() == Gesture::GestureType::Pinch){
pinchGesture = static_cast<PinchGesture*>(m_gesture[i]);
m_gestureRecognizer->unregisterGesture(pinchGesture);
pinchGesture->disconnect();
}
else if(m_gesture[i]->gestureType() == Gesture::GestureType::LongPress){
longPressGesture = static_cast<LongPressGesture*>(m_gesture[i]);
m_gestureRecognizer->unregisterGesture(longPressGesture);
longPressGesture->disconnect();
}
}
for(QVector<Gesture*>::iterator it = m_gesture.begin(); it != m_gesture.end(); it++){
Gesture *deleteGesture = (*it);
delete(deleteGesture);
}
for(QList<GestureAndActionBase*>::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<QString>& 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)
{

View File

@ -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<Gesture *> m_gesture;
QList<GestureAndActionBase *> m_gestureAndAction;
GestureRecognizer* m_gestureRecognizer;
QVector<QRect> m_geometrys;
QList<Edge*> m_edges;
LongPressGesture* longPressGesture;
PinchGesture* pinchGesture;
SwipeGesture* swipeGesture;
GestureAndActionBase *gestureAndAction;
QFileSystemWatcher *m_gestureConfigWatcher;
KWIN_SINGLETON(GestureManager)
};
inline
InputRedirection *input()
{

View File

@ -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<Edge*> ScreenEdges::getEdgeGeometry(){
QList<Edge*> 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

View File

@ -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 &timestamp);
QList<Edge*> getEdgeGeometry();
public Q_SLOTS:
void reconfigure();
/**

View File

@ -0,0 +1,601 @@
#include "touch_gesture_action_control.h"
// will error if include after xlib
#include <QDebug>
#include "input.h"
#include "wayland_server.h"
#include "linux/input-event-codes.h"
#include <X11/Xlib.h>
#include <X11/extensions/XTest.h>
#include <QVariant>
#include <QProcess>
#include <QThread>
#include <QDBusMessage>
#include <QDBusConnection>
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<QString, quint32> 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<quint32, quint32> 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<QString> gesture, QPair<ActionType, QMap<QString, QString>> 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"<<keycodes;
for(const QString &keycode: keycodes) {
if(m_isNeedX){
Display* m_display = XOpenDisplay(nullptr);
KeySym sym = XStringToKeysym(keycode.toStdString().c_str());
KeyCode code = XKeysymToKeycode(m_display, sym);
XTestFakeKeyEvent(m_display, code, is_press ? true : false, 0);
XFlush(m_display);
XCloseDisplay(m_display);
}
else {
if(keyCode.count(keycode) != 1) {
qCritical() << "\t Error: Not find the key's map ! key =" << keycode;
return;
}
if(is_press)
input()->processKeyboardKey(keyCode[keycode], InputRedirection::KeyboardKeyPressed, 0);
else
input()->processKeyboardKey(keyCode[keycode], InputRedirection::KeyboardKeyReleased, 0);
waylandServer()->simulateUserActivity();
}
}
return;
}
void GestureAndActionBase::sendButtonEvent(quint32 button, bool is_press) const {
if(m_isNeedX) {
Display* m_display = XOpenDisplay(nullptr);
XTestFakeButtonEvent(m_display, button, is_press, 0);
XFlush(m_display);
XCloseDisplay(m_display);
}
else {
if(btnCode.count(button) != 1){
// qCritical() << "\t Error: Not find the btn's map ! btn =" << button;
return;
}
input()->processPointerMotion(GestureManager::self()->currentTouchPoint(), 0);
if(is_press)
input()->processPointerButton(btnCode[button], InputRedirection::PointerButtonPressed, 0);
else
input()->processPointerButton(btnCode[button], InputRedirection::PointerButtonReleased, 0);
waylandServer()->simulateUserActivity();
}
return;
}
void GestureAndActionBase::getSignal(bool mode) {
// qDebug() << endl << "\t Get signal from Dbus ! ! !";
if(mode != s_isTabletMode)
s_isTabletMode = mode;
return;
}
quint32 GestureAndActionBase::getTimeOut(){
if(m_gestureType == GestureType::LONGPRESS && m_gestureAction.count("timeout") == 1)
return m_gestureAction["timeout"].toInt();
else
return 0;
}
quint32 GestureAndActionBase::getMinSwipeDistance(){
if(m_gestureAction.count("distance") == 1)
return m_gestureAction["distance"].toInt();
else
return 50; //! 默认滑动距离为50当滑动距离为0时刚开始按下时便有四个方向的started信号
}
bool GestureAndActionBase::getEventStage(QString str){
bool flag = true;
if(m_gestureAction.count("eventstage") == 1){
if(m_gestureAction["eventstage"] == str)
flag = true;
else
flag = false;
}else{
flag = false;
}
return flag;
}
bool GestureAndActionBase::isRepeatable(){
if(m_gestureAction.count("repeat") == 1)
return m_gestureAction["repeat"] == "true";
else
return false;
}
bool GestureAndActionBase::isOnlyTablet(){
if(m_gestureAction.count("onlytablet") == 1)
return m_gestureAction["onlytablet"] == "true";
else
return false;
}
void GestureAndActionBase::onAllDirectionKey(Gesture::GestureDirection direction)
{
QStringList keys;
switch (direction) {
case Gesture::GestureDirection::Down:
//printf("SwipeSequence: Down\n");
keys << "Down";
break;
case Gesture::GestureDirection::Up:
//printf("SwipeSequence: Up\n");
keys << "Up";
break;
case Gesture::GestureDirection::Left:
//printf("SwipeSequence: Left\n");
keys << "Left";
break;
case Gesture::GestureDirection::Right:{
//printf("SwipeSequence: Right\n");
keys << "Right";
break;
}
default:
keys << "";
break;
}
sendKeyEvent(keys, true);
sendKeyEvent(keys, false);
}
void GestureAndActionBase::onGestureActionStart()
{
//printf("start is ok\n");
// qDebug() << endl << "\tGestureAndActionBase::onGestureActionStart()"
// << gestureTypeToStr(m_gestureType) << s_isTabletMode << isOnlyTablet();
if(!s_isTabletMode && isOnlyTablet())
return;
if(!getEventStage("start"))
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('+');
}
sendKeyEvent(modifiers, true);//按键被按下 只有在最后的时候才会释放
sendKeyEvent(keys, true);
sendKeyEvent(keys, false);
//printf("send keys is begin\n");
break;
}
case ActionType::RUN_COMMAND:{
QString command = m_gestureAction.value("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"];
}
sendButtonEvent(btn.toUInt(), true);
sendButtonEvent(btn.toUInt(), false);
break;
}
case ActionType::DBUS_ACTION:{
//printf("dbus\n");
// <dbus>session,org.ukui.ScreenSaver,/,org.ukui.ScreenSaver,ShowScreensaver</dbus>
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<QVariant> 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-"<<keys<<"---";
// qDebug()<<"---------";
// qDebug()<<"m_gestureAction[]-"<<m_gestureAction["keys"]<<"---";
}
sendKeyEvent(keys, true);
sendKeyEvent(keys, false);
break;
}
case ActionType::RUN_COMMAND:{
QString command = m_gestureAction.value("command");
QProcess *myProcess = new QProcess(this);
// qDebug() << command;
myProcess->startDetached(command, QStringList());
break;
}
case ActionType::DBUS_ACTION:{
// <dbus>session,org.ukui.ScreenSaver,/,org.ukui.ScreenSaver,ShowScreensaver</dbus>
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<QVariant> 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;
}
}
}

View File

@ -0,0 +1,106 @@
#ifndef GESTUREBASEACTION_H
#define GESTUREBASEACTION_H
#include "xmlreader.h"
#include <QObject>
#include <QDBusAbstractInterface>
//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<QString> gesture, QPair<ActionType, QMap<QString, QString>> 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<QString, QString>& 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<QString, QString> m_gestureAction;
bool m_isNeedX;
/**
* X11 connection.
*/
// Display* m_display;
static KylinStatusInterface* s_interface;
static bool s_isTabletMode;
};
}
#endif // GESTUREBASEACTION_H

262
src/touchgesture.xml Normal file
View File

@ -0,0 +1,262 @@
<touchgesture version="1.0">
<!-- 1指 长按右键 -->
<gesture type="LONGPRESS" fingers="1" direction="NoDirection" touchdevice="TouchScreen">
<action actiontype="SEND_BUTTON">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<button>3</button>
<timeout>800</timeout>
</action>
</gesture>
<!-- 1指 边缘下划 1左键 2中键 3右键-->
<gesture type="EDGESWIPE" fingers="1" direction="Down" touchdevice="TouchScreen">
<action actiontype="DBUS_ACTION">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<dbus>session,org.ukui.KWin,/KWin,org.ukui.KWin,alwaysShowDesktop</dbus>
<arg></arg>
</action>
</gesture>
<!-- 1指 边缘左划 -->
<gesture type="EDGESWIPE" fingers="1" direction="Left" touchdevice="TouchScreen">
<action actiontype="RUN_COMMAND">
<enable>true</enable>
<eventstage>start</eventstage>
<distance>50</distance>
<command>ukui-sidebar -show</command>
<repeat>false</repeat>
</action>
</gesture>
<!-- 1指 边缘上划 -->
<gesture type="EDGESWIPE" fingers="1" direction="Up" touchdevice="TouchScreen">
<action actiontype="DBUS_ACTION">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<dbus>session,org.ukui.KWin,/MultitaskView,org.ukui.KWin.MultitaskView,show</dbus>
<arg></arg>
</action>
</gesture>
<!-- 1指 边缘右划 -->
<gesture type="EDGESWIPE" fingers="1" direction="Right" touchdevice = "TouchScreen">
<action actiontype="DBUS_SIG">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
</action>
</gesture>
<!-- 1指 上划 -->
<gesture type="SWIPE" fingers="1" direction="Up" touchdevice = "TouchScreen">
<action actiontype="RUN_COMMAND">
<enable>false</enable>
<eventstage>start</eventstage>
<command>ukui-sidebar -show</command>
<repeat>false</repeat>
</action>
</gesture>
<!-- 1指 下划 -->
<gesture type="SWIPE" fingers="1" direction="Down" touchdevice = "TouchScreen">
<action actiontype="RUN_COMMAND">
<enable>false</enable>
<eventstage>start</eventstage>
<command>ukui-sidebar -show</command>
<repeat>true</repeat>
</action>
</gesture>
<!-- 1指 左划 -->
<gesture type="SWIPE" fingers="1" direction="Left" touchdevice = "TouchScreen">
<action actiontype="SEND_KEYS">
<enable>false</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<modifiers>Alt_L</modifiers>
<keys>Left</keys>
</action>
</gesture>
<!-- 1指 右划 -->
<gesture type="SWIPE" fingers="1" direction="Right" touchdevice = "TouchScreen">
<action actiontype="SEND_KEYS">
<enable>false</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<modifiers>Alt_L</modifiers>
<keys>Right</keys>
</action>
</gesture>
<!-- 四指下滑 打开全局搜索 -->
<gesture type="SWIPE" fingers="4" direction="Down" touchdevice = "TouchScreen">
<action actiontype="RUN_COMMAND">
<enable>true</enable>
<eventstage>start</eventstage>
<command>ukui-search -s</command>
<repeat>false</repeat>
</action>
</gesture>
<!-- 四指左右滑动切换任务 -->
<gesture type="SWIPE" fingers="4" direction="Left" touchdevice = "TouchScreen">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>true</repeat>
<distance>80</distance>
<modifiers>Alt_L+Shift_L</modifiers>
<keys>Tab</keys>
</action>
</gesture>
<gesture type="SWIPE" fingers="4" direction="Right" touchdevice = "TouchScreen">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>true</repeat>
<distance>80</distance>
<modifiers>Alt_L</modifiers>
<keys>Tab</keys>
</action>
</gesture>
<!-- 5只捏合 -->
<gesture type="PINCH" fingers="5" direction="In" touchdevice = "TouchScreen">
<action actiontype="SEND_KEYS">
<enable>false</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<modifiers>Super_L</modifiers>
<keys>d</keys>
<decreaseKeys>d</decreaseKeys>
</action>
</gesture>
<!-- 2指捏合 bustypeservicepathinterfacemethod 参数中间以,隔开-->
<gesture type="PINCH" fingers="2" direction="In" touchdevice = "TouchScreen">
<action actiontype="DBUS_ACTION">
<enable>false</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<dbus>session,org.ukui.ScreenSaver,/,org.ukui.ScreenSaver,ShowScreensaver</dbus>
<arg></arg>
</action>
</gesture>
<!-- TouchPad-->
<!-- TouchPad 3指点击 打开全局搜索-->
<gesture type="TAP" fingers="3" direction="NoDirection" touchdevice="TouchPad">
<action actiontype="DBUS_ACTION">
<enable>true</enable>
<eventstage>trigger</eventstage>
<repeat>false</repeat>
<dbus>session,com.ukui.search.service,/,org.ukui.search.service,showWindow</dbus>
<arg></arg>
</action>
</gesture>
<!-- TouchPad 3指上滑 打开多任务视图-->
<gesture type="SWIPE" fingers="3" direction="Up" touchdevice="TouchPad">
<action actiontype="DBUS_ACTION">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<dbus>session,org.ukui.KWin,/MultitaskView,org.ukui.KWin.MultitaskView,show</dbus>
<arg></arg>
</action>
</gesture>
<!-- TouchPad 3指下滑 关闭当前窗口显示桌面-->
<gesture type="SWIPE" fingers="3" direction="Down" touchdevice="TouchPad">
<action actiontype="DBUS_ACTION">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<dbus>session,org.ukui.KWin,/KWin,org.ukui.KWin,alwaysShowDesktop</dbus>
<arg></arg>
</action>
</gesture>
<!-- TouchPad 3指左右滑动 切换上下窗口-->
<gesture type="SWIPESEQUENCE" fingers="3" direction="NoDirection" touchdevice="TouchPad">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>start</eventstage>
<repeat>false</repeat>
<modifiers>Alt_L</modifiers>
<keys>Tab</keys>
<decreaseKeys>Tab</decreaseKeys>
</action>
</gesture>
<!-- TouchPad 4指点击 打开侧边栏-->
<gesture type="TAP" fingers="4" direction="NoDirection" touchdevice="TouchPad">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>trigger</eventstage>
<repeat>false</repeat>
<modifiers>Super_L</modifiers>
<keys>a</keys>
<decreaseKeys>a</decreaseKeys>
</action>
</gesture>
<!-- TouchPad 4指左滑 切换到上一个虚拟桌面-->
<gesture type="SWIPE" fingers="4" direction="Left" touchdevice="TouchPad">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>trigger</eventstage>
<repeat>false</repeat>
<modifiers>Control_L+Alt_L</modifiers>
<keys>Right</keys>
<decreaseKeys>Right</decreaseKeys>
</action>
</gesture>
<!-- TouchPad 4指右滑 切换到下一个虚拟桌面-->
<gesture type="SWIPE" fingers="4" direction="Right" touchdevice="TouchPad">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>trigger</eventstage>
<repeat>false</repeat>
<modifiers>Control_L+Alt_L</modifiers>
<keys>Left</keys>
<decreaseKeys>Left</decreaseKeys>
</action>
</gesture>
<!-- TouchPad 4指下滑-->
<gesture type="SWIPE" fingers="4" direction="Down" touchdevice="TouchPad">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>trigger</eventstage>
<repeat>false</repeat>
<modifiers>Control_L+Alt_L</modifiers>
<keys>Up</keys>
<decreaseKeys>Up</decreaseKeys>
</action>
</gesture>
<!-- TouchPad 4指上滑-->
<gesture type="SWIPE" fingers="4" direction="Up" touchdevice="TouchPad">
<action actiontype="SEND_KEYS">
<enable>true</enable>
<eventstage>trigger</eventstage>
<repeat>false</repeat>
<modifiers>Control_L+Alt_L</modifiers>
<keys>Down</keys>
<decreaseKeys>Down</decreaseKeys>
</action>
</gesture>
</touchgesture>

View File

@ -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();
}

356
src/xmlreader.cpp Normal file
View File

@ -0,0 +1,356 @@
#include <QDebug>
#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<QString, QString> 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<int>(gestureType)) + "_" + fingers + "_" +
QString::number(static_cast<int>(gestureDirection));
}
void XMLReader::saveGestureConfig(QString gestureType, QString &fingers,
QString gestureDirection, QString touchDevice, ActionType actionType,
QMap<QString, QString> &actionSettings)
{
QList<QString> key;
key << gestureType << fingers << gestureDirection << touchDevice;
// = arrangeGestureConfigToEnumStr(gestureType, fingers, gestureDirection);
// GestureSpecific key(gestureType, fingers.toInt(), gestureDirection);
gestureConfig[key] = qMakePair(actionType, actionSettings);
}

117
src/xmlreader.h Normal file
View File

@ -0,0 +1,117 @@
#ifndef XMLREADER_H
#define XMLREADER_H
#include <QFile>
#include <QDir>
#include <QXmlStreamReader>
#include <QMap>
#include <QPair>
#include <gestures.h>
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<QString, QString> &actionSettings);
// 将配置文件中被指定为enable=true的手势注册到数据成员中去
QMap<QList<QString>, QPair<ActionType, QMap<QString, QString>>> 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<QList<QString>, QPair<ActionType, QMap<QString, QString>>> gestureConfig;
};
#endif // XMLREADER_H