Files
kvm/native.go
Alex Howells 1c30bac4f1 fix(display): prevent background events from resetting off timer (#1277)
The display-off timer never fires when a WebRTC session is active because
background system events (video state changes, session count updates, USB
state changes, network state changes) all call requestDisplayUpdate with
shouldWakeDisplay=true. Each wake resets both the dim and off tickers via
wakeDisplay, so the off timer restarts from zero on every event and never
reaches its threshold.

Only physical input device events (touchscreen taps, reported as
indev_event) should wake the display and reset the timers. Background
state updates still need to refresh the LCD content but should not
interfere with the backlight timeout cycle.

Change requestDisplayUpdate calls from background event handlers to pass
shouldWakeDisplay=false in native.go (video_state_changed), network.go
(network_state_changed), usb.go (usb_state_changed), and webrtc.go
(active_sessions_changed). Startup paths (init_display, native_restart)
retain shouldWakeDisplay=true.

Fixes #1133
2026-03-16 11:45:52 +01:00

110 lines
3.3 KiB
Go

package kvm
import (
"os"
"time"
"github.com/jetkvm/kvm/internal/diagnostics"
"github.com/jetkvm/kvm/internal/native"
"github.com/jetkvm/kvm/internal/sync"
"github.com/Masterminds/semver/v3"
"github.com/pion/webrtc/v4/pkg/media"
)
var (
nativeInstance native.NativeInterface
nativeCmdLock = sync.Mutex{}
)
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
if failsafeModeActive {
nativeInstance = &native.EmptyNativeInterface{}
nativeLogger.Warn().Msg("failsafe mode active, using empty native interface")
return
}
nativeLogger.Info().Msg("initializing native proxy")
var err error
nativeInstance, err = native.NewNativeProxy(native.NativeOptions{
SystemVersion: systemVersion,
AppVersion: appVersion,
DisplayRotation: config.GetDisplayRotation(),
DefaultQualityFactor: config.VideoQualityFactor,
MaxRestartAttempts: config.NativeMaxRestart,
OnNativeRestart: func() {
configureDisplayOnNativeRestart()
},
OnVideoStateChange: func(state native.VideoState) {
lastVideoState = state
triggerVideoStateUpdate()
requestDisplayUpdate(false, "video_state_changed")
},
OnIndevEvent: func(event string) {
nativeLogger.Trace().Str("event", event).Msg("indev event received")
wakeDisplay(false, "indev_event")
},
OnRpcEvent: func(event string) {
nativeCmdLock.Lock()
defer nativeCmdLock.Unlock()
nativeLogger.Trace().Str("event", event).Msg("rpc event received")
switch event {
case "resetConfig":
nativeLogger.Info().Msg("Reset configuration request via native rpc event")
err := rpcResetConfig()
if err != nil {
nativeLogger.Warn().Err(err).Msg("error resetting config")
}
_ = rpcReboot(true)
case "reboot":
nativeLogger.Info().Msg("Reboot request via native rpc event")
_ = rpcReboot(true)
case "toggleDHCPClient":
nativeLogger.Info().Msg("Toggle DHCP request via native rpc event")
_ = rpcToggleDHCPClient()
default:
nativeLogger.Warn().Str("event", event).Msg("unknown rpc event received")
}
},
OnVideoFrameReceived: func(frame []byte, duration time.Duration) {
if currentSession != nil {
err := currentSession.VideoTrack.WriteSample(media.Sample{Data: frame, Duration: duration})
if err != nil {
nativeLogger.Warn().Err(err).Msg("error writing sample")
}
}
},
GetSessionInfo: func() diagnostics.SessionInfo {
info := diagnostics.SessionInfo{
ActiveSessions: getActiveSessions(),
HasCurrentSession: currentSession != nil,
}
if currentSession != nil {
sessionInfo := currentSession.GetDiagnosticsInfo()
info.ICEConnectionState = sessionInfo.ICEConnectionState
info.SignalingState = sessionInfo.SignalingState
info.ConnectionState = sessionInfo.ConnectionState
info.DataChannels = sessionInfo.DataChannels
}
return info
},
})
if err != nil {
nativeLogger.Fatal().Err(err).Msg("failed to create native proxy")
}
if err := nativeInstance.Start(); err != nil {
nativeLogger.Fatal().Err(err).Msg("failed to start native proxy")
}
go func() {
if err := nativeInstance.VideoSetEDID(config.EdidString); err != nil {
nativeLogger.Warn().Err(err).Msg("error setting EDID")
}
}()
if os.Getenv("JETKVM_CRASH_TESTING") == "1" {
nativeInstance.DoNotUseThisIsForCrashTestingOnly()
}
}