mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-22 04:23:06 +08:00
[fix] prevent iOS relative cursor jumps after movement
This commit is contained in:
@@ -58,6 +58,7 @@ struct RemoteTouchInputView: UIViewRepresentable {
|
|||||||
let videoSize: CGSize
|
let videoSize: CGSize
|
||||||
let controlMode: MouseControlMode
|
let controlMode: MouseControlMode
|
||||||
let remoteCursorPosition: CGPoint?
|
let remoteCursorPosition: CGPoint?
|
||||||
|
let remoteCursorPositionRevision: UInt64
|
||||||
let viewportScale: CGFloat
|
let viewportScale: CGFloat
|
||||||
let viewportOffset: CGSize
|
let viewportOffset: CGSize
|
||||||
let viewportChanged: (CGFloat, CGSize) -> Void
|
let viewportChanged: (CGFloat, CGSize) -> Void
|
||||||
@@ -81,7 +82,8 @@ struct RemoteTouchInputView: UIViewRepresentable {
|
|||||||
view.onLeftUp = leftUp
|
view.onLeftUp = leftUp
|
||||||
view.onRightClick = rightClick
|
view.onRightClick = rightClick
|
||||||
view.onScroll = scroll
|
view.onScroll = scroll
|
||||||
view.synchronizeRemoteCursor(remoteCursorPosition)
|
view.synchronizeRemoteCursor(remoteCursorPosition,
|
||||||
|
revision: remoteCursorPositionRevision)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,6 +110,7 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
|
|||||||
|
|
||||||
private var relativeCursorPoint: (Float, Float) = (0.5, 0.5)
|
private var relativeCursorPoint: (Float, Float) = (0.5, 0.5)
|
||||||
private var lastRemoteCursorPoint: (Float, Float)?
|
private var lastRemoteCursorPoint: (Float, Float)?
|
||||||
|
private var lastRemoteCursorRevision: UInt64?
|
||||||
private var relativePanActive = false
|
private var relativePanActive = false
|
||||||
private var heldDragPoint: (Float, Float)?
|
private var heldDragPoint: (Float, Float)?
|
||||||
private var heldDragLastLocation: CGPoint?
|
private var heldDragLastLocation: CGPoint?
|
||||||
@@ -184,7 +187,12 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
|
|||||||
viewportOffset = constrainedViewportOffset(offset, scale: nextScale)
|
viewportOffset = constrainedViewportOffset(offset, scale: nextScale)
|
||||||
}
|
}
|
||||||
|
|
||||||
func synchronizeRemoteCursor(_ position: CGPoint?) {
|
func synchronizeRemoteCursor(_ position: CGPoint?, revision: UInt64) {
|
||||||
|
// SwiftUI refreshes this view for video frames and shape-only feedback
|
||||||
|
// too. Consume each position update once, even when it arrives during
|
||||||
|
// local movement, so a later refresh cannot restore an old coordinate.
|
||||||
|
guard revision != lastRemoteCursorRevision else { return }
|
||||||
|
lastRemoteCursorRevision = revision
|
||||||
guard let position else {
|
guard let position else {
|
||||||
lastRemoteCursorPoint = nil
|
lastRemoteCursorPoint = nil
|
||||||
return
|
return
|
||||||
@@ -194,7 +202,11 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
|
|||||||
Float(min(max(position.y, 0), 1))
|
Float(min(max(position.y, 0), 1))
|
||||||
)
|
)
|
||||||
lastRemoteCursorPoint = point
|
lastRemoteCursorPoint = point
|
||||||
applyRemoteCursorIfIdle()
|
guard controlMode == .relative,
|
||||||
|
!relativePanActive,
|
||||||
|
heldDragPoint == nil,
|
||||||
|
hoverLastLocation == nil else { return }
|
||||||
|
relativeCursorPoint = point
|
||||||
}
|
}
|
||||||
|
|
||||||
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
|
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
|
||||||
@@ -400,7 +412,6 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
relativePanActive = false
|
relativePanActive = false
|
||||||
applyRemoteCursorIfIdle()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,7 +440,6 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
|
|||||||
}
|
}
|
||||||
heldDragPoint = nil
|
heldDragPoint = nil
|
||||||
heldDragLastLocation = nil
|
heldDragLastLocation = nil
|
||||||
applyRemoteCursorIfIdle()
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -457,14 +467,6 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
|
|||||||
return relativeCursorPoint
|
return relativeCursorPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
private func applyRemoteCursorIfIdle() {
|
|
||||||
guard controlMode == .relative,
|
|
||||||
!relativePanActive,
|
|
||||||
heldDragPoint == nil,
|
|
||||||
let point = lastRemoteCursorPoint else { return }
|
|
||||||
relativeCursorPoint = point
|
|
||||||
}
|
|
||||||
|
|
||||||
private func normalized(_ point: CGPoint) -> (Float, Float)? {
|
private func normalized(_ point: CGPoint) -> (Float, Float)? {
|
||||||
guard let renderRect = renderRect(), renderRect.contains(point) else { return nil }
|
guard let renderRect = renderRect(), renderRect.contains(point) else { return nil }
|
||||||
return (Float((point.x - renderRect.minX) / renderRect.width),
|
return (Float((point.x - renderRect.minX) / renderRect.width),
|
||||||
|
|||||||
@@ -381,6 +381,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
|
|||||||
@Published private(set) var remoteCursorVisible = true
|
@Published private(set) var remoteCursorVisible = true
|
||||||
@Published private(set) var remoteCursorShape = 0
|
@Published private(set) var remoteCursorShape = 0
|
||||||
@Published private(set) var remoteCursorPosition: CGPoint?
|
@Published private(set) var remoteCursorPosition: CGPoint?
|
||||||
|
@Published private(set) var remoteCursorPositionRevision: UInt64 = 0
|
||||||
@Published private(set) var remoteCursorVisualOffset = CGPoint.zero
|
@Published private(set) var remoteCursorVisualOffset = CGPoint.zero
|
||||||
@Published private(set) var hasRemoteCursorState = false
|
@Published private(set) var hasRemoteCursorState = false
|
||||||
@Published private(set) var bitrate: UInt = 0
|
@Published private(set) var bitrate: UInt = 0
|
||||||
@@ -775,6 +776,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
|
|||||||
remoteCursorVisible = true
|
remoteCursorVisible = true
|
||||||
remoteCursorShape = 0
|
remoteCursorShape = 0
|
||||||
remoteCursorPosition = nil
|
remoteCursorPosition = nil
|
||||||
|
remoteCursorPositionRevision &+= 1
|
||||||
remoteCursorVisualOffset = .zero
|
remoteCursorVisualOffset = .zero
|
||||||
hasRemoteCursorState = false
|
hasRemoteCursorState = false
|
||||||
remoteCursorSequence = nil
|
remoteCursorSequence = nil
|
||||||
@@ -991,6 +993,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
|
|||||||
} else {
|
} else {
|
||||||
remoteCursorPosition = nil
|
remoteCursorPosition = nil
|
||||||
}
|
}
|
||||||
|
remoteCursorPositionRevision &+= 1
|
||||||
}
|
}
|
||||||
hasRemoteCursorState = true
|
hasRemoteCursorState = true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ struct RemoteSessionView: View {
|
|||||||
videoSize: session.displayGeometrySize,
|
videoSize: session.displayGeometrySize,
|
||||||
controlMode: session.mouseControlMode,
|
controlMode: session.mouseControlMode,
|
||||||
remoteCursorPosition: session.remoteCursorPosition,
|
remoteCursorPosition: session.remoteCursorPosition,
|
||||||
|
remoteCursorPositionRevision: session.remoteCursorPositionRevision,
|
||||||
viewportScale: viewport.scale,
|
viewportScale: viewport.scale,
|
||||||
viewportOffset: viewport.offset,
|
viewportOffset: viewport.offset,
|
||||||
viewportChanged: { scale, offset in
|
viewportChanged: { scale, offset in
|
||||||
@@ -161,8 +162,8 @@ struct RemoteSessionView: View {
|
|||||||
.onChange(of: session.mouseControlMode) { _ in
|
.onChange(of: session.mouseControlMode) { _ in
|
||||||
cursorPosition = session.remoteCursorPosition
|
cursorPosition = session.remoteCursorPosition
|
||||||
}
|
}
|
||||||
.onChange(of: session.remoteCursorPosition) { position in
|
.onChange(of: session.remoteCursorPositionRevision) { _ in
|
||||||
guard let position else { return }
|
guard let position = session.remoteCursorPosition else { return }
|
||||||
cursorPosition = position
|
cursorPosition = position
|
||||||
}
|
}
|
||||||
.fileImporter(isPresented: $showingFileImporter,
|
.fileImporter(isPresented: $showingFileImporter,
|
||||||
|
|||||||
Reference in New Issue
Block a user