mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-04-10 13:01:28 +03:00
fix(iPad): Magic Mouse, click (#14188)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -107,6 +107,8 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
// For mouse mode, we need to block the events when the cursor is in a blocked area.
|
// For mouse mode, we need to block the events when the cursor is in a blocked area.
|
||||||
// So we need to cache the last tap down position.
|
// So we need to cache the last tap down position.
|
||||||
Offset? _lastTapDownPositionForMouseMode;
|
Offset? _lastTapDownPositionForMouseMode;
|
||||||
|
// Cache global position for onTap (which lacks position info).
|
||||||
|
Offset? _lastTapDownGlobalPosition;
|
||||||
|
|
||||||
FFI get ffi => widget.ffi;
|
FFI get ffi => widget.ffi;
|
||||||
FfiModel get ffiModel => widget.ffiModel;
|
FfiModel get ffiModel => widget.ffiModel;
|
||||||
@@ -136,6 +138,7 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
|
|
||||||
onTapDown(TapDownDetails d) async {
|
onTapDown(TapDownDetails d) async {
|
||||||
lastDeviceKind = d.kind;
|
lastDeviceKind = d.kind;
|
||||||
|
_lastTapDownGlobalPosition = d.globalPosition;
|
||||||
if (isNotTouchBasedDevice()) {
|
if (isNotTouchBasedDevice()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -154,6 +157,10 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
if (isNotTouchBasedDevice()) {
|
if (isNotTouchBasedDevice()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Filter duplicate touch tap events on iOS (Magic Mouse issue).
|
||||||
|
if (inputModel.shouldIgnoreTouchTap(d.globalPosition)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (handleTouch) {
|
if (handleTouch) {
|
||||||
final isMoved =
|
final isMoved =
|
||||||
await ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
|
await ffi.cursorModel.move(d.localPosition.dx, d.localPosition.dy);
|
||||||
@@ -171,6 +178,11 @@ class _RawTouchGestureDetectorRegionState
|
|||||||
if (isNotTouchBasedDevice()) {
|
if (isNotTouchBasedDevice()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Filter duplicate touch tap events on iOS (Magic Mouse issue).
|
||||||
|
final lastPos = _lastTapDownGlobalPosition;
|
||||||
|
if (lastPos != null && inputModel.shouldIgnoreTouchTap(lastPos)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!handleTouch) {
|
if (!handleTouch) {
|
||||||
// Cannot use `_lastTapDownDetails` because Flutter calls `onTapUp` before `onTap`, clearing the cached details.
|
// Cannot use `_lastTapDownDetails` because Flutter calls `onTapUp` before `onTap`, clearing the cached details.
|
||||||
// Using `_lastTapDownPositionForMouseMode` instead.
|
// Using `_lastTapDownPositionForMouseMode` instead.
|
||||||
|
|||||||
@@ -826,6 +826,9 @@ class InputModel {
|
|||||||
Map<String, dynamic> _getMouseEvent(PointerEvent evt, String type) {
|
Map<String, dynamic> _getMouseEvent(PointerEvent evt, String type) {
|
||||||
final Map<String, dynamic> out = {};
|
final Map<String, dynamic> out = {};
|
||||||
|
|
||||||
|
bool hasStaleButtonsOnMouseUp =
|
||||||
|
type == _kMouseEventUp && evt.buttons == _lastButtons;
|
||||||
|
|
||||||
// Check update event type and set buttons to be sent.
|
// Check update event type and set buttons to be sent.
|
||||||
int buttons = _lastButtons;
|
int buttons = _lastButtons;
|
||||||
if (type == _kMouseEventMove) {
|
if (type == _kMouseEventMove) {
|
||||||
@@ -850,7 +853,7 @@ class InputModel {
|
|||||||
buttons = evt.buttons;
|
buttons = evt.buttons;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_lastButtons = evt.buttons;
|
_lastButtons = hasStaleButtonsOnMouseUp ? 0 : evt.buttons;
|
||||||
|
|
||||||
out['buttons'] = buttons;
|
out['buttons'] = buttons;
|
||||||
out['type'] = type;
|
out['type'] = type;
|
||||||
@@ -1218,6 +1221,28 @@ class InputModel {
|
|||||||
_trackpadLastDelta = Offset.zero;
|
_trackpadLastDelta = Offset.zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iOS Magic Mouse duplicate event detection.
|
||||||
|
// When using Magic Mouse on iPad, iOS may emit both mouse and touch events
|
||||||
|
// for the same click in certain areas (like top-left corner).
|
||||||
|
int _lastMouseDownTimeMs = 0;
|
||||||
|
ui.Offset _lastMouseDownPos = ui.Offset.zero;
|
||||||
|
|
||||||
|
/// Check if a touch tap event should be ignored because it's a duplicate
|
||||||
|
/// of a recent mouse event (iOS Magic Mouse issue).
|
||||||
|
bool shouldIgnoreTouchTap(ui.Offset pos) {
|
||||||
|
if (!isIOS) return false;
|
||||||
|
final nowMs = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
final dt = nowMs - _lastMouseDownTimeMs;
|
||||||
|
final distance = (_lastMouseDownPos - pos).distance;
|
||||||
|
// If touch tap is within 2000ms and 80px of the last mouse down,
|
||||||
|
// it's likely a duplicate event from the same Magic Mouse click.
|
||||||
|
if (dt >= 0 && dt < 2000 && distance < 80.0) {
|
||||||
|
debugPrint("shouldIgnoreTouchTap: IGNORED (dt=$dt, dist=$distance)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void onPointDownImage(PointerDownEvent e) {
|
void onPointDownImage(PointerDownEvent e) {
|
||||||
debugPrint("onPointDownImage ${e.kind}");
|
debugPrint("onPointDownImage ${e.kind}");
|
||||||
_stopFling = true;
|
_stopFling = true;
|
||||||
@@ -1227,6 +1252,13 @@ class InputModel {
|
|||||||
if (isViewOnly && !showMyCursor) return;
|
if (isViewOnly && !showMyCursor) return;
|
||||||
if (isViewCamera) return;
|
if (isViewCamera) return;
|
||||||
|
|
||||||
|
// Track mouse down events for duplicate detection on iOS.
|
||||||
|
final nowMs = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
if (e.kind == ui.PointerDeviceKind.mouse) {
|
||||||
|
_lastMouseDownTimeMs = nowMs;
|
||||||
|
_lastMouseDownPos = e.position;
|
||||||
|
}
|
||||||
|
|
||||||
if (_relativeMouse.enabled.value) {
|
if (_relativeMouse.enabled.value) {
|
||||||
_relativeMouse.updatePointerRegionTopLeftGlobal(e);
|
_relativeMouse.updatePointerRegionTopLeftGlobal(e);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user