mirror of https://github.com/jetkvm/kvm.git
fix: resolve intermittent mouse control loss and add permission logging
This commit addresses three critical issues discovered during testing: Issue 1 - Intermittent mouse control loss requiring page refresh: When a session was promoted to primary, the HID queue handlers were fetching a fresh session copy from the session manager instead of using the original session pointer. This meant the queue handler had a stale Mode field (observer) while the manager had the updated Mode (primary). The permission check would fail, silently dropping all mouse input until the page was refreshed. Issue 2 - Missing permission failure diagnostics: When keyboard/mouse input was blocked due to insufficient permissions, there was no debug logging to help diagnose why input wasn't working. This made troubleshooting observer mode issues extremely difficult. Issue 3 - Session timeout despite active jiggler: The server-side jiggler moves the mouse every 30s after inactivity to prevent screen savers, but wasn't updating the session's LastActive timestamp. This caused sessions to timeout after 60s even with the jiggler active. Issue 4 - Session flapping after emergency promotion: When a session timed out and another was promoted, the newly promoted session had a stale LastActive timestamp (60+ seconds old), causing immediate re-timeout. This created an infinite loop where both sessions rapidly alternated between primary and observer every second. Issue 5 - Unnecessary WebSocket reconnections: The WebSocket fallback was unconditionally closing and reconnecting during emergency promotions, even when the connection was healthy. This caused spurious "Connection Issue Detected" overlays during normal promotions. Changes: - webrtc.go: Use original session pointer in handleQueues() (line 197) - hidrpc.go: Add debug logging when permission checks block input (lines 31-34, 61-64, 75-78) - jiggler.go: Update primary session LastActive after mouse movement (lines 146-152) - session_manager.go: Reset LastActive to time.Now() on promotion (line 1090) - devices.$id.tsx: Only reconnect if connection is unhealthy (lines 413-425) This ensures: 1. Queue handlers always have up-to-date session state 2. Permission failures are visible in logs for debugging 3. Jiggler prevents both screen savers AND session timeout 4. Newly promoted sessions get full timeout period (no immediate re-timeout) 5. Emergency promotions only reconnect when connection is actually stale 6. No spurious "Connection Issue" overlays during normal promotions
This commit is contained in:
parent
827decf803
commit
64a6a1a078
15
jiggler.go
15
jiggler.go
|
|
@ -129,19 +129,22 @@ func runJiggler() {
|
|||
}
|
||||
inactivitySeconds := config.JigglerConfig.InactivityLimitSeconds
|
||||
timeSinceLastInput := time.Since(gadget.GetLastUserInputTime())
|
||||
logger.Debug().Msgf("Time since last user input %v", timeSinceLastInput)
|
||||
if timeSinceLastInput > time.Duration(inactivitySeconds)*time.Second {
|
||||
logger.Debug().Msg("Jiggling mouse...")
|
||||
//TODO: change to rel mouse
|
||||
// Use direct hardware calls for jiggler - bypass session permissions
|
||||
err := gadget.AbsMouseReport(1, 1, 0)
|
||||
err := gadget.RelMouseReport(1, 0, 0)
|
||||
if err != nil {
|
||||
logger.Warn().Msgf("Failed to jiggle mouse: %v", err)
|
||||
}
|
||||
err = gadget.AbsMouseReport(0, 0, 0)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
err = gadget.RelMouseReport(-1, 0, 0)
|
||||
if err != nil {
|
||||
logger.Warn().Msgf("Failed to reset mouse position: %v", err)
|
||||
}
|
||||
|
||||
if sessionManager != nil {
|
||||
if primarySession := sessionManager.GetPrimarySession(); primarySession != nil {
|
||||
sessionManager.UpdateLastActive(primarySession.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1087,6 +1087,7 @@ func (sm *SessionManager) transferPrimaryRole(fromSessionID, toSessionID, transf
|
|||
// Promote target session
|
||||
toSession.Mode = SessionModePrimary
|
||||
toSession.hidRPCAvailable = false // Force re-handshake
|
||||
toSession.LastActive = time.Now() // Reset activity timestamp to prevent immediate timeout
|
||||
sm.primarySessionID = toSessionID
|
||||
|
||||
// ALWAYS set lastPrimaryID to the new primary to support WebRTC reconnections
|
||||
|
|
|
|||
|
|
@ -400,18 +400,29 @@ export default function KvmIdRoute() {
|
|||
const { newMode, action } = parsedMessage.data;
|
||||
|
||||
if (action === "reconnect_required" && newMode) {
|
||||
console.log(`[Websocket] Mode changed to ${newMode}, reconnecting...`);
|
||||
|
||||
// Update session state immediately
|
||||
if (currentSessionId) {
|
||||
setCurrentSession(currentSessionId, newMode);
|
||||
}
|
||||
|
||||
// Trigger RPC event handler
|
||||
handleRpcEvent("connectionModeChanged", parsedMessage.data);
|
||||
|
||||
// Only reconnect if the peer connection is actually stale
|
||||
// If already connected, the mode change via RPC is sufficient
|
||||
const isConnectionHealthy =
|
||||
peerConnection?.connectionState === "connected" &&
|
||||
peerConnection?.iceConnectionState === "connected";
|
||||
|
||||
if (!isConnectionHealthy) {
|
||||
console.log(`[Websocket] Mode changed to ${newMode}, connection unhealthy, reconnecting...`);
|
||||
setTimeout(() => {
|
||||
peerConnection?.close();
|
||||
setupPeerConnection();
|
||||
}, 500);
|
||||
} else {
|
||||
console.log(`[Websocket] Mode changed to ${newMode}, connection healthy, skipping reconnect`);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue