mirror of https://github.com/jetkvm/kvm.git
Compare commits
4 Commits
b8112995fc
...
13aff1109a
| Author | SHA1 | Date |
|---|---|---|
|
|
13aff1109a | |
|
|
748155d815 | |
|
|
cc9ff74276 | |
|
|
b144d9926f |
|
|
@ -104,6 +104,7 @@ type Config struct {
|
||||||
UsbDevices *usbgadget.Devices `json:"usb_devices"`
|
UsbDevices *usbgadget.Devices `json:"usb_devices"`
|
||||||
NetworkConfig *network.NetworkConfig `json:"network_config"`
|
NetworkConfig *network.NetworkConfig `json:"network_config"`
|
||||||
DefaultLogLevel string `json:"default_log_level"`
|
DefaultLogLevel string `json:"default_log_level"`
|
||||||
|
VideoSleepAfterSec int `json:"video_sleep_after_sec"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) GetDisplayRotation() uint16 {
|
func (c *Config) GetDisplayRotation() uint16 {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ type Native struct {
|
||||||
onVideoFrameReceived func(frame []byte, duration time.Duration)
|
onVideoFrameReceived func(frame []byte, duration time.Duration)
|
||||||
onIndevEvent func(event string)
|
onIndevEvent func(event string)
|
||||||
onRpcEvent func(event string)
|
onRpcEvent func(event string)
|
||||||
|
sleepModeSupported bool
|
||||||
videoLock sync.Mutex
|
videoLock sync.Mutex
|
||||||
screenLock sync.Mutex
|
screenLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +63,8 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sleepModeSupported := isSleepModeSupported()
|
||||||
|
|
||||||
return &Native{
|
return &Native{
|
||||||
ready: make(chan struct{}),
|
ready: make(chan struct{}),
|
||||||
l: nativeLogger,
|
l: nativeLogger,
|
||||||
|
|
@ -73,6 +76,7 @@ func NewNative(opts NativeOptions) *Native {
|
||||||
onVideoFrameReceived: onVideoFrameReceived,
|
onVideoFrameReceived: onVideoFrameReceived,
|
||||||
onIndevEvent: onIndevEvent,
|
onIndevEvent: onIndevEvent,
|
||||||
onRpcEvent: onRpcEvent,
|
onRpcEvent: onRpcEvent,
|
||||||
|
sleepModeSupported: sleepModeSupported,
|
||||||
videoLock: sync.Mutex{},
|
videoLock: sync.Mutex{},
|
||||||
screenLock: sync.Mutex{},
|
screenLock: sync.Mutex{},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,12 @@
|
||||||
package native
|
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 {
|
type VideoState struct {
|
||||||
Ready bool `json:"ready"`
|
Ready bool `json:"ready"`
|
||||||
Error string `json:"error,omitempty"` //no_signal, no_lock, out_of_range
|
Error string `json:"error,omitempty"` //no_signal, no_lock, out_of_range
|
||||||
|
|
@ -8,6 +15,58 @@ type VideoState struct {
|
||||||
FramePerSecond float64 `json:"fps"`
|
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 {
|
func (n *Native) VideoSetQualityFactor(factor float64) error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
@ -15,6 +74,7 @@ func (n *Native) VideoSetQualityFactor(factor float64) error {
|
||||||
return videoSetStreamQualityFactor(factor)
|
return videoSetStreamQualityFactor(factor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VideoGetQualityFactor gets the quality factor for the video stream.
|
||||||
func (n *Native) VideoGetQualityFactor() (float64, error) {
|
func (n *Native) VideoGetQualityFactor() (float64, error) {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
@ -22,6 +82,7 @@ func (n *Native) VideoGetQualityFactor() (float64, error) {
|
||||||
return videoGetStreamQualityFactor()
|
return videoGetStreamQualityFactor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VideoSetEDID sets the EDID for the video stream.
|
||||||
func (n *Native) VideoSetEDID(edid string) error {
|
func (n *Native) VideoSetEDID(edid string) error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
@ -29,6 +90,7 @@ func (n *Native) VideoSetEDID(edid string) error {
|
||||||
return videoSetEDID(edid)
|
return videoSetEDID(edid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VideoGetEDID gets the EDID for the video stream.
|
||||||
func (n *Native) VideoGetEDID() (string, error) {
|
func (n *Native) VideoGetEDID() (string, error) {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
@ -36,6 +98,7 @@ func (n *Native) VideoGetEDID() (string, error) {
|
||||||
return videoGetEDID()
|
return videoGetEDID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VideoLogStatus gets the log status for the video stream.
|
||||||
func (n *Native) VideoLogStatus() (string, error) {
|
func (n *Native) VideoLogStatus() (string, error) {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
@ -43,6 +106,7 @@ func (n *Native) VideoLogStatus() (string, error) {
|
||||||
return videoLogStatus(), nil
|
return videoLogStatus(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VideoStop stops the video stream.
|
||||||
func (n *Native) VideoStop() error {
|
func (n *Native) VideoStop() error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
@ -51,10 +115,14 @@ func (n *Native) VideoStop() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// VideoStart starts the video stream.
|
||||||
func (n *Native) VideoStart() error {
|
func (n *Native) VideoStart() error {
|
||||||
n.videoLock.Lock()
|
n.videoLock.Lock()
|
||||||
defer n.videoLock.Unlock()
|
defer n.videoLock.Unlock()
|
||||||
|
|
||||||
|
// disable sleep mode before starting video
|
||||||
|
_ = n.setSleepMode(false)
|
||||||
|
|
||||||
videoStart()
|
videoStart()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1215,6 +1215,8 @@ var rpcHandlers = map[string]RPCHandler{
|
||||||
"getEDID": {Func: rpcGetEDID},
|
"getEDID": {Func: rpcGetEDID},
|
||||||
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
|
"setEDID": {Func: rpcSetEDID, Params: []string{"edid"}},
|
||||||
"getVideoLogStatus": {Func: rpcGetVideoLogStatus},
|
"getVideoLogStatus": {Func: rpcGetVideoLogStatus},
|
||||||
|
"getVideoSleepMode": {Func: rpcGetVideoSleepMode},
|
||||||
|
"setVideoSleepMode": {Func: rpcSetVideoSleepMode, Params: []string{"duration"}},
|
||||||
"getDevChannelState": {Func: rpcGetDevChannelState},
|
"getDevChannelState": {Func: rpcGetDevChannelState},
|
||||||
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
|
"setDevChannelState": {Func: rpcSetDevChannelState, Params: []string{"enabled"}},
|
||||||
"getLocalVersion": {Func: rpcGetLocalVersion},
|
"getLocalVersion": {Func: rpcGetLocalVersion},
|
||||||
|
|
|
||||||
3
main.go
3
main.go
|
|
@ -77,6 +77,9 @@ func Main() {
|
||||||
// initialize display
|
// initialize display
|
||||||
initDisplay()
|
initDisplay()
|
||||||
|
|
||||||
|
// start video sleep mode timer
|
||||||
|
startVideoSleepModeTicker()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(15 * time.Minute)
|
time.Sleep(15 * time.Minute)
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
|
|
@ -31,9 +31,17 @@
|
||||||
"plugin.inlang.messageFormat": {
|
"plugin.inlang.messageFormat": {
|
||||||
"pathPattern": "./messages/{locale}.json"
|
"pathPattern": "./messages/{locale}.json"
|
||||||
},
|
},
|
||||||
|
"plugin.inlang.mFunctionMatcher": {
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"type": "m-function",
|
||||||
|
"function": "plural",
|
||||||
|
"parameter": "count"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"strategy": [
|
"strategy": [
|
||||||
"cookie",
|
"cookie",
|
||||||
"localStorage",
|
|
||||||
"preferredLanguage",
|
"preferredLanguage",
|
||||||
"baseLocale"
|
"baseLocale"
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Du har ændret din adgangskode til beskyttelse af din lokale enhed. Husk din nye adgangskode til senere brug.",
|
"local_auth_success_password_updated_description": "Du har ændret din adgangskode til beskyttelse af din lokale enhed. Husk din nye adgangskode til senere brug.",
|
||||||
"local_auth_success_password_updated_title": "Adgangskode opdateret",
|
"local_auth_success_password_updated_title": "Adgangskode opdateret",
|
||||||
"local_auth_update_password_button": "Opdater adgangskode",
|
"local_auth_update_password_button": "Opdater adgangskode",
|
||||||
|
"locale_auto": "Bil",
|
||||||
|
"locale_change_success": "Sproget er ændret til {locale}",
|
||||||
|
"locale_da": "Dansk",
|
||||||
|
"locale_de": "Tysk",
|
||||||
|
"locale_en": "Engelsk",
|
||||||
|
"locale_es": "Spansk",
|
||||||
|
"locale_fr": "Fransk",
|
||||||
|
"locale_it": "Italiensk",
|
||||||
|
"locale_nb": "Norsk (bokmål)",
|
||||||
|
"locale_sv": "Svensk",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Log ind",
|
"log_in": "Log ind",
|
||||||
"log_out": "Log ud",
|
"log_out": "Log ud",
|
||||||
"logged_in_as": "Logget ind som",
|
"logged_in_as": "Logget ind som",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Afbrudt",
|
"usb_state_disconnected": "Afbrudt",
|
||||||
"usb_state_low_power_mode": "Lavstrømstilstand",
|
"usb_state_low_power_mode": "Lavstrømstilstand",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Vælg det sprog, der skal bruges i JetKVM-brugergrænsefladen",
|
||||||
|
"user_interface_language_title": "Grænsefladesprog",
|
||||||
"video_brightness_description": "Lysstyrkeniveau ( {value} x)",
|
"video_brightness_description": "Lysstyrkeniveau ( {value} x)",
|
||||||
"video_brightness_title": "Lysstyrke",
|
"video_brightness_title": "Lysstyrke",
|
||||||
"video_contrast_description": "Kontrastniveau ( {value} x)",
|
"video_contrast_description": "Kontrastniveau ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Sie haben Ihr lokales Geräteschutzkennwort erfolgreich geändert. Merken Sie sich das neue Kennwort für zukünftige Zugriffe.",
|
"local_auth_success_password_updated_description": "Sie haben Ihr lokales Geräteschutzkennwort erfolgreich geändert. Merken Sie sich das neue Kennwort für zukünftige Zugriffe.",
|
||||||
"local_auth_success_password_updated_title": "Passwort erfolgreich aktualisiert",
|
"local_auth_success_password_updated_title": "Passwort erfolgreich aktualisiert",
|
||||||
"local_auth_update_password_button": "Kennwort aktualisieren",
|
"local_auth_update_password_button": "Kennwort aktualisieren",
|
||||||
|
"locale_auto": "Auto",
|
||||||
|
"locale_change_success": "Die Sprache wurde erfolgreich in {locale} geändert.",
|
||||||
|
"locale_da": "Dänisch",
|
||||||
|
"locale_de": "Deutsch",
|
||||||
|
"locale_en": "Englisch",
|
||||||
|
"locale_es": "Spanisch",
|
||||||
|
"locale_fr": "Deutsch",
|
||||||
|
"locale_it": "Italienisch",
|
||||||
|
"locale_nb": "Norwegisch (bokmål)",
|
||||||
|
"locale_sv": "Schwedisch",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Einloggen",
|
"log_in": "Einloggen",
|
||||||
"log_out": "Ausloggen",
|
"log_out": "Ausloggen",
|
||||||
"logged_in_as": "Angemeldet als",
|
"logged_in_as": "Angemeldet als",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Getrennt",
|
"usb_state_disconnected": "Getrennt",
|
||||||
"usb_state_low_power_mode": "Energiesparmodus",
|
"usb_state_low_power_mode": "Energiesparmodus",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Wählen Sie die Sprache aus, die in der JetKVM-Benutzeroberfläche verwendet werden soll",
|
||||||
|
"user_interface_language_title": "Schnittstellensprache",
|
||||||
"video_brightness_description": "Helligkeitsstufe ( {value} x)",
|
"video_brightness_description": "Helligkeitsstufe ( {value} x)",
|
||||||
"video_brightness_title": "Helligkeit",
|
"video_brightness_title": "Helligkeit",
|
||||||
"video_contrast_description": "Kontraststufe ( {value} x)",
|
"video_contrast_description": "Kontraststufe ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "You've successfully changed your local device protection password. Make sure to remember your new password for future access.",
|
"local_auth_success_password_updated_description": "You've successfully changed your local device protection password. Make sure to remember your new password for future access.",
|
||||||
"local_auth_success_password_updated_title": "Password Updated Successfully",
|
"local_auth_success_password_updated_title": "Password Updated Successfully",
|
||||||
"local_auth_update_password_button": "Update Password",
|
"local_auth_update_password_button": "Update Password",
|
||||||
|
"locale_auto": "Auto",
|
||||||
|
"locale_change_success": "Language changed successfully to {locale}",
|
||||||
|
"locale_da": "Dansk",
|
||||||
|
"locale_de": "Deutsch",
|
||||||
|
"locale_en": "English",
|
||||||
|
"locale_es": "Español",
|
||||||
|
"locale_fr": "Français",
|
||||||
|
"locale_it": "Italiano",
|
||||||
|
"locale_nb": "Norsk (bokmål)",
|
||||||
|
"locale_sv": "Svenska",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Log In",
|
"log_in": "Log In",
|
||||||
"log_out": "Log out",
|
"log_out": "Log out",
|
||||||
"logged_in_as": "Logged in as",
|
"logged_in_as": "Logged in as",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Disconnected",
|
"usb_state_disconnected": "Disconnected",
|
||||||
"usb_state_low_power_mode": "Low power mode",
|
"usb_state_low_power_mode": "Low power mode",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Select the language to use in the JetKVM user interface",
|
||||||
|
"user_interface_language_title": "Interface Language",
|
||||||
"video_brightness_description": "Brightness level ({value}x)",
|
"video_brightness_description": "Brightness level ({value}x)",
|
||||||
"video_brightness_title": "Brightness",
|
"video_brightness_title": "Brightness",
|
||||||
"video_contrast_description": "Contrast level ({value}x)",
|
"video_contrast_description": "Contrast level ({value}x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Has cambiado correctamente la contraseña de protección de tu dispositivo local. Recuerda la nueva contraseña para acceder en el futuro.",
|
"local_auth_success_password_updated_description": "Has cambiado correctamente la contraseña de protección de tu dispositivo local. Recuerda la nueva contraseña para acceder en el futuro.",
|
||||||
"local_auth_success_password_updated_title": "Contraseña actualizada exitosamente",
|
"local_auth_success_password_updated_title": "Contraseña actualizada exitosamente",
|
||||||
"local_auth_update_password_button": "Actualizar contraseña",
|
"local_auth_update_password_button": "Actualizar contraseña",
|
||||||
|
"locale_auto": "Auto",
|
||||||
|
"locale_change_success": "El idioma se cambió correctamente a {locale}",
|
||||||
|
"locale_da": "Danés",
|
||||||
|
"locale_de": "Alemán",
|
||||||
|
"locale_en": "Inglés",
|
||||||
|
"locale_es": "Español",
|
||||||
|
"locale_fr": "Francés",
|
||||||
|
"locale_it": "Italiano",
|
||||||
|
"locale_nb": "Noruego (bokmål)",
|
||||||
|
"locale_sv": "Sueco",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Acceso",
|
"log_in": "Acceso",
|
||||||
"log_out": "Finalizar la sesión",
|
"log_out": "Finalizar la sesión",
|
||||||
"logged_in_as": "Inició sesión como",
|
"logged_in_as": "Inició sesión como",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Desconectado",
|
"usb_state_disconnected": "Desconectado",
|
||||||
"usb_state_low_power_mode": "Modo de bajo consumo",
|
"usb_state_low_power_mode": "Modo de bajo consumo",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Seleccione el idioma que se utilizará en la interfaz de usuario de JetKVM",
|
||||||
|
"user_interface_language_title": "Lenguaje de interfaz",
|
||||||
"video_brightness_description": "Nivel de brillo ( {value} x)",
|
"video_brightness_description": "Nivel de brillo ( {value} x)",
|
||||||
"video_brightness_title": "Brillo",
|
"video_brightness_title": "Brillo",
|
||||||
"video_contrast_description": "Nivel de contraste ( {value} x)",
|
"video_contrast_description": "Nivel de contraste ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Vous avez modifié avec succès le mot de passe de protection de votre appareil local. N'oubliez pas de le mémoriser pour y accéder ultérieurement.",
|
"local_auth_success_password_updated_description": "Vous avez modifié avec succès le mot de passe de protection de votre appareil local. N'oubliez pas de le mémoriser pour y accéder ultérieurement.",
|
||||||
"local_auth_success_password_updated_title": "Mot de passe mis à jour avec succès",
|
"local_auth_success_password_updated_title": "Mot de passe mis à jour avec succès",
|
||||||
"local_auth_update_password_button": "Mettre à jour le mot de passe",
|
"local_auth_update_password_button": "Mettre à jour le mot de passe",
|
||||||
|
"locale_auto": "Auto",
|
||||||
|
"locale_change_success": "La langue a été modifiée avec succès en {locale}",
|
||||||
|
"locale_da": "danois",
|
||||||
|
"locale_de": "Allemand",
|
||||||
|
"locale_en": "Anglais",
|
||||||
|
"locale_es": "Espagnol",
|
||||||
|
"locale_fr": "Français",
|
||||||
|
"locale_it": "italien",
|
||||||
|
"locale_nb": "Norvégien (bokmål)",
|
||||||
|
"locale_sv": "suédois",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Se connecter",
|
"log_in": "Se connecter",
|
||||||
"log_out": "Se déconnecter",
|
"log_out": "Se déconnecter",
|
||||||
"logged_in_as": "Connecté en tant que",
|
"logged_in_as": "Connecté en tant que",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Déconnecté",
|
"usb_state_disconnected": "Déconnecté",
|
||||||
"usb_state_low_power_mode": "Mode basse consommation",
|
"usb_state_low_power_mode": "Mode basse consommation",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Sélectionnez la langue à utiliser dans l'interface utilisateur de JetKVM",
|
||||||
|
"user_interface_language_title": "Langue de l'interface",
|
||||||
"video_brightness_description": "Niveau de luminosité ( {value} x)",
|
"video_brightness_description": "Niveau de luminosité ( {value} x)",
|
||||||
"video_brightness_title": "Luminosité",
|
"video_brightness_title": "Luminosité",
|
||||||
"video_contrast_description": "Niveau de contraste ( {value} x)",
|
"video_contrast_description": "Niveau de contraste ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Hai modificato correttamente la password di protezione del tuo dispositivo locale. Assicurati di ricordare la nuova password per gli accessi futuri.",
|
"local_auth_success_password_updated_description": "Hai modificato correttamente la password di protezione del tuo dispositivo locale. Assicurati di ricordare la nuova password per gli accessi futuri.",
|
||||||
"local_auth_success_password_updated_title": "Password aggiornata con successo",
|
"local_auth_success_password_updated_title": "Password aggiornata con successo",
|
||||||
"local_auth_update_password_button": "Aggiorna password",
|
"local_auth_update_password_button": "Aggiorna password",
|
||||||
|
"locale_auto": "Auto",
|
||||||
|
"locale_change_success": "Lingua modificata correttamente in {locale}",
|
||||||
|
"locale_da": "Danese",
|
||||||
|
"locale_de": "Tedesco",
|
||||||
|
"locale_en": "Inglese",
|
||||||
|
"locale_es": "Spagnolo",
|
||||||
|
"locale_fr": "Francese",
|
||||||
|
"locale_it": "Italiano",
|
||||||
|
"locale_nb": "Norvegese (bokmål)",
|
||||||
|
"locale_sv": "Svedese",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Login",
|
"log_in": "Login",
|
||||||
"log_out": "Disconnetti",
|
"log_out": "Disconnetti",
|
||||||
"logged_in_as": "Accedi come",
|
"logged_in_as": "Accedi come",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Disconnesso",
|
"usb_state_disconnected": "Disconnesso",
|
||||||
"usb_state_low_power_mode": "Modalità a basso consumo",
|
"usb_state_low_power_mode": "Modalità a basso consumo",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Seleziona la lingua da utilizzare nell'interfaccia utente JetKVM",
|
||||||
|
"user_interface_language_title": "Lingua dell'interfaccia",
|
||||||
"video_brightness_description": "Livello di luminosità ( {value} x)",
|
"video_brightness_description": "Livello di luminosità ( {value} x)",
|
||||||
"video_brightness_title": "Luminosità",
|
"video_brightness_title": "Luminosità",
|
||||||
"video_contrast_description": "Livello di contrasto ( {value} x)",
|
"video_contrast_description": "Livello di contrasto ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Du har endret passordet for beskyttelse av den lokale enheten. Husk det nye passordet for fremtidig tilgang.",
|
"local_auth_success_password_updated_description": "Du har endret passordet for beskyttelse av den lokale enheten. Husk det nye passordet for fremtidig tilgang.",
|
||||||
"local_auth_success_password_updated_title": "Passord oppdatert",
|
"local_auth_success_password_updated_title": "Passord oppdatert",
|
||||||
"local_auth_update_password_button": "Oppdater passord",
|
"local_auth_update_password_button": "Oppdater passord",
|
||||||
|
"locale_auto": "Bil",
|
||||||
|
"locale_change_success": "Språket er endret til {locale}",
|
||||||
|
"locale_da": "Dansk",
|
||||||
|
"locale_de": "Tysk",
|
||||||
|
"locale_en": "Engelsk",
|
||||||
|
"locale_es": "Spansk",
|
||||||
|
"locale_fr": "Fransk",
|
||||||
|
"locale_it": "Italiensk",
|
||||||
|
"locale_nb": "Norsk (bokmål)",
|
||||||
|
"locale_sv": "Svensk",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Logg inn",
|
"log_in": "Logg inn",
|
||||||
"log_out": "Logg ut",
|
"log_out": "Logg ut",
|
||||||
"logged_in_as": "Logget inn som",
|
"logged_in_as": "Logget inn som",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Frakoblet",
|
"usb_state_disconnected": "Frakoblet",
|
||||||
"usb_state_low_power_mode": "Lavstrømsmodus",
|
"usb_state_low_power_mode": "Lavstrømsmodus",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Velg språket som skal brukes i JetKVM-brukergrensesnittet",
|
||||||
|
"user_interface_language_title": "Grensesnittspråk",
|
||||||
"video_brightness_description": "Lysstyrkenivå ( {value} x)",
|
"video_brightness_description": "Lysstyrkenivå ( {value} x)",
|
||||||
"video_brightness_title": "Lysstyrke",
|
"video_brightness_title": "Lysstyrke",
|
||||||
"video_contrast_description": "Kontrastnivå ( {value} x)",
|
"video_contrast_description": "Kontrastnivå ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "Du har ändrat ditt lösenord för lokal enhetsskydd. Se till att komma ihåg ditt nya lösenord för framtida åtkomst.",
|
"local_auth_success_password_updated_description": "Du har ändrat ditt lösenord för lokal enhetsskydd. Se till att komma ihåg ditt nya lösenord för framtida åtkomst.",
|
||||||
"local_auth_success_password_updated_title": "Lösenordet har uppdaterats",
|
"local_auth_success_password_updated_title": "Lösenordet har uppdaterats",
|
||||||
"local_auth_update_password_button": "Uppdatera lösenord",
|
"local_auth_update_password_button": "Uppdatera lösenord",
|
||||||
|
"locale_auto": "Bil",
|
||||||
|
"locale_change_success": "Språket har ändrats till {locale}",
|
||||||
|
"locale_da": "Danska",
|
||||||
|
"locale_de": "Deutsch",
|
||||||
|
"locale_en": "Engelska",
|
||||||
|
"locale_es": "Spanska",
|
||||||
|
"locale_fr": "Franska",
|
||||||
|
"locale_it": "italiensk",
|
||||||
|
"locale_nb": "Norska (bokmål)",
|
||||||
|
"locale_sv": "Svenska",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "Logga in",
|
"log_in": "Logga in",
|
||||||
"log_out": "Logga ut",
|
"log_out": "Logga ut",
|
||||||
"logged_in_as": "Inloggad som",
|
"logged_in_as": "Inloggad som",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "Osammanhängande",
|
"usb_state_disconnected": "Osammanhängande",
|
||||||
"usb_state_low_power_mode": "Lågströmsläge",
|
"usb_state_low_power_mode": "Lågströmsläge",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "Välj språket som ska användas i JetKVM-användargränssnittet",
|
||||||
|
"user_interface_language_title": "Gränssnittsspråk",
|
||||||
"video_brightness_description": "Ljusstyrka ( {value} x)",
|
"video_brightness_description": "Ljusstyrka ( {value} x)",
|
||||||
"video_brightness_title": "Ljusstyrka",
|
"video_brightness_title": "Ljusstyrka",
|
||||||
"video_contrast_description": "Kontrastnivå ( {value} x)",
|
"video_contrast_description": "Kontrastnivå ( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -395,6 +395,17 @@
|
||||||
"local_auth_success_password_updated_description": "您已成功更改本地设备保护密码。请务必记住新密码,以便日后访问。",
|
"local_auth_success_password_updated_description": "您已成功更改本地设备保护密码。请务必记住新密码,以便日后访问。",
|
||||||
"local_auth_success_password_updated_title": "密码更新成功",
|
"local_auth_success_password_updated_title": "密码更新成功",
|
||||||
"local_auth_update_password_button": "更新密码",
|
"local_auth_update_password_button": "更新密码",
|
||||||
|
"locale_auto": "汽车",
|
||||||
|
"locale_change_success": "语言已成功更改为{locale}",
|
||||||
|
"locale_da": "丹麦语",
|
||||||
|
"locale_de": "德语",
|
||||||
|
"locale_en": "英语",
|
||||||
|
"locale_es": "西班牙语",
|
||||||
|
"locale_fr": "法语",
|
||||||
|
"locale_it": "意大利语",
|
||||||
|
"locale_nb": "挪威语(博克马尔语)",
|
||||||
|
"locale_sv": "瑞典语",
|
||||||
|
"locale_zh": "中文 (简体)",
|
||||||
"log_in": "登录",
|
"log_in": "登录",
|
||||||
"log_out": "登出",
|
"log_out": "登出",
|
||||||
"logged_in_as": "登录身份",
|
"logged_in_as": "登录身份",
|
||||||
|
|
@ -748,6 +759,8 @@
|
||||||
"usb_state_disconnected": "断开连接",
|
"usb_state_disconnected": "断开连接",
|
||||||
"usb_state_low_power_mode": "低功耗模式",
|
"usb_state_low_power_mode": "低功耗模式",
|
||||||
"usb": "USB",
|
"usb": "USB",
|
||||||
|
"user_interface_language_description": "选择 JetKVM 用户界面使用的语言",
|
||||||
|
"user_interface_language_title": "界面语言",
|
||||||
"video_brightness_description": "亮度级别( {value} x)",
|
"video_brightness_description": "亮度级别( {value} x)",
|
||||||
"video_brightness_title": "亮度",
|
"video_brightness_title": "亮度",
|
||||||
"video_contrast_description": "对比度级别( {value} x)",
|
"video_contrast_description": "对比度级别( {value} x)",
|
||||||
|
|
|
||||||
|
|
@ -244,6 +244,7 @@ export interface MouseMove {
|
||||||
y: number;
|
y: number;
|
||||||
buttons: number;
|
buttons: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MouseState {
|
export interface MouseState {
|
||||||
mouseX: number;
|
mouseX: number;
|
||||||
mouseY: number;
|
mouseY: number;
|
||||||
|
|
@ -347,8 +348,10 @@ export interface SettingsState {
|
||||||
// Video enhancement settings
|
// Video enhancement settings
|
||||||
videoSaturation: number;
|
videoSaturation: number;
|
||||||
setVideoSaturation: (value: number) => void;
|
setVideoSaturation: (value: number) => void;
|
||||||
|
|
||||||
videoBrightness: number;
|
videoBrightness: number;
|
||||||
setVideoBrightness: (value: number) => void;
|
setVideoBrightness: (value: number) => void;
|
||||||
|
|
||||||
videoContrast: number;
|
videoContrast: number;
|
||||||
setVideoContrast: (value: number) => void;
|
setVideoContrast: (value: number) => void;
|
||||||
}
|
}
|
||||||
|
|
@ -392,8 +395,10 @@ export const useSettingsStore = create(
|
||||||
// Video enhancement settings with default values (1.0 = normal)
|
// Video enhancement settings with default values (1.0 = normal)
|
||||||
videoSaturation: 1.0,
|
videoSaturation: 1.0,
|
||||||
setVideoSaturation: (value: number) => set({ videoSaturation: value }),
|
setVideoSaturation: (value: number) => set({ videoSaturation: value }),
|
||||||
|
|
||||||
videoBrightness: 1.0,
|
videoBrightness: 1.0,
|
||||||
setVideoBrightness: (value: number) => set({ videoBrightness: value }),
|
setVideoBrightness: (value: number) => set({ videoBrightness: value }),
|
||||||
|
|
||||||
videoContrast: 1.0,
|
videoContrast: 1.0,
|
||||||
setVideoContrast: (value: number) => set({ videoContrast: value }),
|
setVideoContrast: (value: number) => set({ videoContrast: value }),
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect, useMemo } from "react";
|
||||||
|
|
||||||
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
|
import { JsonRpcResponse, useJsonRpc } from "@hooks/useJsonRpc";
|
||||||
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
|
import { useDeviceUiNavigation } from "@hooks/useAppNavigation";
|
||||||
import { useDeviceStore } from "@hooks/stores";
|
import { useDeviceStore } from "@hooks/stores";
|
||||||
import { Button } from "@components/Button";
|
import { Button } from "@components/Button";
|
||||||
import Checkbox from "@components/Checkbox";
|
import Checkbox from "@components/Checkbox";
|
||||||
|
import { SelectMenuBasic } from "@components/SelectMenuBasic";
|
||||||
import { SettingsItem } from "@components/SettingsItem";
|
import { SettingsItem } from "@components/SettingsItem";
|
||||||
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
import { SettingsPageHeader } from "@components/SettingsPageheader";
|
||||||
import notifications from "@/notifications";
|
import notifications from "@/notifications";
|
||||||
|
import { getLocale, setLocale, locales, baseLocale } from '@localizations/runtime.js';
|
||||||
import { m } from "@localizations/messages.js";
|
import { m } from "@localizations/messages.js";
|
||||||
|
import { deleteCookie, map_locale_code_to_name } from "@/utils";
|
||||||
|
|
||||||
export default function SettingsGeneralRoute() {
|
export default function SettingsGeneralRoute() {
|
||||||
const { send } = useJsonRpc();
|
const { send } = useJsonRpc();
|
||||||
|
|
@ -40,6 +43,37 @@ export default function SettingsGeneralRoute() {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const [currentLocale, setCurrentLocale] = useState(getLocale());
|
||||||
|
|
||||||
|
const localeOptions = useMemo(() => {
|
||||||
|
return ["", ...locales]
|
||||||
|
.map((code) => {
|
||||||
|
const [localizedName, nativeName] = map_locale_code_to_name(code);
|
||||||
|
// don't repeat the name if it's the same in both locales (or blank)
|
||||||
|
const label = nativeName && nativeName !== localizedName ? `${localizedName} - ${nativeName}` : localizedName;
|
||||||
|
return { value: code, label: label }
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleLocaleChange = (newLocale: string) => {
|
||||||
|
if (newLocale === currentLocale) return;
|
||||||
|
|
||||||
|
let validLocale = newLocale as typeof locales[number];
|
||||||
|
|
||||||
|
if (newLocale !== "") {
|
||||||
|
if (!locales.includes(validLocale)) {
|
||||||
|
validLocale = baseLocale;
|
||||||
|
}
|
||||||
|
|
||||||
|
setLocale(validLocale); // tell the i18n system to change locale
|
||||||
|
} else {
|
||||||
|
deleteCookie("JETKVM_LOCALE", "", "/"); // delete the cookie that the i18n system uses to store the locale
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentLocale(validLocale);
|
||||||
|
notifications.success(m.locale_change_success({ locale: validLocale || m.locale_auto() }));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<SettingsPageHeader
|
<SettingsPageHeader
|
||||||
|
|
@ -49,6 +83,20 @@ export default function SettingsGeneralRoute() {
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-4 pb-2">
|
<div className="space-y-4 pb-2">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<SettingsItem
|
||||||
|
title={m.user_interface_language_title()}
|
||||||
|
description={m.user_interface_language_description()}
|
||||||
|
>
|
||||||
|
<SelectMenuBasic
|
||||||
|
size="SM"
|
||||||
|
label=""
|
||||||
|
value={currentLocale}
|
||||||
|
options={localeOptions}
|
||||||
|
onChange={e => { handleLocaleChange(e.target.value); }}
|
||||||
|
/>
|
||||||
|
</SettingsItem>
|
||||||
|
</div>
|
||||||
<div className="mt-2 flex items-center justify-between gap-x-2">
|
<div className="mt-2 flex items-center justify-between gap-x-2">
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title={m.general_check_for_updates()}
|
title={m.general_check_for_updates()}
|
||||||
|
|
@ -82,7 +130,6 @@ export default function SettingsGeneralRoute() {
|
||||||
/>
|
/>
|
||||||
</SettingsItem>
|
</SettingsItem>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-2 flex items-center justify-between gap-x-2">
|
<div className="mt-2 flex items-center justify-between gap-x-2">
|
||||||
<SettingsItem
|
<SettingsItem
|
||||||
title={m.general_reboot_device()}
|
title={m.general_reboot_device()}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { KeySequence } from "@hooks/stores";
|
import { KeySequence } from "@hooks/stores";
|
||||||
import { getLocale } from '@localizations/runtime.js';
|
import { getLocale } from '@localizations/runtime.js';
|
||||||
|
import { m } from "@localizations/messages.js";
|
||||||
|
|
||||||
export const formatters = {
|
export const formatters = {
|
||||||
date: (date: Date, options?: Intl.DateTimeFormatOptions) =>
|
date: (date: Date, options?: Intl.DateTimeFormatOptions) =>
|
||||||
|
|
@ -254,3 +255,29 @@ export function normalizeSortOrders(macros: KeySequence[]): KeySequence[] {
|
||||||
sortOrder: index + 1,
|
sortOrder: index + 1,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function map_locale_code_to_name(locale: string): [string, string] {
|
||||||
|
// the first is the name in the current app locale (e.g. Inglese),
|
||||||
|
// the second is the name in the language of the locale itself (e.g. English)
|
||||||
|
switch (locale) {
|
||||||
|
case '': return [m.locale_auto(), ""];
|
||||||
|
case 'en': return [m.locale_en(), m.locale_en({}, { locale })];
|
||||||
|
case 'da': return [m.locale_da(), m.locale_da({}, { locale })];
|
||||||
|
case 'de': return [m.locale_de(), m.locale_de({}, { locale })];
|
||||||
|
case 'es': return [m.locale_es(), m.locale_es({}, { locale })];
|
||||||
|
case 'fr': return [m.locale_fr(), m.locale_fr({}, { locale })];
|
||||||
|
case 'it': return [m.locale_it(), m.locale_it({}, { locale })];
|
||||||
|
case 'nb': return [m.locale_nb(), m.locale_nb({}, { locale })];
|
||||||
|
case 'sv': return [m.locale_sv(), m.locale_sv({}, { locale })];
|
||||||
|
case 'zh': return [m.locale_zh(), m.locale_zh({}, { locale })];
|
||||||
|
default: return [locale, ""];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteCookie(name: string, domain?: string, path = "/") {
|
||||||
|
const domainPart = domain ? `; domain=${domain}` : "";
|
||||||
|
// max-age=0 removes the cookie immediately in modern browsers
|
||||||
|
document.cookie = `${name}=; path=${path}; max-age=0${domainPart}`;
|
||||||
|
// fallback: set an expires in the past for older agents
|
||||||
|
document.cookie = `${name}=; path=${path}; expires=Thu, 01 Jan 1970 00:00:00 GMT${domainPart}`;
|
||||||
|
}
|
||||||
|
|
@ -33,8 +33,7 @@ export default defineConfig(({ mode, command }) => {
|
||||||
outdir: "./localization/paraglide",
|
outdir: "./localization/paraglide",
|
||||||
outputStructure: 'message-modules',
|
outputStructure: 'message-modules',
|
||||||
cookieName: 'JETKVM_LOCALE',
|
cookieName: 'JETKVM_LOCALE',
|
||||||
localStorageKey: 'JETKVM_LOCALE',
|
strategy: ['cookie', 'preferredLanguage', 'baseLocale'],
|
||||||
strategy: ['cookie', 'localStorage', 'preferredLanguage', 'baseLocale'],
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
103
video.go
103
video.go
|
|
@ -1,10 +1,22 @@
|
||||||
package kvm
|
package kvm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/jetkvm/kvm/internal/native"
|
"github.com/jetkvm/kvm/internal/native"
|
||||||
)
|
)
|
||||||
|
|
||||||
var lastVideoState native.VideoState
|
var (
|
||||||
|
lastVideoState native.VideoState
|
||||||
|
videoSleepModeCtx context.Context
|
||||||
|
videoSleepModeCancel context.CancelFunc
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultVideoSleepModeDuration = 1 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
func triggerVideoStateUpdate() {
|
func triggerVideoStateUpdate() {
|
||||||
go func() {
|
go func() {
|
||||||
|
|
@ -17,3 +29,92 @@ func triggerVideoStateUpdate() {
|
||||||
func rpcGetVideoState() (native.VideoState, error) {
|
func rpcGetVideoState() (native.VideoState, error) {
|
||||||
return lastVideoState, nil
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
38
webrtc.go
38
webrtc.go
|
|
@ -39,6 +39,34 @@ type Session struct {
|
||||||
keysDownStateQueue chan usbgadget.KeysDownState
|
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() {
|
func (s *Session) resetKeepAliveTime() {
|
||||||
s.keepAliveJitterLock.Lock()
|
s.keepAliveJitterLock.Lock()
|
||||||
defer s.keepAliveJitterLock.Unlock()
|
defer s.keepAliveJitterLock.Unlock()
|
||||||
|
|
@ -312,9 +340,8 @@ func newSession(config SessionConfig) (*Session, error) {
|
||||||
if connectionState == webrtc.ICEConnectionStateConnected {
|
if connectionState == webrtc.ICEConnectionStateConnected {
|
||||||
if !isConnected {
|
if !isConnected {
|
||||||
isConnected = true
|
isConnected = true
|
||||||
actionSessions++
|
|
||||||
onActiveSessionsChanged()
|
onActiveSessionsChanged()
|
||||||
if actionSessions == 1 {
|
if incrActiveSessions() == 1 {
|
||||||
onFirstSessionConnected()
|
onFirstSessionConnected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -353,9 +380,8 @@ func newSession(config SessionConfig) (*Session, error) {
|
||||||
}
|
}
|
||||||
if isConnected {
|
if isConnected {
|
||||||
isConnected = false
|
isConnected = false
|
||||||
actionSessions--
|
|
||||||
onActiveSessionsChanged()
|
onActiveSessionsChanged()
|
||||||
if actionSessions == 0 {
|
if decrActiveSessions() == 0 {
|
||||||
onLastSessionDisconnected()
|
onLastSessionDisconnected()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -364,16 +390,16 @@ func newSession(config SessionConfig) (*Session, error) {
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var actionSessions = 0
|
|
||||||
|
|
||||||
func onActiveSessionsChanged() {
|
func onActiveSessionsChanged() {
|
||||||
requestDisplayUpdate(true, "active_sessions_changed")
|
requestDisplayUpdate(true, "active_sessions_changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func onFirstSessionConnected() {
|
func onFirstSessionConnected() {
|
||||||
_ = nativeInstance.VideoStart()
|
_ = nativeInstance.VideoStart()
|
||||||
|
stopVideoSleepModeTicker()
|
||||||
}
|
}
|
||||||
|
|
||||||
func onLastSessionDisconnected() {
|
func onLastSessionDisconnected() {
|
||||||
_ = nativeInstance.VideoStop()
|
_ = nativeInstance.VideoStop()
|
||||||
|
startVideoSleepModeTicker()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue