From e5aa0c83bda850f8642801ec1ac7efeb3f616038 Mon Sep 17 00:00:00 2001 From: qiin <414382190@qq.com> Date: Thu, 18 Jun 2026 17:17:58 +0800 Subject: [PATCH] feat(input): add low latency touch toggle --- entry/src/main/ets/model/StreamConfig.ets | 2 + entry/src/main/ets/pages/SettingsPageV2.ets | 12 +++++ entry/src/main/ets/pages/StreamPage.ets | 1 + .../src/main/ets/service/SettingsService.ets | 5 ++ .../ets/service/input/TouchInputHandler.ets | 52 +++++++++++++++---- .../service/input/TrackpadGestureHandler.ets | 33 +++++++++--- .../service/streaming/StreamInputHandler.ets | 1 - 7 files changed, 86 insertions(+), 20 deletions(-) diff --git a/entry/src/main/ets/model/StreamConfig.ets b/entry/src/main/ets/model/StreamConfig.ets index b0d8d30..15dc5c6 100644 --- a/entry/src/main/ets/model/StreamConfig.ets +++ b/entry/src/main/ets/model/StreamConfig.ets @@ -70,6 +70,7 @@ export interface StreamConfig { // 鼠标/触控设置 showLocalCursor: boolean; touchscreenTrackpad: boolean; + lowLatencyTouch: boolean; enableDoubleClickDrag: boolean; doubleTapTimeThreshold: number; @@ -253,6 +254,7 @@ export function getDefaultStreamConfig(): StreamConfig { // 鼠标/触控设置 showLocalCursor: false, touchscreenTrackpad: false, + lowLatencyTouch: false, enableDoubleClickDrag: false, doubleTapTimeThreshold: 125, diff --git a/entry/src/main/ets/pages/SettingsPageV2.ets b/entry/src/main/ets/pages/SettingsPageV2.ets index 2c2310d..ce2698c 100644 --- a/entry/src/main/ets/pages/SettingsPageV2.ets +++ b/entry/src/main/ets/pages/SettingsPageV2.ets @@ -213,6 +213,7 @@ struct SettingsPageV2 { // 输入 - 鼠标/触控 @State showLocalCursor: boolean = false; // 显示本地指针(双光标模式) @State touchscreenTrackpad: boolean = false; + @State lowLatencyTouch: boolean = false; @State enableDoubleClickDrag: boolean = false; @State doubleTapTimeThreshold: number = 125; @State mouseNavButtons: boolean = false; @@ -561,6 +562,7 @@ struct SettingsPageV2 { // 输入 - 鼠标/触控 this.showLocalCursor = await this.loadBoolean(SettingsKeys.SHOW_LOCAL_CURSOR, false); this.touchscreenTrackpad = await this.loadBoolean(SettingsKeys.TOUCHSCREEN_TRACKPAD, false); + this.lowLatencyTouch = await this.loadBoolean(SettingsKeys.LOW_LATENCY_TOUCH, false); this.enableDoubleClickDrag = await this.loadBoolean(SettingsKeys.ENABLE_DOUBLE_CLICK_DRAG, false); this.doubleTapTimeThreshold = await PreferencesUtil.get(SettingsKeys.DOUBLE_TAP_TIME_THRESHOLD, 125); this.mouseNavButtons = await this.loadBoolean(SettingsKeys.MOUSE_NAV_BUTTONS, false); @@ -1697,6 +1699,16 @@ struct SettingsPageV2 { this.saveSetting(SettingsKeys.LAST_TOUCH_MODE, ''); } }, + { + title: '低延迟触摸参数', + subtitle: '缩短触摸点击确认和释放等待,响应更快但更敏感', + type: 'toggle', + value: this.lowLatencyTouch, + action: () => { + this.lowLatencyTouch = !this.lowLatencyTouch; + this.saveSetting(SettingsKeys.LOW_LATENCY_TOUCH, this.lowLatencyTouch); + } + }, { title: '双击拖动', subtitle: '双击并按住可拖动', diff --git a/entry/src/main/ets/pages/StreamPage.ets b/entry/src/main/ets/pages/StreamPage.ets index f90a9c7..33a89dc 100644 --- a/entry/src/main/ets/pages/StreamPage.ets +++ b/entry/src/main/ets/pages/StreamPage.ets @@ -1349,6 +1349,7 @@ struct StreamPage { if (streamConfig) { this.touchInputHandler.setDoubleTapTimeThreshold(streamConfig.doubleTapTimeThreshold); this.touchInputHandler.setEnableDoubleClickDrag(streamConfig.enableDoubleClickDrag); + this.touchInputHandler.setLowLatencyTouch(streamConfig.lowLatencyTouch); } // 设置连接终止回调 diff --git a/entry/src/main/ets/service/SettingsService.ets b/entry/src/main/ets/service/SettingsService.ets index f6a6531..8112f57 100644 --- a/entry/src/main/ets/service/SettingsService.ets +++ b/entry/src/main/ets/service/SettingsService.ets @@ -108,6 +108,7 @@ export class SettingsKeys { // 输入 - 鼠标/触控 static readonly SHOW_LOCAL_CURSOR: string = 'settings_show_local_cursor'; static readonly TOUCHSCREEN_TRACKPAD: string = 'settings_touchscreen_trackpad'; + static readonly LOW_LATENCY_TOUCH: string = 'settings_low_latency_touch'; static readonly ENABLE_DOUBLE_CLICK_DRAG: string = 'settings_enable_double_click_drag'; static readonly DOUBLE_TAP_TIME_THRESHOLD: string = 'settings_double_tap_time_threshold'; static readonly MOUSE_NAV_BUTTONS: string = 'settings_mouse_nav_buttons'; @@ -221,6 +222,7 @@ export interface InputSettings { // 鼠标/触控 showLocalCursor: boolean; touchscreenTrackpad: boolean; + lowLatencyTouch: boolean; enableDoubleClickDrag: boolean; doubleTapTimeThreshold: number; mouseNavButtons: boolean; @@ -617,6 +619,7 @@ export class SettingsService { // 获取鼠标/触控设置 const showLocalCursor = await this.getBoolean(SettingsKeys.SHOW_LOCAL_CURSOR, false); // 默认不显示本地光标 const touchscreenTrackpad = await this.getBoolean(SettingsKeys.TOUCHSCREEN_TRACKPAD, false); + const lowLatencyTouch = await this.getBoolean(SettingsKeys.LOW_LATENCY_TOUCH, false); const enableDoubleClickDrag = await this.getBoolean(SettingsKeys.ENABLE_DOUBLE_CLICK_DRAG, false); const doubleTapTimeThreshold = await this.getNumber(SettingsKeys.DOUBLE_TAP_TIME_THRESHOLD, 125); @@ -698,6 +701,7 @@ export class SettingsService { // 鼠标/触控设置 showLocalCursor: showLocalCursor, touchscreenTrackpad: touchscreenTrackpad, + lowLatencyTouch: lowLatencyTouch, enableDoubleClickDrag: enableDoubleClickDrag, doubleTapTimeThreshold: doubleTapTimeThreshold, @@ -788,6 +792,7 @@ export class SettingsService { // 鼠标/触控 showLocalCursor: await this.getBoolean(SettingsKeys.SHOW_LOCAL_CURSOR, false), touchscreenTrackpad: await this.getBoolean(SettingsKeys.TOUCHSCREEN_TRACKPAD, false), + lowLatencyTouch: await this.getBoolean(SettingsKeys.LOW_LATENCY_TOUCH, false), enableDoubleClickDrag: await this.getBoolean(SettingsKeys.ENABLE_DOUBLE_CLICK_DRAG, false), doubleTapTimeThreshold: await this.getNumber(SettingsKeys.DOUBLE_TAP_TIME_THRESHOLD, 125), mouseNavButtons: await this.getBoolean(SettingsKeys.MOUSE_NAV_BUTTONS, false), diff --git a/entry/src/main/ets/service/input/TouchInputHandler.ets b/entry/src/main/ets/service/input/TouchInputHandler.ets index f9a5135..8592b7c 100644 --- a/entry/src/main/ets/service/input/TouchInputHandler.ets +++ b/entry/src/main/ets/service/input/TouchInputHandler.ets @@ -162,13 +162,13 @@ export class TouchInputHandler { private mouseCancelled: boolean = false; /** 是否已确认为点击(死区时间/距离超过后) */ private mouseConfirmedTap: boolean = false; - /** 是否已确认为长按(650ms 后切换为右键) */ + /** 是否已确认为长按(默认 650ms 后切换为右键) */ private mouseConfirmedLongPress: boolean = false; /** 长按定时器 */ private mouseLongPressTimer: number = -1; - /** 死区定时器(100ms 后确认点击) */ + /** 死区定时器(默认 100ms 后确认点击) */ private mouseTapDownTimer: number = -1; - /** 左键释放延迟定时器(快速点击时延迟 100ms 释放) */ + /** 左键释放延迟定时器(快速点击时默认延迟 100ms 释放) */ private mouseLeftButtonUpTimer: number = -1; /** 主手指 ID */ private mousePrimaryFingerId: number = -1; @@ -184,14 +184,22 @@ export class TouchInputHandler { private coordinateTransform: ((x: number, y: number) => PointXY) | null = null; // 鼠标模式常量(与 Android AbsoluteTouchContext 一致) - /** 长按时间阈值(ms) */ - private static readonly MOUSE_LONG_PRESS_TIME = 650; + /** 标准长按时间阈值(ms) */ + private static readonly MOUSE_LONG_PRESS_TIME_NORMAL = 650; + /** 低延迟长按时间阈值(ms) */ + private static readonly MOUSE_LONG_PRESS_TIME_LOW_LATENCY = 500; /** 长按移动距离阈值(px)—— 超过后取消长按 */ private static readonly MOUSE_LONG_PRESS_DISTANCE = 30; - /** 死区时间阈值(ms)—— 按下后此时间内不响应移动 */ - private static readonly MOUSE_DEADZONE_TIME = 100; + /** 标准死区时间阈值(ms)—— 按下后此时间内不响应移动 */ + private static readonly MOUSE_DEADZONE_TIME_NORMAL = 100; + /** 低延迟死区时间阈值(ms) */ + private static readonly MOUSE_DEADZONE_TIME_LOW_LATENCY = 50; /** 死区距离阈值(px)—— 移动超过此距离立即确认点击 */ private static readonly MOUSE_DEADZONE_DISTANCE = 20; + /** 标准点击释放延迟(ms) */ + private static readonly MOUSE_CLICK_RELEASE_DELAY_NORMAL = 100; + /** 低延迟点击释放延迟(ms) */ + private static readonly MOUSE_CLICK_RELEASE_DELAY_LOW_LATENCY = 40; /** 双击时间阈值(ms)—— 两次点击间隔小于此值视为双击 */ private mouseDoubleTapTime: number = 250; /** 双击距离阈值(px)—— 两次点击位置距离小于此值视为双击 */ @@ -200,6 +208,12 @@ export class TouchInputHandler { private static readonly MOUSE_SCROLL_SPEED_FACTOR = 3; /** 是否启用双击拖动 */ private enableDoubleClickDrag: boolean = false; + /** 当前长按时间阈值(ms) */ + private mouseLongPressTime: number = TouchInputHandler.MOUSE_LONG_PRESS_TIME_NORMAL; + /** 当前死区时间阈值(ms) */ + private mouseDeadzoneTime: number = TouchInputHandler.MOUSE_DEADZONE_TIME_NORMAL; + /** 当前点击释放延迟(ms) */ + private mouseClickReleaseDelay: number = TouchInputHandler.MOUSE_CLICK_RELEASE_DELAY_NORMAL; // 默认配置 private static readonly DEFAULT_SENSITIVITY = 1.5; @@ -235,6 +249,22 @@ export class TouchInputHandler { this.enableDoubleClickDrag = enable; } + /** + * 配置低延迟触摸参数(从设置读取后调用) + */ + setLowLatencyTouch(enable: boolean): void { + this.mouseLongPressTime = enable + ? TouchInputHandler.MOUSE_LONG_PRESS_TIME_LOW_LATENCY + : TouchInputHandler.MOUSE_LONG_PRESS_TIME_NORMAL; + this.mouseDeadzoneTime = enable + ? TouchInputHandler.MOUSE_DEADZONE_TIME_LOW_LATENCY + : TouchInputHandler.MOUSE_DEADZONE_TIME_NORMAL; + this.mouseClickReleaseDelay = enable + ? TouchInputHandler.MOUSE_CLICK_RELEASE_DELAY_LOW_LATENCY + : TouchInputHandler.MOUSE_CLICK_RELEASE_DELAY_NORMAL; + this.trackpad.updateLatencyMode(enable); + } + /** * 设置坐标变换函数(用于画面缩放/平移状态下的坐标修正) * 传入 null 清除变换 @@ -574,7 +604,7 @@ export class TouchInputHandler { this.nativeInput.sendMouseButton(BUTTON_ACTION_RELEASE, BUTTON_LEFT); } this.nativeInput.sendMouseButton(BUTTON_ACTION_PRESS, BUTTON_RIGHT); - }, TouchInputHandler.MOUSE_LONG_PRESS_TIME); + }, this.mouseLongPressTime); } private cancelMouseLongPressTimer(): void { @@ -588,7 +618,7 @@ export class TouchInputHandler { this.cancelMouseTapDownTimer(); this.mouseTapDownTimer = setTimeout(() => { this.mouseTapConfirmed(); - }, TouchInputHandler.MOUSE_DEADZONE_TIME); + }, this.mouseDeadzoneTime); } private cancelMouseTapDownTimer(): void { @@ -738,7 +768,7 @@ export class TouchInputHandler { } else if (this.mouseConfirmedTap) { this.nativeInput.sendMouseButton(BUTTON_ACTION_RELEASE, BUTTON_LEFT); } else { - // 死区时间内完成的快速点击:立即按下,延迟 100ms 释放 + // 死区时间内完成的快速点击:立即按下,按当前触摸参数延迟释放 this.mouseTapConfirmed(); if (this.mouseLeftButtonUpTimer !== -1) { clearTimeout(this.mouseLeftButtonUpTimer); @@ -746,7 +776,7 @@ export class TouchInputHandler { this.mouseLeftButtonUpTimer = setTimeout(() => { this.nativeInput.sendMouseButton(BUTTON_ACTION_RELEASE, BUTTON_LEFT); this.mouseLeftButtonUpTimer = -1; - }, 100); + }, this.mouseClickReleaseDelay); } } diff --git a/entry/src/main/ets/service/input/TrackpadGestureHandler.ets b/entry/src/main/ets/service/input/TrackpadGestureHandler.ets index 3e074dc..ea02e01 100644 --- a/entry/src/main/ets/service/input/TrackpadGestureHandler.ets +++ b/entry/src/main/ets/service/input/TrackpadGestureHandler.ets @@ -61,18 +61,26 @@ const TAP_MOVEMENT_THRESHOLD = 40; const TAP_DISTANCE_THRESHOLD = 50; /** 轻触最大时长 (ms) */ const TAP_TIME_THRESHOLD = 250; -/** 长按拖拽时间 (ms) */ -const DRAG_TIME_THRESHOLD = 650; +/** 标准长按拖拽时间 (ms) */ +const DRAG_TIME_THRESHOLD_NORMAL = 650; +/** 低延迟长按拖拽时间 (ms) */ +const DRAG_TIME_THRESHOLD_LOW_LATENCY = 500; /** 双击待定态下开始拖拽的移动阈值 (px) */ const DRAG_START_THRESHOLD = 10; -/** 双击按住确认拖拽时间 (ms) */ -const DOUBLE_TAP_HOLD_THRESHOLD = 200; +/** 标准双击按住确认拖拽时间 (ms) */ +const DOUBLE_TAP_HOLD_THRESHOLD_NORMAL = 200; +/** 低延迟双击按住确认拖拽时间 (ms) */ +const DOUBLE_TAP_HOLD_THRESHOLD_LOW_LATENCY = 120; /** 双击两次位置最大偏差 (px) */ const DOUBLE_TAP_MOVEMENT_THRESHOLD = 40; /** 双指移动阈值 — 超过后视为滚轮 (px) */ const TWO_FINGER_MOVE_THRESHOLD = 30; /** 滚轮速度因子 */ const SCROLL_SPEED_FACTOR = 5; +/** 标准点击释放延迟 (ms) */ +const CLICK_RELEASE_DELAY_NORMAL = 100; +/** 低延迟点击释放延迟 (ms) */ +const CLICK_RELEASE_DELAY_LOW_LATENCY = 40; export class TrackpadGestureHandler { private sink: TrackpadInputSink; @@ -106,6 +114,9 @@ export class TrackpadGestureHandler { private twoFingerStartX = 0; private twoFingerStartY = 0; private confirmedScroll = false; + private dragTimeThreshold: number = DRAG_TIME_THRESHOLD_NORMAL; + private doubleTapHoldThreshold: number = DOUBLE_TAP_HOLD_THRESHOLD_NORMAL; + private clickReleaseDelay: number = CLICK_RELEASE_DELAY_NORMAL; constructor(sink: TrackpadInputSink, config: TrackpadConfig) { this.sink = sink; @@ -120,6 +131,12 @@ export class TrackpadGestureHandler { if (enableDoubleClickDrag !== undefined) this.config.enableDoubleClickDrag = enableDoubleClickDrag; } + updateLatencyMode(enable: boolean): void { + this.dragTimeThreshold = enable ? DRAG_TIME_THRESHOLD_LOW_LATENCY : DRAG_TIME_THRESHOLD_NORMAL; + this.doubleTapHoldThreshold = enable ? DOUBLE_TAP_HOLD_THRESHOLD_LOW_LATENCY : DOUBLE_TAP_HOLD_THRESHOLD_NORMAL; + this.clickReleaseDelay = enable ? CLICK_RELEASE_DELAY_LOW_LATENCY : CLICK_RELEASE_DELAY_NORMAL; + } + // ==================== 事件入口 ==================== handleEvent(event: TouchEvent): void { @@ -437,7 +454,7 @@ export class TrackpadGestureHandler { private sendClick(button: number): void { this.sink.sendMouseButton(BUTTON_ACTION_PRESS, button); - setTimeout(() => this.sink.sendMouseButton(BUTTON_ACTION_RELEASE, button), 100); + setTimeout(() => this.sink.sendMouseButton(BUTTON_ACTION_RELEASE, button), this.clickReleaseDelay); } private sendRightClick(): void { @@ -449,7 +466,7 @@ export class TrackpadGestureHandler { this.sink.sendMouseButton(BUTTON_ACTION_PRESS, BUTTON_LEFT); this.sink.sendMouseButton(BUTTON_ACTION_RELEASE, BUTTON_LEFT); this.sink.sendMouseButton(BUTTON_ACTION_PRESS, BUTTON_LEFT); - setTimeout(() => this.sink.sendMouseButton(BUTTON_ACTION_RELEASE, BUTTON_LEFT), 100); + setTimeout(() => this.sink.sendMouseButton(BUTTON_ACTION_RELEASE, BUTTON_LEFT), this.clickReleaseDelay); } // ---- 手指管理 ---- @@ -472,7 +489,7 @@ export class TrackpadGestureHandler { if (this.confirmedMove || this.maxCountInGesture !== 1) return; this.confirmedDrag = true; this.sink.sendMouseButton(BUTTON_ACTION_PRESS, BUTTON_LEFT); - }, DRAG_TIME_THRESHOLD); + }, this.dragTimeThreshold); } private cancelDragTimer(): void { @@ -490,7 +507,7 @@ export class TrackpadGestureHandler { this.isDoubleClickDrag = true; this.confirmedMove = true; this.sink.sendMouseButton(BUTTON_ACTION_PRESS, BUTTON_LEFT); - }, DOUBLE_TAP_HOLD_THRESHOLD); + }, this.doubleTapHoldThreshold); } private cancelDoubleTapHoldTimer(): void { diff --git a/entry/src/main/ets/service/streaming/StreamInputHandler.ets b/entry/src/main/ets/service/streaming/StreamInputHandler.ets index 3143d11..32d7c12 100644 --- a/entry/src/main/ets/service/streaming/StreamInputHandler.ets +++ b/entry/src/main/ets/service/streaming/StreamInputHandler.ets @@ -332,7 +332,6 @@ export class StreamInputHandler { // 归一化后的值 / scrollStep 为每格原始值,× 8 = 120 units(Windows WHEEL_DELTA) if (vertical !== 0) { const scrollAmount = Math.round(-vertical / scrollStep * 8); - console.info(`滚轮: vertical=${vertical.toFixed(2)}, scrollStep=${scrollStep}, send=${scrollAmount}`); MoonBridge.sendMouseHighResScroll(scrollAmount); }