Phase 3: 输入抽象层详细设计文档¶
文档信息¶
| 项目 | 内容 |
|---|---|
| 文档版本 | v1.0 |
| 创建日期 | 2026-02-20 |
| 阶段代号 | Phase 3 - 输入抽象层 |
| 预计周期 | 1~2 周 |
| 依赖阶段 | Phase 0, Phase 1, Phase 2 |
一、阶段目标¶
1.1 核心目标¶
屏蔽底层输入差异,统一触摸、物理按键、旋钮等输入事件,支持焦点导航模式。
1.2 具体交付物¶
- [ ]
InputManager统一分发层 - [ ]
TouchInputHandler触摸处理器 - [ ]
KeyInputHandler按键处理器 - [ ]
RotaryInputHandler旋钮处理器 - [ ]
FocusNavigator焦点导航器 - [ ] 单元测试
二、模块架构设计¶
2.1 整体架构图¶
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ (Widgets receive events) │
├─────────────────────────────────────────────────────────────┤
│ Input Manager │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 事件转换与分发 │ │
│ │ - 统一事件接口 │ │
│ │ - 事件过滤与拦截 │ │
│ │ - 手势识别 │ │
│ └────────────────────────────────────────────────────┘ │
├──────────────────────┬──────────────────────────────────────┤
│ Native Input Sources│ Qt Input Sources │
│ ┌────────────────┐ │ ┌─────────────────────────────────┐ │
│ │ evdev Devices │ │ │ QTouchEvent (linuxfb/eglfs) │ │
│ │ /dev/input/event│ │ │ QKeyEvent (键盘) │ │
│ │ GPIO Buttons │ │ │ QMouseEvent (鼠标) │ │
│ │ Rotary Encoder │ │ │ QWheelEvent (滚轮) │ │
│ └────────────────┘ │ └─────────────────────────────────┘ │
├──────────────────────┴──────────────────────────────────────┤
│ Kernel / Driver Layer │
│ evdev, GPIO, I2C, SPI input drivers │
└─────────────────────────────────────────────────────────────┘
2.2 文件结构¶
src/base/
├── include/CFDesktop/Base/Input/
│ ├── InputManager.h # 输入管理器主类
│ ├── InputEvent.h # 统一事件结构
│ ├── TouchInputHandler.h # 触摸处理器
│ ├── KeyInputHandler.h # 按键处理器
│ ├── RotaryInputHandler.h # 旋钮处理器
│ ├── GestureRecognizer.h # 手势识别器
│ ├── FocusNavigator.h # 焦点导航器
│ └── InputDevice.h # 输入设备抽象
│
└── src/input/
├── InputManager.cpp
├── TouchInputHandler.cpp
├── KeyInputHandler.cpp
├── RotaryInputHandler.cpp
├── GestureRecognizer.cpp
├── FocusNavigator.cpp
├── native/ # 平台特定实现
│ ├── EvdevDevice.cpp # Linux evdev 设备
│ ├── GPIOButton.cpp # GPIO 按键
│ └── RotaryEncoder.cpp # 旋转编码器
└── simulator/ # 模拟器支持
└── SimulatedInput.cpp
三、统一事件结构¶
3.1 输入事件类型¶
文件: include/CFDesktop/Base/Input/InputEvent.h
#pragma once
#include <QObject>
#include <QPoint>
#include <QEvent>
#include <QVariant>
namespace CFDesktop::Base {
/**
* @brief 输入设备类型
*/
enum class InputDeviceType {
Unknown,
Touchscreen, # 触摸屏
Mouse, # 鼠标
Keyboard, # 键盘
PhysicalButton, # 物理按键
RotaryEncoder, # 旋转编码器
Gamepad, # 游戏手柄
Custom # 自定义设备
};
/**
* @brief 统一输入事件基类
*/
class InputEvent {
public:
InputDeviceType deviceType = InputDeviceType::Unknown;
QString deviceId; # 设备 ID
quint64 timestamp; # 时间戳 (微秒)
virtual ~InputEvent() = default;
virtual QEvent* toQtEvent() const = 0;
};
/**
* @brief 触摸/指针位置事件
*/
class PointerEvent : public InputEvent {
public:
enum class Type {
Press,
Release,
Move,
Enter,
Leave
};
Type type;
QPointF position; # 屏幕坐标
int pointerId = -1; # 多点触摸 ID
qreal pressure = 0.0; # 压力 (0.0 - 1.0)
int buttons = 0; # 按钮状态
QEvent* toQtEvent() const override;
};
/**
* @brief 按键事件
*/
class KeyEvent : public InputEvent {
public:
enum class Type {
Press,
Release,
Repeat,
LongPress, # 长按
MultiPress # 多连击
};
Type type;
int keyCode; # Qt Key_Code
quint32 nativeCode; # 原生扫描码
QString text; # 文本内容
bool isAutoRepeat = false;
int clickCount = 0; # 连击计数
QEvent* toQtEvent() const override;
};
/**
* @brief 旋钮事件
*/
class RotaryEvent : public InputEvent {
public:
enum class Direction {
Clockwise,
CounterClockwise
};
Direction direction;
int steps = 1; # 步数
qreal angleDelta = 0.0; # 角度变化
bool isPressed = false; # 是否被按下
int clickCount = 0; # 旋转档位
QEvent* toQtEvent() const override;
};
/**
* @brief 手势事件
*/
class GestureEvent : public InputEvent {
public:
enum class Type {
Swipe,
Pinch,
Rotate,
LongPress,
DoubleTap,
Custom
};
Type type;
QVariantMap data; # 手势特定数据
QPointF centerPoint;
qreal angle = 0.0;
qreal scale = 1.0;
QPointF delta;
};
} // namespace CFDesktop::Base
四、输入管理器 (InputManager)¶
4.1 InputManager 类接口¶
文件: include/CFDesktop/Base/Input/InputManager.h
#pragma once
#include <QObject>
#include <QList>
#include <QPointer>
#include "InputEvent.h"
namespace CFDesktop::Base {
class InputDevice;
class TouchInputHandler;
class KeyInputHandler;
class RotaryInputHandler;
class GestureRecognizer;
/**
* @brief 输入管理器
*
* 统一管理所有输入设备,提供事件分发和过滤。
* 单例模式,在应用启动时初始化。
*/
class InputManager : public QObject {
Q_OBJECT
public:
static InputManager* instance();
/**
* @brief 初始化输入管理器
*/
bool initialize();
/**
* @brief 注册输入设备
*/
void registerDevice(InputDevice* device);
/**
* @brief 注销输入设备
*/
void unregisterDevice(InputDevice* device);
/**
* @brief 获取所有设备
*/
QList<InputDevice*> devices() const;
/**
* @brief 获取指定类型的设备
*/
QList<InputDevice*> devices(InputDeviceType type) const;
/**
* @brief 添加事件过滤器
*
* 过滤器返回 true 表示事件被拦截。
*/
void addEventFilter(QObject* filter);
/**
* @brief 移除事件过滤器
*/
void removeEventFilter(QObject* filter);
/**
* @brief 分发事件到指定对象
*/
bool dispatchEvent(QObject* receiver, InputEvent* event);
/**
* @brief 启用/禁用输入
*/
void setEnabled(bool enabled);
bool isEnabled() const;
signals:
/**
* @brief 设备连接信号
*/
void deviceConnected(InputDevice* device);
/**
* @brief 设备断开信号
*/
void deviceDisconnected(InputDevice* device);
/**
* @brief 输入事件信号(用于调试)
*/
void inputEventReceived(InputEvent* event);
protected:
bool eventFilter(QObject* watched, QEvent* event) override;
private:
InputManager(QObject* parent = nullptr);
~InputManager() = default;
void setupNativeInputDevices();
void processQtEvent(QEvent* event);
private:
static InputManager* s_instance;
QList<QPointer<InputDevice>> m_devices;
QList<QPointer<QObject>> m_eventFilters;
QPointer<TouchInputHandler> m_touchHandler;
QPointer<KeyInputHandler> m_keyHandler;
QPointer<RotaryInputHandler> m_rotaryHandler;
QPointer<GestureRecognizer> m_gestureRecognizer;
bool m_enabled = true;
};
} // namespace CFDesktop::Base
五、触摸处理器 (TouchInputHandler)¶
5.1 TouchInputHandler 类接口¶
文件: include/CFDesktop/Base/Input/TouchInputHandler.h
#pragma once
#include <QObject>
#include <QHash>
#include "InputEvent.h"
namespace CFDesktop::Base {
/**
* @brief 触摸点状态
*/
struct TouchPoint {
int id;
QPointF position;
QPointF startPosition;
qreal pressure = 0.0;
qint64 startTime = 0;
bool isPressed = false;
};
/**
* @brief 触摸处理器
*
* 处理 Qt 原生触摸事件,转换为统一事件格式。
* 支持多点触摸和手势识别。
*/
class TouchInputHandler : public QObject {
Q_OBJECT
public:
explicit TouchInputHandler(QObject* parent = nullptr);
~TouchInputHandler() = default;
/**
* @brief 处理 Qt 触摸事件
*/
bool handleEvent(QTouchEvent* event);
/**
* @brief 启用/禁用触摸输入
*/
void setEnabled(bool enabled);
bool isEnabled() const;
/**
* @brief 设置点击判定阈值
*
* 移动距离小于此值视为点击。
*/
void setClickThreshold(qreal pixels);
/**
* @brief 设置长按超时
*/
void setLongPressTimeout(int milliseconds);
/**
* @brief 获取当前触摸点
*/
QHash<int, TouchPoint> activeTouches() const;
signals:
/**
* @brief 触摸事件信号
*/
void touchEvent(PointerEvent* event);
/**
* @brief 单击信号
*/
void singleTap(const QPointF& position);
/**
* @brief 双击信号
*/
void doubleTap(const QPointF& position);
/**
* @brief 长按信号
*/
void longPress(const QPointF& position);
private:
void updateTouchPoints(const QTouchEvent::TouchPoint& point);
void detectGestures();
void resetGestureState();
private:
bool m_enabled = true;
qreal m_clickThreshold = 10.0; # dp(10)
int m_longPressTimeout = 500;
QHash<int, TouchPoint> m_activeTouches;
QTimer* m_longPressTimer;
QPointF m_lastPosition;
};
} // namespace CFDesktop::Base
5.2 触摸事件流程图¶
QTouchEvent
│
├─> TouchPress
│ │
│ ├─> 记录触摸点
│ ├─> 启动长按定时器
│ └─> emit touchEvent()
│
├─> TouchMove
│ │
│ ├─> 更新触摸点位置
│ ├─> 检测是否超过点击阈值
│ ├─> 如果超过阈值,取消长按定时器
│ └─> emit touchEvent()
│
└─> TouchRelease
│
├─> 计算移动距离
├─> 如果距离小且时间短:单击
├─> 如果是第二次单击:双击
├─> 如果定时器触发:长按
└─> 清理触摸点
六、按键处理器 (KeyInputHandler)¶
6.1 KeyInputHandler 类接口¶
文件: include/CFDesktop/Base/Input/KeyInputHandler.h
#pragma once
#include <QObject>
#include <QHash>
#include "InputEvent.h"
namespace CFDesktop::Base {
/**
* @brief 按键配置
*/
struct KeyConfig {
int keyCode; # Qt 键码
QString action; # 动作名称
int longPressThreshold = 500; # 长按阈值 (毫秒)
int repeatDelay = 300; # 重复延迟
int repeatInterval = 100; # 重复间隔
bool supportLongPress = true;
bool supportMultiPress = true;
int maxMultiPressCount = 3; # 最大连击数
};
/**
* @brief 按键处理器
*
* 处理键盘和物理按键输入,支持长按、连击检测。
*/
class KeyInputHandler : public QObject {
Q_OBJECT
public:
explicit KeyInputHandler(QObject* parent = nullptr);
~KeyInputHandler() = default;
/**
* @brief 处理 Qt 按键事件
*/
bool handleEvent(QKeyEvent* event);
/**
* @brief 注册按键配置
*/
void registerKey(const KeyConfig& config);
/**
* @brief 取消按键注册
*/
void unregisterKey(int keyCode);
/**
* @brief 映射物理按键到 Qt 键码
*
* 用于 evdev 扫描码到 Qt Key 的映射。
*/
void mapKey(int nativeScanCode, int qtKeyCode);
/**
* @brief 获取当前按下的按键
*/
QList<int> pressedKeys() const;
signals:
/**
* @brief 按键事件信号
*/
void keyEvent(KeyEvent* event);
/**
* @brief 动作触发信号
*/
void actionTriggered(const QString& action, int count);
/**
* @brief 长按信号
*/
void longPress(int keyCode);
/**
* @brief 组合键信号
*/
void combinationPressed(const QList<int>& keyCodes);
private:
void handleKeyPress(QKeyEvent* event);
void handleKeyRelease(QKeyEvent* event);
bool detectCombination();
private:
QHash<int, KeyConfig> m_keyConfigs;
QHash<int, int> m_scanCodeToQtKey;
QList<int> m_pressedKeys;
QHash<int, qint64> m_keyPressTime;
QHash<int, int> m_keyPressCount;
QHash<int, QTimer*> m_longPressTimers;
QHash<int, QTimer*> m_repeatTimers;
};
} // namespace CFDesktop::Base
6.2 预定义按键映射¶
namespace CFDesktop::Base {
/**
* @brief 预定义按键动作
*/
enum class StandardAction {
// 导航
Up,
Down,
Left,
Right,
Enter,
Back,
Home,
Menu,
// 媒体
VolumeUp,
VolumeDown,
Mute,
PlayPause,
Next,
Previous,
// 系统
Power,
Sleep,
WakeUp,
// 自定义
Custom1,
Custom2,
Custom3,
Custom4
};
/**
* @brief 标准按键映射表
*/
struct StandardKeyMapping {
static void initializeDefaults(KeyInputHandler* handler);
// GPIO 按键默认映射
static const QHash<int, StandardAction> gpioButtonMap;
};
} // namespace CFDesktop::Base
七、旋钮处理器 (RotaryInputHandler)¶
7.1 RotaryInputHandler 类接口¶
文件: include/CFDesktop/Base/Input/RotaryInputHandler.h
#pragma once
#include <QObject>
#include "InputEvent.h"
namespace CFDesktop::Base {
/**
* @brief 旋钮配置
*/
struct RotaryConfig {
int deviceId = 0;
int stepsPerRevolution = 24; # 每圈步数
bool hasButton = true; # 是否带按钮
qreal acceleration = 1.0; # 加速因子
int velocitySamples = 3; # 速度采样数
};
/**
* @brief 旋钮处理器
*
* 处理旋转编码器输入,支持加速和按钮模式。
*/
class RotaryInputHandler : public QObject {
Q_OBJECT
public:
explicit RotaryInputHandler(QObject* parent = nullptr);
~RotaryInputHandler() = default;
/**
* @brief 处理旋钮事件
*/
bool handleEvent(int delta, bool buttonPressed);
/**
* @brief 设置旋钮配置
*/
void setConfig(const RotaryConfig& config);
/**
* @brief 获取当前旋转速度
*
* 返回步数/秒
*/
qreal currentVelocity() const;
/**
* @brief 获取累计旋转角度
*/
qreal accumulatedAngle() const;
/**
* @brief 重置累计角度
*/
void resetAccumulatedAngle();
signals:
/**
* @brief 旋转事件信号
*/
void rotated(RotaryEvent* event);
/**
* @brief 简单旋转信号
*/
void stepped(int steps, RotaryEvent::Direction direction);
/**
* @brief 按钮按下信号
*/
void buttonPressed();
/**
* @brief 按钮释放信号
*/
void buttonReleased();
/**
* @brief 速度变化信号
*/
void velocityChanged(qreal velocity);
private:
void updateVelocity();
int calculateAcceleratedSteps(int rawSteps);
private:
RotaryConfig m_config;
qreal m_accumulatedAngle = 0.0;
QList<qint64> m_timestampSamples;
QList<int> m_deltaSamples;
qreal m_currentVelocity = 0.0;
bool m_buttonPressed = false;
};
} // namespace CFDesktop::Base
八、手势识别器 (GestureRecognizer)¶
8.1 GestureRecognizer 类接口¶
文件: include/CFDesktop/Base/Input/GestureRecognizer.h
#pragma once
#include <QObject>
#include <QPointF>
#include "InputEvent.h"
namespace CFDesktop::Base {
/**
* @brief 手势类型
*/
enum class GestureType {
None,
Swipe,
Pinch,
Rotate,
LongPress,
DoubleTap,
TwoFingerTap,
ThreeFingerSwipe,
Custom
};
/**
* @brief 手势方向
*/
enum class GestureDirection {
Up,
Down,
Left,
Right,
In,
Out,
Clockwise,
CounterClockwise
};
/**
* @brief 手势识别结果
*/
struct GestureResult {
GestureType type = GestureType::None;
GestureDirection direction = GestureDirection::Up;
QPointF centerPoint;
QPointF displacement; # 位移
qreal scale = 1.0; # 缩放比例
qreal angle = 0.0; # 旋转角度
qint64 duration = 0; # 持续时间
int fingerCount = 1; # 触摸点数
};
/**
* @brief 手势配置
*/
struct GestureConfig {
int swipeMinDistance = 50; # 最小滑动距离 (dp)
int swipeMaxDeviation = 30; # 最大偏离 (dp)
int swipeTimeout = 500; # 滑动超时 (毫秒)
int pinchMinDistance = 20; # 最小捏合距离
qreal pinchMinScale = 0.5; # 最小缩放比例
int longPressTimeout = 500; # 长按超时
int longPressMaxMovement = 10; # 长按最大移动
int doubleTapInterval = 300; # 双击间隔
int doubleTapMaxMovement = 20; # 双击最大移动
bool enableAll = true; # 启用所有手势
};
/**
* @brief 手势识别器
*
* 从触摸点流中识别常见手势。
*/
class GestureRecognizer : public QObject {
Q_OBJECT
public:
explicit GestureRecognizer(QObject* parent = nullptr);
~GestureRecognizer() = default;
/**
* @brief 处理触摸点更新
*/
void processTouchPoints(const QHash<int, TouchPoint>& points);
/**
* @brief 设置手势配置
*/
void setConfig(const GestureConfig& config);
/**
* @brief 启用/禁用特定手势
*/
void setGestureEnabled(GestureType type, bool enabled);
/**
* @brief 取消当前手势
*/
void cancelCurrentGesture();
signals:
/**
* @brief 手势识别信号
*/
void gestureRecognized(const GestureResult& gesture);
/**
* @brief 手势开始信号
*/
void gestureStarted(GestureType type);
/**
* @brief 手势更新信号
*/
void gestureUpdated(const GestureResult& gesture);
/**
* @brief 手势结束信号
*/
void gestureEnded(const GestureResult& gesture);
/**
* @brief 手势取消信号
*/
void gestureCancelled(GestureType type);
private:
bool detectSwipe();
bool detectPinch();
bool detectRotate();
bool detectLongPress();
bool detectDoubleTap();
GestureDirection detectDirection(const QPointF& delta);
void reset();
private:
GestureConfig m_config;
QSet<GestureType> m_enabledGestures;
QHash<int, TouchPoint> m_activeTouches;
QList<TouchPoint> m_startPoints;
GestureResult m_currentGesture;
qint64 m_gestureStartTime = 0;
bool m_isGestureActive = false;
};
} // namespace CFDesktop::Base
九、焦点导航器 (FocusNavigator)¶
9.1 FocusNavigator 类接口¶
文件: include/CFDesktop/Base/Input/FocusNavigator.h
#pragma once
#include <QObject>
#include <QWidget>
#include "InputEvent.h"
namespace CFDesktop::Base {
/**
* @brief 焦点移动方向
*/
enum class FocusDirection {
Up,
Down,
Left,
Right,
Next, # Tab 顺序下一个
Previous, # Tab 顺序上一个
First, # 第一个控件
Last # 最后一个控件
};
/**
* @brief 焦点策略
*/
enum class FocusPolicy {
// 自动策略
Auto,
// 严格策略:只跳到可视控件
Strict,
// 包含策略:包含隐藏/禁用控件
Inclusive,
// 循环策略:边界循环
Wrap,
// 自定义策略
Custom
};
/**
* @brief 焦点导航器
*
* 为无触摸设备提供键盘/按键焦点导航。
*/
class FocusNavigator : public QObject {
Q_OBJECT
public:
static FocusNavigator* instance();
/**
* @brief 设置导航根窗口
*/
void setRootWidget(QWidget* widget);
/**
* @brief 移动焦点
*/
bool moveFocus(FocusDirection direction);
/**
* @brief 设置焦点策略
*/
void setFocusPolicy(FocusPolicy policy);
/**
* @brief 添加焦点链
*
* 自定义焦点跳转顺序。
*/
void addFocusChain(QWidget* from, QWidget* to, FocusDirection direction);
/**
* @brief 移除焦点链
*/
void removeFocusChain(QWidget* from, FocusDirection direction);
/**
* @brief 设置当前焦点控件
*/
void setFocusWidget(QWidget* widget);
/**
* @brief 获取当前焦点控件
*/
QWidget* currentFocusWidget() const;
/**
* @brief 获取下一个焦点控件
*/
QWidget* nextFocusWidget(FocusDirection direction) const;
signals:
/**
* @brief 焦点变化信号
*/
void focusChanged(QWidget* oldWidget, QWidget* newWidget);
/**
* @brief 无焦点可移动信号
*/
void focusBlocked(QWidget* currentWidget, FocusDirection direction);
private:
FocusNavigator(QObject* parent = nullptr);
~FocusNavigator() = default;
QWidget* findNearestWidget(QWidget* start, FocusDirection direction) const;
bool isFocusable(QWidget* widget) const;
qreal calculateDistance(QWidget* from, QWidget* to, FocusDirection direction) const;
bool isInDirection(QWidget* from, QWidget* to, FocusDirection direction) const;
private:
static FocusNavigator* s_instance;
QPointer<QWidget> m_rootWidget;
QPointer<QWidget> m_currentWidget;
FocusPolicy m_policy = FocusPolicy::Auto;
QMultiHash<QWidget*, QPair<QWidget*, FocusDirection>> m_focusChains;
};
} // namespace CFDesktop::Base
9.2 焦点导航算法¶
QWidget* FocusNavigator::findNearestWidget(
QWidget* start,
FocusDirection direction
) const {
if (!start || !m_rootWidget) {
return nullptr;
}
QWidget* nearest = nullptr;
qreal minDistance = std::numeric_limits<qreal>::max();
// 获取所有可聚焦的子控件
QList<QWidget*> candidates = m_rootWidget->findChildren<QWidget*>();
QRect startRect = start->geometry();
for (QWidget* candidate : candidates) {
if (!isFocusable(candidate) || candidate == start) {
continue;
}
if (!isInDirection(start, candidate, direction)) {
continue;
}
qreal distance = calculateDistance(start, candidate, direction);
if (distance < minDistance) {
minDistance = distance;
nearest = candidate;
}
}
return nearest;
}
bool FocusNavigator::isInDirection(
QWidget* from,
QWidget* to,
FocusDirection direction
) const {
QRect fromRect = from->geometry();
QRect toRect = to->geometry();
QPointF fromCenter = fromRect.center();
QPointF toCenter = toRect.center();
switch (direction) {
case FocusDirection::Up:
return toCenter.y() < fromCenter.y();
case FocusDirection::Down:
return toCenter.y() > fromCenter.y();
case FocusDirection::Left:
return toCenter.x() < fromCenter.x();
case FocusDirection::Right:
return toCenter.x() > fromCenter.x();
default:
return true;
}
}
十、原生输入设备¶
10.1 Evdev 设备¶
文件: src/input/native/EvdevDevice.h
#pragma once
#include <QObject>
#include <QString>
#include "InputDevice.h"
namespace CFDesktop::Base {
/**
* @brief Linux evdev 输入设备
*
* 直接读取 /dev/input/eventX 设备。
* 支持按键、相对轴(鼠标)、绝对轴(触摸屏)。
*/
class EvdevDevice : public InputDevice {
Q_OBJECT
public:
/**
* @brief 设备类型
*/
enum class EvdevType {
Unknown,
Keyboard,
Mouse,
Touchscreen,
Joystick,
Accelerometer
};
/**
* @brief 打开设备
*/
static EvdevDevice* open(const QString& devicePath);
~EvdevDevice() override;
/**
* @brief 开始读取事件
*/
bool start() override;
/**
* @brief 停止读取事件
*/
void stop() override;
/**
* @brief 设备类型
*/
EvdevType evdevType() const;
/**
* @brief 设备名称
*/
QString deviceName() const;
signals:
/**
* @brief evdev 原始事件信号
*/
void rawEvent(quint16 type, quint16 code, qint32 value);
private:
EvdevDevice(const QString& path, int fd, QObject* parent = nullptr);
void readEvents();
void identifyDevice();
private:
QString m_devicePath;
int m_fd = -1;
EvdevType m_type = EvdevType::Unknown;
QString m_name;
QSocketNotifier* m_notifier = nullptr;
};
} // namespace CFDesktop::Base
10.2 GPIO 按键¶
文件: src/input/native/GPIOButton.h
#pragma once
#include <QObject>
#include "InputDevice.h"
namespace CFDesktop::Base {
/**
* @brief GPIO 按键设备
*
* 通过 sysfs 或 libgpiod 读取 GPIO 按键。
*/
class GPIOButton : public InputDevice {
Q_OBJECT
public:
/**
* @brief 打开 GPIO 按键
* @param gpioPin GPIO 引脚号
* @param activeLow 低电平有效
*/
static GPIOButton* open(int gpioPin, bool activeLow = true);
~GPIOButton() override;
bool start() override;
void stop() override;
/**
* @brief 关联的 Qt 键码
*/
int mappedKeyCode() const;
void setMappedKeyCode(int keyCode);
/**
* @brief 防抖延迟 (毫秒)
*/
int debounceDelay() const;
void setDebounceDelay(int milliseconds);
signals:
/**
* @brief 按钮按下信号
*/
void pressed();
/**
* @brief 按钮释放信号
*/
void released();
private:
GPIOButton(int gpioPin, QObject* parent = nullptr);
void handleEdgeDetect();
void setupGPIO();
private:
int m_gpioPin;
bool m_activeLow = true;
int m_mappedKeyCode = Qt::Key_unknown;
int m_debounceDelay = 50;
QFile m_valueFile;
QTimer* m_debounceTimer = nullptr;
bool m_lastState = false;
};
} // namespace CFDesktop::Base
10.3 旋转编码器¶
文件: src/input/native/RotaryEncoder.h
#pragma once
#include <QObject>
#include "InputDevice.h"
namespace CFDesktop::Base {
/**
* @brief 旋转编码器设备
*
* 支持 AB 相位编码器。
*/
class RotaryEncoder : public InputDevice {
Q_OBJECT
public:
/**
* @brief 打开旋转编码器
* @param pinA A 相引脚
* @param pinB B 相引脚
*/
static RotaryEncoder* open(int pinA, int pinB);
~RotaryEncoder() override;
bool start() override;
void stop() override;
/**
* @brief 每圈步数
*/
void setStepsPerRevolution(int steps);
/**
* @brief 是否反转方向
*/
void setInverted(bool inverted);
signals:
/**
* @brief 旋转信号
*/
void rotated(int steps);
/**
* @brief 按钮信号(如果带按钮)
*/
void buttonPressed();
void buttonReleased();
private:
RotaryEncoder(int pinA, int pinB, QObject* parent = nullptr);
void decodeState(int stateA, int stateB);
private:
int m_pinA;
int m_pinB;
int m_stepsPerRevolution = 24;
bool m_inverted = false;
int m_lastState = 0;
int m_position = 0;
};
} // namespace CFDesktop::Base
十一、模拟器支持¶
11.1 模拟输入配置¶
namespace CFDesktop::Base {
/**
* @brief 模拟输入配置
*/
struct SimulatedInputConfig {
// 鼠标模拟触摸
bool mouseEmulatesTouch = true;
// 键盘模拟方向键
QHash<int, FocusDirection> keyToDirection = {
{ Qt::Key_Up, FocusDirection::Up },
{ Qt::Key_Down, FocusDirection::Down },
{ Qt::Key_Left, FocusDirection::Left },
{ Qt::Key_Right, FocusDirection::Right },
{ Qt::Key_Tab, FocusDirection::Next },
{ Qt::Key_Backtab, FocusDirection::Previous }
};
// 触摸视觉反馈
bool showTouchRipple = true;
int rippleDuration = 300;
// 旋钮模拟
bool mouseWheelEmulatesRotary = true;
qreal wheelScale = 1.0;
};
} // namespace CFDesktop::Base
十二、详细任务清单¶
12.1 Week 1: 核心处理器¶
Day 1-2: InputManager 基础¶
- [ ] 创建 InputManager 类
- [ ] 实现设备注册/注销
- [ ] 实现事件分发机制
- [ ] 添加事件过滤器支持
Day 3: TouchInputHandler¶
- [ ] 创建 TouchInputHandler 类
- [ ] 实现触摸点跟踪
- [ ] 实现单击/双击检测
- [ ] 实现长按检测
Day 4: KeyInputHandler¶
- [ ] 创建 KeyInputHandler 类
- [ ] 实现按键状态跟踪
- [ ] 实现长按检测
- [ ] 实现连击检测
Day 5: RotaryInputHandler¶
- [ ] 创建 RotaryInputHandler 类
- [ ] 实现旋转解码
- [ ] 实现速度计算
- [ ] 实现加速功能
12.2 Week 2: 手势与导航¶
Day 1-2: GestureRecognizer¶
- [ ] 创建 GestureRecognizer 类
- [ ] 实现滑动手势
- [ ] 实现捏合手势
- [ ] 实现旋转手势
Day 3: FocusNavigator¶
- [ ] 创建 FocusNavigator 类
- [ ] 实现方向导航算法
- [ ] 实现焦点链自定义
- [ ] 实现边界策略
Day 4: 原生设备¶
- [ ] 实现 EvdevDevice
- [ ] 实现 GPIOButton
- [ ] 实现 RotaryEncoder
Day 5: 测试与集成¶
- [ ] 编写单元测试
- [ ] 集成测试
- [ ] 性能测试
十三、验收标准¶
13.1 功能验收¶
- [ ] 触摸输入正常响应
- [ ] 手势识别准确率 > 95%
- [ ] 焦点导航无死循环
- [ ] 原生设备正常读取
13.2 性能验收¶
- [ ] 事件延迟 < 16ms
- [ ] CPU 占用 < 5%
13.3 兼容性验收¶
- [ ] 模拟器和真机行为一致
十四、下一步行动¶
完成 Phase 3 后,进入 Phase 4: Shell UI 主体。