From 53789ebd7530579b43c5a500c52a3373b4a545f7 Mon Sep 17 00:00:00 2001 From: Siyuan Miao Date: Wed, 17 Sep 2025 17:21:27 +0200 Subject: [PATCH] clean up code --- hidrpc.go | 121 +++++++++++++++++++++++++++++------------------------- usb.go | 4 -- 2 files changed, 64 insertions(+), 61 deletions(-) diff --git a/hidrpc.go b/hidrpc.go index 62eb8491..7475b358 100644 --- a/hidrpc.go +++ b/hidrpc.go @@ -39,63 +39,7 @@ func handleHidRPCMessage(message hidrpc.Message, session *Session) { rpcCancelKeyboardMacro() return case hidrpc.TypeKeypressKeepAliveReport: - session.keepAliveJitterLock.Lock() - defer session.keepAliveJitterLock.Unlock() - - now := time.Now() - - // Tunables - // Keep in mind - // macOS default: 15 * 15 = 225ms https://discussions.apple.com/thread/1316947?sortBy=rank - // Linux default: 250ms https://man.archlinux.org/man/kbdrate.8.en - // Windows default: 1s `HKEY_CURRENT_USER\Control Panel\Accessibility\Keyboard Response\AutoRepeatDelay` - - const expectedRate = 50 * time.Millisecond // expected keepalive interval - const maxLateness = 50 * time.Millisecond // max jitter we'll tolerate OR jitter budget - const baseExtension = expectedRate + maxLateness // 100ms extension on perfect tick - - const maxStaleness = 225 * time.Millisecond // discard ancient packets outright - - // 1) Staleness guard: ensures packets that arrive far beyond the life of a valid key hold - // (e.g. after a network stall, retransmit burst, or machine sleep) are ignored outright. - // This prevents “zombie” keepalives from reviving a key that should already be released. - if !session.lastTimerResetTime.IsZero() && now.Sub(session.lastTimerResetTime) > maxStaleness { - return - } - - validTick := true - timerExtension := baseExtension - - if !session.lastKeepAliveArrivalTime.IsZero() { - timeSinceLastTick := now.Sub(session.lastKeepAliveArrivalTime) - lateness := timeSinceLastTick - expectedRate - - if lateness > 0 { - if lateness <= maxLateness { - // --- Small lateness (within jitterBudget) --- - // This is normal jitter (e.g., Wi-Fi contention). - // We still accept the tick, but *reduce the extension* - // so that the total hold time stays aligned with REAL client side intent. - timerExtension -= lateness - } else { - // --- Large lateness (beyond jitterBudget) --- - // This is likely a retransmit stall or ordering delay. - // We reject the tick entirely and DO NOT extend, - // so the auto-release still fires on time. - validTick = false - } - } - } - - if validTick { - // Only valid ticks update our state and extend the timer. - session.lastKeepAliveArrivalTime = now - session.lastTimerResetTime = now - if ug := getUsbGadget(); ug != nil { - ug.DelayAutoReleaseWithDuration(timerExtension) - } - } - // On a miss: do not advance any state — keeps baseline stable. + rpcErr = handleHidRPCKeypressKeepAlive(session) case hidrpc.TypePointerReport: pointerReport, err := message.PointerReport() if err != nil { @@ -159,6 +103,69 @@ func onHidMessage(msg hidQueueMessage, session *Session) { } } +// Tunables +// Keep in mind +// macOS default: 15 * 15 = 225ms https://discussions.apple.com/thread/1316947?sortBy=rank +// Linux default: 250ms https://man.archlinux.org/man/kbdrate.8.en +// Windows default: 1s `HKEY_CURRENT_USER\Control Panel\Accessibility\Keyboard Response\AutoRepeatDelay` + +const expectedRate = 50 * time.Millisecond // expected keepalive interval +const maxLateness = 50 * time.Millisecond // max jitter we'll tolerate OR jitter budget +const baseExtension = expectedRate + maxLateness // 100ms extension on perfect tick + +const maxStaleness = 225 * time.Millisecond // discard ancient packets outright + +func handleHidRPCKeypressKeepAlive(session *Session) error { + session.keepAliveJitterLock.Lock() + defer session.keepAliveJitterLock.Unlock() + + now := time.Now() + + // 1) Staleness guard: ensures packets that arrive far beyond the life of a valid key hold + // (e.g. after a network stall, retransmit burst, or machine sleep) are ignored outright. + // This prevents “zombie” keepalives from reviving a key that should already be released. + if !session.lastTimerResetTime.IsZero() && now.Sub(session.lastTimerResetTime) > maxStaleness { + return nil + } + + validTick := true + timerExtension := baseExtension + + if !session.lastKeepAliveArrivalTime.IsZero() { + timeSinceLastTick := now.Sub(session.lastKeepAliveArrivalTime) + lateness := timeSinceLastTick - expectedRate + + if lateness > 0 { + if lateness <= maxLateness { + // --- Small lateness (within jitterBudget) --- + // This is normal jitter (e.g., Wi-Fi contention). + // We still accept the tick, but *reduce the extension* + // so that the total hold time stays aligned with REAL client side intent. + timerExtension -= lateness + } else { + // --- Large lateness (beyond jitterBudget) --- + // This is likely a retransmit stall or ordering delay. + // We reject the tick entirely and DO NOT extend, + // so the auto-release still fires on time. + validTick = false + } + } + } + + if !validTick { + return nil + } + // Only valid ticks update our state and extend the timer. + session.lastKeepAliveArrivalTime = now + session.lastTimerResetTime = now + if gadget != nil { + gadget.DelayAutoReleaseWithDuration(timerExtension) + } + + // On a miss: do not advance any state — keeps baseline stable. + return nil +} + func handleHidRPCKeyboardInput(message hidrpc.Message) error { switch message.Type() { case hidrpc.TypeKeypressReport: diff --git a/usb.go b/usb.go index d7ddf605..99287a30 100644 --- a/usb.go +++ b/usb.go @@ -8,10 +8,6 @@ import ( var gadget *usbgadget.UsbGadget -func getUsbGadget() *usbgadget.UsbGadget { - return gadget -} - // initUsbGadget initializes the USB gadget. // call it only after the config is loaded. func initUsbGadget() {