Compare commits

..

8 Commits

Author SHA1 Message Date
Marc Brooks fe277517c1 Fix update status rendering 2025-10-15 04:51:49 +00:00
Marc Brooks b74f779daa Merged in dev hotfix 2025-10-15 04:18:00 +00:00
Marc Brooks 748155d815
Added setting to choose locale 2025-10-14 22:35:05 -05:00
Marc Brooks 423bf1a53f
Delete unused messages 2025-10-14 18:50:21 -05:00
Marc Brooks 9e822850e0
Fix eslint 2025-10-14 17:15:25 -05:00
Marc Brooks c6a12588f5
Restore some missing blanks 2025-10-14 17:06:49 -05:00
Marc Brooks 75716405d5
Use getLocale for date, relative time, and money formatting 2025-10-14 14:49:45 -05:00
Marc Brooks 5363baa37a
Add inlang/paraglide-js localization
Remove the temporary directory after extracting buildkit
Localize the extension popovers.
Update package and fix tsconfig.json
Expand development directory guide
Move messages under localization
Popovers and sidebar
Update Chinese translations
Accidentally lost the changes that @ym provided, brought them back
File formatting pass
Localized all components, hooks, providers, hooks
Localize all pages except Settings
Bump packages
Settings Access page
Settings local auth page
Fix ref lint warning
Settings Advanced page
Fix UI lint warnings
There were a bunch of ref and useEffect violations.
Settings appearance page
Settings general pages
Settings hardware page
Settings keyboard page
Settings macros pages
Settings mouse page
Settings page
Settings video page
Settings network page
Fix compilation issues
Ran machine translate
2025-10-14 06:41:12 -05:00
8 changed files with 8 additions and 214 deletions

View File

