跳到主要内容

按键过滤器 (keyfilter)

按键过滤器,基于状态机将原始按下/抬起事件转换为高级按键语义(单击、双击、长按、重复)。

架构

feed(原始事件) → 环形队列 → poll(当前时间) → 过滤后事件

输入与处理解耦:keyfilter_feed 将原始事件入队,keyfilter_poll 从队列消费并结合时间判定产生过滤后事件。调用者需以固定频率轮询 keyfilter_poll 以触发超时类事件(CLICK、LONGPRESS、REPEAT)。

API

keyfilter_init

void keyfilter_init(struct keyfilter_t * filter);

初始化过滤器,默认超时:click=200ms, longpress=500ms, repeat=100ms。

keyfilter_settimeout

void keyfilter_settimeout(struct keyfilter_t * filter, int click, int longpress, int repeat);

设置超时参数(单位:毫秒),带最小值保护:click >= 20, longpress >= 50, repeat >= 0。repeat 设为 0 禁用重复事件。

keyfilter_feed

int keyfilter_feed(struct keyfilter_t * filter, ktime_t timestamp, int pressed);

将原始按键事件入队。pressed 为 1 表示按下,0 表示抬起。

连续相同状态的事件会被去重丢弃(不入队),并返回 1。仅发生状态翻转(按下后抬起、或抬起后按下)的事件才真正入队,避免硬件抖动产生的重复事件占满队列。队列已满时返回 0。

keyfilter_poll

enum keyfilter_type_t keyfilter_poll(struct keyfilter_t * filter, ktime_t now);

从队列消费事件并结合 now(当前绝对时间)进行状态判定,返回过滤后的事件类型。需高频调用以保证超时判定的及时性。

keyfilter_clear

void keyfilter_clear(struct keyfilter_t * filter);

重置过滤器状态,清空队列。

事件类型

类型说明
KEYFILTER_TYPE_NONE0无事件
KEYFILTER_TYPE_DOWN1按下
KEYFILTER_TYPE_UP2抬起
KEYFILTER_TYPE_CLICK3单击(抬起后超时确认)
KEYFILTER_TYPE_DBLCLICK4双击
KEYFILTER_TYPE_LONGPRESS5长按
KEYFILTER_TYPE_REPEAT6长按重复

状态机

按下 抬起 超时(click)
IDLE ──────────→ PRESSED ──────────→ RELEASED ──────────→ IDLE
│ ↑
│ 超时(longpress) │
↓ │
HELD ──────────────────────────────────────┘
↑ 抬起

│ 超时(longpress) 按下
REPRESSED ←──────── RELEASED

│ 抬起

IDLE (pending=DBLCLICK)
状态含义
IDLE空闲,等待按下
PRESSED已按下,等待抬起或长按超时
RELEASED已抬起,等待再次按下(双击)或超时确认单击
REPRESSED短时间内再次按下,等待抬起确认双击或长按超时
HELD长按保持中,等待抬起或重复超时

事件序列示例

单击

操作: 按下──────抬起──────────(200ms超时)
事件: DOWN UP CLICK

双击

操作: 按下──抬起──按下──抬起
事件: DOWN UP DOWN UP DBLCLICK

单击与双击互斥:双击时不会产生 CLICK,CLICK 需等待超时确认不是双击后才发出。

长按 + 重复

操作: 按下──────────────(500ms)────(100ms)──(100ms)──抬起
事件: DOWN LONGPRESS REPEAT REPEAT UP

使用示例

struct keyfilter_t kf;
keyfilter_init(&kf);
keyfilter_settimeout(&kf, 200, 500, 100);

/* 按键回调中 */
keyfilter_feed(&kf, timestamp, pressed);

/* 定时轮询中(建议 >= 50Hz) */
ktime_t now = ktime_get();
enum keyfilter_type_t t = keyfilter_poll(&kf, now);
if(t != KEYFILTER_TYPE_NONE)
handle_event(t);