@ -104,7 +104,6 @@ type Config struct {
UsbDevices *usbgadget.Devices `json:"usb_devices"`
NetworkConfig *network.NetworkConfig `json:"network_config"`
DefaultLogLevel string `json:"default_log_level"`
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
}
func (c *Config) GetDisplayRotation() uint16 {

View File

@ -19,7 +19,6 @@ type Native struct {
onVideoFrameReceived func(frame []byte, duration time.Duration)
onIndevEvent func(event string)
onRpcEvent func(event string)
sleepModeSupported bool
videoLock sync.Mutex
screenLock sync.Mutex
}
@ -63,8 +62,6 @@ func NewNative(opts NativeOptions) *Native {
}
}
sleepModeSupported := isSleepModeSupported()
return &Native{
ready: make(chan struct{}),
l: nativeLogger,
@ -76,7 +73,6 @@ func NewNative(opts NativeOptions) *Native {
onVideoFrameReceived: onVideoFrameReceived,
onIndevEvent: onIndevEvent,
onRpcEvent: onRpcEvent,
sleepModeSupported: sleepModeSupported,
videoLock: sync.Mutex{},
screenLock: sync.Mutex{},
}

View File

@ -1,12 +1,5 @@
package native
import (
"os"
)
const sleepModeFile = "/sys/devices/platform/ff470000.i2c/i2c-4/4-000f/sleep_mode"
// VideoState is the state of the video stream.
type VideoState struct {
Ready bool `json:"ready"`
Error string `json:"error,omitempty"` //no_signal, no_lock, out_of_range
@ -15,58 +8,6 @@ type VideoState struct {
FramePerSecond float64 `json:"fps"`
}
func isSleepModeSupported() bool {
_, err := os.Stat(sleepModeFile)
return err == nil
}
func (n *Native) setSleepMode(enabled bool) error {
if !n.sleepModeSupported {
return nil
}
bEnabled := "0"
if enabled {
bEnabled = "1"
}
return os.WriteFile(sleepModeFile, []byte(bEnabled), 0644)
}
func (n *Native) getSleepMode() (bool, error) {
if !n.sleepModeSupported {
return false, nil
}
data, err := os.ReadFile(sleepModeFile)
if err == nil {
return string(data) == "1", nil
}
return false, nil
}
// VideoSetSleepMode sets the sleep mode for the video stream.
func (n *Native) VideoSetSleepMode(enabled bool) error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return n.setSleepMode(enabled)
}
// VideoGetSleepMode gets the sleep mode for the video stream.
func (n *Native) VideoGetSleepMode() (bool, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
return n.getSleepMode()
}
// VideoSleepModeSupported checks if the sleep mode is supported.
func (n *Native) VideoSleepModeSupported() bool {
return n.sleepModeSupported
}
// VideoSetQualityFactor sets the quality factor for the video stream.
func (n *Native) VideoSetQualityFactor(factor float64) error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
@ -74,7 +15,6 @@ func (n *Native) VideoSetQualityFactor(factor float64) error {
return videoSetStreamQualityFactor(factor)
}
// VideoGetQualityFactor gets the quality factor for the video stream.
func (n *Native) VideoGetQualityFactor() (float64, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
@ -82,7 +22,6 @@ func (n *Native) VideoGetQualityFactor() (float64, error) {
return videoGetStreamQualityFactor()
}
// VideoSetEDID sets the EDID for the video stream.
func (n *Native) VideoSetEDID(edid string) error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
@ -90,7 +29,6 @@ func (n *Native) VideoSetEDID(edid string) error {
return videoSetEDID(edid)
}
// VideoGetEDID gets the EDID for the video stream.
func (n *Native) VideoGetEDID() (string, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
@ -98,7 +36,6 @@ func (n *Native) VideoGetEDID() (string, error) {
return videoGetEDID()
}
// VideoLogStatus gets the log status for the video stream.
func (n *Native) VideoLogStatus() (string, error) {
n.videoLock.Lock()
defer n.videoLock.Unlock()
@ -106,7 +43,6 @@ func (n *Native) VideoLogStatus() (string, error) {
return videoLogStatus(), nil
}
// VideoStop stops the video stream.
func (n *Native) VideoStop() error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
@ -115,14 +51,10 @@ func (n *Native) VideoStop() error {
return nil
}
// VideoStart starts the video stream.
func (n *Native) VideoStart() error {
n.videoLock.Lock()
defer n.videoLock.Unlock()
// disable sleep mode before starting video
_ = n.setSleepMode(false)
videoStart()
return nil
}

View File

@ -1215,8 +1215,6 @@ var rpcHandlers = map[string]RPCHandler{
"getEDID": {Func: rpcGetEDID},
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
"getVideoLogStatus": {Func: rpcGetVideoLogStatus},
"getVideoSleepMode": {Func: rpcGetVideoSleepMode},
"setVideoSleepMode": {Func: rpcSetVideoSleepMode, Params: []string{"duration"}},
"getDevChannelState": {Func: rpcGetDevChannelState},
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
"getLocalVersion": {Func: rpcGetLocalVersion},

View File

@ -77,9 +77,6 @@ func Main() {
// initialize display
initDisplay()
// start video sleep mode timer
startVideoSleepModeTicker()
go func() {
time.Sleep(15 * time.Minute)
for {

View File

@ -179,7 +179,6 @@ export function MacroStepCard({
</div>
</div>
<div className="w-full flex flex-col gap-1">
<div className="flex items-center gap-1">
<FieldLabel label={m.macro_step_keys_label()} description={m.macro_step_keys_description({ max: MAX_KEYS_PER_STEP })} />

103
video.go
View File

@ -1,22 +1,10 @@
package kvm
import (
"context"
"fmt"
"time"
"github.com/jetkvm/kvm/internal/native"
)
var (
lastVideoState native.VideoState
videoSleepModeCtx context.Context
videoSleepModeCancel context.CancelFunc
)
const (
defaultVideoSleepModeDuration = 1 * time.Minute
)
var lastVideoState native.VideoState
func triggerVideoStateUpdate() {
go func() {
@ -29,92 +17,3 @@ func triggerVideoStateUpdate() {
func rpcGetVideoState() (native.VideoState, error) {
return lastVideoState, nil
}
type rpcVideoSleepModeResponse struct {
Supported bool `json:"supported"`
Enabled bool `json:"enabled"`
Duration int `json:"duration"`
}
func rpcGetVideoSleepMode() rpcVideoSleepModeResponse {
sleepMode, _ := nativeInstance.VideoGetSleepMode()
return rpcVideoSleepModeResponse{
Supported: nativeInstance.VideoSleepModeSupported(),
Enabled: sleepMode,
Duration: config.VideoSleepAfterSec,
}
}
func rpcSetVideoSleepMode(duration int) error {
if duration < 0 {
duration = -1 // disable
}
config.VideoSleepAfterSec = duration
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
// we won't restart the ticker here,
// as the session can't be inactive when this function is called
return nil
}
func stopVideoSleepModeTicker() {
nativeLogger.Trace().Msg("stopping HDMI sleep mode ticker")
if videoSleepModeCancel != nil {
nativeLogger.Trace().Msg("canceling HDMI sleep mode ticker context")
videoSleepModeCancel()
videoSleepModeCancel = nil
videoSleepModeCtx = nil
}
}
func startVideoSleepModeTicker() {
if !nativeInstance.VideoSleepModeSupported() {
return
}
var duration time.Duration
if config.VideoSleepAfterSec == 0 {
duration = defaultVideoSleepModeDuration
} else if config.VideoSleepAfterSec > 0 {
duration = time.Duration(config.VideoSleepAfterSec) * time.Second
} else {
stopVideoSleepModeTicker()
return
}
// Stop any existing timer and goroutine
stopVideoSleepModeTicker()
// Create new context for this ticker
videoSleepModeCtx, videoSleepModeCancel = context.WithCancel(context.Background())
go doVideoSleepModeTicker(videoSleepModeCtx, duration)
}
func doVideoSleepModeTicker(ctx context.Context, duration time.Duration) {
timer := time.NewTimer(duration)
defer timer.Stop()
nativeLogger.Trace().Msg("HDMI sleep mode ticker started")
for {
select {
case <-timer.C:
if getActiveSessions() > 0 {
nativeLogger.Warn().Msg("not going to enter HDMI sleep mode because there are active sessions")
continue
}
nativeLogger.Trace().Msg("entering HDMI sleep mode")
_ = nativeInstance.VideoSetSleepMode(true)
case <-ctx.Done():
nativeLogger.Trace().Msg("HDMI sleep mode ticker stopped")
return
}
}
}

View File

@ -39,34 +39,6 @@ type Session struct {
keysDownStateQueue chan usbgadget.KeysDownState
}
var (
actionSessions int = 0
activeSessionsMutex = &sync.Mutex{}
)
func incrActiveSessions() int {
activeSessionsMutex.Lock()
defer activeSessionsMutex.Unlock()
actionSessions++
return actionSessions
}
func decrActiveSessions() int {
activeSessionsMutex.Lock()
defer activeSessionsMutex.Unlock()
actionSessions--
return actionSessions
}
func getActiveSessions() int {
activeSessionsMutex.Lock()
defer activeSessionsMutex.Unlock()
return actionSessions
}
func (s *Session) resetKeepAliveTime() {
s.keepAliveJitterLock.Lock()
defer s.keepAliveJitterLock.Unlock()
@ -340,8 +312,9 @@ func newSession(config SessionConfig) (*Session, error) {
if connectionState == webrtc.ICEConnectionStateConnected {
if !isConnected {
isConnected = true
actionSessions++
onActiveSessionsChanged()
if incrActiveSessions() == 1 {
if actionSessions == 1 {
onFirstSessionConnected()
}
}
@ -380,8 +353,9 @@ func newSession(config SessionConfig) (*Session, error) {
}
if isConnected {
isConnected = false
actionSessions--
onActiveSessionsChanged()
if decrActiveSessions() == 0 {
if actionSessions == 0 {
onLastSessionDisconnected()
}
}
@ -390,16 +364,16 @@ func newSession(config SessionConfig) (*Session, error) {
return session, nil
}
var actionSessions = 0
func onActiveSessionsChanged() {
requestDisplayUpdate(true, "active_sessions_changed")
}
func onFirstSessionConnected() {
_ = nativeInstance.VideoStart()
stopVideoSleepModeTicker()
}
func onLastSessionDisconnected() {
_ = nativeInstance.VideoStop()
startVideoSleepModeTicker()
}